diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8f15cb2 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,30 @@ +**/.classpath +**/.dockerignore +**/.env +#**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +!**/.gitignore +!.git/HEAD +!.git/config +!.git/packed-refs +!.git/refs/heads/** \ No newline at end of file diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..dd28f5c --- /dev/null +++ b/.drone.yml @@ -0,0 +1,93 @@ +kind: pipeline +type: docker +name: Build and test + +trigger: + event: + include: + - push + - pull_request + - custom + +steps: + - name: compile (.NET) + image: mcr.microsoft.com/dotnet/sdk:9.0-alpine + environment: + CI_WORKSPACE: "/drone/src" + commands: + - dotnet build ./vegasco-server.slnx + + - name: compile (Angular) + image: node:lts + commands: + - npm install -g pnpm + - cd src/Vegasco-Web + - pnpm install + - pnpm build + + - name: test + image: quay.io/testcontainers/dind-drone-plugin + environment: + CI_WORKSPACE: "/drone/src" + settings: + build_image: mcr.microsoft.com/dotnet/sdk:9.0-alpine + cmd: + - dotnet test --no-build + volumes: + - name: dockersock + path: /var/run + depends_on: + - compile (.NET) + + - name: docker build and push + image: docker:24.0.7 + commands: + - docker build . -t $docker_registry$docker_repo:$DRONE_BRANCH + - echo $docker_password | docker login --username $docker_username --password-stdin $docker_registry + - docker push $docker_registry$docker_repo:$DRONE_BRANCH + environment: + docker_username: + from_secret: docker_username + docker_password: + from_secret: docker_password + docker_repo: + from_secret: docker_repo + docker_registry: + from_secret: docker_registry + volumes: + - name: dockersock + path: /var/run + when: + branch: + - main + depends_on: + - compile (.NET) + - test + + - name: Telegram notification + image: appleboy/drone-telegram + settings: + token: + from_secret: telegram_token + to: + from_secret: telegram_user_id + when: + status: + - failure + depends_on: + - compile (.NET) + - compile (Angular) + - test + - docker build and push + +services: + - name: docker + image: docker:dind + privileged: true + volumes: + - name: dockersock + path: /var/run + +volumes: + - name: dockersock + temp: { } diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5055012 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,271 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = tab +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = false:suggestion +dotnet_style_allow_statement_immediately_after_block_experimental = false:suggestion + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true + +# Modifier preferences +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# Code-block preferences +csharp_prefer_braces = true:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = file_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_top_level_statements = true:silent + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = warning +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.private_or_internal_field_should_be_underscore.severity = warning +dotnet_naming_rule.private_or_internal_field_should_be_underscore.symbols = private_or_internal_field +dotnet_naming_rule.private_or_internal_field_should_be_underscore.style = underscore + +dotnet_naming_rule.private_or_internal_static_field_should_be_sunderscore.severity = warning +dotnet_naming_rule.private_or_internal_static_field_should_be_sunderscore.symbols = private_or_internal_static_field +dotnet_naming_rule.private_or_internal_static_field_should_be_sunderscore.style = sunderscore + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected +dotnet_naming_symbols.private_or_internal_field.required_modifiers = + +dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private, private_protected +dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = static + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.underscore.required_prefix = _ +dotnet_naming_style.underscore.required_suffix = +dotnet_naming_style.underscore.word_separator = +dotnet_naming_style.underscore.capitalization = camel_case + +dotnet_naming_style.sunderscore.required_prefix = s_ +dotnet_naming_style.sunderscore.required_suffix = +dotnet_naming_style.sunderscore.word_separator = +dotnet_naming_style.sunderscore.capitalization = camel_case + +[*.{cs,vb}] +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..104b544 --- /dev/null +++ b/.gitignore @@ -0,0 +1,484 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from `dotnet new gitignore` + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/Create-Migration.ps1 b/Create-Migration.ps1 new file mode 100644 index 0000000..03f6af2 --- /dev/null +++ b/Create-Migration.ps1 @@ -0,0 +1,2 @@ +dotnet ef migrations add $args[0] --project .\src\Vegasco.Server.Api\Vegasco.Server.Api.csproj --output-dir Persistence/Migrations +dotnet ef migrations script --idempotent --project .\src\Vegasco.Server.Api\Vegasco.Server.Api.csproj --output ./src/Vegasco.Server.Api/migrations/migration.sql diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..5023fe7 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,9 @@ + + + + + all + 3.6.141 + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a72abbb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base +WORKDIR /app +EXPOSE 8080 +EXPOSE 8081 +RUN apt-get update && apt-get install -y curl +USER app + +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["src/Vegasco.Server.Api/Vegasco.Server.Api.csproj", "src/Vegasco.Server.Api/"] +RUN dotnet restore "./src/Vegasco.Server.Api/Vegasco.Server.Api.csproj" +COPY . . +WORKDIR "/src/src/Vegasco.Server.Api" +RUN dotnet build "./Vegasco.Server.Api.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./Vegasco.Server.Api.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +HEALTHCHECK --interval=20s --timeout=1s --start-period=10s --retries=3 CMD curl --fail http://localhost:8080/health || exit 1 +ENTRYPOINT ["dotnet", "Vegasco.Server.Api.dll"] \ No newline at end of file diff --git a/README.md b/README.md index 0845b31..13a6fa5 100644 --- a/README.md +++ b/README.md @@ -1 +1,69 @@ -# vegasco-server \ No newline at end of file +# Vegasco Server + +Vegasco (**VE**hicle **GAS** **CO**nsumption) application. + +Includes the backend (`src/Vegasco.Server.Api`) and the frontend (`src/Vegasco-Web`). Utilizes [Aspire](https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview). + +## Getting Started + +### Configuration + +| Configuration | Description | Default | Required | +|--------------------------|---------------------------------------------------------------------------------------------------------------|------------------------------------------------------------|----------| +| JWT:MetadataUrl | The oidc meta data url | - | true | +| JWT:ValidAudience | The valid audience of the JWT token. | - | true | +| JWT:NameClaimType | The claim type of the user's name claim. For keycloak, using `preferred_username` is often the better choice. | http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name | false | +| JWT:AllowHttpMetadataUrl | Whether to allow the meta data url to have http as protocol. Always true when `ASPNETCORE_ENVIRONMENT=true` | false | false | + +The application uses the prefix `Vegasco_` for environment variable names. The prefix is removed when the application reads the environment variables and duplicate entries are overwritten by the environment variables. + +Example: + +- `foo=bar1` +- `Vegasco_foo=bar2` + +Results in: + +- `foo=bar2` +- `Vegasco_foo=bar2` + +Configuration hierarchy in environment variables is usually denoted using a colon (`:`). But because on some systems the colon character is a reserved character, you can use a double underscore (`__`) as an alternative. The application will replace the double underscore with a colon when reading the environment variables. + +Example: + +The environment variable `foo__bar=value` (as well as `Vegasco_foo__bar=value`) will be converted to `foo:bar=value` in the application. + +### Configuration examples + +As environment variables: + +```env +Vegasco_JWT__Authority=https://example.authority.com +Vegasco_JWT__Audience=example-audience +Vegasco_JWT__Issuer=https://example.authority.com/realms/example-realm/ +Vegasco_JWT__NameClaimType=preferred_username +``` + +As appsettings.json (or a environment specific appsettings.*.json): + +**Note: the `Vegasco_` prefix is only for environment variables** + +```json +{ + "JWT": { + "Authority": "https://example.authority.com/realms/example-realm", + "Audience": "example-audience", + "Issuer": "https://example.authority.com/realms/example-realm/", + "NameClaimType": "preferred_username" + } +} +``` + +### Running the application + +The solution uses Aspire to orchestrate the application. Specifically, it introduces sensible service defaults, including but not limited to OpenTelemetry, +creates a Postgres database as a docker container, and starts the Api with the correct configuration to communicate with the database. + +Ensure you have an identity provider set up, for example Keycloak, and configured the relevant options described above. + +Then, to run the application, ensure you have Docker running, then run either the `http` or `https` launch profile of the `Vegasco.Server.AppHost` project. diff --git a/Run-PostgresDb.ps1 b/Run-PostgresDb.ps1 new file mode 100644 index 0000000..f96dca7 --- /dev/null +++ b/Run-PostgresDb.ps1 @@ -0,0 +1 @@ +docker run -d -p 5432:5432 --restart always --name vegasco-test-db -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres postgres:16.3-alpine \ No newline at end of file diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..432b482 --- /dev/null +++ b/nuget.config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/UploadData/Program.cs b/src/UploadData/Program.cs new file mode 100644 index 0000000..88d3719 --- /dev/null +++ b/src/UploadData/Program.cs @@ -0,0 +1,2000 @@ +using System.Net; +using System.Net.Http.Headers; +using System.Net.Http.Json; +using System.Text.Json; +using System.Text.Json.Serialization; +using Vegasco.Server.Api.Cars; +using Vegasco.Server.Api.Consumptions; + +const string CARS = """ + [ + { + "id": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "name": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98" + }, + { + "id": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "name": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543" + }, + { + "id": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "name": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98" + }, + { + "id": "fcb76d13-b02d-43c5-bc1d-2b16ad7bd2bc", + "name": "Space Star", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98" + } + ] + """; + +const string CONSUMPTION_ENTRIES = """ + [ + { + "id": "01f73963-4954-44f5-9a2d-c4bffee5b9c8", + "dateTime": "2024-04-13T14:28:21.673277Z", + "distance": 25548, + "amount": 41.77, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "0307ed64-576b-4c1d-83bf-458a2f5b5818", + "dateTime": "2023-04-24T14:57:49.570989Z", + "distance": 1471, + "amount": 43.54, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "0489d64c-e96a-4836-bfb2-d921cb7ee480", + "dateTime": "2024-03-02T22:01:34.06098Z", + "distance": 23040, + "amount": 38.96, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "0a097b4f-acd4-4821-8be3-2fe3f4aeb0ee", + "dateTime": "2023-10-06T13:57:39.833304Z", + "distance": 12749, + "amount": 38.9, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "0dcbf410-f107-4c56-8914-b21217e6bfdf", + "dateTime": "2023-07-23T17:31:31.964664Z", + "distance": 7098, + "amount": 41.31, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "119a788f-5cab-4678-998c-7cd591e3a6be", + "dateTime": "2024-01-17T19:56:19.621374Z", + "distance": 18816, + "amount": 38.52, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "13b5069b-8607-465f-84ab-5dc7cdd571c5", + "dateTime": "2024-03-19T15:36:20.770462Z", + "distance": 24146, + "amount": 34.34, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "1706ca36-8005-4ad8-8e86-9fe3c9a82b65", + "dateTime": "2023-04-12T22:12:45.342117Z", + "distance": 775, + "amount": 46.72, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "1b9e379b-c9f4-4d63-8269-04c9be526621", + "dateTime": "2023-05-17T14:48:28.58834Z", + "distance": 2654, + "amount": 39.76, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "1d8a511d-9ed5-40e3-a7d4-5b3e750e69cb", + "dateTime": "2023-06-12T13:28:50.3917Z", + "distance": 4890, + "amount": 43.25, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "1edd2675-fcf7-44f4-8290-6ebbf06c7f01", + "dateTime": "2023-05-23T11:04:35.252603Z", + "distance": 3384, + "amount": 44.1, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "27495889-5432-4ea8-9a07-2abe26e5de44", + "dateTime": "2024-04-04T20:17:05.76651Z", + "distance": 24841, + "amount": 44.58, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "302eb64c-0d03-4a7b-ba44-f92d12d1ad42", + "dateTime": "2023-08-01T15:12:40.933346Z", + "distance": 7811, + "amount": 44.17, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "37339c4b-a7ee-4035-8f26-71e14bc088ff", + "dateTime": "2023-09-07T06:28:18.969443Z", + "distance": 9838, + "amount": 39.77, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "4172e4ab-2e1f-4d5f-9c50-d0b5c91fec45", + "dateTime": "2024-03-10T16:18:50.303989Z", + "distance": 23615, + "amount": 36.26, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "469f4749-f51b-4419-a44c-9a86de744ac1", + "dateTime": "2023-08-14T20:16:43.98009Z", + "distance": 8549, + "amount": 43.66, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "52a2470e-3d51-464e-955c-f961da56fa8f", + "dateTime": "2023-11-05T09:27:06.64729Z", + "distance": 14196, + "amount": 41.89, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "571e95df-028a-4611-ad26-6f0aef9f31a2", + "dateTime": "2024-04-20T16:03:06.002924Z", + "distance": 26044, + "amount": 32.28, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "6307ae2b-987f-4943-bccc-5b2d62a5aba7", + "dateTime": "2023-06-03T14:17:12.844994Z", + "distance": 4139, + "amount": 43.35, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "65ccdc6b-8bd5-4ec1-bf4a-41353a9af7de", + "dateTime": "2024-02-10T09:02:56.546327Z", + "distance": 21230, + "amount": 42.3, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "6c1a9bbc-4903-4e23-97e7-6a1da48a910f", + "dateTime": "2023-11-29T21:13:32.703526Z", + "distance": 15536, + "amount": 41.26, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "733f6210-1091-4c0b-a2a4-5036870cc71f", + "dateTime": "2023-09-21T08:23:27.495244Z", + "distance": 11244, + "amount": 41.27, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "7e343ace-923a-4f90-af71-367fa94bedbc", + "dateTime": "2023-12-15T11:13:28.669293Z", + "distance": 16934, + "amount": 45.26, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "7f43d623-41a6-4b47-861c-27f7835e48dd", + "dateTime": "2024-01-26T17:41:23.867847Z", + "distance": 19892, + "amount": 29.1, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "8ddc6824-2593-4a09-a9d5-3657113da441", + "dateTime": "2023-07-11T10:20:32.354789Z", + "distance": 6362, + "amount": 41.45, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "92137d35-0c68-495f-83d8-4426f39d69bd", + "dateTime": "2023-09-14T14:18:46.345989Z", + "distance": 10510, + "amount": 43.46, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "a015b057-0786-419b-936e-ba20c9575d0e", + "dateTime": "2023-03-31T17:38:54.165672Z", + "distance": 30, + "amount": 40.67, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "a28d0af6-123e-4f9f-8ac3-436605f3f399", + "dateTime": "2023-05-07T16:14:14.792537Z", + "distance": 2017, + "amount": 36.52, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "a308d158-74db-4623-9724-f352792583a6", + "dateTime": "2023-09-27T12:21:51.980811Z", + "distance": 11727, + "amount": 27.12, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "a4f5f297-ba69-4473-917c-2c2d5aca6d7a", + "dateTime": "2024-01-31T15:41:40.537012Z", + "distance": 20588, + "amount": 41.92, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "c1260f03-48da-448c-9166-2a384249a42e", + "dateTime": "2023-12-25T13:00:29.83254Z", + "distance": 17529, + "amount": 37.5, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "c5edac65-cc7c-4888-af86-1a0db0ae8f87", + "dateTime": "2024-02-26T05:46:02.099197Z", + "distance": 22456, + "amount": 37.64, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "d5005e8f-1159-43ac-ad1e-67bbd7db99b3", + "dateTime": "2024-01-08T11:32:57.203414Z", + "distance": 18232, + "amount": 43.01, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "d8732260-207b-4cfa-beae-0f1123706817", + "dateTime": "2024-01-24T20:46:00.939327Z", + "distance": 19443, + "amount": 38.13, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "e25938ad-eb6a-4aac-8053-75ee37ab655b", + "dateTime": "2024-02-18T17:42:19.80403Z", + "distance": 21905, + "amount": 41.17, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "e8b7c835-8fc0-44df-8d35-ae539d612eb3", + "dateTime": "2024-04-29T14:51:04.272933Z", + "distance": 26729, + "amount": 44.2, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "eaae0fb6-1817-400d-85eb-347524138a15", + "dateTime": "2023-10-02T11:20:58.79843Z", + "distance": 11967, + "amount": 12.39, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "f5dda045-8c53-4f94-945b-5757867cb8f3", + "dateTime": "2023-08-29T13:40:37.936807Z", + "distance": 9165, + "amount": 39.92, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "f681f6e6-8e75-458b-b9ee-b837715cde62", + "dateTime": "2023-10-21T16:05:08.598667Z", + "distance": 13454, + "amount": 43.05, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "f77d532a-e53b-4449-b7c8-ea1dbad5642c", + "dateTime": "2023-06-24T18:02:57.123919Z", + "distance": 5669, + "amount": 44.84, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "fa924d18-4436-4fa9-946e-cedd227dcdf2", + "dateTime": "2023-12-08T16:07:30.413954Z", + "distance": 16241, + "amount": 42.23, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "faf2de55-5ac8-4a62-86c4-b3e4a256af97", + "dateTime": "2023-11-19T13:37:02.306525Z", + "distance": 14872, + "amount": 43.24, + "ignoreInCalculation": false, + "carId": "08db320e-b5c7-4b9f-8aab-6743b0447198", + "carName": "Focus2", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "31dcd060-3fae-479b-a9c0-ed45c0eb7d91", + "dateTime": "2024-11-26T16:48:35.310185Z", + "distance": 21948, + "amount": 40.09, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "431cb61c-09e2-400a-8ddf-130e4bea3a4a", + "dateTime": "2024-12-27T12:54:29.080303Z", + "distance": 24053, + "amount": 40.22, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "f04b414e-cdfe-4f28-81be-d05e545113d4", + "dateTime": "2024-12-13T16:19:19.809025Z", + "distance": 23350, + "amount": 38.41, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "0592edee-946e-40ab-b4ce-7c335e36e222", + "dateTime": "2022-06-11T18:33:00.849168Z", + "distance": 27214, + "amount": 37.73, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "0c0d5e92-e06c-4582-8631-18261fcaeea9", + "dateTime": "2022-09-13T11:35:36.553554Z", + "distance": 34097, + "amount": 42.7, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "0cc2a781-a2d6-4302-88f4-ef1992fb0ccc", + "dateTime": "2021-06-09T16:18:30.493941Z", + "distance": 8900, + "amount": 32.44, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "0fba66ce-9aa7-41d0-a27c-45deb6ad271b", + "dateTime": "2021-07-07T20:34:37.514765Z", + "distance": 10305, + "amount": 37.99, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "1211d9ab-7b03-4f72-97a4-3970d708bf44", + "dateTime": "2020-12-05T11:16:24.135397Z", + "distance": 2940, + "amount": 36.95, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "1f1e4251-1d21-49a3-8672-fb0b47a45a6c", + "dateTime": "2022-03-10T16:06:28.705308Z", + "distance": 22067, + "amount": 43.29, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "20108459-2786-4a3b-9850-a0f636f19fa0", + "dateTime": "2021-11-26T16:30:24.377052Z", + "distance": 17781, + "amount": 40.41, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "214f640b-917e-4f4d-b5c6-aa7e42068798", + "dateTime": "2022-05-02T17:07:51.686201Z", + "distance": 25153, + "amount": 40.47, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "220b5b1c-13c9-4224-a3cb-a4da1ba2fb83", + "dateTime": "2022-03-25T15:40:28.643524Z", + "distance": 22654, + "amount": 29.6, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "24011fde-db6b-4920-877d-fc729d86b6d0", + "dateTime": "2021-09-26T17:57:43.08389Z", + "distance": 13996, + "amount": 40.16, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "294633e4-1e4b-47de-9148-4f59197c78b5", + "dateTime": "2021-10-28T11:50:25.692511Z", + "distance": 15577, + "amount": 42.28, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "2d565629-6054-41c2-9b52-d03b9f70fa8c", + "dateTime": "2021-08-31T11:01:52.448188Z", + "distance": 12536, + "amount": 39.46, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "2e38085e-f05a-496c-8c46-a2ccc81332b4", + "dateTime": "2022-11-07T17:58:06.217348Z", + "distance": 37681, + "amount": 42.15, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "30745fd4-6f18-42b5-88c8-b934597d6476", + "dateTime": "2022-10-26T20:43:30.009959Z", + "distance": 36842, + "amount": 35.2, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "3157e5f4-26ee-45ea-b828-57be70611e98", + "dateTime": "2021-07-26T16:55:01.18988Z", + "distance": 11041, + "amount": 36.96, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "323be6e8-2d77-404d-825b-dba1d05169c3", + "dateTime": "2021-01-05T17:01:23.184485Z", + "distance": 4330, + "amount": 40.99, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "34e50168-61f9-4942-8829-86ad138c7aff", + "dateTime": "2022-08-14T14:59:24.476668Z", + "distance": 31385, + "amount": 43.97, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "36acd968-ec82-4aaf-96bc-c3c016afa20d", + "dateTime": "2022-09-03T08:24:51.333663Z", + "distance": 32582, + "amount": 15.33, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "38f77c95-a4b5-4735-b46f-1c6e4552f670", + "dateTime": "2022-02-23T23:00:00Z", + "distance": 21258, + "amount": 25.06, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "45301837-039b-4208-a54b-3121412a19d3", + "dateTime": "2021-09-08T04:49:05.032728Z", + "distance": 13211, + "amount": 35.48, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "4b9c2dd7-b77d-42cf-9b9d-4421872d4fa0", + "dateTime": "2023-03-09T14:37:10.238524Z", + "distance": 45219, + "amount": 39.23, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "4c329d8b-f2e8-4542-8c78-3c2c1f5b142e", + "dateTime": "2023-01-26T17:34:56.181247Z", + "distance": 42942, + "amount": 29.68, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "5129ec72-a354-4b21-8b3e-ac4f853dc742", + "dateTime": "2022-11-18T14:52:44.773375Z", + "distance": 38513, + "amount": 41.13, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "51b5c374-471c-4a5c-a079-1632bb0e5003", + "dateTime": "2022-07-26T08:52:12.318525Z", + "distance": 29820, + "amount": 32.48, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "55404373-8117-479e-958b-981769048095", + "dateTime": "2023-02-21T19:21:39.548129Z", + "distance": 44507, + "amount": 41.42, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "6f0c73ad-55ce-48b6-9ff3-3fd20b22a209", + "dateTime": "2022-08-30T17:42:42.867606Z", + "distance": 32275, + "amount": 45.41, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "74c75cb3-3fc3-43ff-a46c-f4f7cf26404a", + "dateTime": "2022-04-09T09:14:31.946051Z", + "distance": 23364, + "amount": 39.14, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "7ffa209f-2ff6-46eb-8c26-afe5966586c4", + "dateTime": "2022-05-19T15:30:16.958362Z", + "distance": 25970, + "amount": 43.03, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "86b2f7b6-d8cf-4c05-a295-22a681d756bf", + "dateTime": "2021-12-29T23:00:00Z", + "distance": 19245, + "amount": 42.62, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "8a53872c-063b-4d1b-9383-6661372f623b", + "dateTime": "2022-07-30T10:30:54.939251Z", + "distance": 30526, + "amount": 32.57, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "8f7ba349-d608-4900-83e7-2c1fe6c06e04", + "dateTime": "2022-07-22T18:45:51.863192Z", + "distance": 29150, + "amount": 17.99, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "9ad077f4-8bb4-4848-a10b-994a430dccaa", + "dateTime": "2022-11-29T15:41:55.846908Z", + "distance": 39230, + "amount": 37.63, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "a4827f0a-66d7-46ce-8d3a-aafeecca9030", + "dateTime": "2021-05-19T18:56:13.507733Z", + "distance": 8340, + "amount": 38.96, + "ignoreInCalculation": true, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "a681f503-14b3-4050-a052-ca74175cce21", + "dateTime": "2020-11-16T19:15:49.576109Z", + "distance": 2239, + "amount": 36.6, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "a74b7b1f-cb25-4480-90e3-8f90169394c9", + "dateTime": "2022-04-14T22:00:00Z", + "distance": 24361, + "amount": 43.88, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "a95fc036-5e77-44de-9a1e-787d6e9a275f", + "dateTime": "2023-03-23T17:59:09.404792Z", + "distance": 46060, + "amount": 43.77, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "a99be52b-22cf-4ebe-b339-79f887817199", + "dateTime": "2022-12-14T17:27:17.448214Z", + "distance": 40046, + "amount": 42.66, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "b03aa77a-58b4-4949-b230-57b1ac10606a", + "dateTime": "2022-06-24T15:19:09.718529Z", + "distance": 27972, + "amount": 40.28, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "b172172a-b48c-4ea8-8abc-727bed6ae2fe", + "dateTime": "2021-11-04T16:57:52.538748Z", + "distance": 16342, + "amount": 38.74, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "b3c0bab5-63bc-40d2-9f83-d85bb6c509c8", + "dateTime": "2022-09-07T06:09:51.093382Z", + "distance": 33236, + "amount": 33.89, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "ba354961-095d-4225-9465-3baf53b69a9c", + "dateTime": "2022-10-14T16:44:19.278493Z", + "distance": 36143, + "amount": 38.42, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "bc659bc7-5b18-4ee4-8c24-8bc5feda1fed", + "dateTime": "2023-01-12T15:28:52.295031Z", + "distance": 41489, + "amount": 42.3, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "bf8bdb29-4cc8-48df-a5b8-d8d25a796c01", + "dateTime": "2021-06-23T15:17:39.388071Z", + "distance": 9597, + "amount": 37.12, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "c1076652-48b2-4b33-935c-b083b3e755d2", + "dateTime": "2022-07-18T22:00:00Z", + "distance": 28809, + "amount": 42.89, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "c1135b21-93c2-417e-bfcb-e1cd1d236985", + "dateTime": "2022-02-05T23:00:00Z", + "distance": 20807, + "amount": 40.89, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "c1cae3bc-df27-4ec1-9e93-8610f9588863", + "dateTime": "2022-06-01T19:41:41.02851Z", + "distance": 26692, + "amount": 29.39, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "c2d8dbca-81ec-4b16-9f69-1de3f8a9a8b5", + "dateTime": "2022-01-19T17:08:19.719042Z", + "distance": 20025, + "amount": 40.05, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "ca75a9a7-6faa-4443-8b65-399f6a24b919", + "dateTime": "2021-11-17T17:20:34.226081Z", + "distance": 16990, + "amount": 32.92, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "cb980539-4296-45c7-9c40-2ee2f86bf60a", + "dateTime": "2021-10-08T05:45:13.948945Z", + "distance": 14741, + "amount": 39.52, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "d18ca527-bc73-47a5-b345-353a6509a5c0", + "dateTime": "2023-01-19T20:53:41.266591Z", + "distance": 42338, + "amount": 44.94, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "d511a7e1-275f-4e9f-81c6-00b171303c67", + "dateTime": "2020-10-20T14:52:41.897002Z", + "distance": 769, + "amount": 39.76, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "d6e1f671-fa61-4460-abb0-8650cfa83ec2", + "dateTime": "2021-01-22T19:57:12.802226Z", + "distance": 4974, + "amount": 35.19, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "d9f640d9-b0c2-4757-8bec-c881ca2d4eda", + "dateTime": "2020-12-20T11:42:07.009878Z", + "distance": 3533, + "amount": 30.63, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "db5df6e0-2726-42fa-9716-126c25c43f39", + "dateTime": "2023-02-06T21:43:29.236896Z", + "distance": 43716, + "amount": 38.89, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "f259f57d-607e-4bd9-bbc5-919320b5e077", + "dateTime": "2021-08-13T15:42:51.461757Z", + "distance": 11753, + "amount": 40.74, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "f352d571-e77e-4a06-bce7-d21e9c488f40", + "dateTime": "2022-09-22T16:26:50.697147Z", + "distance": 34895, + "amount": 42.63, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "f3ba9bd2-1a54-4857-ae1d-6e6d999aec19", + "dateTime": "2021-12-10T09:57:37.499003Z", + "distance": 18399, + "amount": 34.07, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "f9d015bf-742c-4269-8c79-445c793fd324", + "dateTime": "2022-12-25T18:40:51.9949Z", + "distance": 40656, + "amount": 34.36, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "fd0ca5d4-69a2-4145-93ef-c228fe54be57", + "dateTime": "2022-10-04T22:00:00Z", + "distance": 35660, + "amount": 28.13, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "ff00aa02-72e6-465d-a65f-1613022af079", + "dateTime": "2020-11-06T08:43:34.054224Z", + "distance": 1521, + "amount": 39.29, + "ignoreInCalculation": false, + "carId": "8800feb6-9109-4385-b52b-27c94e5dd1d8", + "carName": "Focus", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "02faf182-6930-4d98-b436-659e177f2d27", + "dateTime": "2019-10-26T22:00:00Z", + "distance": 24395, + "amount": 20.31, + "ignoreInCalculation": false, + "carId": "fcb76d13-b02d-43c5-bc1d-2b16ad7bd2bc", + "carName": "Space Star", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "13fcf697-f6ee-4d75-b391-2f5f701265cc", + "dateTime": "2019-12-09T23:00:00Z", + "distance": 25875, + "amount": 26.6, + "ignoreInCalculation": false, + "carId": "fcb76d13-b02d-43c5-bc1d-2b16ad7bd2bc", + "carName": "Space Star", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "2b2bd195-8ccc-4561-a755-751c3fff6080", + "dateTime": "2020-10-04T22:00:00Z", + "distance": 30558, + "amount": 15.89, + "ignoreInCalculation": false, + "carId": "fcb76d13-b02d-43c5-bc1d-2b16ad7bd2bc", + "carName": "Space Star", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "2d9a6845-5dbf-4929-957f-33f66f4f8afc", + "dateTime": "2020-01-23T23:00:00Z", + "distance": 26881, + "amount": 24.4, + "ignoreInCalculation": false, + "carId": "fcb76d13-b02d-43c5-bc1d-2b16ad7bd2bc", + "carName": "Space Star", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "40c83d6b-e9b5-4c3a-93c3-fac48f4a932b", + "dateTime": "2020-02-12T23:00:00Z", + "distance": 27964, + "amount": 25.29, + "ignoreInCalculation": false, + "carId": "fcb76d13-b02d-43c5-bc1d-2b16ad7bd2bc", + "carName": "Space Star", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "43c5824e-ff0b-483a-b758-bf8c09161ea3", + "dateTime": "2020-03-18T23:00:00Z", + "distance": 28983, + "amount": 20.78, + "ignoreInCalculation": false, + "carId": "fcb76d13-b02d-43c5-bc1d-2b16ad7bd2bc", + "carName": "Space Star", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "46fb3cd5-42f3-43f5-9009-b0f841e992d1", + "dateTime": "2020-09-11T09:28:57.231388Z", + "distance": 30035, + "amount": 22.71, + "ignoreInCalculation": false, + "carId": "fcb76d13-b02d-43c5-bc1d-2b16ad7bd2bc", + "carName": "Space Star", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "67dfba1f-8cd5-4b29-8c4b-082c542a50fb", + "dateTime": "2020-06-12T22:00:00Z", + "distance": 29507, + "amount": 25.73, + "ignoreInCalculation": false, + "carId": "fcb76d13-b02d-43c5-bc1d-2b16ad7bd2bc", + "carName": "Space Star", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "701e67d9-62b5-4a9e-a5e4-4cd1a75db3b2", + "dateTime": "2020-02-02T23:00:00Z", + "distance": 27397, + "amount": 24.37, + "ignoreInCalculation": false, + "carId": "fcb76d13-b02d-43c5-bc1d-2b16ad7bd2bc", + "carName": "Space Star", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "79e2e24b-49d6-479a-88fa-662dcd616679", + "dateTime": "2019-11-25T23:00:00Z", + "distance": 25364, + "amount": 24.37, + "ignoreInCalculation": false, + "carId": "fcb76d13-b02d-43c5-bc1d-2b16ad7bd2bc", + "carName": "Space Star", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "92915abe-42e4-4fde-a4e1-f51a94c3dc71", + "dateTime": "2020-01-09T23:00:00Z", + "distance": 26376, + "amount": 25.36, + "ignoreInCalculation": false, + "carId": "fcb76d13-b02d-43c5-bc1d-2b16ad7bd2bc", + "carName": "Space Star", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "dcc9ae9e-f31a-4846-a16f-500e6e8b6b46", + "dateTime": "2019-11-14T23:00:00Z", + "distance": 24917, + "amount": 25.64, + "ignoreInCalculation": false, + "carId": "fcb76d13-b02d-43c5-bc1d-2b16ad7bd2bc", + "carName": "Space Star", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "f4903978-9786-4a35-b0dd-99a5065305e7", + "dateTime": "2020-03-01T23:00:00Z", + "distance": 28498, + "amount": 26.46, + "ignoreInCalculation": false, + "carId": "fcb76d13-b02d-43c5-bc1d-2b16ad7bd2bc", + "carName": "Space Star", + "userId": "7a08af5c-31d6-479a-88a3-729e787e6c98", + "userName": "nuykent@gmail.com" + }, + { + "id": "037c47f2-187b-479e-ae8f-ec5d2ce29f54", + "dateTime": "2023-09-13T22:00:00Z", + "distance": 10914, + "amount": 42, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "054a4472-490f-4d5f-8ade-a07f6aa3b624", + "dateTime": "2022-11-06T23:00:00Z", + "distance": 7110, + "amount": 38.59, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "05590f42-d5f2-4310-8866-da19eafa8ab8", + "dateTime": "2023-04-21T22:00:00Z", + "distance": 9161, + "amount": 41.21, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "07cd9d9c-5e7d-4b1f-92ca-1d34251049d5", + "dateTime": "2024-07-19T17:26:28.557923Z", + "distance": 16281, + "amount": 35.33, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "0a590a76-8f05-4531-981a-444f2db628ea", + "dateTime": "2025-06-01T16:35:51.836058Z", + "distance": 32365, + "amount": 37.66, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "0c68b4d0-5b32-4c06-a349-3b3637f2b096", + "dateTime": "2025-02-28T21:03:03.300558Z", + "distance": 27126, + "amount": 30.85, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "13b10dae-1d2c-4197-9ac6-f6b28a6d0309", + "dateTime": "2022-06-25T22:00:00Z", + "distance": 4648, + "amount": 21.59, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "1b12c6fe-f4a2-4d89-924a-732ad0ffdc73", + "dateTime": "2024-08-21T19:53:11.630698Z", + "distance": 16904, + "amount": 36.64, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "1d731baa-caee-4ef5-aa65-622c8d83bc03", + "dateTime": "2021-04-19T22:00:00Z", + "distance": 4, + "amount": 0.1, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "1f7f8a1a-497f-4c01-9763-9cd517664f7e", + "dateTime": "2025-06-16T19:49:36.156446Z", + "distance": 33055, + "amount": 38.7, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "20638693-37ac-4b35-821a-1d5445c5cf16", + "dateTime": "2025-03-13T08:21:05.913067Z", + "distance": 27853, + "amount": 40.99, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "20820000-8aa1-4f51-bf0d-112cd156672d", + "dateTime": "2024-10-29T16:54:54.329016Z", + "distance": 19962, + "amount": 29.09, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "2235d264-6588-4648-b5b0-e49e559bbdeb", + "dateTime": "2022-04-29T22:00:00Z", + "distance": 4295, + "amount": 40.93, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "22373a99-4ca7-4c08-b833-bb49d82ffce6", + "dateTime": "2025-05-07T19:33:49.768528Z", + "distance": 31053, + "amount": 34.31, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "2c8227f2-a1f3-44d5-bc4b-2d0ebc344a2a", + "dateTime": "2021-09-15T22:00:00Z", + "distance": 1924, + "amount": 37.99, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "2fe3a2de-6c2c-41a0-8be1-ab7ed4d53607", + "dateTime": "2024-09-12T16:38:20.280842Z", + "distance": 17574, + "amount": 38.35, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "307422cf-1322-4a9a-94fb-68f4b4c7e769", + "dateTime": "2025-03-31T16:09:55.422505Z", + "distance": 29139, + "amount": 41.82, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "397e31a5-8514-4ed9-8f78-fb7be5ed783b", + "dateTime": "2022-02-08T23:00:00Z", + "distance": 3166, + "amount": 36.2, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "45aad2cf-ca12-4b12-914a-d4b2ae459f95", + "dateTime": "2025-01-10T18:55:07.933084Z", + "distance": 24677, + "amount": 36.45, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "46228ff4-550f-40e8-8223-902557ed21f7", + "dateTime": "2025-02-02T18:41:32.729285Z", + "distance": 25988, + "amount": 32.77, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "4bc9c633-98df-46cc-bc1a-59d6d42012d7", + "dateTime": "2024-10-02T19:48:47.864307Z", + "distance": 18777, + "amount": 38.26, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "4d047cf4-da5c-44c5-8bbf-964e7ccf8e85", + "dateTime": "2024-11-10T14:43:53.016738Z", + "distance": 20622, + "amount": 38.02, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "4f1afe77-2266-4125-8966-e09c9a06ea17", + "dateTime": "2025-04-26T15:54:35.911227Z", + "distance": 30462, + "amount": 37.59, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "4fccf3d2-fa34-472a-a114-e734ba99c565", + "dateTime": "2023-10-25T19:46:45.632562Z", + "distance": 11487, + "amount": 34.21, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "5028639b-cd90-4887-878c-9c6d5aa5ba83", + "dateTime": "2022-12-12T23:00:00Z", + "distance": 7766, + "amount": 38.64, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "5141c870-9070-468d-bce0-fdaf6a619009", + "dateTime": "2024-06-17T20:04:35.660765Z", + "distance": 15054, + "amount": 34.16, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "5236728a-fc44-433b-8087-06669d248434", + "dateTime": "2023-02-07T23:00:00Z", + "distance": 8475, + "amount": 42.54, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "52afd1b8-287a-42da-94c4-92a19284e1ef", + "dateTime": "2022-03-08T23:00:00Z", + "distance": 3600, + "amount": 28.47, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "52b65f25-a2e7-489e-9afa-31a71d886d80", + "dateTime": "2022-09-25T22:00:00Z", + "distance": 6437, + "amount": 40.46, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "5331e59a-e18b-41b7-b670-4ff63fbe3e54", + "dateTime": "2023-07-16T22:00:00Z", + "distance": 10194, + "amount": 61.48, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "5482802c-01e3-4cbf-bbaa-15eb1a0faeed", + "dateTime": "2021-10-21T22:00:00Z", + "distance": 2567, + "amount": 37.53, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "5a5ed2a2-6d33-4182-88f7-9387ec584063", + "dateTime": "2021-07-17T22:00:00Z", + "distance": 1280, + "amount": 34.68, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "5e57b1f6-8dfd-45cb-87be-37f55d5e10c2", + "dateTime": "2025-03-19T23:00:00Z", + "distance": 28402, + "amount": 31.6, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "79d2e147-5502-422b-b5eb-bc0537a3449b", + "dateTime": "2024-12-05T17:49:11.148738Z", + "distance": 22677, + "amount": 40.93, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "79e88e94-313e-489f-8712-41399f0e1e7a", + "dateTime": "2024-10-16T19:40:27.918829Z", + "distance": 19453, + "amount": 39.76, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "827a21dc-acdb-40d4-93b4-89134f28d582", + "dateTime": "2025-01-20T20:38:10.611457Z", + "distance": 25399, + "amount": 40.68, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "858d9bb4-dfb9-40bc-8d06-5c683b193396", + "dateTime": "2024-06-09T18:42:56.065453Z", + "distance": 14417, + "amount": 33.03, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "8fa900fc-8fc3-43e0-b30c-18c6887071dc", + "dateTime": "2023-12-03T17:46:50.161242Z", + "distance": 12161, + "amount": 39.84, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "962b55fc-4d7a-4544-9877-f9ab97b3daa7", + "dateTime": "2024-11-18T17:48:51.083936Z", + "distance": 21214, + "amount": 33.84, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "96b3b8c9-3965-4e4a-9818-9274f5391026", + "dateTime": "2022-08-28T22:00:00Z", + "distance": 5715, + "amount": 33.86, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "a7b242ed-c981-41df-b226-e280befeec63", + "dateTime": "2024-05-01T15:55:07.989666Z", + "distance": 13377, + "amount": 39.5, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "a9d767c2-b45e-4c6c-a131-8f18561068a2", + "dateTime": "2024-09-20T16:01:59.047492Z", + "distance": 18104, + "amount": 30.97, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "ae53b088-2f51-4db5-9e93-a643be9e1e53", + "dateTime": "2021-04-20T22:00:00Z", + "distance": 705, + "amount": 39.93, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "b58b114c-939e-4281-8173-e060dc4b7c6b", + "dateTime": "2025-02-16T15:59:21.91065Z", + "distance": 26606, + "amount": 36.3, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "b74ee637-bc28-45b4-b62f-f28c6c9b5be9", + "dateTime": "2025-04-09T19:47:57.154868Z", + "distance": 29798, + "amount": 37.73, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "c6f526c6-6f81-4ad1-aa3b-aa9c6f5407eb", + "dateTime": "2022-07-20T22:00:00Z", + "distance": 5145, + "amount": 28.83, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "e3f42f52-cca4-408a-844b-0319f1ab7a8b", + "dateTime": "2025-05-18T18:06:11.930089Z", + "distance": 31697, + "amount": 36.56, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "eda0795a-efe2-4df5-aea6-64554bbc03e4", + "dateTime": "2024-04-05T12:13:40.576712Z", + "distance": 12729, + "amount": 35.83, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "f3491bd7-c386-48bb-a30f-7c5b602ac0e0", + "dateTime": "2024-07-02T22:00:00Z", + "distance": 15680, + "amount": 36.43, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + }, + { + "id": "f3ebdf4f-6889-4396-ae8a-2cf1d9d36898", + "dateTime": "2024-05-08T19:43:51.888065Z", + "distance": 13831, + "amount": 26.93, + "ignoreInCalculation": false, + "carId": "08dbd004-e67a-44b5-8760-28fafe2aada6", + "carName": "Corsa", + "userId": "9c8e9ab1-bf51-408e-9a17-db3e1564a543", + "userName": "celine.hold99@gmail.com" + } + ] + """; + +const string ACCESS_TOKEN = ""; + +Car[]? cars = JsonSerializer.Deserialize(CARS); +if (cars is null) +{ + Console.WriteLine("Could not parse cars"); + Environment.ExitCode = 1; + return; +} + +ConsumptionEntry[]? consumptionEntries = JsonSerializer.Deserialize(CONSUMPTION_ENTRIES); +if (consumptionEntries is null) +{ + Console.WriteLine("Could not parse consumption entries"); + Environment.ExitCode = 2; + return; +} + + +using HttpClient httpClient = new(); +httpClient.BaseAddress = new Uri("https://api.qa.vegasco.nuyken.dev"); +httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ACCESS_TOKEN); + +Dictionary oldCarIdToNewCarIdMapping = []; + +foreach (Car car in cars) +{ + CreateCar.Request request = new(car.Name); + using HttpResponseMessage response = await httpClient.PostAsJsonAsync("v1/cars", request); + + if (!response.IsSuccessStatusCode && response.StatusCode != HttpStatusCode.Conflict) + { + Console.Error.WriteLine("Failed to create car '{0}', got status: {1}", car.Name, response.StatusCode); + continue; + } + + CreateCar.Response? created = await response.Content.ReadFromJsonAsync(); + if (created is null) + { + Console.Error.WriteLine( + "Could not deserialize create car response content into {0}, response status code was '{1}' and content was: {2}", + nameof(CreateCar.Response), + response.StatusCode, + await response.Content.ReadAsStringAsync()); + continue; + } + + oldCarIdToNewCarIdMapping.Add(car.Id, created.Id); +} + +foreach (ConsumptionEntry consumptionEntry in consumptionEntries) +{ + if (!oldCarIdToNewCarIdMapping.TryGetValue(consumptionEntry.CarId, out Guid newCarId)) + { + Console.Error.WriteLine("Could not determine new car id for consumption entry with old id '{0}'", consumptionEntry.Id); + continue; + } + + CreateConsumption.Request request = new( + consumptionEntry.DateTime, + consumptionEntry.Distance, + consumptionEntry.Amount, + newCarId); + + using HttpResponseMessage response = await httpClient.PostAsJsonAsync("v1/consumptions", request); + + if (!response.IsSuccessStatusCode) + { + Console.Error.WriteLine("Failed to create consumption entry with old id '{0}', got status: {1}", consumptionEntry.Id, response.StatusCode); + continue; + } + + CreateConsumption.Response? created = await response.Content.ReadFromJsonAsync(); + if (created is null) + { + Console.Error.WriteLine( + "Could not deserialize create consumption entry response content into {0}, response status code was '{1}' and content was: {2}", + nameof(CreateConsumption.Response), + response.StatusCode, + await response.Content.ReadAsStringAsync()); + continue; + } +} + +internal class Car +{ + [JsonPropertyName("id")] + public required Guid Id { get; init; } + + [JsonPropertyName("name")] + public required string Name { get; init; } +} + +internal class ConsumptionEntry +{ + [JsonPropertyName("id")] + public required Guid Id { get; init; } + + [JsonPropertyName("dateTime")] + public required DateTimeOffset DateTime { get; init; } + + [JsonPropertyName("distance")] + public required int Distance { get; init; } + + [JsonPropertyName("amount")] + public required double Amount { get; init; } + + [JsonPropertyName("carId")] + public required Guid CarId { get; init; } + + [JsonPropertyName("carName")] + public required string CarName { get; init; } +} + diff --git a/src/UploadData/UploadData.csproj b/src/UploadData/UploadData.csproj new file mode 100644 index 0000000..3becbe6 --- /dev/null +++ b/src/UploadData/UploadData.csproj @@ -0,0 +1,14 @@ + + + + Exe + net9.0 + enable + enable + + + + + + + diff --git a/src/Vegasco-Web/.dockerignore b/src/Vegasco-Web/.dockerignore new file mode 100644 index 0000000..6b38648 --- /dev/null +++ b/src/Vegasco-Web/.dockerignore @@ -0,0 +1,10 @@ +node_modules +npm-debug.log +Dockerfile* +docker-compose* +.dockerignore +.git +.gitignore +README.md +LICENSE +.vscode diff --git a/src/Vegasco-Web/.editorconfig b/src/Vegasco-Web/.editorconfig new file mode 100644 index 0000000..f166060 --- /dev/null +++ b/src/Vegasco-Web/.editorconfig @@ -0,0 +1,17 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single +ij_typescript_use_double_quotes = false + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/src/Vegasco-Web/.gitignore b/src/Vegasco-Web/.gitignore new file mode 100644 index 0000000..cc7b141 --- /dev/null +++ b/src/Vegasco-Web/.gitignore @@ -0,0 +1,42 @@ +# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings + +# System files +.DS_Store +Thumbs.db diff --git a/src/Vegasco-Web/.postcssrc.json b/src/Vegasco-Web/.postcssrc.json new file mode 100644 index 0000000..72f908d --- /dev/null +++ b/src/Vegasco-Web/.postcssrc.json @@ -0,0 +1,5 @@ +{ + "plugins": { + "@tailwindcss/postcss": {} + } +} \ No newline at end of file diff --git a/src/Vegasco-Web/.vscode/extensions.json b/src/Vegasco-Web/.vscode/extensions.json new file mode 100644 index 0000000..77b3745 --- /dev/null +++ b/src/Vegasco-Web/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 + "recommendations": ["angular.ng-template"] +} diff --git a/src/Vegasco-Web/.vscode/launch.json b/src/Vegasco-Web/.vscode/launch.json new file mode 100644 index 0000000..5d733e3 --- /dev/null +++ b/src/Vegasco-Web/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Web (Chrome)", + "type": "chrome", + "request": "launch", + "preLaunchTask": "npm: start", + "postDebugTask": "Terminate All Tasks", + "url": "http://localhost:44200/", + } + ] +} diff --git a/src/Vegasco-Web/.vscode/tasks.json b/src/Vegasco-Web/.vscode/tasks.json new file mode 100644 index 0000000..5fe42c3 --- /dev/null +++ b/src/Vegasco-Web/.vscode/tasks.json @@ -0,0 +1,63 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 + "version": "2.0.0", + "tasks": [ + { + "label": "Terminate All Tasks", + "command": "echo ${input:terminate}", + "type": "shell", + "problemMatcher": [] + }, + { + "type": "npm", + "script": "start", + "options": { + "env": { + "PORT": "44200", + "services__Api__https__0": "https://localhost:7098", + "NODE_ENV": "development" + } + }, + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "(.*?)" + }, + "endsPattern": { + "regexp": "bundle generation complete" + } + } + } + }, + { + "type": "npm", + "script": "test", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "(.*?)" + }, + "endsPattern": { + "regexp": "bundle generation complete" + } + } + } + } + ], + "inputs": [ + { + "id": "terminate", + "type": "command", + "command": "workbench.action.tasks.terminate", + "args": "terminateAll" + } + ] +} diff --git a/src/Vegasco-Web/Dockerfile b/src/Vegasco-Web/Dockerfile new file mode 100644 index 0000000..7a66bc5 --- /dev/null +++ b/src/Vegasco-Web/Dockerfile @@ -0,0 +1,19 @@ +FROM node:lts AS build +RUN npm install -g pnpm +ARG CONFIGURATION=development +WORKDIR /usr/local/app +COPY . . +RUN pnpm install +RUN pnpm "build:$CONFIGURATION" + +FROM nginx:alpine +RUN rm /etc/nginx/conf.d/* +RUN apk add --update dos2unix +ENV DOLLAR=$ +WORKDIR /usr/share/nginx/html +COPY --from=build /usr/local/app/dist/Vegasco-Web/browser . +COPY nginx.conf /etc/nginx/nginx.conf +RUN dos2unix /etc/nginx/nginx.conf +COPY webserver.conf.template /etc/nginx/templates/webserver.conf.template +RUN dos2unix /etc/nginx/templates/webserver.conf.template +EXPOSE 80 diff --git a/src/Vegasco-Web/README.md b/src/Vegasco-Web/README.md new file mode 100644 index 0000000..dcb34ef --- /dev/null +++ b/src/Vegasco-Web/README.md @@ -0,0 +1,69 @@ +# VegascoWeb + +This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.0.1. + +## Development server + +To start a local development server, run: + +```bash +ng serve +``` + +Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. + +## API Proxy + +Because the solution utilizes Aspire which injects endpoint references for the API as environment variables, this application uses a proxy to access the API. The proxy is configured in the `proxy.config.js` file which is used in the `serve` section of the `angular.json` file. This makes the dev server provide a proxy when serving the application. + +The environment variables for the API endpoint are named `services__Api__https__0` and `services__Api__http__0` for the https and the http endpoints respectively. If the https endpoint is not configured, the http endpoint is used. At least one of them has to be configured. + +To allow the dev proxy to accept otherwise untrusted server certificates, set `NODE_ENV` to `development`. Otherwise the dev proxy rejects untrusted certificates. + +When deploying the application elsewhere, another proxy has to be configured to provide the same functionality to ensure the application works correctly. + +## Code scaffolding + +Angular CLI includes powerful code scaffolding tools. To generate a new component, run: + +```bash +ng generate component component-name +``` + +For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: + +```bash +ng generate --help +``` + +## Building + +To build the project run: + +```bash +ng build +``` + +This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. + +## Running unit tests + +To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command: + +```bash +ng test +``` + +## Running end-to-end tests + +For end-to-end (e2e) testing, run: + +```bash +ng e2e +``` + +Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. + +## Additional Resources + +For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/src/Vegasco-Web/angular.json b/src/Vegasco-Web/angular.json new file mode 100644 index 0000000..776e5f4 --- /dev/null +++ b/src/Vegasco-Web/angular.json @@ -0,0 +1,120 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "Vegasco-Web": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:application", + "options": { + "outputPath": "dist/Vegasco-Web", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": [ + "zone.js" + ], + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "src/styles.scss" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kB", + "maximumError": "1MB" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "4kB", + "maximumError": "8kB" + } + ], + "outputHashing": "all", + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.production.ts" + } + ] + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "proxyConfig": "proxy.config.js" + }, + "configurations": { + "production": { + "buildTarget": "Vegasco-Web:build:production" + }, + "development": { + "buildTarget": "Vegasco-Web:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "polyfills": [ + "zone.js", + "zone.js/testing" + ], + "tsConfig": "tsconfig.spec.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "src/styles.scss" + ], + "scripts": [] + } + } + } + } + }, + "cli": { + "analytics": false + } +} diff --git a/src/Vegasco-Web/nginx.conf b/src/Vegasco-Web/nginx.conf new file mode 100644 index 0000000..2fc7d50 --- /dev/null +++ b/src/Vegasco-Web/nginx.conf @@ -0,0 +1,8 @@ +events { } +http { + include mime.types; + + resolver 127.0.0.11; + + include /etc/nginx/conf.d/webserver.conf; +} diff --git a/src/Vegasco-Web/package.json b/src/Vegasco-Web/package.json new file mode 100644 index 0000000..8ab96ff --- /dev/null +++ b/src/Vegasco-Web/package.json @@ -0,0 +1,53 @@ +{ + "name": "vegasco-web", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start:withInstall": "pnpm install && pnpm run start", + "start": "run-script-os", + "start:win32": "ng serve --port %PORT% --configuration development", + "start:default": "ng serve --port $PORT --configuration development", + "build": "pnpm build:development", + "build:development": "ng build", + "build:production": "ng build --configuration production", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "private": true, + "dependencies": { + "@angular/common": "^19.2.14", + "@angular/compiler": "^19.2.14", + "@angular/core": "^19.2.14", + "@angular/forms": "^19.2.14", + "@angular/platform-browser": "^19.2.14", + "@angular/router": "^19.2.14", + "@ng-icons/core": "^31.4.0", + "@ng-icons/material-file-icons": "^31.4.0", + "@ng-icons/material-icons": "^31.4.0", + "@primeng/themes": "^19.1.3", + "@tailwindcss/postcss": "^4.1.10", + "dayjs": "^1.11.13", + "keycloak-angular": "^19.0.2", + "postcss": "^8.5.6", + "primeng": "^19.1.3", + "rxjs": "~7.8.2", + "tailwindcss": "^4.1.10", + "tailwindcss-primeui": "^0.6.1", + "tslib": "^2.8.1", + "zone.js": "~0.15.1" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^19.2.15", + "@angular/cli": "^19.2.15", + "@angular/compiler-cli": "^19.2.14", + "@types/jasmine": "~5.1.8", + "jasmine-core": "~5.7.1", + "karma": "~6.4.4", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.1", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "run-script-os": "^1.1.6", + "typescript": "~5.8.3" + } +} \ No newline at end of file diff --git a/src/Vegasco-Web/pnpm-lock.yaml b/src/Vegasco-Web/pnpm-lock.yaml new file mode 100644 index 0000000..9f99747 --- /dev/null +++ b/src/Vegasco-Web/pnpm-lock.yaml @@ -0,0 +1,9091 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@angular/common': + specifier: ^19.2.14 + version: 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/compiler': + specifier: ^19.2.14 + version: 19.2.14 + '@angular/core': + specifier: ^19.2.14 + version: 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/forms': + specifier: ^19.2.14 + version: 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + '@angular/platform-browser': + specifier: ^19.2.14 + version: 19.2.14(@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/router': + specifier: ^19.2.14 + version: 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + '@ng-icons/core': + specifier: ^31.4.0 + version: 31.4.0(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@ng-icons/material-file-icons': + specifier: ^31.4.0 + version: 31.4.0 + '@ng-icons/material-icons': + specifier: ^31.4.0 + version: 31.4.0 + '@primeng/themes': + specifier: ^19.1.3 + version: 19.1.3 + '@tailwindcss/postcss': + specifier: ^4.1.10 + version: 4.1.10 + dayjs: + specifier: ^1.11.13 + version: 1.11.13 + keycloak-angular: + specifier: ^19.0.2 + version: 19.0.2(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/router@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(keycloak-js@26.2.0) + postcss: + specifier: ^8.5.6 + version: 8.5.6 + primeng: + specifier: ^19.1.3 + version: 19.1.3(47ee1c247593ea8ad66380722e410532) + rxjs: + specifier: ~7.8.2 + version: 7.8.2 + tailwindcss: + specifier: ^4.1.10 + version: 4.1.10 + tailwindcss-primeui: + specifier: ^0.6.1 + version: 0.6.1(tailwindcss@4.1.10) + tslib: + specifier: ^2.8.1 + version: 2.8.1 + zone.js: + specifier: ~0.15.1 + version: 0.15.1 + devDependencies: + '@angular-devkit/build-angular': + specifier: ^19.2.15 + version: 19.2.15(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3))(@angular/compiler@19.2.14)(@types/node@24.0.3)(chokidar@4.0.3)(jiti@2.4.2)(karma@6.4.4)(lightningcss@1.30.1)(tailwindcss@4.1.10)(typescript@5.8.3)(vite@6.2.7(@types/node@24.0.3)(jiti@2.4.2)(less@4.2.2)(lightningcss@1.30.1)(sass@1.85.0)(terser@5.39.0)) + '@angular/cli': + specifier: ^19.2.15 + version: 19.2.15(@types/node@24.0.3)(chokidar@4.0.3) + '@angular/compiler-cli': + specifier: ^19.2.14 + version: 19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3) + '@types/jasmine': + specifier: ~5.1.8 + version: 5.1.8 + jasmine-core: + specifier: ~5.7.1 + version: 5.7.1 + karma: + specifier: ~6.4.4 + version: 6.4.4 + karma-chrome-launcher: + specifier: ~3.2.0 + version: 3.2.0 + karma-coverage: + specifier: ~2.2.1 + version: 2.2.1 + karma-jasmine: + specifier: ~5.1.0 + version: 5.1.0(karma@6.4.4) + karma-jasmine-html-reporter: + specifier: ~2.1.0 + version: 2.1.0(jasmine-core@5.7.1)(karma-jasmine@5.1.0(karma@6.4.4))(karma@6.4.4) + run-script-os: + specifier: ^1.1.6 + version: 1.1.6 + typescript: + specifier: ~5.8.3 + version: 5.8.3 + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@angular-devkit/architect@0.1902.15': + resolution: {integrity: sha512-RbqhStc6ZoRv57ZqLB36VOkBkAdU3nNezCvIs0AJV5V4+vLPMrb0hpIB0sF+9yMlMjWsolnRsj0/Fil+zQG3bw==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + + '@angular-devkit/build-angular@19.2.15': + resolution: {integrity: sha512-mqudAcyrSp/E7ZQdQoHfys0/nvQuwyJDaAzj3qL3HUStuUzb5ULNOj2f6sFBo+xYo+/WT8IzmzDN9DCqDgvFaA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + '@angular/compiler-cli': ^19.0.0 || ^19.2.0-next.0 + '@angular/localize': ^19.0.0 || ^19.2.0-next.0 + '@angular/platform-server': ^19.0.0 || ^19.2.0-next.0 + '@angular/service-worker': ^19.0.0 || ^19.2.0-next.0 + '@angular/ssr': ^19.2.15 + '@web/test-runner': ^0.20.0 + browser-sync: ^3.0.2 + jest: ^29.5.0 + jest-environment-jsdom: ^29.5.0 + karma: ^6.3.0 + ng-packagr: ^19.0.0 || ^19.2.0-next.0 + protractor: ^7.0.0 + tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0 + typescript: '>=5.5 <5.9' + peerDependenciesMeta: + '@angular/localize': + optional: true + '@angular/platform-server': + optional: true + '@angular/service-worker': + optional: true + '@angular/ssr': + optional: true + '@web/test-runner': + optional: true + browser-sync: + optional: true + jest: + optional: true + jest-environment-jsdom: + optional: true + karma: + optional: true + ng-packagr: + optional: true + protractor: + optional: true + tailwindcss: + optional: true + + '@angular-devkit/build-webpack@0.1902.15': + resolution: {integrity: sha512-pIfZeizWsViXx8bsMoBLZw7Tl7uFf7bM7hAfmNwk0bb0QGzx5k1BiW6IKWyaG+Dg6U4UCrlNpIiut2b78HwQZw==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + webpack: ^5.30.0 + webpack-dev-server: ^5.0.2 + + '@angular-devkit/core@19.2.15': + resolution: {integrity: sha512-pU2RZYX6vhd7uLSdLwPnuBcr0mXJSjp3EgOXKsrlQFQZevc+Qs+2JdXgIElnOT/aDqtRtriDmLlSbtdE8n3ZbA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^4.0.0 + peerDependenciesMeta: + chokidar: + optional: true + + '@angular-devkit/schematics@19.2.15': + resolution: {integrity: sha512-kNOJ+3vekJJCQKWihNmxBkarJzNW09kP5a9E1SRNiQVNOUEeSwcRR0qYotM65nx821gNzjjhJXnAZ8OazWldrg==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + + '@angular/animations@19.2.14': + resolution: {integrity: sha512-xhl8fLto5HHJdVj8Nb6EoBEiTAcXuWDYn1q5uHcGxyVH3kiwENWy/2OQXgCr2CuWo2e6hNUGzSLf/cjbsMNqEA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + peerDependencies: + '@angular/common': 19.2.14 + '@angular/core': 19.2.14 + + '@angular/build@19.2.15': + resolution: {integrity: sha512-iE4fp4d5ALu702uoL6/YkjM2JlGEXZ5G+RVzq3W2jg/Ft6ISAQnRKB6mymtetDD6oD7i87e8uSu9kFVNBauX2w==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + '@angular/compiler': ^19.0.0 || ^19.2.0-next.0 + '@angular/compiler-cli': ^19.0.0 || ^19.2.0-next.0 + '@angular/localize': ^19.0.0 || ^19.2.0-next.0 + '@angular/platform-server': ^19.0.0 || ^19.2.0-next.0 + '@angular/service-worker': ^19.0.0 || ^19.2.0-next.0 + '@angular/ssr': ^19.2.15 + karma: ^6.4.0 + less: ^4.2.0 + ng-packagr: ^19.0.0 || ^19.2.0-next.0 + postcss: ^8.4.0 + tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0 + typescript: '>=5.5 <5.9' + peerDependenciesMeta: + '@angular/localize': + optional: true + '@angular/platform-server': + optional: true + '@angular/service-worker': + optional: true + '@angular/ssr': + optional: true + karma: + optional: true + less: + optional: true + ng-packagr: + optional: true + postcss: + optional: true + tailwindcss: + optional: true + + '@angular/cdk@19.2.18': + resolution: {integrity: sha512-aGMHOYK/VV9PhxGTUDwiu/4ozoR/RKz8cimI+QjRxEBhzn4EPqjUDSganvlhmgS7cTN3+aqozdvF/GopMRJjLg==} + peerDependencies: + '@angular/common': ^19.0.0 || ^20.0.0 + '@angular/core': ^19.0.0 || ^20.0.0 + rxjs: ^6.5.3 || ^7.4.0 + + '@angular/cli@19.2.15': + resolution: {integrity: sha512-YRIpARHWSOnWkHusUWTQgeUrPWMjWvtQrOkjWc6stF36z2KUzKMEng6EzUvH6sZolNSwVwOFpODEP0ut4aBkvQ==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + hasBin: true + + '@angular/common@19.2.14': + resolution: {integrity: sha512-NcNklcuyqaTjOVGf7aru8APX9mjsnZ01gFZrn47BxHozhaR0EMRrotYQTdi8YdVjPkeYFYanVntSLfhyobq/jg==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + peerDependencies: + '@angular/core': 19.2.14 + rxjs: ^6.5.3 || ^7.4.0 + + '@angular/compiler-cli@19.2.14': + resolution: {integrity: sha512-e9/h86ETjoIK2yTLE9aUeMCKujdg/du2pq7run/aINjop4RtnNOw+ZlSTUa6R65lP5CVwDup1kPytpAoifw8cA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + hasBin: true + peerDependencies: + '@angular/compiler': 19.2.14 + typescript: '>=5.5 <5.9' + + '@angular/compiler@19.2.14': + resolution: {integrity: sha512-ZqJDYOdhgKpVGNq3+n/Gbxma8DVYElDsoRe0tvNtjkWBVdaOxdZZUqmJ3kdCBsqD/aqTRvRBu0KGo9s2fCChkA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + + '@angular/core@19.2.14': + resolution: {integrity: sha512-EVErpW9tGqJ/wNcAN3G/ErH8pHCJ8mM1E6bsJ8UJIpDTZkpqqYjBMtZS9YWH5n3KwUd1tAkAB2w8FK125AjDUQ==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + peerDependencies: + rxjs: ^6.5.3 || ^7.4.0 + zone.js: ~0.15.0 + + '@angular/forms@19.2.14': + resolution: {integrity: sha512-hWtDOj2B0AuRTf+nkMJeodnFpDpmEK9OIhIv1YxcRe73ooaxrIdjgugkElO8I9Tj0E4/7m117ezhWDUkbqm1zA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + peerDependencies: + '@angular/common': 19.2.14 + '@angular/core': 19.2.14 + '@angular/platform-browser': 19.2.14 + rxjs: ^6.5.3 || ^7.4.0 + + '@angular/platform-browser@19.2.14': + resolution: {integrity: sha512-hzkT5nmA64oVBQl6PRjdL4dIFT1n7lfM9rm5cAoS+6LUUKRgiE2d421Kpn/Hz3jaCJfo+calMIdtSMIfUJBmww==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + peerDependencies: + '@angular/animations': 19.2.14 + '@angular/common': 19.2.14 + '@angular/core': 19.2.14 + peerDependenciesMeta: + '@angular/animations': + optional: true + + '@angular/router@19.2.14': + resolution: {integrity: sha512-cBTWY9Jx7YhbmDYDb7Hqz4Q7UNIMlKTkdKToJd2pbhIXyoS+kHVQrySmyca+jgvYMjWnIjsAEa3dpje12D4mFw==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + peerDependencies: + '@angular/common': 19.2.14 + '@angular/core': 19.2.14 + '@angular/platform-browser': 19.2.14 + rxjs: ^6.5.3 || ^7.4.0 + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.27.5': + resolution: {integrity: sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.26.10': + resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.26.9': + resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.27.4': + resolution: {integrity: sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.26.10': + resolution: {integrity: sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.27.5': + resolution: {integrity: sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.25.9': + resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.27.1': + resolution: {integrity: sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.27.1': + resolution: {integrity: sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.4': + resolution: {integrity: sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-member-expression-to-functions@7.27.1': + resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.27.3': + resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-split-export-declaration@7.24.7': + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.27.1': + resolution: {integrity: sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.27.6': + resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.27.5': + resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1': + resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1': + resolution: {integrity: sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.27.1': + resolution: {integrity: sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.26.0': + resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.26.8': + resolution: {integrity: sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.25.9': + resolution: {integrity: sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.27.5': + resolution: {integrity: sha512-JF6uE2s67f0y2RZcm2kpAUEbD50vH62TyWVebxwHAlbSdM49VqPz8t4a1uIjp4NIOIZ4xzLfjY5emt/RCyC7TQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.27.1': + resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.27.1': + resolution: {integrity: sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.27.1': + resolution: {integrity: sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.27.1': + resolution: {integrity: sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.27.3': + resolution: {integrity: sha512-s4Jrok82JpiaIprtY2nHsYmrThKvvwgHwjgd7UMiYhZaN0asdXNLr0y+NjTfkA7SyQE5i2Fb7eawUOZmLvyqOA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.27.1': + resolution: {integrity: sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1': + resolution: {integrity: sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.27.1': + resolution: {integrity: sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.27.1': + resolution: {integrity: sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.27.1': + resolution: {integrity: sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.27.1': + resolution: {integrity: sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1': + resolution: {integrity: sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1': + resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.27.1': + resolution: {integrity: sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.27.3': + resolution: {integrity: sha512-7ZZtznF9g4l2JCImCo5LNKFHB5eXnN39lLtLY5Tg+VkR0jwOt7TBciMckuiQIOIW7L5tkQOCh3bVGYeXgMx52Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.27.1': + resolution: {integrity: sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.27.1': + resolution: {integrity: sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.27.1': + resolution: {integrity: sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.27.1': + resolution: {integrity: sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.27.1': + resolution: {integrity: sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.27.5': + resolution: {integrity: sha512-uhB8yHerfe3MWnuLAhEbeQ4afVoqv8BQsPqrTv7e/jZ9y00kJL6l9a/f4OWaKxotmjzewfEyXE1vgDJenkQ2/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.27.1': + resolution: {integrity: sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-runtime@7.26.10': + resolution: {integrity: sha512-NWaL2qG6HRpONTnj4JvDU6th4jYeZOJgu3QhmFTCihib0ermtOJqktA5BduGm3suhhVe9EMP9c9+mfJ/I9slqw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.27.1': + resolution: {integrity: sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.27.1': + resolution: {integrity: sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.27.1': + resolution: {integrity: sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.26.9': + resolution: {integrity: sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/runtime@7.26.10': + resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.27.4': + resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.27.6': + resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} + engines: {node: '>=6.9.0'} + + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + + '@discoveryjs/json-ext@0.6.3': + resolution: {integrity: sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==} + engines: {node: '>=14.17.0'} + + '@esbuild/aix-ppc64@0.25.4': + resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.4': + resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.4': + resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.4': + resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.4': + resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.4': + resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.4': + resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.4': + resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.4': + resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.4': + resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.4': + resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.4': + resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.4': + resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.4': + resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.4': + resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.4': + resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.4': + resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.4': + resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.4': + resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.4': + resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.4': + resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.4': + resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.4': + resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.4': + resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.4': + resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@inquirer/checkbox@4.1.8': + resolution: {integrity: sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@5.1.12': + resolution: {integrity: sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@5.1.6': + resolution: {integrity: sha512-6ZXYK3M1XmaVBZX6FCfChgtponnL0R6I7k8Nu+kaoNkT828FVZTcca1MqmWQipaW2oNREQl5AaPCUOOCVNdRMw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@10.1.13': + resolution: {integrity: sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@4.2.13': + resolution: {integrity: sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@4.0.15': + resolution: {integrity: sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@1.0.12': + resolution: {integrity: sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==} + engines: {node: '>=18'} + + '@inquirer/input@4.1.12': + resolution: {integrity: sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@3.0.15': + resolution: {integrity: sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@4.0.15': + resolution: {integrity: sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@7.3.2': + resolution: {integrity: sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@4.1.3': + resolution: {integrity: sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@3.0.15': + resolution: {integrity: sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@4.2.3': + resolution: {integrity: sha512-OAGhXU0Cvh0PhLz9xTF/kx6g6x+sP+PcyTiLvCrewI99P3BBeexD+VbuwkNDvqGkk3y2h5ZiWLeRP7BFlhkUDg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@1.5.5': + resolution: {integrity: sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==} + engines: {node: '>=18'} + + '@inquirer/type@3.0.7': + resolution: {integrity: sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.6': + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@jsonjoy.com/base64@1.1.2': + resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@1.2.0': + resolution: {integrity: sha512-io1zEbbYcElht3tdlqEOFxZ0dMTYrHz9iMf0gqn1pPjZFTCgM5R4R5IMA20Chb2UPYYsxjzs8CgZ7Nb5n2K2rA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@1.6.0': + resolution: {integrity: sha512-sw/RMbehRhN68WRtcKCpQOPfnH6lLP4GJfqzi3iYej8tnzpZUDr6UkZYJjcjjC0FWEJOJbyM3PTIwxucUmDG2A==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@leichtgewicht/ip-codec@2.0.5': + resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} + + '@listr2/prompt-adapter-inquirer@2.0.18': + resolution: {integrity: sha512-0hz44rAcrphyXcA8IS7EJ2SCoaBZD2u5goE8S/e+q/DL+dOGpqpcLidVOFeLG3VgML62SXmfRLAhWt0zL1oW4Q==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@inquirer/prompts': '>= 3 < 8' + + '@lmdb/lmdb-darwin-arm64@3.2.6': + resolution: {integrity: sha512-yF/ih9EJJZc72psFQbwnn8mExIWfTnzWJg+N02hnpXtDPETYLmQswIMBn7+V88lfCaFrMozJsUvcEQIkEPU0Gg==} + cpu: [arm64] + os: [darwin] + + '@lmdb/lmdb-darwin-x64@3.2.6': + resolution: {integrity: sha512-5BbCumsFLbCi586Bb1lTWQFkekdQUw8/t8cy++Uq251cl3hbDIGEwD9HAwh8H6IS2F6QA9KdKmO136LmipRNkg==} + cpu: [x64] + os: [darwin] + + '@lmdb/lmdb-linux-arm64@3.2.6': + resolution: {integrity: sha512-l5VmJamJ3nyMmeD1ANBQCQqy7do1ESaJQfKPSm2IG9/ADZryptTyCj8N6QaYgIWewqNUrcbdMkJajRQAt5Qjfg==} + cpu: [arm64] + os: [linux] + + '@lmdb/lmdb-linux-arm@3.2.6': + resolution: {integrity: sha512-+6XgLpMb7HBoWxXj+bLbiiB4s0mRRcDPElnRS3LpWRzdYSe+gFk5MT/4RrVNqd2MESUDmb53NUXw1+BP69bjiQ==} + cpu: [arm] + os: [linux] + + '@lmdb/lmdb-linux-x64@3.2.6': + resolution: {integrity: sha512-nDYT8qN9si5+onHYYaI4DiauDMx24OAiuZAUsEqrDy+ja/3EbpXPX/VAkMV8AEaQhy3xc4dRC+KcYIvOFefJ4Q==} + cpu: [x64] + os: [linux] + + '@lmdb/lmdb-win32-x64@3.2.6': + resolution: {integrity: sha512-XlqVtILonQnG+9fH2N3Aytria7P/1fwDgDhl29rde96uH2sLB8CHORIf2PfuLVzFQJ7Uqp8py9AYwr3ZUCFfWg==} + cpu: [x64] + os: [win32] + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} + cpu: [arm64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} + cpu: [x64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} + cpu: [arm64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} + cpu: [arm] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} + cpu: [x64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} + cpu: [x64] + os: [win32] + + '@napi-rs/nice-android-arm-eabi@1.0.1': + resolution: {integrity: sha512-5qpvOu5IGwDo7MEKVqqyAxF90I6aLj4n07OzpARdgDRfz8UbBztTByBp0RC59r3J1Ij8uzYi6jI7r5Lws7nn6w==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + + '@napi-rs/nice-android-arm64@1.0.1': + resolution: {integrity: sha512-GqvXL0P8fZ+mQqG1g0o4AO9hJjQaeYG84FRfZaYjyJtZZZcMjXW5TwkL8Y8UApheJgyE13TQ4YNUssQaTgTyvA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@napi-rs/nice-darwin-arm64@1.0.1': + resolution: {integrity: sha512-91k3HEqUl2fsrz/sKkuEkscj6EAj3/eZNCLqzD2AA0TtVbkQi8nqxZCZDMkfklULmxLkMxuUdKe7RvG/T6s2AA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/nice-darwin-x64@1.0.1': + resolution: {integrity: sha512-jXnMleYSIR/+TAN/p5u+NkCA7yidgswx5ftqzXdD5wgy/hNR92oerTXHc0jrlBisbd7DpzoaGY4cFD7Sm5GlgQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/nice-freebsd-x64@1.0.1': + resolution: {integrity: sha512-j+iJ/ezONXRQsVIB/FJfwjeQXX7A2tf3gEXs4WUGFrJjpe/z2KB7sOv6zpkm08PofF36C9S7wTNuzHZ/Iiccfw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@napi-rs/nice-linux-arm-gnueabihf@1.0.1': + resolution: {integrity: sha512-G8RgJ8FYXYkkSGQwywAUh84m946UTn6l03/vmEXBYNJxQJcD+I3B3k5jmjFG/OPiU8DfvxutOP8bi+F89MCV7Q==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@napi-rs/nice-linux-arm64-gnu@1.0.1': + resolution: {integrity: sha512-IMDak59/W5JSab1oZvmNbrms3mHqcreaCeClUjwlwDr0m3BoR09ZiN8cKFBzuSlXgRdZ4PNqCYNeGQv7YMTjuA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@napi-rs/nice-linux-arm64-musl@1.0.1': + resolution: {integrity: sha512-wG8fa2VKuWM4CfjOjjRX9YLIbysSVV1S3Kgm2Fnc67ap/soHBeYZa6AGMeR5BJAylYRjnoVOzV19Cmkco3QEPw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@napi-rs/nice-linux-ppc64-gnu@1.0.1': + resolution: {integrity: sha512-lxQ9WrBf0IlNTCA9oS2jg/iAjQyTI6JHzABV664LLrLA/SIdD+I1i3Mjf7TsnoUbgopBcCuDztVLfJ0q9ubf6Q==} + engines: {node: '>= 10'} + cpu: [ppc64] + os: [linux] + + '@napi-rs/nice-linux-riscv64-gnu@1.0.1': + resolution: {integrity: sha512-3xs69dO8WSWBb13KBVex+yvxmUeEsdWexxibqskzoKaWx9AIqkMbWmE2npkazJoopPKX2ULKd8Fm9veEn0g4Ig==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + + '@napi-rs/nice-linux-s390x-gnu@1.0.1': + resolution: {integrity: sha512-lMFI3i9rlW7hgToyAzTaEybQYGbQHDrpRkg+1gJWEpH0PLAQoZ8jiY0IzakLfNWnVda1eTYYlxxFYzW8Rqczkg==} + engines: {node: '>= 10'} + cpu: [s390x] + os: [linux] + + '@napi-rs/nice-linux-x64-gnu@1.0.1': + resolution: {integrity: sha512-XQAJs7DRN2GpLN6Fb+ZdGFeYZDdGl2Fn3TmFlqEL5JorgWKrQGRUrpGKbgZ25UeZPILuTKJ+OowG2avN8mThBA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@napi-rs/nice-linux-x64-musl@1.0.1': + resolution: {integrity: sha512-/rodHpRSgiI9o1faq9SZOp/o2QkKQg7T+DK0R5AkbnI/YxvAIEHf2cngjYzLMQSQgUhxym+LFr+UGZx4vK4QdQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@napi-rs/nice-win32-arm64-msvc@1.0.1': + resolution: {integrity: sha512-rEcz9vZymaCB3OqEXoHnp9YViLct8ugF+6uO5McifTedjq4QMQs3DHz35xBEGhH3gJWEsXMUbzazkz5KNM5YUg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/nice-win32-ia32-msvc@1.0.1': + resolution: {integrity: sha512-t7eBAyPUrWL8su3gDxw9xxxqNwZzAqKo0Szv3IjVQd1GpXXVkb6vBBQUuxfIYaXMzZLwlxRQ7uzM2vdUE9ULGw==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@napi-rs/nice-win32-x64-msvc@1.0.1': + resolution: {integrity: sha512-JlF+uDcatt3St2ntBG8H02F1mM45i5SF9W+bIKiReVE6wiy3o16oBP/yxt+RZ+N6LbCImJXJ6bXNO2kn9AXicg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/nice@1.0.1': + resolution: {integrity: sha512-zM0mVWSXE0a0h9aKACLwKmD6nHcRiKrPpCfvaKqG1CqDEyjEawId0ocXxVzPMCAm6kkWr2P025msfxXEnt8UGQ==} + engines: {node: '>= 10'} + + '@ng-icons/core@31.4.0': + resolution: {integrity: sha512-JfLiJGDX/ihWmawcnLGXtwyCqMi2qXz7gMJyXXWdUN5JA18EAnt3JnyuxDAGkoU/u7wRlcOI7irlXHU4spAKOg==} + peerDependencies: + '@angular/common': '>=18.0.0' + '@angular/core': '>=18.0.0' + rxjs: ^6.5.3 || ^7.4.0 + + '@ng-icons/material-file-icons@31.4.0': + resolution: {integrity: sha512-Ffh61ghuuDRxelfTe/rHQ5IFCqUget/JeZ/NLq6QWLBycxUC6PjiEIIAXQvnVmYwCHNgxjBIRExP1/+vdHriNQ==} + + '@ng-icons/material-icons@31.4.0': + resolution: {integrity: sha512-JCxwM0LXwOgT5LD99p5TwPM6dPQ5x1BGieNzAstz7vk5+aiASg3fqs3rjNx7CbN3c2QjJ8+KuKrCCBzT9DCkOQ==} + + '@ngtools/webpack@19.2.15': + resolution: {integrity: sha512-H37nop/wWMkSgoU2VvrMzanHePdLRRrX52nC5tT2ZhH3qP25+PrnMyw11PoLDLv3iWXC68uB1AiKNIT+jiQbuQ==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + '@angular/compiler-cli': ^19.0.0 || ^19.2.0-next.0 + typescript: '>=5.5 <5.9' + webpack: ^5.54.0 + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@npmcli/agent@3.0.0': + resolution: {integrity: sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@npmcli/fs@4.0.0': + resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@npmcli/git@6.0.3': + resolution: {integrity: sha512-GUYESQlxZRAdhs3UhbB6pVRNUELQOHXwK9ruDkwmCv2aZ5y0SApQzUJCg02p3A7Ue2J5hxvlk1YI53c00NmRyQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@npmcli/installed-package-contents@3.0.0': + resolution: {integrity: sha512-fkxoPuFGvxyrH+OQzyTkX2LUEamrF4jZSmxjAtPPHHGO0dqsQ8tTKjnIS8SAnPHdk2I03BDtSMR5K/4loKg79Q==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + '@npmcli/node-gyp@4.0.0': + resolution: {integrity: sha512-+t5DZ6mO/QFh78PByMq1fGSAub/agLJZDRfJRMeOSNCt8s9YVlTjmGpIPwPhvXTGUIJk+WszlT0rQa1W33yzNA==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@npmcli/package-json@6.2.0': + resolution: {integrity: sha512-rCNLSB/JzNvot0SEyXqWZ7tX2B5dD2a1br2Dp0vSYVo5jh8Z0EZ7lS9TsZ1UtziddB1UfNUaMCc538/HztnJGA==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@npmcli/promise-spawn@8.0.2': + resolution: {integrity: sha512-/bNJhjc+o6qL+Dwz/bqfTQClkEO5nTQ1ZEcdCkAQjhkZMHIh22LPG7fNh1enJP1NKWDqYiiABnjFCY7E0zHYtQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@npmcli/redact@3.2.2': + resolution: {integrity: sha512-7VmYAmk4csGv08QzrDKScdzn11jHPFGyqJW39FyPgPuAp3zIaUmuCo1yxw9aGs+NEJuTGQ9Gwqpt93vtJubucg==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@npmcli/run-script@9.1.0': + resolution: {integrity: sha512-aoNSbxtkePXUlbZB+anS1LqsJdctG5n3UVhfU47+CDdwMi6uNTBMF9gPcQRnqghQd2FGzcwwIFBruFMxjhBewg==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@primeng/themes@19.1.3': + resolution: {integrity: sha512-y4VryHHUTPWlmfR56NBANC0QPIxEngTUE/J3pGs4SJquq1n5EE/U16dxa1qW/wXqLF3jn3l/AO/4KZqGj5UuAA==} + + '@primeuix/styled@0.3.2': + resolution: {integrity: sha512-ColZes0+/WKqH4ob2x8DyNYf1NENpe5ZguOvx5yCLxaP8EIMVhLjWLO/3umJiDnQU4XXMLkn2mMHHw+fhTX/mw==} + engines: {node: '>=12.11.0'} + + '@primeuix/utils@0.3.2': + resolution: {integrity: sha512-B+nphqTQeq+i6JuICLdVWnDMjONome2sNz0xI65qIOyeB4EF12CoKRiCsxuZ5uKAkHi/0d1LqlQ9mIWRSdkavw==} + engines: {node: '>=12.11.0'} + + '@rollup/rollup-android-arm-eabi@4.34.8': + resolution: {integrity: sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.34.8': + resolution: {integrity: sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.34.8': + resolution: {integrity: sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.34.8': + resolution: {integrity: sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.34.8': + resolution: {integrity: sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.34.8': + resolution: {integrity: sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.34.8': + resolution: {integrity: sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.34.8': + resolution: {integrity: sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.34.8': + resolution: {integrity: sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.34.8': + resolution: {integrity: sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.34.8': + resolution: {integrity: sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': + resolution: {integrity: sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.34.8': + resolution: {integrity: sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.34.8': + resolution: {integrity: sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.34.8': + resolution: {integrity: sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.34.8': + resolution: {integrity: sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.34.8': + resolution: {integrity: sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.34.8': + resolution: {integrity: sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.34.8': + resolution: {integrity: sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==} + cpu: [x64] + os: [win32] + + '@schematics/angular@19.2.15': + resolution: {integrity: sha512-dz/eoFQKG09POSygpEDdlCehFIMo35HUM2rVV8lx9PfQEibpbGwl1NNQYEbqwVjTyCyD/ILyIXCWPE+EfTnG4g==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + + '@sigstore/bundle@3.1.0': + resolution: {integrity: sha512-Mm1E3/CmDDCz3nDhFKTuYdB47EdRFRQMOE/EAbiG1MJW77/w1b3P7Qx7JSrVJs8PfwOLOVcKQCHErIwCTyPbag==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@sigstore/core@2.0.0': + resolution: {integrity: sha512-nYxaSb/MtlSI+JWcwTHQxyNmWeWrUXJJ/G4liLrGG7+tS4vAz6LF3xRXqLH6wPIVUoZQel2Fs4ddLx4NCpiIYg==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@sigstore/protobuf-specs@0.4.3': + resolution: {integrity: sha512-fk2zjD9117RL9BjqEwF7fwv7Q/P9yGsMV4MUJZ/DocaQJ6+3pKr+syBq1owU5Q5qGw5CUbXzm+4yJ2JVRDQeSA==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@sigstore/sign@3.1.0': + resolution: {integrity: sha512-knzjmaOHOov1Ur7N/z4B1oPqZ0QX5geUfhrVaqVlu+hl0EAoL4o+l0MSULINcD5GCWe3Z0+YJO8ues6vFlW0Yw==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@sigstore/tuf@3.1.1': + resolution: {integrity: sha512-eFFvlcBIoGwVkkwmTi/vEQFSva3xs5Ot3WmBcjgjVdiaoelBLQaQ/ZBfhlG0MnG0cmTYScPpk7eDdGDWUcFUmg==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@sigstore/verify@2.1.1': + resolution: {integrity: sha512-hVJD77oT67aowHxwT4+M6PGOp+E2LtLdTK3+FC0lBO9T7sYwItDMXZ7Z07IDCvR1M717a4axbIWckrW67KMP/w==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@sindresorhus/merge-streams@2.3.0': + resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} + engines: {node: '>=18'} + + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + + '@tailwindcss/node@4.1.10': + resolution: {integrity: sha512-2ACf1znY5fpRBwRhMgj9ZXvb2XZW8qs+oTfotJ2C5xR0/WNL7UHZ7zXl6s+rUqedL1mNi+0O+WQr5awGowS3PQ==} + + '@tailwindcss/oxide-android-arm64@4.1.10': + resolution: {integrity: sha512-VGLazCoRQ7rtsCzThaI1UyDu/XRYVyH4/EWiaSX6tFglE+xZB5cvtC5Omt0OQ+FfiIVP98su16jDVHDEIuH4iQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.10': + resolution: {integrity: sha512-ZIFqvR1irX2yNjWJzKCqTCcHZbgkSkSkZKbRM3BPzhDL/18idA8uWCoopYA2CSDdSGFlDAxYdU2yBHwAwx8euQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.10': + resolution: {integrity: sha512-eCA4zbIhWUFDXoamNztmS0MjXHSEJYlvATzWnRiTqJkcUteSjO94PoRHJy1Xbwp9bptjeIxxBHh+zBWFhttbrQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.10': + resolution: {integrity: sha512-8/392Xu12R0cc93DpiJvNpJ4wYVSiciUlkiOHOSOQNH3adq9Gi/dtySK7dVQjXIOzlpSHjeCL89RUUI8/GTI6g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.10': + resolution: {integrity: sha512-t9rhmLT6EqeuPT+MXhWhlRYIMSfh5LZ6kBrC4FS6/+M1yXwfCtp24UumgCWOAJVyjQwG+lYva6wWZxrfvB+NhQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.10': + resolution: {integrity: sha512-3oWrlNlxLRxXejQ8zImzrVLuZ/9Z2SeKoLhtCu0hpo38hTO2iL86eFOu4sVR8cZc6n3z7eRXXqtHJECa6mFOvA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.10': + resolution: {integrity: sha512-saScU0cmWvg/Ez4gUmQWr9pvY9Kssxt+Xenfx1LG7LmqjcrvBnw4r9VjkFcqmbBb7GCBwYNcZi9X3/oMda9sqQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.10': + resolution: {integrity: sha512-/G3ao/ybV9YEEgAXeEg28dyH6gs1QG8tvdN9c2MNZdUXYBaIY/Gx0N6RlJzfLy/7Nkdok4kaxKPHKJUlAaoTdA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.10': + resolution: {integrity: sha512-LNr7X8fTiKGRtQGOerSayc2pWJp/9ptRYAa4G+U+cjw9kJZvkopav1AQc5HHD+U364f71tZv6XamaHKgrIoVzA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.10': + resolution: {integrity: sha512-d6ekQpopFQJAcIK2i7ZzWOYGZ+A6NzzvQ3ozBvWFdeyqfOZdYHU66g5yr+/HC4ipP1ZgWsqa80+ISNILk+ae/Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.10': + resolution: {integrity: sha512-i1Iwg9gRbwNVOCYmnigWCCgow8nDWSFmeTUU5nbNx3rqbe4p0kRbEqLwLJbYZKmSSp23g4N6rCDmm7OuPBXhDA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.10': + resolution: {integrity: sha512-sGiJTjcBSfGq2DVRtaSljq5ZgZS2SDHSIfhOylkBvHVjwOsodBhnb3HdmiKkVuUGKD0I7G63abMOVaskj1KpOA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.10': + resolution: {integrity: sha512-v0C43s7Pjw+B9w21htrQwuFObSkio2aV/qPx/mhrRldbqxbWJK6KizM+q7BF1/1CmuLqZqX3CeYF7s7P9fbA8Q==} + engines: {node: '>= 10'} + + '@tailwindcss/postcss@4.1.10': + resolution: {integrity: sha512-B+7r7ABZbkXJwpvt2VMnS6ujcDoR2OOcFaqrLIo1xbcdxje4Vf+VgJdBzNNbrAjBj/rLZ66/tlQ1knIGNLKOBQ==} + + '@tufjs/canonical-json@2.0.0': + resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@tufjs/models@3.0.1': + resolution: {integrity: sha512-UUYHISyhCU3ZgN8yaear3cGATHb3SMuKHsQ/nVbHXcmnBf+LzQ/cQfhNG+rfaSHgqGKNEm2cOCLVLELStUQ1JA==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/bonjour@3.5.13': + resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} + + '@types/connect-history-api-fallback@1.5.4': + resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/cors@2.8.19': + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} + + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + + '@types/express-serve-static-core@4.19.6': + resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==} + + '@types/express@4.17.23': + resolution: {integrity: sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + + '@types/http-proxy@1.17.16': + resolution: {integrity: sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==} + + '@types/jasmine@5.1.8': + resolution: {integrity: sha512-u7/CnvRdh6AaaIzYjCgUuVbREFgulhX05Qtf6ZtW+aOcjCKKVvKgpkPYJBFTZSHtFBYimzU4zP0V2vrEsq9Wcg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/node-forge@1.3.11': + resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} + + '@types/node@24.0.3': + resolution: {integrity: sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==} + + '@types/qs@6.14.0': + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/retry@0.12.2': + resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} + + '@types/send@0.17.5': + resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} + + '@types/serve-index@1.9.4': + resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} + + '@types/serve-static@1.15.8': + resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==} + + '@types/sockjs@0.3.36': + resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@vitejs/plugin-basic-ssl@1.2.0': + resolution: {integrity: sha512-mkQnxTkcldAzIsomk1UuLfAu9n+kpQ3JbHcpCp7d2Oo6ITtji8pHS3QToOWjhPFvNQSnhlkAjmGbhv2QvwO/7Q==} + engines: {node: '>=14.21.3'} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 + + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + '@yarnpkg/lockfile@1.1.0': + resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} + + abbrev@3.0.1: + resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} + engines: {node: ^18.17.0 || >=20.5.0} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + adjust-sourcemap-loader@4.0.0: + resolution: {integrity: sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==} + engines: {node: '>=8.9'} + + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-escapes@7.0.0: + resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + engines: {node: '>=18'} + + ansi-html-community@0.0.8: + resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} + engines: {'0': node >= 0.8.0} + hasBin: true + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + babel-loader@9.2.1: + resolution: {integrity: sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==} + engines: {node: '>= 14.15.0'} + peerDependencies: + '@babel/core': ^7.12.0 + webpack: '>=5' + + babel-plugin-polyfill-corejs2@0.4.13: + resolution: {integrity: sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.11.1: + resolution: {integrity: sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.4: + resolution: {integrity: sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + base64id@2.0.0: + resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} + engines: {node: ^4.5.0 || >= 5.9} + + batch@0.6.1: + resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + + beasties@0.3.2: + resolution: {integrity: sha512-p4AF8uYzm9Fwu8m/hSVTCPXrRBPmB34hQpHsec2KOaR9CZmgoU8IOv4Cvwq4hgz2p4hLMNbsdNl5XeA6XbAQwA==} + engines: {node: '>=14.0.0'} + + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + body-parser@1.20.3: + resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + bonjour-service@1.3.0: + resolution: {integrity: sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.25.0: + resolution: {integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cacache@19.0.1: + resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001723: + resolution: {integrity: sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone-deep@4.0.1: + resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} + engines: {node: '>=6'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + common-path-prefix@3.0.0: + resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} + + compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + + compression@1.8.0: + resolution: {integrity: sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==} + engines: {node: '>= 0.8.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + connect-history-api-fallback@2.0.0: + resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} + engines: {node: '>=0.8'} + + connect@3.7.0: + resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} + engines: {node: '>= 0.10.0'} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} + engines: {node: '>= 0.6'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + copy-anything@2.0.6: + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + + copy-webpack-plugin@12.0.2: + resolution: {integrity: sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + webpack: ^5.1.0 + + core-js-compat@3.43.0: + resolution: {integrity: sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-loader@7.1.2: + resolution: {integrity: sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + webpack: ^5.27.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + custom-event@1.0.1: + resolution: {integrity: sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==} + + date-format@4.0.14: + resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} + engines: {node: '>=4.0'} + + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + default-browser-id@5.0.0: + resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} + engines: {node: '>=18'} + + default-browser@5.2.1: + resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} + engines: {node: '>=18'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + + di@0.0.1: + resolution: {integrity: sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==} + + dns-packet@5.6.1: + resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} + engines: {node: '>=6'} + + dom-serialize@2.2.1: + resolution: {integrity: sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.167: + resolution: {integrity: sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==} + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + engine.io@6.6.4: + resolution: {integrity: sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==} + engines: {node: '>=10.2.0'} + + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + + ent@2.2.2: + resolution: {integrity: sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==} + engines: {node: '>= 0.4'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + + errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + esbuild-wasm@0.25.4: + resolution: {integrity: sha512-2HlCS6rNvKWaSKhWaG/YIyRsTsL3gUrMP2ToZMBIjw9LM7vVcIs+rz8kE2vExvTJgvM8OKPqNpcHawY/BQc/qQ==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.25.4: + resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + exponential-backoff@3.1.2: + resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==} + + express@4.21.2: + resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} + engines: {node: '>= 0.10.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-uri@3.0.6: + resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + + finalhandler@1.3.1: + resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} + engines: {node: '>= 0.8'} + + find-cache-dir@4.0.0: + resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==} + engines: {node: '>=14.16'} + + find-up@6.3.0: + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globby@14.1.0: + resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} + engines: {node: '>=18'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + handle-thing@2.0.1: + resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hosted-git-info@8.1.0: + resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} + engines: {node: ^18.17.0 || >=20.5.0} + + hpack.js@2.1.6: + resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + htmlparser2@10.0.0: + resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} + + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + http-deceiver@1.2.7: + resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} + + http-errors@1.6.3: + resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} + engines: {node: '>= 0.6'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + http-parser-js@0.5.10: + resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + http-proxy-middleware@2.0.9: + resolution: {integrity: sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/express': ^4.17.13 + peerDependenciesMeta: + '@types/express': + optional: true + + http-proxy-middleware@3.0.5: + resolution: {integrity: sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + hyperdyperid@1.2.0: + resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} + engines: {node: '>=10.18'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + icss-utils@5.1.0: + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore-walk@7.0.0: + resolution: {integrity: sha512-T4gbf83A4NH95zvhVYZc+qWocBBGlpzUXLPGurJggw/WIOwicfXJChLDP/iBZnN5WqROSu5Bm3hhle4z8a8YGQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + image-size@0.5.5: + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + engines: {node: '>=0.10.0'} + hasBin: true + + immutable@5.1.3: + resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@5.0.0: + resolution: {integrity: sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==} + engines: {node: ^18.17.0 || >=20.5.0} + + ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + ipaddr.js@2.2.0: + resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + engines: {node: '>= 10'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-network-error@1.1.0: + resolution: {integrity: sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==} + engines: {node: '>=16'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@3.0.0: + resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} + engines: {node: '>=10'} + + is-plain-object@2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-what@3.14.1: + resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isbinaryfile@4.0.10: + resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} + engines: {node: '>= 8.0.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + + isobject@3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jasmine-core@4.6.1: + resolution: {integrity: sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==} + + jasmine-core@5.7.1: + resolution: {integrity: sha512-QnurrtpKsPoixxG2R3d1xP0St/2kcX5oTZyDyQJMY+Vzi/HUlu1kGm+2V8Tz+9lV991leB1l0xcsyz40s9xOOw==} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + + jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-parse-even-better-errors@4.0.0: + resolution: {integrity: sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==} + engines: {node: ^18.17.0 || >=20.5.0} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + karma-chrome-launcher@3.2.0: + resolution: {integrity: sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==} + + karma-coverage@2.2.1: + resolution: {integrity: sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==} + engines: {node: '>=10.0.0'} + + karma-jasmine-html-reporter@2.1.0: + resolution: {integrity: sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==} + peerDependencies: + jasmine-core: ^4.0.0 || ^5.0.0 + karma: ^6.0.0 + karma-jasmine: ^5.0.0 + + karma-jasmine@5.1.0: + resolution: {integrity: sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==} + engines: {node: '>=12'} + peerDependencies: + karma: ^6.0.0 + + karma-source-map-support@1.4.0: + resolution: {integrity: sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==} + + karma@6.4.4: + resolution: {integrity: sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==} + engines: {node: '>= 10'} + hasBin: true + + keycloak-angular@19.0.2: + resolution: {integrity: sha512-GzQKC/jFJLZRmUxWOEXkla+6shDAZFAOe6Z3qsw916Ckb/UhZnO704HMZrd8xyVB3RH6xOcNCp45oHmIiqJ7dA==} + peerDependencies: + '@angular/common': ^19 + '@angular/core': ^19 + '@angular/router': ^19 + keycloak-js: ^18 || ^19 || ^20 || ^21 || ^22 || ^23 || ^24 || ^25 || ^26 + + keycloak-js@26.2.0: + resolution: {integrity: sha512-CrFcXTN+d6J0V/1v3Zpioys6qHNWE6yUzVVIsCUAmFn9H14GZ0vuYod+lt+SSpMgWGPuneDZBSGBAeLBFuqjsw==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + launch-editor@2.10.0: + resolution: {integrity: sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA==} + + less-loader@12.2.0: + resolution: {integrity: sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + less: ^3.5.0 || ^4.0.0 + webpack: ^5.0.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + less@4.2.2: + resolution: {integrity: sha512-tkuLHQlvWUTeQ3doAqnHbNn8T6WX1KA8yvbKG9x4VtKtIjHsVKQZCH11zRgAfbDAXC2UNIg/K9BYAAcEzUIrNg==} + engines: {node: '>=6'} + hasBin: true + + license-webpack-plugin@4.0.2: + resolution: {integrity: sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==} + peerDependencies: + webpack: '*' + peerDependenciesMeta: + webpack: + optional: true + + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + engines: {node: '>= 12.0.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + listr2@8.2.5: + resolution: {integrity: sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==} + engines: {node: '>=18.0.0'} + + lmdb@3.2.6: + resolution: {integrity: sha512-SuHqzPl7mYStna8WRotY8XX/EUZBjjv3QyKIByeCLFfC9uXT/OIHByEcA07PzbMfQAM0KYJtLgtpMRlIe5dErQ==} + hasBin: true + + loader-runner@4.3.0: + resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + engines: {node: '>=6.11.5'} + + loader-utils@2.0.4: + resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} + engines: {node: '>=8.9.0'} + + loader-utils@3.3.1: + resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} + engines: {node: '>= 12.13.0'} + + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + log4js@6.9.1: + resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==} + engines: {node: '>=8.0'} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-fetch-happen@14.0.3: + resolution: {integrity: sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + memfs@4.17.2: + resolution: {integrity: sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg==} + engines: {node: '>= 4.0.0'} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + mini-css-extract-plugin@2.9.2: + resolution: {integrity: sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass-collect@2.0.1: + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass-fetch@4.0.1: + resolution: {integrity: sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + msgpackr-extract@3.0.3: + resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} + hasBin: true + + msgpackr@1.11.4: + resolution: {integrity: sha512-uaff7RG9VIC4jacFW9xzL3jc0iM32DNHe4jYVycBcjUePT/Klnfj7pqtWJt9khvDFizmjN2TlYniYmSS2LIaZg==} + + multicast-dns@7.2.5: + resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} + hasBin: true + + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + needle@3.3.1: + resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} + engines: {node: '>= 4.4.x'} + hasBin: true + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + node-addon-api@6.1.0: + resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-forge@1.3.1: + resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + engines: {node: '>= 6.13.0'} + + node-gyp-build-optional-packages@5.2.2: + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} + hasBin: true + + node-gyp@11.2.0: + resolution: {integrity: sha512-T0S1zqskVUSxcsSTkAsLc7xCycrRYmtDHadDinzocrThjyQCn5kMlEBSj6H4qDbgsIOSLmmlRIeb0lZXj+UArA==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + nopt@8.1.0: + resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + npm-bundled@4.0.0: + resolution: {integrity: sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==} + engines: {node: ^18.17.0 || >=20.5.0} + + npm-install-checks@7.1.1: + resolution: {integrity: sha512-u6DCwbow5ynAX5BdiHQ9qvexme4U3qHW3MWe5NqH+NeBm0LbiH6zvGjNNew1fY+AZZUtVHbOPF3j7mJxbUzpXg==} + engines: {node: ^18.17.0 || >=20.5.0} + + npm-normalize-package-bin@4.0.0: + resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==} + engines: {node: ^18.17.0 || >=20.5.0} + + npm-package-arg@12.0.2: + resolution: {integrity: sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==} + engines: {node: ^18.17.0 || >=20.5.0} + + npm-packlist@9.0.0: + resolution: {integrity: sha512-8qSayfmHJQTx3nJWYbbUmflpyarbLMBc6LCAjYsiGtXxDB68HaZpb8re6zeaLGxZzDuMdhsg70jryJe+RrItVQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + npm-pick-manifest@10.0.0: + resolution: {integrity: sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + npm-registry-fetch@18.0.2: + resolution: {integrity: sha512-LeVMZBBVy+oQb5R6FDV9OlJCcWDU+al10oKpe+nsvcHnG24Z3uM3SvJYKfGJlfGjVU8v9liejCrUR/M5HO5NEQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + + on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.0.2: + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + open@10.1.0: + resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} + engines: {node: '>=18'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + ordered-binary@1.5.3: + resolution: {integrity: sha512-oGFr3T+pYdTGJ+YFEILMpS3es+GiIbs9h/XQrclBXUtd44ey7XwfsMzM31f64I1SQOawDoDr/D823kNCADI8TA==} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-map@7.0.3: + resolution: {integrity: sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==} + engines: {node: '>=18'} + + p-retry@6.2.1: + resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} + engines: {node: '>=16.17'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + pacote@20.0.0: + resolution: {integrity: sha512-pRjC5UFwZCgx9kUFDVM9YEahv4guZ1nSLqwmWiLUnDbGsjs+U5w7z6Uc8HNR1a6x8qnu5y9xtGE6D1uAuYz+0A==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-node-version@1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + + parse5-html-rewriting-stream@7.0.0: + resolution: {integrity: sha512-mazCyGWkmCRWDI15Zp+UiCqMp/0dgEmkZRvhlsqqKYr4SsVm/TvnSpD9fCvqCA2zoWJcfRym846ejWBBHRiYEg==} + + parse5-sax-parser@7.0.0: + resolution: {integrity: sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + + path-type@6.0.0: + resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} + engines: {node: '>=18'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + piscina@4.8.0: + resolution: {integrity: sha512-EZJb+ZxDrQf3dihsUL7p42pjNyrNIFJCrRHPMgxu/svsj+P3xS3fuEWp7k2+rfsavfl1N0G29b1HGs7J0m8rZA==} + + pkg-dir@7.0.0: + resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} + engines: {node: '>=14.16'} + + postcss-loader@8.1.1: + resolution: {integrity: sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + postcss: ^7.0.0 || ^8.0.1 + webpack: ^5.0.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + postcss-media-query-parser@0.2.3: + resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} + + postcss-modules-extract-imports@3.1.0: + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-local-by-default@4.2.0: + resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-scope@3.2.1: + resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-values@4.0.0: + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.2: + resolution: {integrity: sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + primeng@19.1.3: + resolution: {integrity: sha512-KsrvJFblKg28kA6d4npnnABjKClmJv0CgDT/kOxFq5onQNBy4547DJzRGMba4+CMLKjHWWkYWuC+XSkPMNFrZg==} + peerDependencies: + '@angular/animations': ^19.0.0 + '@angular/cdk': ^19.0.0 + '@angular/common': ^19.0.0 + '@angular/core': ^19.0.0 + '@angular/forms': ^19.0.0 + '@angular/platform-browser': ^19.0.0 + '@angular/router': ^19.0.0 + rxjs: ^6.0.0 || ^7.8.1 + + proc-log@5.0.0: + resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + + punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + + qjobs@1.2.0: + resolution: {integrity: sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==} + engines: {node: '>=0.9'} + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + + regenerate-unicode-properties@10.2.0: + resolution: {integrity: sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + regex-parser@2.3.1: + resolution: {integrity: sha512-yXLRqatcCuKtVHsWrNg0JL3l1zGfdXeEvDa0bdu4tCDQw0RpMDZsqbkyRTUnKMR0tXF627V2oEWjBEaEdqTwtQ==} + + regexpu-core@6.2.0: + resolution: {integrity: sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.12.0: + resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} + hasBin: true + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-url-loader@5.0.0: + resolution: {integrity: sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==} + engines: {node: '>=12'} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rollup@4.34.8: + resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-applescript@7.0.0: + resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} + engines: {node: '>=18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + run-script-os@1.1.6: + resolution: {integrity: sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==} + hasBin: true + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sass-loader@16.0.5: + resolution: {integrity: sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + sass: ^1.3.0 + sass-embedded: '*' + webpack: ^5.0.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + node-sass: + optional: true + sass: + optional: true + sass-embedded: + optional: true + webpack: + optional: true + + sass@1.85.0: + resolution: {integrity: sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww==} + engines: {node: '>=14.0.0'} + hasBin: true + + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + + schema-utils@4.3.2: + resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} + engines: {node: '>= 10.13.0'} + + select-hose@2.0.0: + resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} + + selfsigned@2.4.1: + resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} + engines: {node: '>=10'} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + engines: {node: '>= 0.8.0'} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + serve-index@1.9.1: + resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.2: + resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} + engines: {node: '>= 0.8.0'} + + setprototypeof@1.1.0: + resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shallow-clone@3.0.1: + resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} + engines: {node: '>=8'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sigstore@3.1.0: + resolution: {integrity: sha512-ZpzWAFHIFqyFE56dXqgX/DkDRZdz+rRcjoIk/RQU4IX0wiCv1l8S7ZrXDHcCc+uaf+6o7w3h2l3g6GYG5TKN9Q==} + engines: {node: ^18.17.0 || >=20.5.0} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socket.io-adapter@2.5.5: + resolution: {integrity: sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + + socket.io@4.8.1: + resolution: {integrity: sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==} + engines: {node: '>=10.2.0'} + + sockjs@0.3.24: + resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.5: + resolution: {integrity: sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-loader@5.0.0: + resolution: {integrity: sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + webpack: ^5.72.1 + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.21: + resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} + + spdy-transport@3.0.0: + resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} + + spdy@4.0.2: + resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} + engines: {node: '>=6.0.0'} + + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + + ssri@12.0.0: + resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + streamroller@3.1.5: + resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==} + engines: {node: '>=8.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + symbol-observable@4.0.0: + resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} + engines: {node: '>=0.10'} + + tailwindcss-primeui@0.6.1: + resolution: {integrity: sha512-T69Rylcrmnt8zy9ik+qZvsLuRIrS9/k6rYJSIgZ1trnbEzGDDQSCIdmfyZknevqiHwpSJHSmQ9XT2C+S/hJY4A==} + peerDependencies: + tailwindcss: '>=3.1.0' + + tailwindcss@4.1.10: + resolution: {integrity: sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA==} + + tapable@2.2.2: + resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + engines: {node: '>=6'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + + terser-webpack-plugin@5.3.14: + resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@5.39.0: + resolution: {integrity: sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==} + engines: {node: '>=10'} + hasBin: true + + thingies@1.21.0: + resolution: {integrity: sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==} + engines: {node: '>=10.18'} + peerDependencies: + tslib: ^2 + + thunky@1.1.0: + resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + tmp@0.2.3: + resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} + engines: {node: '>=14.14'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tree-dump@1.0.3: + resolution: {integrity: sha512-il+Cv80yVHFBwokQSfd4bldvr1Md951DpgAGfmhydt04L+YzHgubm2tQ7zueWDcGENKHq0ZvGFR/hjvNXilHEg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tuf-js@3.0.1: + resolution: {integrity: sha512-+68OP1ZzSF84rTckf3FA95vJ1Zlx/uaXyiiKyPd1pA4rZNkpEvDAKmsu1xUSmbF/chCRYgZ6UZkDwC7PmzmAyA==} + engines: {node: ^18.17.0 || >=20.5.0} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typed-assert@1.0.9: + resolution: {integrity: sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==} + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + ua-parser-js@0.7.40: + resolution: {integrity: sha512-us1E3K+3jJppDBa3Tl0L3MOJiGhe1C6P0+nIvQAFYbxlMAx0h81eOwLmU57xgqToduDDPx3y5QsdjPfDu+FgOQ==} + hasBin: true + + undici-types@7.8.0: + resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.0: + resolution: {integrity: sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.1.0: + resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} + engines: {node: '>=4'} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + unique-filename@4.0.0: + resolution: {integrity: sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + unique-slug@5.0.0: + resolution: {integrity: sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==} + engines: {node: ^18.17.0 || >=20.5.0} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + validate-npm-package-name@6.0.1: + resolution: {integrity: sha512-OaI//3H0J7ZkR1OqlhGA8cA+Cbk/2xFOQpJOt5+s27/ta9eZwpeervh4Mxh4w0im/kdgktowaqVNR7QOrUd7Yg==} + engines: {node: ^18.17.0 || >=20.5.0} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite@6.2.7: + resolution: {integrity: sha512-qg3LkeuinTrZoJHHF94coSaTfIPyBYoywp+ys4qu20oSJFbKMYoIJo0FWJT9q6Vp49l6z9IsJRbHdcGtiKbGoQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + void-elements@2.0.1: + resolution: {integrity: sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==} + engines: {node: '>=0.10.0'} + + watchpack@2.4.2: + resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==} + engines: {node: '>=10.13.0'} + + wbuf@1.7.3: + resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + weak-lru-cache@1.2.2: + resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==} + + webpack-dev-middleware@7.4.2: + resolution: {integrity: sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + webpack: ^5.0.0 + peerDependenciesMeta: + webpack: + optional: true + + webpack-dev-server@5.2.2: + resolution: {integrity: sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg==} + engines: {node: '>= 18.12.0'} + hasBin: true + peerDependencies: + webpack: ^5.0.0 + webpack-cli: '*' + peerDependenciesMeta: + webpack: + optional: true + webpack-cli: + optional: true + + webpack-merge@6.0.1: + resolution: {integrity: sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==} + engines: {node: '>=18.0.0'} + + webpack-sources@3.3.2: + resolution: {integrity: sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==} + engines: {node: '>=10.13.0'} + + webpack-subresource-integrity@5.1.0: + resolution: {integrity: sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==} + engines: {node: '>= 12'} + peerDependencies: + html-webpack-plugin: '>= 5.0.0-beta.1 < 6' + webpack: ^5.12.0 + peerDependenciesMeta: + html-webpack-plugin: + optional: true + + webpack@5.98.0: + resolution: {integrity: sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + websocket-driver@0.7.4: + resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@5.0.0: + resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + wildcard@2.0.1: + resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.2: + resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@1.2.1: + resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + engines: {node: '>=12.20'} + + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + + zone.js@0.15.1: + resolution: {integrity: sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + + '@angular-devkit/architect@0.1902.15(chokidar@4.0.3)': + dependencies: + '@angular-devkit/core': 19.2.15(chokidar@4.0.3) + rxjs: 7.8.1 + transitivePeerDependencies: + - chokidar + + '@angular-devkit/build-angular@19.2.15(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3))(@angular/compiler@19.2.14)(@types/node@24.0.3)(chokidar@4.0.3)(jiti@2.4.2)(karma@6.4.4)(lightningcss@1.30.1)(tailwindcss@4.1.10)(typescript@5.8.3)(vite@6.2.7(@types/node@24.0.3)(jiti@2.4.2)(less@4.2.2)(lightningcss@1.30.1)(sass@1.85.0)(terser@5.39.0))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@angular-devkit/architect': 0.1902.15(chokidar@4.0.3) + '@angular-devkit/build-webpack': 0.1902.15(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.4)) + '@angular-devkit/core': 19.2.15(chokidar@4.0.3) + '@angular/build': 19.2.15(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3))(@angular/compiler@19.2.14)(@types/node@24.0.3)(chokidar@4.0.3)(jiti@2.4.2)(karma@6.4.4)(less@4.2.2)(lightningcss@1.30.1)(postcss@8.5.2)(tailwindcss@4.1.10)(terser@5.39.0)(typescript@5.8.3) + '@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3) + '@babel/core': 7.26.10 + '@babel/generator': 7.26.10 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/plugin-transform-async-generator-functions': 7.26.8(@babel/core@7.26.10) + '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-runtime': 7.26.10(@babel/core@7.26.10) + '@babel/preset-env': 7.26.9(@babel/core@7.26.10) + '@babel/runtime': 7.26.10 + '@discoveryjs/json-ext': 0.6.3 + '@ngtools/webpack': 19.2.15(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.4)) + '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.2.7(@types/node@24.0.3)(jiti@2.4.2)(less@4.2.2)(lightningcss@1.30.1)(sass@1.85.0)(terser@5.39.0)) + ansi-colors: 4.1.3 + autoprefixer: 10.4.20(postcss@8.5.2) + babel-loader: 9.2.1(@babel/core@7.26.10)(webpack@5.98.0(esbuild@0.25.4)) + browserslist: 4.25.0 + copy-webpack-plugin: 12.0.2(webpack@5.98.0(esbuild@0.25.4)) + css-loader: 7.1.2(webpack@5.98.0(esbuild@0.25.4)) + esbuild-wasm: 0.25.4 + fast-glob: 3.3.3 + http-proxy-middleware: 3.0.5 + istanbul-lib-instrument: 6.0.3 + jsonc-parser: 3.3.1 + karma-source-map-support: 1.4.0 + less: 4.2.2 + less-loader: 12.2.0(less@4.2.2)(webpack@5.98.0(esbuild@0.25.4)) + license-webpack-plugin: 4.0.2(webpack@5.98.0(esbuild@0.25.4)) + loader-utils: 3.3.1 + mini-css-extract-plugin: 2.9.2(webpack@5.98.0(esbuild@0.25.4)) + open: 10.1.0 + ora: 5.4.1 + picomatch: 4.0.2 + piscina: 4.8.0 + postcss: 8.5.2 + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.4)) + resolve-url-loader: 5.0.0 + rxjs: 7.8.1 + sass: 1.85.0 + sass-loader: 16.0.5(sass@1.85.0)(webpack@5.98.0(esbuild@0.25.4)) + semver: 7.7.1 + source-map-loader: 5.0.0(webpack@5.98.0(esbuild@0.25.4)) + source-map-support: 0.5.21 + terser: 5.39.0 + tree-kill: 1.2.2 + tslib: 2.8.1 + typescript: 5.8.3 + webpack: 5.98.0(esbuild@0.25.4) + webpack-dev-middleware: 7.4.2(webpack@5.98.0) + webpack-dev-server: 5.2.2(webpack@5.98.0) + webpack-merge: 6.0.1 + webpack-subresource-integrity: 5.1.0(webpack@5.98.0(esbuild@0.25.4)) + optionalDependencies: + esbuild: 0.25.4 + karma: 6.4.4 + tailwindcss: 4.1.10 + transitivePeerDependencies: + - '@angular/compiler' + - '@rspack/core' + - '@swc/core' + - '@types/node' + - bufferutil + - chokidar + - debug + - html-webpack-plugin + - jiti + - lightningcss + - node-sass + - sass-embedded + - stylus + - sugarss + - supports-color + - tsx + - uglify-js + - utf-8-validate + - vite + - webpack-cli + - yaml + + '@angular-devkit/build-webpack@0.1902.15(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.4))': + dependencies: + '@angular-devkit/architect': 0.1902.15(chokidar@4.0.3) + rxjs: 7.8.1 + webpack: 5.98.0(esbuild@0.25.4) + webpack-dev-server: 5.2.2(webpack@5.98.0) + transitivePeerDependencies: + - chokidar + + '@angular-devkit/core@19.2.15(chokidar@4.0.3)': + dependencies: + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + jsonc-parser: 3.3.1 + picomatch: 4.0.2 + rxjs: 7.8.1 + source-map: 0.7.4 + optionalDependencies: + chokidar: 4.0.3 + + '@angular-devkit/schematics@19.2.15(chokidar@4.0.3)': + dependencies: + '@angular-devkit/core': 19.2.15(chokidar@4.0.3) + jsonc-parser: 3.3.1 + magic-string: 0.30.17 + ora: 5.4.1 + rxjs: 7.8.1 + transitivePeerDependencies: + - chokidar + + '@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))': + dependencies: + '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) + tslib: 2.8.1 + + '@angular/build@19.2.15(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3))(@angular/compiler@19.2.14)(@types/node@24.0.3)(chokidar@4.0.3)(jiti@2.4.2)(karma@6.4.4)(less@4.2.2)(lightningcss@1.30.1)(postcss@8.5.2)(tailwindcss@4.1.10)(terser@5.39.0)(typescript@5.8.3)': + dependencies: + '@ampproject/remapping': 2.3.0 + '@angular-devkit/architect': 0.1902.15(chokidar@4.0.3) + '@angular/compiler': 19.2.14 + '@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3) + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.10) + '@inquirer/confirm': 5.1.6(@types/node@24.0.3) + '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.2.7(@types/node@24.0.3)(jiti@2.4.2)(less@4.2.2)(lightningcss@1.30.1)(sass@1.85.0)(terser@5.39.0)) + beasties: 0.3.2 + browserslist: 4.25.0 + esbuild: 0.25.4 + fast-glob: 3.3.3 + https-proxy-agent: 7.0.6 + istanbul-lib-instrument: 6.0.3 + listr2: 8.2.5 + magic-string: 0.30.17 + mrmime: 2.0.1 + parse5-html-rewriting-stream: 7.0.0 + picomatch: 4.0.2 + piscina: 4.8.0 + rollup: 4.34.8 + sass: 1.85.0 + semver: 7.7.1 + source-map-support: 0.5.21 + typescript: 5.8.3 + vite: 6.2.7(@types/node@24.0.3)(jiti@2.4.2)(less@4.2.2)(lightningcss@1.30.1)(sass@1.85.0)(terser@5.39.0) + watchpack: 2.4.2 + optionalDependencies: + karma: 6.4.4 + less: 4.2.2 + lmdb: 3.2.6 + postcss: 8.5.2 + tailwindcss: 4.1.10 + transitivePeerDependencies: + - '@types/node' + - chokidar + - jiti + - lightningcss + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + '@angular/cdk@19.2.18(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': + dependencies: + '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) + parse5: 7.3.0 + rxjs: 7.8.2 + tslib: 2.8.1 + + '@angular/cli@19.2.15(@types/node@24.0.3)(chokidar@4.0.3)': + dependencies: + '@angular-devkit/architect': 0.1902.15(chokidar@4.0.3) + '@angular-devkit/core': 19.2.15(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.15(chokidar@4.0.3) + '@inquirer/prompts': 7.3.2(@types/node@24.0.3) + '@listr2/prompt-adapter-inquirer': 2.0.18(@inquirer/prompts@7.3.2(@types/node@24.0.3)) + '@schematics/angular': 19.2.15(chokidar@4.0.3) + '@yarnpkg/lockfile': 1.1.0 + ini: 5.0.0 + jsonc-parser: 3.3.1 + listr2: 8.2.5 + npm-package-arg: 12.0.2 + npm-pick-manifest: 10.0.0 + pacote: 20.0.0 + resolve: 1.22.10 + semver: 7.7.1 + symbol-observable: 4.0.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - chokidar + - supports-color + + '@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': + dependencies: + '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) + rxjs: 7.8.2 + tslib: 2.8.1 + + '@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3)': + dependencies: + '@angular/compiler': 19.2.14 + '@babel/core': 7.26.9 + '@jridgewell/sourcemap-codec': 1.5.0 + chokidar: 4.0.3 + convert-source-map: 1.9.0 + reflect-metadata: 0.2.2 + semver: 7.7.2 + tslib: 2.8.1 + typescript: 5.8.3 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + + '@angular/compiler@19.2.14': + dependencies: + tslib: 2.8.1 + + '@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)': + dependencies: + rxjs: 7.8.2 + tslib: 2.8.1 + zone.js: 0.15.1 + + '@angular/forms@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': + dependencies: + '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': 19.2.14(@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)) + rxjs: 7.8.2 + tslib: 2.8.1 + + '@angular/platform-browser@19.2.14(@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))': + dependencies: + '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) + tslib: 2.8.1 + optionalDependencies: + '@angular/animations': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)) + + '@angular/router@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': + dependencies: + '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': 19.2.14(@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)) + rxjs: 7.8.2 + tslib: 2.8.1 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.27.5': {} + + '@babel/core@7.26.10': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.26.10) + '@babel/helpers': 7.27.6 + '@babel/parser': 7.27.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/core@7.26.9': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.26.9) + '@babel/helpers': 7.27.6 + '@babel/parser': 7.27.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/core@7.27.4': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/helpers': 7.27.6 + '@babel/parser': 7.27.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.26.10': + dependencies: + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.1.0 + + '@babel/generator@7.27.5': + dependencies: + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.25.9': + dependencies: + '@babel/types': 7.27.6 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.27.6 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.27.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.0 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.26.10) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.27.4 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.2.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.4(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + debug: 4.4.1 + lodash.debounce: 4.0.8 + resolve: 1.22.10 + transitivePeerDependencies: + - supports-color + + '@babel/helper-member-expression-to-functions@7.27.1': + dependencies: + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.27.3(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.27.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.27.3(@babel/core@7.26.9)': + dependencies: + '@babel/core': 7.26.9 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.27.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.27.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.27.6 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.27.1 + '@babel/traverse': 7.27.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.27.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-split-export-declaration@7.24.7': + dependencies: + '@babel/types': 7.27.6 + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.27.1': + dependencies: + '@babel/template': 7.27.2 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.27.6': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.27.6 + + '@babel/parser@7.27.5': + dependencies: + '@babel/types': 7.27.6 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.27.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.26.10) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.27.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + + '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-async-generator-functions@7.26.8(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.26.10) + '@babel/traverse': 7.27.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.26.10) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-block-scoping@7.27.5(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.26.10) + '@babel/traverse': 7.27.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/template': 7.27.2 + + '@babel/plugin-transform-destructuring@7.27.3(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.27.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.27.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-object-rest-spread@7.27.3(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.27.3(@babel/core@7.26.10) + '@babel/plugin-transform-parameters': 7.27.1(@babel/core@7.26.10) + + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.26.10) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regenerator@7.27.5(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-runtime@7.26.10(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + babel-plugin-polyfill-corejs2: 0.4.13(@babel/core@7.26.10) + babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.26.10) + babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.26.10) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-spread@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/preset-env@7.26.9(@babel/core@7.26.10)': + dependencies: + '@babel/compat-data': 7.27.5 + '@babel/core': 7.26.10 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.10) + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.10) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.26.10) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-async-generator-functions': 7.26.8(@babel/core@7.26.10) + '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-block-scoping': 7.27.5(@babel/core@7.26.10) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-class-static-block': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-classes': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-destructuring': 7.27.3(@babel/core@7.26.10) + '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-object-rest-spread': 7.27.3(@babel/core@7.26.10) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-parameters': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-regenerator': 7.27.5(@babel/core@7.26.10) + '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.26.10) + '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.26.10) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.26.10) + babel-plugin-polyfill-corejs2: 0.4.13(@babel/core@7.26.10) + babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.26.10) + babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.26.10) + core-js-compat: 3.43.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/types': 7.27.6 + esutils: 2.0.3 + + '@babel/runtime@7.26.10': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + + '@babel/traverse@7.27.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.5 + '@babel/parser': 7.27.5 + '@babel/template': 7.27.2 + '@babel/types': 7.27.6 + debug: 4.4.1 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.27.6': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@colors/colors@1.5.0': {} + + '@discoveryjs/json-ext@0.6.3': {} + + '@esbuild/aix-ppc64@0.25.4': + optional: true + + '@esbuild/android-arm64@0.25.4': + optional: true + + '@esbuild/android-arm@0.25.4': + optional: true + + '@esbuild/android-x64@0.25.4': + optional: true + + '@esbuild/darwin-arm64@0.25.4': + optional: true + + '@esbuild/darwin-x64@0.25.4': + optional: true + + '@esbuild/freebsd-arm64@0.25.4': + optional: true + + '@esbuild/freebsd-x64@0.25.4': + optional: true + + '@esbuild/linux-arm64@0.25.4': + optional: true + + '@esbuild/linux-arm@0.25.4': + optional: true + + '@esbuild/linux-ia32@0.25.4': + optional: true + + '@esbuild/linux-loong64@0.25.4': + optional: true + + '@esbuild/linux-mips64el@0.25.4': + optional: true + + '@esbuild/linux-ppc64@0.25.4': + optional: true + + '@esbuild/linux-riscv64@0.25.4': + optional: true + + '@esbuild/linux-s390x@0.25.4': + optional: true + + '@esbuild/linux-x64@0.25.4': + optional: true + + '@esbuild/netbsd-arm64@0.25.4': + optional: true + + '@esbuild/netbsd-x64@0.25.4': + optional: true + + '@esbuild/openbsd-arm64@0.25.4': + optional: true + + '@esbuild/openbsd-x64@0.25.4': + optional: true + + '@esbuild/sunos-x64@0.25.4': + optional: true + + '@esbuild/win32-arm64@0.25.4': + optional: true + + '@esbuild/win32-ia32@0.25.4': + optional: true + + '@esbuild/win32-x64@0.25.4': + optional: true + + '@inquirer/checkbox@4.1.8(@types/node@24.0.3)': + dependencies: + '@inquirer/core': 10.1.13(@types/node@24.0.3) + '@inquirer/figures': 1.0.12 + '@inquirer/type': 3.0.7(@types/node@24.0.3) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 24.0.3 + + '@inquirer/confirm@5.1.12(@types/node@24.0.3)': + dependencies: + '@inquirer/core': 10.1.13(@types/node@24.0.3) + '@inquirer/type': 3.0.7(@types/node@24.0.3) + optionalDependencies: + '@types/node': 24.0.3 + + '@inquirer/confirm@5.1.6(@types/node@24.0.3)': + dependencies: + '@inquirer/core': 10.1.13(@types/node@24.0.3) + '@inquirer/type': 3.0.7(@types/node@24.0.3) + optionalDependencies: + '@types/node': 24.0.3 + + '@inquirer/core@10.1.13(@types/node@24.0.3)': + dependencies: + '@inquirer/figures': 1.0.12 + '@inquirer/type': 3.0.7(@types/node@24.0.3) + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 24.0.3 + + '@inquirer/editor@4.2.13(@types/node@24.0.3)': + dependencies: + '@inquirer/core': 10.1.13(@types/node@24.0.3) + '@inquirer/type': 3.0.7(@types/node@24.0.3) + external-editor: 3.1.0 + optionalDependencies: + '@types/node': 24.0.3 + + '@inquirer/expand@4.0.15(@types/node@24.0.3)': + dependencies: + '@inquirer/core': 10.1.13(@types/node@24.0.3) + '@inquirer/type': 3.0.7(@types/node@24.0.3) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 24.0.3 + + '@inquirer/figures@1.0.12': {} + + '@inquirer/input@4.1.12(@types/node@24.0.3)': + dependencies: + '@inquirer/core': 10.1.13(@types/node@24.0.3) + '@inquirer/type': 3.0.7(@types/node@24.0.3) + optionalDependencies: + '@types/node': 24.0.3 + + '@inquirer/number@3.0.15(@types/node@24.0.3)': + dependencies: + '@inquirer/core': 10.1.13(@types/node@24.0.3) + '@inquirer/type': 3.0.7(@types/node@24.0.3) + optionalDependencies: + '@types/node': 24.0.3 + + '@inquirer/password@4.0.15(@types/node@24.0.3)': + dependencies: + '@inquirer/core': 10.1.13(@types/node@24.0.3) + '@inquirer/type': 3.0.7(@types/node@24.0.3) + ansi-escapes: 4.3.2 + optionalDependencies: + '@types/node': 24.0.3 + + '@inquirer/prompts@7.3.2(@types/node@24.0.3)': + dependencies: + '@inquirer/checkbox': 4.1.8(@types/node@24.0.3) + '@inquirer/confirm': 5.1.12(@types/node@24.0.3) + '@inquirer/editor': 4.2.13(@types/node@24.0.3) + '@inquirer/expand': 4.0.15(@types/node@24.0.3) + '@inquirer/input': 4.1.12(@types/node@24.0.3) + '@inquirer/number': 3.0.15(@types/node@24.0.3) + '@inquirer/password': 4.0.15(@types/node@24.0.3) + '@inquirer/rawlist': 4.1.3(@types/node@24.0.3) + '@inquirer/search': 3.0.15(@types/node@24.0.3) + '@inquirer/select': 4.2.3(@types/node@24.0.3) + optionalDependencies: + '@types/node': 24.0.3 + + '@inquirer/rawlist@4.1.3(@types/node@24.0.3)': + dependencies: + '@inquirer/core': 10.1.13(@types/node@24.0.3) + '@inquirer/type': 3.0.7(@types/node@24.0.3) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 24.0.3 + + '@inquirer/search@3.0.15(@types/node@24.0.3)': + dependencies: + '@inquirer/core': 10.1.13(@types/node@24.0.3) + '@inquirer/figures': 1.0.12 + '@inquirer/type': 3.0.7(@types/node@24.0.3) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 24.0.3 + + '@inquirer/select@4.2.3(@types/node@24.0.3)': + dependencies: + '@inquirer/core': 10.1.13(@types/node@24.0.3) + '@inquirer/figures': 1.0.12 + '@inquirer/type': 3.0.7(@types/node@24.0.3) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 24.0.3 + + '@inquirer/type@1.5.5': + dependencies: + mute-stream: 1.0.0 + + '@inquirer/type@3.0.7(@types/node@24.0.3)': + optionalDependencies: + '@types/node': 24.0.3 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@istanbuljs/schema@0.1.3': {} + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/source-map@0.3.6': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@jsonjoy.com/base64@1.1.2(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@1.2.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) + '@jsonjoy.com/util': 1.6.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 1.21.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@1.6.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@leichtgewicht/ip-codec@2.0.5': {} + + '@listr2/prompt-adapter-inquirer@2.0.18(@inquirer/prompts@7.3.2(@types/node@24.0.3))': + dependencies: + '@inquirer/prompts': 7.3.2(@types/node@24.0.3) + '@inquirer/type': 1.5.5 + + '@lmdb/lmdb-darwin-arm64@3.2.6': + optional: true + + '@lmdb/lmdb-darwin-x64@3.2.6': + optional: true + + '@lmdb/lmdb-linux-arm64@3.2.6': + optional: true + + '@lmdb/lmdb-linux-arm@3.2.6': + optional: true + + '@lmdb/lmdb-linux-x64@3.2.6': + optional: true + + '@lmdb/lmdb-win32-x64@3.2.6': + optional: true + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + optional: true + + '@napi-rs/nice-android-arm-eabi@1.0.1': + optional: true + + '@napi-rs/nice-android-arm64@1.0.1': + optional: true + + '@napi-rs/nice-darwin-arm64@1.0.1': + optional: true + + '@napi-rs/nice-darwin-x64@1.0.1': + optional: true + + '@napi-rs/nice-freebsd-x64@1.0.1': + optional: true + + '@napi-rs/nice-linux-arm-gnueabihf@1.0.1': + optional: true + + '@napi-rs/nice-linux-arm64-gnu@1.0.1': + optional: true + + '@napi-rs/nice-linux-arm64-musl@1.0.1': + optional: true + + '@napi-rs/nice-linux-ppc64-gnu@1.0.1': + optional: true + + '@napi-rs/nice-linux-riscv64-gnu@1.0.1': + optional: true + + '@napi-rs/nice-linux-s390x-gnu@1.0.1': + optional: true + + '@napi-rs/nice-linux-x64-gnu@1.0.1': + optional: true + + '@napi-rs/nice-linux-x64-musl@1.0.1': + optional: true + + '@napi-rs/nice-win32-arm64-msvc@1.0.1': + optional: true + + '@napi-rs/nice-win32-ia32-msvc@1.0.1': + optional: true + + '@napi-rs/nice-win32-x64-msvc@1.0.1': + optional: true + + '@napi-rs/nice@1.0.1': + optionalDependencies: + '@napi-rs/nice-android-arm-eabi': 1.0.1 + '@napi-rs/nice-android-arm64': 1.0.1 + '@napi-rs/nice-darwin-arm64': 1.0.1 + '@napi-rs/nice-darwin-x64': 1.0.1 + '@napi-rs/nice-freebsd-x64': 1.0.1 + '@napi-rs/nice-linux-arm-gnueabihf': 1.0.1 + '@napi-rs/nice-linux-arm64-gnu': 1.0.1 + '@napi-rs/nice-linux-arm64-musl': 1.0.1 + '@napi-rs/nice-linux-ppc64-gnu': 1.0.1 + '@napi-rs/nice-linux-riscv64-gnu': 1.0.1 + '@napi-rs/nice-linux-s390x-gnu': 1.0.1 + '@napi-rs/nice-linux-x64-gnu': 1.0.1 + '@napi-rs/nice-linux-x64-musl': 1.0.1 + '@napi-rs/nice-win32-arm64-msvc': 1.0.1 + '@napi-rs/nice-win32-ia32-msvc': 1.0.1 + '@napi-rs/nice-win32-x64-msvc': 1.0.1 + optional: true + + '@ng-icons/core@31.4.0(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': + dependencies: + '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) + rxjs: 7.8.2 + tslib: 2.8.1 + + '@ng-icons/material-file-icons@31.4.0': + dependencies: + tslib: 2.8.1 + + '@ng-icons/material-icons@31.4.0': + dependencies: + tslib: 2.8.1 + + '@ngtools/webpack@19.2.15(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.4))': + dependencies: + '@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3) + typescript: 5.8.3 + webpack: 5.98.0(esbuild@0.25.4) + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@npmcli/agent@3.0.0': + dependencies: + agent-base: 7.1.3 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 10.4.3 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + '@npmcli/fs@4.0.0': + dependencies: + semver: 7.7.1 + + '@npmcli/git@6.0.3': + dependencies: + '@npmcli/promise-spawn': 8.0.2 + ini: 5.0.0 + lru-cache: 10.4.3 + npm-pick-manifest: 10.0.0 + proc-log: 5.0.0 + promise-retry: 2.0.1 + semver: 7.7.1 + which: 5.0.0 + + '@npmcli/installed-package-contents@3.0.0': + dependencies: + npm-bundled: 4.0.0 + npm-normalize-package-bin: 4.0.0 + + '@npmcli/node-gyp@4.0.0': {} + + '@npmcli/package-json@6.2.0': + dependencies: + '@npmcli/git': 6.0.3 + glob: 10.4.5 + hosted-git-info: 8.1.0 + json-parse-even-better-errors: 4.0.0 + proc-log: 5.0.0 + semver: 7.7.1 + validate-npm-package-license: 3.0.4 + + '@npmcli/promise-spawn@8.0.2': + dependencies: + which: 5.0.0 + + '@npmcli/redact@3.2.2': {} + + '@npmcli/run-script@9.1.0': + dependencies: + '@npmcli/node-gyp': 4.0.0 + '@npmcli/package-json': 6.2.0 + '@npmcli/promise-spawn': 8.0.2 + node-gyp: 11.2.0 + proc-log: 5.0.0 + which: 5.0.0 + transitivePeerDependencies: + - supports-color + + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + optional: true + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@primeng/themes@19.1.3': + dependencies: + '@primeuix/styled': 0.3.2 + + '@primeuix/styled@0.3.2': + dependencies: + '@primeuix/utils': 0.3.2 + + '@primeuix/utils@0.3.2': {} + + '@rollup/rollup-android-arm-eabi@4.34.8': + optional: true + + '@rollup/rollup-android-arm64@4.34.8': + optional: true + + '@rollup/rollup-darwin-arm64@4.34.8': + optional: true + + '@rollup/rollup-darwin-x64@4.34.8': + optional: true + + '@rollup/rollup-freebsd-arm64@4.34.8': + optional: true + + '@rollup/rollup-freebsd-x64@4.34.8': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.34.8': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.34.8': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.34.8': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-x64-musl@4.34.8': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.34.8': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.34.8': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.34.8': + optional: true + + '@schematics/angular@19.2.15(chokidar@4.0.3)': + dependencies: + '@angular-devkit/core': 19.2.15(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.15(chokidar@4.0.3) + jsonc-parser: 3.3.1 + transitivePeerDependencies: + - chokidar + + '@sigstore/bundle@3.1.0': + dependencies: + '@sigstore/protobuf-specs': 0.4.3 + + '@sigstore/core@2.0.0': {} + + '@sigstore/protobuf-specs@0.4.3': {} + + '@sigstore/sign@3.1.0': + dependencies: + '@sigstore/bundle': 3.1.0 + '@sigstore/core': 2.0.0 + '@sigstore/protobuf-specs': 0.4.3 + make-fetch-happen: 14.0.3 + proc-log: 5.0.0 + promise-retry: 2.0.1 + transitivePeerDependencies: + - supports-color + + '@sigstore/tuf@3.1.1': + dependencies: + '@sigstore/protobuf-specs': 0.4.3 + tuf-js: 3.0.1 + transitivePeerDependencies: + - supports-color + + '@sigstore/verify@2.1.1': + dependencies: + '@sigstore/bundle': 3.1.0 + '@sigstore/core': 2.0.0 + '@sigstore/protobuf-specs': 0.4.3 + + '@sindresorhus/merge-streams@2.3.0': {} + + '@socket.io/component-emitter@3.1.2': {} + + '@tailwindcss/node@4.1.10': + dependencies: + '@ampproject/remapping': 2.3.0 + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + lightningcss: 1.30.1 + magic-string: 0.30.17 + source-map-js: 1.2.1 + tailwindcss: 4.1.10 + + '@tailwindcss/oxide-android-arm64@4.1.10': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.10': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.10': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.10': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.10': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.10': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.10': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.10': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.10': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.10': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.10': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.10': + optional: true + + '@tailwindcss/oxide@4.1.10': + dependencies: + detect-libc: 2.0.4 + tar: 7.4.3 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.10 + '@tailwindcss/oxide-darwin-arm64': 4.1.10 + '@tailwindcss/oxide-darwin-x64': 4.1.10 + '@tailwindcss/oxide-freebsd-x64': 4.1.10 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.10 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.10 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.10 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.10 + '@tailwindcss/oxide-linux-x64-musl': 4.1.10 + '@tailwindcss/oxide-wasm32-wasi': 4.1.10 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.10 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.10 + + '@tailwindcss/postcss@4.1.10': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.1.10 + '@tailwindcss/oxide': 4.1.10 + postcss: 8.5.6 + tailwindcss: 4.1.10 + + '@tufjs/canonical-json@2.0.0': {} + + '@tufjs/models@3.0.1': + dependencies: + '@tufjs/canonical-json': 2.0.0 + minimatch: 9.0.5 + + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 24.0.3 + + '@types/bonjour@3.5.13': + dependencies: + '@types/node': 24.0.3 + + '@types/connect-history-api-fallback@1.5.4': + dependencies: + '@types/express-serve-static-core': 4.19.6 + '@types/node': 24.0.3 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 24.0.3 + + '@types/cors@2.8.19': + dependencies: + '@types/node': 24.0.3 + + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.6 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + + '@types/estree@1.0.6': {} + + '@types/express-serve-static-core@4.19.6': + dependencies: + '@types/node': 24.0.3 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.5 + + '@types/express@4.17.23': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 4.19.6 + '@types/qs': 6.14.0 + '@types/serve-static': 1.15.8 + + '@types/http-errors@2.0.5': {} + + '@types/http-proxy@1.17.16': + dependencies: + '@types/node': 24.0.3 + + '@types/jasmine@5.1.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/mime@1.3.5': {} + + '@types/node-forge@1.3.11': + dependencies: + '@types/node': 24.0.3 + + '@types/node@24.0.3': + dependencies: + undici-types: 7.8.0 + + '@types/qs@6.14.0': {} + + '@types/range-parser@1.2.7': {} + + '@types/retry@0.12.2': {} + + '@types/send@0.17.5': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 24.0.3 + + '@types/serve-index@1.9.4': + dependencies: + '@types/express': 4.17.23 + + '@types/serve-static@1.15.8': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 24.0.3 + '@types/send': 0.17.5 + + '@types/sockjs@0.3.36': + dependencies: + '@types/node': 24.0.3 + + '@types/ws@8.18.1': + dependencies: + '@types/node': 24.0.3 + + '@vitejs/plugin-basic-ssl@1.2.0(vite@6.2.7(@types/node@24.0.3)(jiti@2.4.2)(less@4.2.2)(lightningcss@1.30.1)(sass@1.85.0)(terser@5.39.0))': + dependencies: + vite: 6.2.7(@types/node@24.0.3)(jiti@2.4.2)(less@4.2.2)(lightningcss@1.30.1)(sass@1.85.0)(terser@5.39.0) + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + '@yarnpkg/lockfile@1.1.0': {} + + abbrev@3.0.1: {} + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + acorn@8.15.0: {} + + adjust-sourcemap-loader@4.0.0: + dependencies: + loader-utils: 2.0.4 + regex-parser: 2.3.1 + + agent-base@7.1.3: {} + + ajv-formats@2.1.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + + ajv-formats@3.0.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + + ajv-keywords@5.1.0(ajv@8.17.1): + dependencies: + ajv: 8.17.1 + fast-deep-equal: 3.1.3 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.6 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-colors@4.1.3: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 + + ansi-html-community@0.0.8: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + argparse@2.0.1: {} + + array-flatten@1.1.1: {} + + autoprefixer@10.4.20(postcss@8.5.2): + dependencies: + browserslist: 4.25.0 + caniuse-lite: 1.0.30001723 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.2 + postcss-value-parser: 4.2.0 + + babel-loader@9.2.1(@babel/core@7.26.10)(webpack@5.98.0(esbuild@0.25.4)): + dependencies: + '@babel/core': 7.26.10 + find-cache-dir: 4.0.0 + schema-utils: 4.3.2 + webpack: 5.98.0(esbuild@0.25.4) + + babel-plugin-polyfill-corejs2@0.4.13(@babel/core@7.26.10): + dependencies: + '@babel/compat-data': 7.27.5 + '@babel/core': 7.26.10 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.26.10) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.26.10): + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.26.10) + core-js-compat: 3.43.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.4(@babel/core@7.26.10): + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.26.10) + transitivePeerDependencies: + - supports-color + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + base64id@2.0.0: {} + + batch@0.6.1: {} + + beasties@0.3.2: + dependencies: + css-select: 5.1.0 + css-what: 6.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + htmlparser2: 10.0.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-media-query-parser: 0.2.3 + + big.js@5.2.2: {} + + binary-extensions@2.3.0: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + body-parser@1.20.3: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + 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.13.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + bonjour-service@1.3.0: + dependencies: + fast-deep-equal: 3.1.3 + multicast-dns: 7.2.5 + + boolbase@1.0.0: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.25.0: + dependencies: + caniuse-lite: 1.0.30001723 + electron-to-chromium: 1.5.167 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.0) + + buffer-from@1.1.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bundle-name@4.1.0: + dependencies: + run-applescript: 7.0.0 + + bytes@3.1.2: {} + + cacache@19.0.1: + dependencies: + '@npmcli/fs': 4.0.0 + fs-minipass: 3.0.3 + glob: 10.4.5 + lru-cache: 10.4.3 + minipass: 7.1.2 + minipass-collect: 2.0.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + p-map: 7.0.3 + ssri: 12.0.0 + tar: 7.4.3 + unique-filename: 4.0.0 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001723: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chardet@0.7.0: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chownr@2.0.0: {} + + chownr@3.0.0: {} + + chrome-trace-event@1.0.4: {} + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-spinners@2.9.2: {} + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + cli-width@4.1.0: {} + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone-deep@4.0.1: + dependencies: + is-plain-object: 2.0.4 + kind-of: 6.0.3 + shallow-clone: 3.0.1 + + clone@1.0.4: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colorette@2.0.20: {} + + commander@2.20.3: {} + + common-path-prefix@3.0.0: {} + + compressible@2.0.18: + dependencies: + mime-db: 1.52.0 + + compression@1.8.0: + dependencies: + bytes: 3.1.2 + compressible: 2.0.18 + debug: 2.6.9 + negotiator: 0.6.4 + on-headers: 1.0.2 + safe-buffer: 5.2.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + concat-map@0.0.1: {} + + connect-history-api-fallback@2.0.0: {} + + connect@3.7.0: + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: 1.3.3 + utils-merge: 1.0.1 + transitivePeerDependencies: + - supports-color + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + convert-source-map@1.9.0: {} + + convert-source-map@2.0.0: {} + + cookie-signature@1.0.6: {} + + cookie@0.7.1: {} + + cookie@0.7.2: {} + + copy-anything@2.0.6: + dependencies: + is-what: 3.14.1 + + copy-webpack-plugin@12.0.2(webpack@5.98.0(esbuild@0.25.4)): + dependencies: + fast-glob: 3.3.3 + glob-parent: 6.0.2 + globby: 14.1.0 + normalize-path: 3.0.0 + schema-utils: 4.3.2 + serialize-javascript: 6.0.2 + webpack: 5.98.0(esbuild@0.25.4) + + core-js-compat@3.43.0: + dependencies: + browserslist: 4.25.0 + + core-util-is@1.0.3: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig@9.0.0(typescript@5.8.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.8.3 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-loader@7.1.2(webpack@5.98.0(esbuild@0.25.4)): + dependencies: + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.6) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.6) + postcss-modules-scope: 3.2.1(postcss@8.5.6) + postcss-modules-values: 4.0.0(postcss@8.5.6) + postcss-value-parser: 4.2.0 + semver: 7.7.2 + optionalDependencies: + webpack: 5.98.0(esbuild@0.25.4) + + css-select@5.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-what@6.1.0: {} + + cssesc@3.0.0: {} + + custom-event@1.0.1: {} + + date-format@4.0.14: {} + + dayjs@1.11.13: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + default-browser-id@5.0.0: {} + + default-browser@5.2.1: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.0 + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + define-lazy-prop@3.0.0: {} + + depd@1.1.2: {} + + depd@2.0.0: {} + + destroy@1.2.0: {} + + detect-libc@1.0.3: + optional: true + + detect-libc@2.0.4: {} + + detect-node@2.1.0: {} + + di@0.0.1: {} + + dns-packet@5.6.1: + dependencies: + '@leichtgewicht/ip-codec': 2.0.5 + + dom-serialize@2.2.1: + dependencies: + custom-event: 1.0.1 + ent: 2.2.2 + extend: 3.0.2 + void-elements: 2.0.1 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.167: {} + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + emojis-list@3.0.0: {} + + encodeurl@1.0.2: {} + + encodeurl@2.0.0: {} + + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + + engine.io-parser@5.2.3: {} + + engine.io@6.6.4: + dependencies: + '@types/cors': 2.8.19 + '@types/node': 24.0.3 + accepts: 1.3.8 + base64id: 2.0.0 + cookie: 0.7.2 + cors: 2.8.5 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.2 + + ent@2.2.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + punycode: 1.4.1 + safe-regex-test: 1.1.0 + + entities@4.5.0: {} + + entities@6.0.1: {} + + env-paths@2.2.1: {} + + environment@1.1.0: {} + + err-code@2.0.3: {} + + errno@0.1.8: + dependencies: + prr: 1.0.1 + optional: true + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + esbuild-wasm@0.25.4: {} + + esbuild@0.25.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.4 + '@esbuild/android-arm': 0.25.4 + '@esbuild/android-arm64': 0.25.4 + '@esbuild/android-x64': 0.25.4 + '@esbuild/darwin-arm64': 0.25.4 + '@esbuild/darwin-x64': 0.25.4 + '@esbuild/freebsd-arm64': 0.25.4 + '@esbuild/freebsd-x64': 0.25.4 + '@esbuild/linux-arm': 0.25.4 + '@esbuild/linux-arm64': 0.25.4 + '@esbuild/linux-ia32': 0.25.4 + '@esbuild/linux-loong64': 0.25.4 + '@esbuild/linux-mips64el': 0.25.4 + '@esbuild/linux-ppc64': 0.25.4 + '@esbuild/linux-riscv64': 0.25.4 + '@esbuild/linux-s390x': 0.25.4 + '@esbuild/linux-x64': 0.25.4 + '@esbuild/netbsd-arm64': 0.25.4 + '@esbuild/netbsd-x64': 0.25.4 + '@esbuild/openbsd-arm64': 0.25.4 + '@esbuild/openbsd-x64': 0.25.4 + '@esbuild/sunos-x64': 0.25.4 + '@esbuild/win32-arm64': 0.25.4 + '@esbuild/win32-ia32': 0.25.4 + '@esbuild/win32-x64': 0.25.4 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + eventemitter3@4.0.7: {} + + eventemitter3@5.0.1: {} + + events@3.3.0: {} + + exponential-backoff@3.1.2: {} + + express@4.21.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.3 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.1 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.13.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.0 + serve-static: 1.16.2 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + extend@3.0.2: {} + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-uri@3.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + faye-websocket@0.11.4: + dependencies: + websocket-driver: 0.7.4 + + fdir@6.4.6(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@1.1.2: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + finalhandler@1.3.1: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + find-cache-dir@4.0.0: + dependencies: + common-path-prefix: 3.0.0 + pkg-dir: 7.0.0 + + find-up@6.3.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + + flat@5.0.2: {} + + flatted@3.3.3: {} + + follow-redirects@1.15.9(debug@4.4.1): + optionalDependencies: + debug: 4.4.1 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + forwarded@0.2.0: {} + + fraction.js@4.3.7: {} + + fresh@0.5.2: {} + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.2 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.3.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regexp@0.4.1: {} + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@11.12.0: {} + + globby@14.1.0: + dependencies: + '@sindresorhus/merge-streams': 2.3.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + path-type: 6.0.0 + slash: 5.1.0 + unicorn-magic: 0.3.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + handle-thing@2.0.1: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hosted-git-info@8.1.0: + dependencies: + lru-cache: 10.4.3 + + hpack.js@2.1.6: + dependencies: + inherits: 2.0.4 + obuf: 1.1.2 + readable-stream: 2.3.8 + wbuf: 1.7.3 + + html-escaper@2.0.2: {} + + htmlparser2@10.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 6.0.1 + + http-cache-semantics@4.2.0: {} + + http-deceiver@1.2.7: {} + + http-errors@1.6.3: + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.0 + statuses: 1.5.0 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + http-parser-js@0.5.10: {} + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.3 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + http-proxy-middleware@2.0.9(@types/express@4.17.23): + dependencies: + '@types/http-proxy': 1.17.16 + http-proxy: 1.18.1(debug@4.4.1) + is-glob: 4.0.3 + is-plain-obj: 3.0.0 + micromatch: 4.0.8 + optionalDependencies: + '@types/express': 4.17.23 + transitivePeerDependencies: + - debug + + http-proxy-middleware@3.0.5: + dependencies: + '@types/http-proxy': 1.17.16 + debug: 4.4.1 + http-proxy: 1.18.1(debug@4.4.1) + is-glob: 4.0.3 + is-plain-object: 5.0.0 + micromatch: 4.0.8 + transitivePeerDependencies: + - supports-color + + http-proxy@1.18.1(debug@4.4.1): + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.15.9(debug@4.4.1) + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + hyperdyperid@1.2.0: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + icss-utils@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + ieee754@1.2.1: {} + + ignore-walk@7.0.0: + dependencies: + minimatch: 9.0.5 + + ignore@7.0.5: {} + + image-size@0.5.5: + optional: true + + immutable@5.1.3: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.3: {} + + inherits@2.0.4: {} + + ini@5.0.0: {} + + ip-address@9.0.5: + dependencies: + jsbn: 1.1.0 + sprintf-js: 1.1.3 + + ipaddr.js@1.9.1: {} + + ipaddr.js@2.2.0: {} + + is-arrayish@0.2.1: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.0.0: + dependencies: + get-east-asian-width: 1.3.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-interactive@1.0.0: {} + + is-network-error@1.1.0: {} + + is-number@7.0.0: {} + + is-plain-obj@3.0.0: {} + + is-plain-object@2.0.4: + dependencies: + isobject: 3.0.1 + + is-plain-object@5.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-unicode-supported@0.1.0: {} + + is-what@3.14.1: {} + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + isarray@1.0.0: {} + + isbinaryfile@4.0.10: {} + + isexe@2.0.0: {} + + isexe@3.1.1: {} + + isobject@3.0.1: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.27.4 + '@babel/parser': 7.27.5 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.27.4 + '@babel/parser': 7.27.5 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.4.1 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jasmine-core@4.6.1: {} + + jasmine-core@5.7.1: {} + + jest-worker@27.5.1: + dependencies: + '@types/node': 24.0.3 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jiti@1.21.7: {} + + jiti@2.4.2: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsbn@1.1.0: {} + + jsesc@3.0.2: {} + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json-parse-even-better-errors@4.0.0: {} + + json-schema-traverse@1.0.0: {} + + json5@2.2.3: {} + + jsonc-parser@3.3.1: {} + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + jsonparse@1.3.1: {} + + karma-chrome-launcher@3.2.0: + dependencies: + which: 1.3.1 + + karma-coverage@2.2.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 5.2.1 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.7 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + karma-jasmine-html-reporter@2.1.0(jasmine-core@5.7.1)(karma-jasmine@5.1.0(karma@6.4.4))(karma@6.4.4): + dependencies: + jasmine-core: 5.7.1 + karma: 6.4.4 + karma-jasmine: 5.1.0(karma@6.4.4) + + karma-jasmine@5.1.0(karma@6.4.4): + dependencies: + jasmine-core: 4.6.1 + karma: 6.4.4 + + karma-source-map-support@1.4.0: + dependencies: + source-map-support: 0.5.21 + + karma@6.4.4: + dependencies: + '@colors/colors': 1.5.0 + body-parser: 1.20.3 + braces: 3.0.3 + chokidar: 3.6.0 + connect: 3.7.0 + di: 0.0.1 + dom-serialize: 2.2.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + http-proxy: 1.18.1(debug@4.4.1) + isbinaryfile: 4.0.10 + lodash: 4.17.21 + log4js: 6.9.1 + mime: 2.6.0 + minimatch: 3.1.2 + mkdirp: 0.5.6 + qjobs: 1.2.0 + range-parser: 1.2.1 + rimraf: 3.0.2 + socket.io: 4.8.1 + source-map: 0.6.1 + tmp: 0.2.3 + ua-parser-js: 0.7.40 + yargs: 16.2.0 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + keycloak-angular@19.0.2(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/router@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(keycloak-js@26.2.0): + dependencies: + '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/router': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + keycloak-js: 26.2.0 + tslib: 2.8.1 + + keycloak-js@26.2.0: {} + + kind-of@6.0.3: {} + + launch-editor@2.10.0: + dependencies: + picocolors: 1.1.1 + shell-quote: 1.8.3 + + less-loader@12.2.0(less@4.2.2)(webpack@5.98.0(esbuild@0.25.4)): + dependencies: + less: 4.2.2 + optionalDependencies: + webpack: 5.98.0(esbuild@0.25.4) + + less@4.2.2: + dependencies: + copy-anything: 2.0.6 + parse-node-version: 1.0.1 + tslib: 2.8.1 + optionalDependencies: + errno: 0.1.8 + graceful-fs: 4.2.11 + image-size: 0.5.5 + make-dir: 2.1.0 + mime: 1.6.0 + needle: 3.3.1 + source-map: 0.6.1 + + license-webpack-plugin@4.0.2(webpack@5.98.0(esbuild@0.25.4)): + dependencies: + webpack-sources: 3.3.2 + optionalDependencies: + webpack: 5.98.0(esbuild@0.25.4) + + lightningcss-darwin-arm64@1.30.1: + optional: true + + lightningcss-darwin-x64@1.30.1: + optional: true + + lightningcss-freebsd-x64@1.30.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.1: + optional: true + + lightningcss-linux-arm64-gnu@1.30.1: + optional: true + + lightningcss-linux-arm64-musl@1.30.1: + optional: true + + lightningcss-linux-x64-gnu@1.30.1: + optional: true + + lightningcss-linux-x64-musl@1.30.1: + optional: true + + lightningcss-win32-arm64-msvc@1.30.1: + optional: true + + lightningcss-win32-x64-msvc@1.30.1: + optional: true + + lightningcss@1.30.1: + dependencies: + detect-libc: 2.0.4 + optionalDependencies: + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 + + lines-and-columns@1.2.4: {} + + listr2@8.2.5: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.0 + + lmdb@3.2.6: + dependencies: + msgpackr: 1.11.4 + node-addon-api: 6.1.0 + node-gyp-build-optional-packages: 5.2.2 + ordered-binary: 1.5.3 + weak-lru-cache: 1.2.2 + optionalDependencies: + '@lmdb/lmdb-darwin-arm64': 3.2.6 + '@lmdb/lmdb-darwin-x64': 3.2.6 + '@lmdb/lmdb-linux-arm': 3.2.6 + '@lmdb/lmdb-linux-arm64': 3.2.6 + '@lmdb/lmdb-linux-x64': 3.2.6 + '@lmdb/lmdb-win32-x64': 3.2.6 + optional: true + + loader-runner@4.3.0: {} + + loader-utils@2.0.4: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 2.2.3 + + loader-utils@3.3.1: {} + + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash.debounce@4.0.8: {} + + lodash@4.17.21: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.0.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.0 + strip-ansi: 7.1.0 + wrap-ansi: 9.0.0 + + log4js@6.9.1: + dependencies: + date-format: 4.0.14 + debug: 4.4.1 + flatted: 3.3.3 + rfdc: 1.4.1 + streamroller: 3.1.5 + transitivePeerDependencies: + - supports-color + + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + make-dir@2.1.0: + dependencies: + pify: 4.0.1 + semver: 5.7.2 + optional: true + + make-dir@4.0.0: + dependencies: + semver: 7.7.2 + + make-fetch-happen@14.0.3: + dependencies: + '@npmcli/agent': 3.0.0 + cacache: 19.0.1 + http-cache-semantics: 4.2.0 + minipass: 7.1.2 + minipass-fetch: 4.0.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 1.0.0 + proc-log: 5.0.0 + promise-retry: 2.0.1 + ssri: 12.0.0 + transitivePeerDependencies: + - supports-color + + math-intrinsics@1.1.0: {} + + media-typer@0.3.0: {} + + memfs@4.17.2: + dependencies: + '@jsonjoy.com/json-pack': 1.2.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.6.0(tslib@2.8.1) + tree-dump: 1.0.3(tslib@2.8.1) + tslib: 2.8.1 + + merge-descriptors@1.0.3: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + methods@1.1.2: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@1.6.0: {} + + mime@2.6.0: {} + + mimic-fn@2.1.0: {} + + mimic-function@5.0.1: {} + + mini-css-extract-plugin@2.9.2(webpack@5.98.0(esbuild@0.25.4)): + dependencies: + schema-utils: 4.3.2 + tapable: 2.2.2 + webpack: 5.98.0(esbuild@0.25.4) + + minimalistic-assert@1.0.1: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + minipass-collect@2.0.1: + dependencies: + minipass: 7.1.2 + + minipass-fetch@4.0.1: + dependencies: + minipass: 7.1.2 + minipass-sized: 1.0.3 + minizlib: 3.0.2 + optionalDependencies: + encoding: 0.1.13 + + minipass-flush@1.0.5: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass-sized@1.0.3: + dependencies: + minipass: 3.3.6 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minipass@7.1.2: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + minizlib@3.0.2: + dependencies: + minipass: 7.1.2 + + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + + mkdirp@1.0.4: {} + + mkdirp@3.0.1: {} + + mrmime@2.0.1: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + msgpackr-extract@3.0.3: + dependencies: + node-gyp-build-optional-packages: 5.2.2 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 + optional: true + + msgpackr@1.11.4: + optionalDependencies: + msgpackr-extract: 3.0.3 + optional: true + + multicast-dns@7.2.5: + dependencies: + dns-packet: 5.6.1 + thunky: 1.1.0 + + mute-stream@1.0.0: {} + + mute-stream@2.0.0: {} + + nanoid@3.3.11: {} + + needle@3.3.1: + dependencies: + iconv-lite: 0.6.3 + sax: 1.4.1 + optional: true + + negotiator@0.6.3: {} + + negotiator@0.6.4: {} + + negotiator@1.0.0: {} + + neo-async@2.6.2: {} + + node-addon-api@6.1.0: + optional: true + + node-addon-api@7.1.1: + optional: true + + node-forge@1.3.1: {} + + node-gyp-build-optional-packages@5.2.2: + dependencies: + detect-libc: 2.0.4 + optional: true + + node-gyp@11.2.0: + dependencies: + env-paths: 2.2.1 + exponential-backoff: 3.1.2 + graceful-fs: 4.2.11 + make-fetch-happen: 14.0.3 + nopt: 8.1.0 + proc-log: 5.0.0 + semver: 7.7.1 + tar: 7.4.3 + tinyglobby: 0.2.14 + which: 5.0.0 + transitivePeerDependencies: + - supports-color + + node-releases@2.0.19: {} + + nopt@8.1.0: + dependencies: + abbrev: 3.0.1 + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + npm-bundled@4.0.0: + dependencies: + npm-normalize-package-bin: 4.0.0 + + npm-install-checks@7.1.1: + dependencies: + semver: 7.7.1 + + npm-normalize-package-bin@4.0.0: {} + + npm-package-arg@12.0.2: + dependencies: + hosted-git-info: 8.1.0 + proc-log: 5.0.0 + semver: 7.7.1 + validate-npm-package-name: 6.0.1 + + npm-packlist@9.0.0: + dependencies: + ignore-walk: 7.0.0 + + npm-pick-manifest@10.0.0: + dependencies: + npm-install-checks: 7.1.1 + npm-normalize-package-bin: 4.0.0 + npm-package-arg: 12.0.2 + semver: 7.7.1 + + npm-registry-fetch@18.0.2: + dependencies: + '@npmcli/redact': 3.2.2 + jsonparse: 1.3.1 + make-fetch-happen: 14.0.3 + minipass: 7.1.2 + minipass-fetch: 4.0.1 + minizlib: 3.0.2 + npm-package-arg: 12.0.2 + proc-log: 5.0.0 + transitivePeerDependencies: + - supports-color + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + obuf@1.1.2: {} + + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + on-headers@1.0.2: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + open@10.1.0: + dependencies: + default-browser: 5.2.1 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + is-wsl: 3.1.0 + + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + ordered-binary@1.5.3: + optional: true + + os-tmpdir@1.0.2: {} + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.1 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + p-map@7.0.3: {} + + p-retry@6.2.1: + dependencies: + '@types/retry': 0.12.2 + is-network-error: 1.1.0 + retry: 0.13.1 + + package-json-from-dist@1.0.1: {} + + pacote@20.0.0: + dependencies: + '@npmcli/git': 6.0.3 + '@npmcli/installed-package-contents': 3.0.0 + '@npmcli/package-json': 6.2.0 + '@npmcli/promise-spawn': 8.0.2 + '@npmcli/run-script': 9.1.0 + cacache: 19.0.1 + fs-minipass: 3.0.3 + minipass: 7.1.2 + npm-package-arg: 12.0.2 + npm-packlist: 9.0.0 + npm-pick-manifest: 10.0.0 + npm-registry-fetch: 18.0.2 + proc-log: 5.0.0 + promise-retry: 2.0.1 + sigstore: 3.1.0 + ssri: 12.0.0 + tar: 6.2.1 + transitivePeerDependencies: + - supports-color + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-node-version@1.0.1: {} + + parse5-html-rewriting-stream@7.0.0: + dependencies: + entities: 4.5.0 + parse5: 7.3.0 + parse5-sax-parser: 7.0.0 + + parse5-sax-parser@7.0.0: + dependencies: + parse5: 7.3.0 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + parseurl@1.3.3: {} + + path-exists@5.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-to-regexp@0.1.12: {} + + path-type@6.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + pify@4.0.1: + optional: true + + piscina@4.8.0: + optionalDependencies: + '@napi-rs/nice': 1.0.1 + + pkg-dir@7.0.0: + dependencies: + find-up: 6.3.0 + + postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.4)): + dependencies: + cosmiconfig: 9.0.0(typescript@5.8.3) + jiti: 1.21.7 + postcss: 8.5.2 + semver: 7.7.2 + optionalDependencies: + webpack: 5.98.0(esbuild@0.25.4) + transitivePeerDependencies: + - typescript + + postcss-media-query-parser@0.2.3: {} + + postcss-modules-extract-imports@3.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-modules-local-by-default@4.2.0(postcss@8.5.6): + dependencies: + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 + + postcss-modules-scope@3.2.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-modules-values@4.0.0(postcss@8.5.6): + dependencies: + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + + postcss-selector-parser@7.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.2: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + primeng@19.1.3(47ee1c247593ea8ad66380722e410532): + dependencies: + '@angular/animations': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/cdk': 19.2.18(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/common': 19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 19.2.14(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/forms': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + '@angular/platform-browser': 19.2.14(@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/router': 19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.14(@angular/animations@19.2.14(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.14(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.14(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + '@primeuix/styled': 0.3.2 + '@primeuix/utils': 0.3.2 + rxjs: 7.8.2 + tslib: 2.8.1 + + proc-log@5.0.0: {} + + process-nextick-args@2.0.1: {} + + promise-retry@2.0.1: + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + prr@1.0.1: + optional: true + + punycode@1.4.1: {} + + qjobs@1.2.0: {} + + qs@6.13.0: + dependencies: + side-channel: 1.1.0 + + queue-microtask@1.2.3: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + range-parser@1.2.1: {} + + raw-body@2.5.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + readdirp@4.1.2: {} + + reflect-metadata@0.2.2: {} + + regenerate-unicode-properties@10.2.0: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regenerator-runtime@0.14.1: {} + + regex-parser@2.3.1: {} + + regexpu-core@6.2.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.0 + regjsgen: 0.8.0 + regjsparser: 0.12.0 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.0 + + regjsgen@0.8.0: {} + + regjsparser@0.12.0: + dependencies: + jsesc: 3.0.2 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + requires-port@1.0.0: {} + + resolve-from@4.0.0: {} + + resolve-url-loader@5.0.0: + dependencies: + adjust-sourcemap-loader: 4.0.0 + convert-source-map: 1.9.0 + loader-utils: 2.0.4 + postcss: 8.5.6 + source-map: 0.6.1 + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + retry@0.12.0: {} + + retry@0.13.1: {} + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + rollup@4.34.8: + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.34.8 + '@rollup/rollup-android-arm64': 4.34.8 + '@rollup/rollup-darwin-arm64': 4.34.8 + '@rollup/rollup-darwin-x64': 4.34.8 + '@rollup/rollup-freebsd-arm64': 4.34.8 + '@rollup/rollup-freebsd-x64': 4.34.8 + '@rollup/rollup-linux-arm-gnueabihf': 4.34.8 + '@rollup/rollup-linux-arm-musleabihf': 4.34.8 + '@rollup/rollup-linux-arm64-gnu': 4.34.8 + '@rollup/rollup-linux-arm64-musl': 4.34.8 + '@rollup/rollup-linux-loongarch64-gnu': 4.34.8 + '@rollup/rollup-linux-powerpc64le-gnu': 4.34.8 + '@rollup/rollup-linux-riscv64-gnu': 4.34.8 + '@rollup/rollup-linux-s390x-gnu': 4.34.8 + '@rollup/rollup-linux-x64-gnu': 4.34.8 + '@rollup/rollup-linux-x64-musl': 4.34.8 + '@rollup/rollup-win32-arm64-msvc': 4.34.8 + '@rollup/rollup-win32-ia32-msvc': 4.34.8 + '@rollup/rollup-win32-x64-msvc': 4.34.8 + fsevents: 2.3.3 + + run-applescript@7.0.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + run-script-os@1.1.6: {} + + rxjs@7.8.1: + dependencies: + tslib: 2.8.1 + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safer-buffer@2.1.2: {} + + sass-loader@16.0.5(sass@1.85.0)(webpack@5.98.0(esbuild@0.25.4)): + dependencies: + neo-async: 2.6.2 + optionalDependencies: + sass: 1.85.0 + webpack: 5.98.0(esbuild@0.25.4) + + sass@1.85.0: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.3 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + + sax@1.4.1: + optional: true + + schema-utils@4.3.2: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + ajv-keywords: 5.1.0(ajv@8.17.1) + + select-hose@2.0.0: {} + + selfsigned@2.4.1: + dependencies: + '@types/node-forge': 1.3.11 + node-forge: 1.3.1 + + semver@5.7.2: + optional: true + + semver@6.3.1: {} + + semver@7.7.1: {} + + semver@7.7.2: {} + + send@0.19.0: + 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 + transitivePeerDependencies: + - supports-color + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + serve-index@1.9.1: + dependencies: + accepts: 1.3.8 + batch: 0.6.1 + debug: 2.6.9 + escape-html: 1.0.3 + http-errors: 1.6.3 + mime-types: 2.1.35 + parseurl: 1.3.3 + transitivePeerDependencies: + - supports-color + + serve-static@1.16.2: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.0 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.1.0: {} + + setprototypeof@1.2.0: {} + + shallow-clone@3.0.1: + dependencies: + kind-of: 6.0.3 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.8.3: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + sigstore@3.1.0: + dependencies: + '@sigstore/bundle': 3.1.0 + '@sigstore/core': 2.0.0 + '@sigstore/protobuf-specs': 0.4.3 + '@sigstore/sign': 3.1.0 + '@sigstore/tuf': 3.1.1 + '@sigstore/verify': 2.1.1 + transitivePeerDependencies: + - supports-color + + slash@5.1.0: {} + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 + + smart-buffer@4.2.0: {} + + socket.io-adapter@2.5.5: + dependencies: + debug: 4.3.7 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + socket.io@4.8.1: + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.5 + debug: 4.3.7 + engine.io: 6.6.4 + socket.io-adapter: 2.5.5 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + sockjs@0.3.24: + dependencies: + faye-websocket: 0.11.4 + uuid: 8.3.2 + websocket-driver: 0.7.4 + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.3 + debug: 4.4.1 + socks: 2.8.5 + transitivePeerDependencies: + - supports-color + + socks@2.8.5: + dependencies: + ip-address: 9.0.5 + smart-buffer: 4.2.0 + + source-map-js@1.2.1: {} + + source-map-loader@5.0.0(webpack@5.98.0(esbuild@0.25.4)): + dependencies: + iconv-lite: 0.6.3 + source-map-js: 1.2.1 + webpack: 5.98.0(esbuild@0.25.4) + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + source-map@0.7.4: {} + + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.21 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.21 + + spdx-license-ids@3.0.21: {} + + spdy-transport@3.0.0: + dependencies: + debug: 4.4.1 + detect-node: 2.1.0 + hpack.js: 2.1.6 + obuf: 1.1.2 + readable-stream: 3.6.2 + wbuf: 1.7.3 + transitivePeerDependencies: + - supports-color + + spdy@4.0.2: + dependencies: + debug: 4.4.1 + handle-thing: 2.0.1 + http-deceiver: 1.2.7 + select-hose: 2.0.0 + spdy-transport: 3.0.0 + transitivePeerDependencies: + - supports-color + + sprintf-js@1.1.3: {} + + ssri@12.0.0: + dependencies: + minipass: 7.1.2 + + statuses@1.5.0: {} + + statuses@2.0.1: {} + + streamroller@3.1.5: + dependencies: + date-format: 4.0.14 + debug: 4.4.1 + fs-extra: 8.1.0 + transitivePeerDependencies: + - supports-color + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + symbol-observable@4.0.0: {} + + tailwindcss-primeui@0.6.1(tailwindcss@4.1.10): + dependencies: + tailwindcss: 4.1.10 + + tailwindcss@4.1.10: {} + + tapable@2.2.2: {} + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.2 + mkdirp: 3.0.1 + yallist: 5.0.0 + + terser-webpack-plugin@5.3.14(esbuild@0.25.4)(webpack@5.98.0): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + jest-worker: 27.5.1 + schema-utils: 4.3.2 + serialize-javascript: 6.0.2 + terser: 5.39.0 + webpack: 5.98.0(esbuild@0.25.4) + optionalDependencies: + esbuild: 0.25.4 + + terser@5.39.0: + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.15.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + thingies@1.21.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + thunky@1.1.0: {} + + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + tmp@0.2.3: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + tree-dump@1.0.3(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + tree-kill@1.2.2: {} + + tslib@2.8.1: {} + + tuf-js@3.0.1: + dependencies: + '@tufjs/models': 3.0.1 + debug: 4.4.1 + make-fetch-happen: 14.0.3 + transitivePeerDependencies: + - supports-color + + type-fest@0.21.3: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + typed-assert@1.0.9: {} + + typescript@5.8.3: {} + + ua-parser-js@0.7.40: {} + + undici-types@7.8.0: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.1.0 + + unicode-match-property-value-ecmascript@2.2.0: {} + + unicode-property-aliases-ecmascript@2.1.0: {} + + unicorn-magic@0.3.0: {} + + unique-filename@4.0.0: + dependencies: + unique-slug: 5.0.0 + + unique-slug@5.0.0: + dependencies: + imurmurhash: 0.1.4 + + universalify@0.1.2: {} + + unpipe@1.0.0: {} + + update-browserslist-db@1.1.3(browserslist@4.25.0): + dependencies: + browserslist: 4.25.0 + escalade: 3.2.0 + picocolors: 1.1.1 + + util-deprecate@1.0.2: {} + + utils-merge@1.0.1: {} + + uuid@8.3.2: {} + + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + validate-npm-package-name@6.0.1: {} + + vary@1.1.2: {} + + vite@6.2.7(@types/node@24.0.3)(jiti@2.4.2)(less@4.2.2)(lightningcss@1.30.1)(sass@1.85.0)(terser@5.39.0): + dependencies: + esbuild: 0.25.4 + postcss: 8.5.6 + rollup: 4.34.8 + optionalDependencies: + '@types/node': 24.0.3 + fsevents: 2.3.3 + jiti: 2.4.2 + less: 4.2.2 + lightningcss: 1.30.1 + sass: 1.85.0 + terser: 5.39.0 + + void-elements@2.0.1: {} + + watchpack@2.4.2: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + wbuf@1.7.3: + dependencies: + minimalistic-assert: 1.0.1 + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + weak-lru-cache@1.2.2: + optional: true + + webpack-dev-middleware@7.4.2(webpack@5.98.0): + dependencies: + colorette: 2.0.20 + memfs: 4.17.2 + mime-types: 2.1.35 + on-finished: 2.4.1 + range-parser: 1.2.1 + schema-utils: 4.3.2 + optionalDependencies: + webpack: 5.98.0(esbuild@0.25.4) + + webpack-dev-server@5.2.2(webpack@5.98.0): + dependencies: + '@types/bonjour': 3.5.13 + '@types/connect-history-api-fallback': 1.5.4 + '@types/express': 4.17.23 + '@types/express-serve-static-core': 4.19.6 + '@types/serve-index': 1.9.4 + '@types/serve-static': 1.15.8 + '@types/sockjs': 0.3.36 + '@types/ws': 8.18.1 + ansi-html-community: 0.0.8 + bonjour-service: 1.3.0 + chokidar: 3.6.0 + colorette: 2.0.20 + compression: 1.8.0 + connect-history-api-fallback: 2.0.0 + express: 4.21.2 + graceful-fs: 4.2.11 + http-proxy-middleware: 2.0.9(@types/express@4.17.23) + ipaddr.js: 2.2.0 + launch-editor: 2.10.0 + open: 10.1.0 + p-retry: 6.2.1 + schema-utils: 4.3.2 + selfsigned: 2.4.1 + serve-index: 1.9.1 + sockjs: 0.3.24 + spdy: 4.0.2 + webpack-dev-middleware: 7.4.2(webpack@5.98.0) + ws: 8.18.2 + optionalDependencies: + webpack: 5.98.0(esbuild@0.25.4) + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + webpack-merge@6.0.1: + dependencies: + clone-deep: 4.0.1 + flat: 5.0.2 + wildcard: 2.0.1 + + webpack-sources@3.3.2: {} + + webpack-subresource-integrity@5.1.0(webpack@5.98.0(esbuild@0.25.4)): + dependencies: + typed-assert: 1.0.9 + webpack: 5.98.0(esbuild@0.25.4) + + webpack@5.98.0(esbuild@0.25.4): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + browserslist: 4.25.0 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.1 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.2 + tapable: 2.2.2 + terser-webpack-plugin: 5.3.14(esbuild@0.25.4)(webpack@5.98.0) + watchpack: 2.4.2 + webpack-sources: 3.3.2 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + websocket-driver@0.7.4: + dependencies: + http-parser-js: 0.5.10 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 + + websocket-extensions@0.1.4: {} + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + which@5.0.0: + dependencies: + isexe: 3.1.1 + + wildcard@2.0.1: {} + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + wrappy@1.0.2: {} + + ws@8.17.1: {} + + ws@8.18.2: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yallist@5.0.0: {} + + yargs-parser@20.2.9: {} + + yargs-parser@21.1.1: {} + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@1.2.1: {} + + yoctocolors-cjs@2.1.2: {} + + zone.js@0.15.1: {} diff --git a/src/Vegasco-Web/proxy.config.js b/src/Vegasco-Web/proxy.config.js new file mode 100644 index 0000000..086e764 --- /dev/null +++ b/src/Vegasco-Web/proxy.config.js @@ -0,0 +1,11 @@ +module.exports = { + "/api": { + target: + process.env["services__Api__https__0"] || + process.env["services__Api__http__0"], + secure: process.env["NODE_ENV"] !== "development", + pathRewrite: { + "^/api": "", + }, + }, +}; diff --git a/src/Vegasco-Web/public/favicon.ico b/src/Vegasco-Web/public/favicon.ico new file mode 100644 index 0000000..57614f9 Binary files /dev/null and b/src/Vegasco-Web/public/favicon.ico differ diff --git a/src/Vegasco-Web/src/app/api/api-base-path.ts b/src/Vegasco-Web/src/app/api/api-base-path.ts new file mode 100644 index 0000000..173a941 --- /dev/null +++ b/src/Vegasco-Web/src/app/api/api-base-path.ts @@ -0,0 +1,6 @@ +import { InjectionToken } from "@angular/core"; + +/** + * The base path for all API requests, e.g. when using a proxy on the origin's address. + */ +export const API_BASE_PATH = new InjectionToken('API_BASE_PATH'); diff --git a/src/Vegasco-Web/src/app/api/cars/car-client.ts b/src/Vegasco-Web/src/app/api/cars/car-client.ts new file mode 100644 index 0000000..ee0be41 --- /dev/null +++ b/src/Vegasco-Web/src/app/api/cars/car-client.ts @@ -0,0 +1,35 @@ +import { HttpClient } from '@angular/common/http'; +import { inject, Injectable } from "@angular/core"; +import { map, Observable } from 'rxjs'; +import { API_BASE_PATH } from "../api-base-path"; + +@Injectable({ + providedIn: 'root', +}) +export class CarClient { + private readonly http = inject(HttpClient); + private readonly apiBasePath = inject(API_BASE_PATH, { optional: true }); + + getAll(): Observable { + return this.http.get(`${this.apiBasePath}/v1/cars`); + } + + getSingle(id: string): Observable { + return this.http.get(`${this.apiBasePath}/v1/cars/${id}`); + } + + create(request: CreateCarRequest): Observable { + return this.http.post(`${this.apiBasePath}/v1/cars`, request); + } + + update(id: string, request: UpdateCarRequest): Observable { + return this.http.put(`${this.apiBasePath}/v1/cars/${id}`, request); + } + + delete(id: string): Observable { + return this.http.delete(`${this.apiBasePath}/v1/cars/${id}`) + .pipe( + map(_ => undefined) + ); + } +} diff --git a/src/Vegasco-Web/src/app/api/cars/car.ts b/src/Vegasco-Web/src/app/api/cars/car.ts new file mode 100644 index 0000000..ecd122a --- /dev/null +++ b/src/Vegasco-Web/src/app/api/cars/car.ts @@ -0,0 +1,4 @@ +interface Car { + id: string; + name: string; +} diff --git a/src/Vegasco-Web/src/app/api/cars/create-car-request.ts b/src/Vegasco-Web/src/app/api/cars/create-car-request.ts new file mode 100644 index 0000000..2d462f3 --- /dev/null +++ b/src/Vegasco-Web/src/app/api/cars/create-car-request.ts @@ -0,0 +1,3 @@ +interface CreateCarRequest { + name: string; +} diff --git a/src/Vegasco-Web/src/app/api/cars/get-cars-response.ts b/src/Vegasco-Web/src/app/api/cars/get-cars-response.ts new file mode 100644 index 0000000..6287ec1 --- /dev/null +++ b/src/Vegasco-Web/src/app/api/cars/get-cars-response.ts @@ -0,0 +1,3 @@ +interface GetCarsResponse { + cars: Car[]; +} diff --git a/src/Vegasco-Web/src/app/api/cars/update-car-request.ts b/src/Vegasco-Web/src/app/api/cars/update-car-request.ts new file mode 100644 index 0000000..f883f34 --- /dev/null +++ b/src/Vegasco-Web/src/app/api/cars/update-car-request.ts @@ -0,0 +1,3 @@ +interface UpdateCarRequest { + name: string; +} diff --git a/src/Vegasco-Web/src/app/api/consumptions/consumption-client.ts b/src/Vegasco-Web/src/app/api/consumptions/consumption-client.ts new file mode 100644 index 0000000..c360d2c --- /dev/null +++ b/src/Vegasco-Web/src/app/api/consumptions/consumption-client.ts @@ -0,0 +1,35 @@ +import { HttpClient } from '@angular/common/http'; +import { inject, Injectable } from '@angular/core'; +import { map, Observable } from 'rxjs'; +import { API_BASE_PATH } from '../api-base-path'; + +@Injectable({ + providedIn: 'root', +}) +export class ConsumptionClient { + private readonly http = inject(HttpClient); + private readonly apiBasePath = inject(API_BASE_PATH, { optional: true }); + + getAll(): Observable { + return this.http.get(`${this.apiBasePath}/v1/consumptions`); + } + + getSingle(id: string): Observable { + return this.http.get(`${this.apiBasePath}/v1/consumptions/${id}`); + } + + create(request: CreateConsumptionEntry): Observable { + return this.http.post(`${this.apiBasePath}/v1/consumptions`, request); + } + + update(id: string, request: UpdateConsumptionEntry): Observable { + return this.http.put(`${this.apiBasePath}/v1/consumptions/${id}`, request); + } + + delete(id: string): Observable { + return this.http.delete(`${this.apiBasePath}/v1/consumptions/${id}`) + .pipe( + map(_ => undefined), + ); + } +} diff --git a/src/Vegasco-Web/src/app/api/consumptions/consumption-entry.ts b/src/Vegasco-Web/src/app/api/consumptions/consumption-entry.ts new file mode 100644 index 0000000..89774b5 --- /dev/null +++ b/src/Vegasco-Web/src/app/api/consumptions/consumption-entry.ts @@ -0,0 +1,7 @@ +interface ConsumptionEntry { + id: string; + dateTime: string; + distance: number; + amount: number; + carId: string; +} diff --git a/src/Vegasco-Web/src/app/api/consumptions/create-consumption-entry.ts b/src/Vegasco-Web/src/app/api/consumptions/create-consumption-entry.ts new file mode 100644 index 0000000..dea5b78 --- /dev/null +++ b/src/Vegasco-Web/src/app/api/consumptions/create-consumption-entry.ts @@ -0,0 +1,6 @@ +interface CreateConsumptionEntry { + dateTime: string; + distance: number; + amount: number; + carId: string; +} diff --git a/src/Vegasco-Web/src/app/api/consumptions/get-consumption-entries-entry.ts b/src/Vegasco-Web/src/app/api/consumptions/get-consumption-entries-entry.ts new file mode 100644 index 0000000..2585c9b --- /dev/null +++ b/src/Vegasco-Web/src/app/api/consumptions/get-consumption-entries-entry.ts @@ -0,0 +1,11 @@ +interface GetConsumptionEntriesEntry { + id: string; + dateTime: string; + distance: number; + amount: number; + car: { + id: string; + name: string; + }; + literPer100Km: number | null; +} diff --git a/src/Vegasco-Web/src/app/api/consumptions/get-consumption-entries-response.ts b/src/Vegasco-Web/src/app/api/consumptions/get-consumption-entries-response.ts new file mode 100644 index 0000000..a18ec60 --- /dev/null +++ b/src/Vegasco-Web/src/app/api/consumptions/get-consumption-entries-response.ts @@ -0,0 +1,3 @@ +interface GetConsumptionEntriesResponse { + consumptions: GetConsumptionEntriesEntry[]; +} diff --git a/src/Vegasco-Web/src/app/api/consumptions/update-consumption-entry.ts b/src/Vegasco-Web/src/app/api/consumptions/update-consumption-entry.ts new file mode 100644 index 0000000..41e169c --- /dev/null +++ b/src/Vegasco-Web/src/app/api/consumptions/update-consumption-entry.ts @@ -0,0 +1,6 @@ +interface UpdateConsumptionEntry { + dateTime: string; + distance: number; + amount: number; + carId: string; +} diff --git a/src/Vegasco-Web/src/app/app.config.ts b/src/Vegasco-Web/src/app/app.config.ts new file mode 100644 index 0000000..ad64235 --- /dev/null +++ b/src/Vegasco-Web/src/app/app.config.ts @@ -0,0 +1,30 @@ +import { provideHttpClient, withInterceptors } from '@angular/common/http'; +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; +import { provideRouter, withComponentInputBinding } from '@angular/router'; +import Lara from '@primeng/themes/lara'; +import { includeBearerTokenInterceptor } from 'keycloak-angular'; +import { providePrimeNG } from 'primeng/config'; +import { routes } from './app.routes'; +import { provideKeycloakAngular } from './auth/auth.config'; +import {API_BASE_PATH} from './api/api-base-path'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideKeycloakAngular(), + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes, withComponentInputBinding()), + provideHttpClient(withInterceptors([includeBearerTokenInterceptor])), + provideAnimationsAsync(), + providePrimeNG({ + theme: { + preset: Lara + }, + ripple: true + }), + { + provide: API_BASE_PATH, + useValue: '/api' + } + ] +}; diff --git a/src/Vegasco-Web/src/app/app.html b/src/Vegasco-Web/src/app/app.html new file mode 100644 index 0000000..3628171 --- /dev/null +++ b/src/Vegasco-Web/src/app/app.html @@ -0,0 +1,21 @@ +
+
+ +
+
+ + +
+
\ No newline at end of file diff --git a/src/Vegasco-Web/src/app/app.routes.ts b/src/Vegasco-Web/src/app/app.routes.ts new file mode 100644 index 0000000..b30947e --- /dev/null +++ b/src/Vegasco-Web/src/app/app.routes.ts @@ -0,0 +1,17 @@ +import { Routes } from '@angular/router'; + +export const routes: Routes = [ + { + path: '', + redirectTo: 'entries', + pathMatch: 'full' + }, + { + path: 'entries', + loadChildren: () => import('./modules/entries/entries.routes').then(m => m.routes) + }, + { + path: 'cars', + loadChildren: () => import('./modules/cars/cars.routes').then(m => m.routes) + } +]; diff --git a/src/Vegasco-Web/src/app/app.scss b/src/Vegasco-Web/src/app/app.scss new file mode 100644 index 0000000..01e3fdc --- /dev/null +++ b/src/Vegasco-Web/src/app/app.scss @@ -0,0 +1,10 @@ +.content { + padding: 1rem; +} + +.header { + padding: 0 1rem; + display: flex; + align-items: center; + height: 100%; +} diff --git a/src/Vegasco-Web/src/app/app.spec.ts b/src/Vegasco-Web/src/app/app.spec.ts new file mode 100644 index 0000000..33b1aed --- /dev/null +++ b/src/Vegasco-Web/src/app/app.spec.ts @@ -0,0 +1,23 @@ +import { TestBed } from '@angular/core/testing'; +import { App } from './app'; + +describe('App', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [App], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain('Hello, Vegasco-Web'); + }); +}); diff --git a/src/Vegasco-Web/src/app/app.ts b/src/Vegasco-Web/src/app/app.ts new file mode 100644 index 0000000..799a02f --- /dev/null +++ b/src/Vegasco-Web/src/app/app.ts @@ -0,0 +1,15 @@ +import { Component } from '@angular/core'; +import { RouterLink, RouterOutlet } from '@angular/router'; +import { MessageService } from 'primeng/api'; +import { ToastModule } from 'primeng/toast'; + + +@Component({ + selector: 'app-root', + imports: [RouterLink, RouterOutlet, ToastModule], + providers: [MessageService], + templateUrl: './app.html', + styleUrl: './app.scss' +}) +export class App { +} diff --git a/src/Vegasco-Web/src/app/auth/auth.config.ts b/src/Vegasco-Web/src/app/auth/auth.config.ts new file mode 100644 index 0000000..db5d77b --- /dev/null +++ b/src/Vegasco-Web/src/app/auth/auth.config.ts @@ -0,0 +1,45 @@ +import { environment } from '@vegasco-web/environments/environment'; +import { + AutoRefreshTokenService, + createInterceptorCondition, + INCLUDE_BEARER_TOKEN_INTERCEPTOR_CONFIG, + IncludeBearerTokenCondition, + provideKeycloak, + UserActivityService, + withAutoRefreshToken +} from 'keycloak-angular'; + +const serverHostBearerInterceptorCondition = createInterceptorCondition({ + // The API is consumed through a proxy running on the same origin as the application. + // This means that the interceptor should include the bearer token for requests to the same origin + // which includes requests starting to / which implicitly sends the request to the same origin. + urlPattern: new RegExp(`^(${window.origin}|/)`) +}); + +export const provideKeycloakAngular = () => + provideKeycloak({ + config: { + url: environment.keycloak.host, + realm: environment.keycloak.realm, + clientId: environment.keycloak.clientId, + }, + initOptions: { + onLoad: 'login-required', + silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html', + redirectUri: window.location.origin + '/', + checkLoginIframe: false, + }, + features: [ + withAutoRefreshToken({ + onInactivityTimeout: 'login', + }) + ], + providers: [ + AutoRefreshTokenService, + UserActivityService, + { + provide: INCLUDE_BEARER_TOKEN_INTERCEPTOR_CONFIG, + useValue: [serverHostBearerInterceptorCondition] + } + ] + }); diff --git a/src/Vegasco-Web/src/app/modules/cars/cars.routes.ts b/src/Vegasco-Web/src/app/modules/cars/cars.routes.ts new file mode 100644 index 0000000..889800c --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/cars/cars.routes.ts @@ -0,0 +1,17 @@ +import { Routes } from "@angular/router"; +import { CarsComponent } from "./cars/cars.component"; + +export const routes: Routes = [ + { + path: '', + component: CarsComponent + }, + { + path: 'create', + loadComponent: () => import('./edit-car/edit-car.component').then(m => m.EditCarComponent) + }, + { + path: 'edit/:id', + loadComponent: () => import('./edit-car/edit-car.component').then(m => m.EditCarComponent) + } +]; \ No newline at end of file diff --git a/src/Vegasco-Web/src/app/modules/cars/cars/cars.component.html b/src/Vegasco-Web/src/app/modules/cars/cars/cars.component.html new file mode 100644 index 0000000..766bdf8 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/cars/cars/cars.component.html @@ -0,0 +1,38 @@ +
+ +
+
+ + + +
+
+
+ @if (nonDeletedCars$ | async; as cars) { + + +
+ @for (car of cars; track car.id) { + + } +
+
+
+ } @else { +
+ @for (_ of skeletonsIterationSource; track $index) { + + } +
+ } +
+
diff --git a/src/Vegasco-Web/src/app/modules/cars/cars/cars.component.scss b/src/Vegasco-Web/src/app/modules/cars/cars/cars.component.scss new file mode 100644 index 0000000..e71c33b --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/cars/cars/cars.component.scss @@ -0,0 +1,3 @@ +th, td { + padding: 0.5rem; +} \ No newline at end of file diff --git a/src/Vegasco-Web/src/app/modules/cars/cars/cars.component.ts b/src/Vegasco-Web/src/app/modules/cars/cars/cars.component.ts new file mode 100644 index 0000000..1024907 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/cars/cars/cars.component.ts @@ -0,0 +1,127 @@ +import { AsyncPipe, CommonModule } from '@angular/common'; +import { HttpErrorResponse } from '@angular/common/http'; +import { Component, DestroyRef, inject } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { RouterLink } from '@angular/router'; +import { NgIconComponent, provideIcons } from '@ng-icons/core'; +import { + matAddSharp, +} from '@ng-icons/material-icons/sharp'; +import { CarClient } from '@vegasco-web/api/cars/car-client'; +import { MessageService } from 'primeng/api'; +import { ButtonModule } from 'primeng/button'; +import { DataViewModule } from 'primeng/dataview'; +import { ScrollTopModule } from 'primeng/scrolltop'; +import { SelectModule } from 'primeng/select'; +import { SkeletonModule } from 'primeng/skeleton'; +import { + BehaviorSubject, + catchError, + combineLatest, + EMPTY, + map, + Observable, + throwError +} from 'rxjs'; +import { CarCardComponent } from './components/car-card/car-card.component'; +import { SelectedCarService } from '@vegasco-web/modules/entries/services/selected-car.service'; + +@Component({ + selector: 'app-entries', + imports: [ + AsyncPipe, + ButtonModule, + CommonModule, + DataViewModule, + CarCardComponent, + NgIconComponent, + ReactiveFormsModule, + RouterLink, + ScrollTopModule, + SelectModule, + SkeletonModule, + ], + providers: [ + provideIcons({ + matAddSharp, + }), + ], + templateUrl: './cars.component.html', + styleUrl: './cars.component.scss' +}) +export class CarsComponent { + private readonly carClient = inject(CarClient); + private readonly messageService = inject(MessageService); + private readonly selectedCarService = inject(SelectedCarService); + + protected readonly nonDeletedCars$: Observable; + + protected readonly skeletonsIterationSource = Array(10).fill(0); + + private readonly deletedCars$ = new BehaviorSubject([]); + + constructor() { + const cars$ = this.carClient.getAll() + .pipe( + map(response => response.cars), + map((cars) => cars + .sort((a, b) => a.name.localeCompare(b.name))), + ); + + this.nonDeletedCars$ = combineLatest([ + cars$, + this.deletedCars$ + ]) + .pipe( + takeUntilDestroyed(), + map(([cars, deletedCars]) => cars.filter(car => !deletedCars.includes(car.id))), + catchError((error) => this.handleGetCarsError(error)), + ); + } + + onCarDeleted(car: Car): void { + this.deletedCars$.next([...this.deletedCars$.value, car.id]); + this.messageService.add({ + severity: 'success', + summary: 'Auto gelöscht', + detail: 'Das Auto wurde erfolgreich gelöscht.', + }); + this.resetSelectedCarIfDeleted(car); + } + + private resetSelectedCarIfDeleted(car: Car) { + const selectedCarId = this.selectedCarService.getSelectedCarId(); + if (selectedCarId === car.id) { + this.selectedCarService.setSelectedCarId(null); + } + } + + private handleGetCarsError(error: unknown): Observable { + if (!(error instanceof HttpErrorResponse)) { + return throwError(() => new Error('An unexpected error occurred')); + } + + switch (true) { + case error.status >= 500 && error.status <= 599: + this.messageService.add({ + severity: 'error', + summary: 'Serverfehler', + detail: + 'Beim Abrufen der Einträge ist ein Fehler aufgetreten. Bitte versuche es erneut.', + }); + break; + default: + console.error(error); + this.messageService.add({ + severity: 'error', + summary: 'Unerwarteter Fehler', + detail: + 'Beim Abrufen der Einträge hat der Server eine unerwartete Antwort zurückgegeben.', + }); + break; + } + + return EMPTY; + } +} diff --git a/src/Vegasco-Web/src/app/modules/cars/cars/components/car-card/car-card.component.html b/src/Vegasco-Web/src/app/modules/cars/cars/components/car-card/car-card.component.html new file mode 100644 index 0000000..671d678 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/cars/cars/components/car-card/car-card.component.html @@ -0,0 +1,22 @@ + + +
+
+
+ +
+
+ +
{{ car().name }}
+
+
+
+
+
+ +
+
\ No newline at end of file diff --git a/src/Vegasco-Web/src/app/modules/cars/cars/components/car-card/car-card.component.scss b/src/Vegasco-Web/src/app/modules/cars/cars/components/car-card/car-card.component.scss new file mode 100644 index 0000000..42ece6f --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/cars/cars/components/car-card/car-card.component.scss @@ -0,0 +1,3 @@ +.edit-button { + cursor: pointer; +} diff --git a/src/Vegasco-Web/src/app/modules/cars/cars/components/car-card/car-card.component.ts b/src/Vegasco-Web/src/app/modules/cars/cars/components/car-card/car-card.component.ts new file mode 100644 index 0000000..8170817 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/cars/cars/components/car-card/car-card.component.ts @@ -0,0 +1,116 @@ +import { HttpErrorResponse } from '@angular/common/http'; +import { Component, DestroyRef, inject, input, output } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { NgIconComponent, provideIcons } from '@ng-icons/core'; +import { + matDirectionsCarOutline, +} from '@ng-icons/material-icons/outline'; +import { + matDeleteSharp +} from '@ng-icons/material-icons/sharp'; +import { CarClient } from '@vegasco-web/api/cars/car-client'; +import { RoutingService } from '@vegasco-web/services/routing.service'; +import { ConfirmationService, MessageService } from 'primeng/api'; +import { ButtonModule } from 'primeng/button'; +import { CardModule } from 'primeng/card'; +import { ConfirmDialogModule } from 'primeng/confirmdialog'; +import { catchError, EMPTY, Observable, tap, throwError } from 'rxjs'; + +@Component({ + selector: 'app-car-card', + imports: [ + ButtonModule, + CardModule, + ConfirmDialogModule, + NgIconComponent, + ], + providers: [ + provideIcons({ + matDeleteSharp, + matDirectionsCarOutline, + }), + ConfirmationService, + ], + templateUrl: './car-card.component.html', + styleUrl: './car-card.component.scss' +}) +export class CarCardComponent { + readonly car = input.required(); + + readonly carDeleted = output(); + + private readonly routingService = inject(RoutingService); + private readonly carClient = inject(CarClient); + private readonly messageService = inject(MessageService); + private readonly confirmationService = inject(ConfirmationService); + + private readonly destroyRef = inject(DestroyRef); + + async navigateToEdit(): Promise { + await this.routingService.navigateToEditCar(this.car().id); + } + + confirmDeleteCar(): void { + this.confirmationService.confirm({ + closeOnEscape: true, + dismissableMask: true, + header: 'Bist du sicher?', + message: `Möchtest du das Auto "${this.car().name}" wirklich löschen?`, + acceptButtonProps: { + label: 'Löschen', + severity: 'danger', + }, + rejectButtonProps: { + label: 'Abbrechen', + outlined: true, + }, + accept: () => this.deleteCar(), + }); + } + + deleteCar(): void { + this.carClient.delete(this.car().id) + .pipe( + takeUntilDestroyed(this.destroyRef), + tap(() => this.carDeleted.emit(this.car())), + catchError((error) => this.handleError(error)), + ) + .subscribe(); + } + + private handleError(error: unknown): Observable { + if (!(error instanceof HttpErrorResponse)) { + return throwError(() => error); + } + + switch (true) { + case error.status >= 500 && error.status <= 599: + this.messageService.add({ + severity: 'error', + summary: 'Serverfehler', + detail: + 'Beim Löschen des Autos ist ein Fehler aufgetreten. Bitte versuche es erneut.', + }); + break; + case error.status === 400: + this.messageService.add({ + severity: 'error', + summary: 'Clientfehler', + detail: + 'Die Anwendung scheint falsche Daten an den Server zu senden.', + }); + break; + default: + console.error(error); + this.messageService.add({ + severity: 'error', + summary: 'Unerwarteter Fehler', + detail: + 'Beim Löschen des Autos hat der Server eine unerwartete Antwort zurückgegeben.', + }); + break; + } + + return EMPTY; + } +} diff --git a/src/Vegasco-Web/src/app/modules/cars/edit-car/components/required-marker.component.html b/src/Vegasco-Web/src/app/modules/cars/edit-car/components/required-marker.component.html new file mode 100644 index 0000000..a01d1a4 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/cars/edit-car/components/required-marker.component.html @@ -0,0 +1 @@ +* diff --git a/src/Vegasco-Web/src/app/modules/cars/edit-car/components/required-marker.component.scss b/src/Vegasco-Web/src/app/modules/cars/edit-car/components/required-marker.component.scss new file mode 100644 index 0000000..00a2ac1 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/cars/edit-car/components/required-marker.component.scss @@ -0,0 +1,3 @@ +.required { + color: red; +} \ No newline at end of file diff --git a/src/Vegasco-Web/src/app/modules/cars/edit-car/components/required-marker.component.ts b/src/Vegasco-Web/src/app/modules/cars/edit-car/components/required-marker.component.ts new file mode 100644 index 0000000..5017a19 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/cars/edit-car/components/required-marker.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-required-marker', + imports: [ + ], + templateUrl: './required-marker.component.html', + styleUrl: './required-marker.component.scss' +}) +export class RequiredMarkerComponent { +} diff --git a/src/Vegasco-Web/src/app/modules/cars/edit-car/edit-car.component.html b/src/Vegasco-Web/src/app/modules/cars/edit-car/edit-car.component.html new file mode 100644 index 0000000..8f3d4c4 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/cars/edit-car/edit-car.component.html @@ -0,0 +1,31 @@ +@if (!isCarDataLoaded()) { +
+ +
+ + +
+
+} @else { +
+ +
+ + +
+ +
+ + +
+ +
+} diff --git a/src/Vegasco-Web/src/app/modules/cars/edit-car/edit-car.component.scss b/src/Vegasco-Web/src/app/modules/cars/edit-car/edit-car.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/Vegasco-Web/src/app/modules/cars/edit-car/edit-car.component.ts b/src/Vegasco-Web/src/app/modules/cars/edit-car/edit-car.component.ts new file mode 100644 index 0000000..3185433 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/cars/edit-car/edit-car.component.ts @@ -0,0 +1,225 @@ +import { HttpErrorResponse } from '@angular/common/http'; +import { Component, DestroyRef, inject, input, OnInit, signal } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { CarClient } from '@vegasco-web/api/cars/car-client'; +import { RoutingService } from '@vegasco-web/services/routing.service'; +import { MessageService } from 'primeng/api'; +import { ButtonModule } from 'primeng/button'; +import { ChipModule } from 'primeng/chip'; +import { DatePickerModule } from 'primeng/datepicker'; +import { FloatLabelModule } from 'primeng/floatlabel'; +import { InputGroupModule } from 'primeng/inputgroup'; +import { InputGroupAddonModule } from 'primeng/inputgroupaddon'; +import { InputNumberModule } from 'primeng/inputnumber'; +import { InputTextModule } from 'primeng/inputtext'; +import { MultiSelectModule } from 'primeng/multiselect'; +import { SelectModule } from 'primeng/select'; +import { SkeletonModule } from 'primeng/skeleton'; +import { catchError, EMPTY, Observable, switchMap, tap, throwError } from 'rxjs'; +import { RequiredMarkerComponent } from './components/required-marker.component'; + +@Component({ + selector: 'app-edit-entry', + imports: [ + ButtonModule, + ChipModule, + DatePickerModule, + FloatLabelModule, + InputGroupAddonModule, + InputGroupModule, + InputNumberModule, + InputTextModule, + MultiSelectModule, + ReactiveFormsModule, + RequiredMarkerComponent, + SelectModule, + SkeletonModule, + ], + templateUrl: './edit-car.component.html', + styleUrl: './edit-car.component.scss' +}) +export class EditCarComponent implements OnInit { + private readonly carClient = inject(CarClient); + private readonly routingService = inject(RoutingService); + private readonly destroyRef = inject(DestroyRef); + private readonly messageService = inject(MessageService); + + protected readonly id = input(undefined); + + protected readonly today = new Date(); + + protected readonly formFieldNames = { + name: 'name', + } as const; + + protected readonly formGroup = new FormGroup({ + [this.formFieldNames.name]: new FormControl({ value: null, disabled: true }, [Validators.required]), + }); + + protected readonly isCarDataLoaded = signal(false); + + ngOnInit(): void { + this.loadEntryDetailsAndEnableControls(); + } + + private loadEntryDetailsAndEnableControls() { + const carId = this.id(); + + if (carId === undefined || carId === null) { + this.enableFormControls(); + this.isCarDataLoaded.set(true); + return; + } + + this.carClient + .getSingle(carId) + .pipe( + takeUntilDestroyed(this.destroyRef), + catchError((error) => this.handleGetError(error)), + tap((car) => { + this.formGroup.patchValue({ + [this.formFieldNames.name]: car.name, + }); + }), + tap(() => { + this.enableFormControls(); + this.isCarDataLoaded.set(true); + }), + ) + .subscribe(); + + } + + private enableFormControls(): void { + for (const controlName of Object.values(this.formFieldNames)) { + const control = this.formGroup.get(controlName); + if (control) { + control.enable(); + } else { + console.warn(`Form control '${controlName}' not found.`); + } + } + } + + async navigateToOverviewPage(): Promise { + await this.routingService.navigateToCars(); + } + + onSubmit(): void { + if (this.formGroup.invalid) { + this.formGroup.markAllAsTouched(); + return; + } + + var carId = this.id(); + if (carId === undefined || carId === null) { + this.createCar(); + return; + } + + this.updateCar(carId); + } + + private getFormData() { + return { + name: this.formGroup.controls[this.formFieldNames.name].value!, + }; + } + + createCar() { + var request: CreateCarRequest = this.getFormData(); + this.carClient.create(request) + .pipe( + takeUntilDestroyed(this.destroyRef), + catchError((error) => this.handleCreateOrUpdateError(error, false)), + switchMap(() => this.routingService.navigateToCars()) + ) + .subscribe(); + } + + updateCar(id: string) { + var request: UpdateCarRequest = this.getFormData(); + this.carClient.update(id, request) + .pipe( + takeUntilDestroyed(this.destroyRef), + catchError((error) => this.handleCreateOrUpdateError(error, true)), + switchMap(() => this.routingService.navigateToCars()) + ) + .subscribe(); + } + + private handleGetError(error: unknown): Observable { + if (!(error instanceof HttpErrorResponse)) { + return throwError(() => error); + } + + switch (true) { + case error.status >= 500 && error.status <= 599: + this.messageService.add({ + severity: 'error', + summary: 'Serverfehler', + detail: + 'Beim Abrufen des Autos ist ein Fehler aufgetreten. Bitte versuche es erneut.', + }); + break; + default: + console.error(error); + this.messageService.add({ + severity: 'error', + summary: 'Unerwarteter Fehler', + detail: + 'Beim Abrufen des Autos hat der Server eine unerwartete Antwort zurückgegeben.', + }); + break; + } + + return EMPTY; + } + + private handleCreateOrUpdateError(error: unknown, isUpdate: boolean): Observable { + if (!(error instanceof HttpErrorResponse)) { + return throwError(() => error); + } + + const action = isUpdate ? 'Aktualisieren' : 'Erstellen'; + + switch (true) { + case error.status >= 500 && error.status <= 599: + this.messageService.add({ + severity: 'error', + summary: 'Serverfehler', + detail: + `Beim ${action} des Eintrags ist ein Fehler aufgetreten. Bitte versuche es erneut.`, + }); + break; + case error.status === 400: + this.messageService.add({ + severity: 'error', + summary: 'Clientfehler', + detail: + 'Die Anwendung scheint falsche Daten an den Server zu senden.', + }); + break; + case error.status === 409: + this.messageService.add({ + severity: 'warn', + summary: 'Konflikt', + detail: + 'Es existiert bereits ein Auto mit diesem Namen. Bitte wähle einen anderen Namen.', + }); + break; + default: + console.error(error); + this.messageService.add({ + severity: 'error', + summary: 'Unerwarteter Fehler', + detail: + `Beim ${action} des Eintrags hat der Server eine unerwartete Antwort zurückgegeben.`, + }); + break; + } + + return EMPTY; + } +} diff --git a/src/Vegasco-Web/src/app/modules/entries/edit-entry/components/required-marker.component.html b/src/Vegasco-Web/src/app/modules/entries/edit-entry/components/required-marker.component.html new file mode 100644 index 0000000..a01d1a4 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/edit-entry/components/required-marker.component.html @@ -0,0 +1 @@ +* diff --git a/src/Vegasco-Web/src/app/modules/entries/edit-entry/components/required-marker.component.scss b/src/Vegasco-Web/src/app/modules/entries/edit-entry/components/required-marker.component.scss new file mode 100644 index 0000000..00a2ac1 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/edit-entry/components/required-marker.component.scss @@ -0,0 +1,3 @@ +.required { + color: red; +} \ No newline at end of file diff --git a/src/Vegasco-Web/src/app/modules/entries/edit-entry/components/required-marker.component.ts b/src/Vegasco-Web/src/app/modules/entries/edit-entry/components/required-marker.component.ts new file mode 100644 index 0000000..5017a19 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/edit-entry/components/required-marker.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-required-marker', + imports: [ + ], + templateUrl: './required-marker.component.html', + styleUrl: './required-marker.component.scss' +}) +export class RequiredMarkerComponent { +} diff --git a/src/Vegasco-Web/src/app/modules/entries/edit-entry/edit-entry.component.html b/src/Vegasco-Web/src/app/modules/entries/edit-entry/edit-entry.component.html new file mode 100644 index 0000000..24bee44 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/edit-entry/edit-entry.component.html @@ -0,0 +1,90 @@ +@if (isLoading()) { +
+ + + + +
+ + +
+
+} @else { +
+ +
+ + @if (cars(); as cars) { + + } +
+ +
+
+ +
+ +
+ +
+ + + + km + +
+ +
+ + + + + +
+ +
+ + +
+ +
+} diff --git a/src/Vegasco-Web/src/app/modules/entries/edit-entry/edit-entry.component.scss b/src/Vegasco-Web/src/app/modules/entries/edit-entry/edit-entry.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/Vegasco-Web/src/app/modules/entries/edit-entry/edit-entry.component.ts b/src/Vegasco-Web/src/app/modules/entries/edit-entry/edit-entry.component.ts new file mode 100644 index 0000000..7a8cb4a --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/edit-entry/edit-entry.component.ts @@ -0,0 +1,293 @@ +import dayjs from 'dayjs'; +import { HttpErrorResponse } from '@angular/common/http'; +import { Component, computed, DestroyRef, inject, input, OnInit, signal, Signal } from '@angular/core'; +import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; +import { FormControl, FormGroup, ReactiveFormsModule, ValidationErrors, Validators } from '@angular/forms'; +import { CarClient } from '@vegasco-web/api/cars/car-client'; +import { ConsumptionClient } from '@vegasco-web/api/consumptions/consumption-client'; +import { RoutingService } from '@vegasco-web/services/routing.service'; +import { MessageService } from 'primeng/api'; +import { ButtonModule } from 'primeng/button'; +import { ChipModule } from 'primeng/chip'; +import { DatePickerModule } from 'primeng/datepicker'; +import { FloatLabelModule } from 'primeng/floatlabel'; +import { InputGroupModule } from 'primeng/inputgroup'; +import { InputGroupAddonModule } from 'primeng/inputgroupaddon'; +import { InputNumberModule } from 'primeng/inputnumber'; +import { InputTextModule } from 'primeng/inputtext'; +import { MultiSelectModule } from 'primeng/multiselect'; +import { SelectModule } from 'primeng/select'; +import { SkeletonModule } from 'primeng/skeleton'; +import { catchError, combineLatest, EMPTY, filter, map, Observable, switchMap, tap, throwError } from 'rxjs'; +import { RequiredMarkerComponent } from './components/required-marker.component'; +import { SelectedCarService } from '../services/selected-car.service'; + +@Component({ + selector: 'app-edit-entry', + imports: [ + ButtonModule, + ChipModule, + DatePickerModule, + FloatLabelModule, + InputGroupAddonModule, + InputGroupModule, + InputNumberModule, + InputTextModule, + MultiSelectModule, + ReactiveFormsModule, + RequiredMarkerComponent, + SelectModule, + SkeletonModule, + ], + templateUrl: './edit-entry.component.html', + styleUrl: './edit-entry.component.scss' +}) +export class EditEntryComponent implements OnInit { + private readonly carClient = inject(CarClient); + private readonly consumptionClient = inject(ConsumptionClient); + private readonly routingService = inject(RoutingService); + private readonly destroyRef = inject(DestroyRef); + private readonly messageService = inject(MessageService); + private readonly selectedCarService = inject(SelectedCarService); + + protected readonly id = input(undefined); + + protected readonly today = new Date(); + + protected readonly formFieldNames = { + car: 'car', + date: 'date', + mileage: 'mileage', + amount: 'amount', + } as const; + + protected readonly formGroup = new FormGroup({ + [this.formFieldNames.car]: new FormControl({ value: null, disabled: true }, [Validators.required]), + [this.formFieldNames.date]: new FormControl({ value: new Date(), disabled: true }, [Validators.required, this.dateTimeGreaterThanOrEqualToTodayValidator]), + [this.formFieldNames.mileage]: new FormControl({ value: null, disabled: true }, [Validators.required, Validators.min(1)]), + [this.formFieldNames.amount]: new FormControl({ value: null, disabled: true }, [Validators.required, Validators.min(1)]), + }); + + private readonly cars$: Observable; + protected readonly cars: Signal; + + private readonly isEntryDataLoaded = signal(false); + + protected readonly isLoading = computed(() => { + var cars = this.cars(); + var isEntryDataLoaded = this.isEntryDataLoaded(); + return cars === undefined || !isEntryDataLoaded; + }); + + constructor() { + this.cars$ = this.carClient + .getAll() + .pipe( + takeUntilDestroyed(), + map(response => response.cars + .sort((a, b) => a.name.localeCompare(b.name))), + tap(cars => { + const selectedCarId = this.selectedCarService.getSelectedCarId(); + + if (selectedCarId === null) { + const firstCar = cars[0]; + this.formGroup.controls[this.formFieldNames.car].setValue(firstCar); + this.selectedCarService.setSelectedCarId(firstCar?.id ?? null); + return; + } + + const selectedCar = cars.find(car => car.id === selectedCarId); + this.formGroup.controls[this.formFieldNames.car].setValue(selectedCar ?? null); + this.selectedCarService.setSelectedCarId(selectedCar?.id ?? null); + }), + ); + this.cars = toSignal(this.cars$); + } + + ngOnInit(): void { + this.loadEntryDetailsAndEnableControls(); + + this.formGroup.controls[this.formFieldNames.car] + .valueChanges + .pipe( + takeUntilDestroyed(this.destroyRef), + tap((car) => { + this.selectedCarService.setSelectedCarId(car?.id ?? null); + }) + ) + .subscribe(); + } + + private loadEntryDetailsAndEnableControls() { + const entryId = this.id(); + + if (entryId === undefined || entryId === null) { + this.enableFormControls(); + this.isEntryDataLoaded.set(true); + return; + } + + const consumption$ = this.consumptionClient + .getSingle(entryId); + + combineLatest([ + consumption$, this.cars$ + ]) + .pipe( + filter(([_, cars]) => cars !== undefined), + takeUntilDestroyed(this.destroyRef), + catchError((error) => this.handleGetError(error)), + tap(([consumption, cars]) => { + this.formGroup.patchValue({ + [this.formFieldNames.car]: cars!.find(c => c.id === consumption.carId) ?? null, + [this.formFieldNames.date]: new Date(consumption.dateTime), + [this.formFieldNames.mileage]: consumption.distance, + [this.formFieldNames.amount]: consumption.amount, + }); + }), + tap(() => { + this.enableFormControls(); + this.isEntryDataLoaded.set(true); + }), + ) + .subscribe(); + } + + private enableFormControls(): void { + for (const controlName of Object.values(this.formFieldNames)) { + const control = this.formGroup.get(controlName); + if (control) { + control.enable(); + } else { + console.warn(`Form control '${controlName}' not found.`); + } + } + } + + async navigateToOverviewPage(): Promise { + await this.routingService.navigateToEntries(); + } + + onSubmit(): void { + if (this.formGroup.invalid) { + this.formGroup.markAllAsTouched(); + return; + } + + var entryId = this.id(); + if (entryId === undefined || entryId === null) { + this.createEntry(); + return; + } + + this.updateEntry(entryId); + } + + private getFormData() { + var dateTime = new Date((this.formGroup.controls[this.formFieldNames.date].value ?? new Date).setHours(0, 0, 0, 0)); + + return { + carId: this.formGroup.controls[this.formFieldNames.car].value!.id, + dateTime: dateTime.toISOString(), + distance: this.formGroup.controls[this.formFieldNames.mileage].value!, + amount: this.formGroup.controls[this.formFieldNames.amount].value!, + }; + } + + createEntry() { + var request: CreateConsumptionEntry = this.getFormData(); + this.consumptionClient.create(request) + .pipe( + takeUntilDestroyed(this.destroyRef), + catchError((error) => this.handleCreateOrUpdateError(error)), + switchMap(() => this.routingService.navigateToEntries()) + ) + .subscribe(); + } + + updateEntry(id: string) { + var request: UpdateConsumptionEntry = this.getFormData(); + this.consumptionClient.update(id, request) + .pipe( + takeUntilDestroyed(this.destroyRef), + catchError((error) => this.handleCreateOrUpdateError(error)), + switchMap(() => this.routingService.navigateToEntries()) + ) + .subscribe(); + } + + private handleGetError(error: unknown): Observable { + if (!(error instanceof HttpErrorResponse)) { + return throwError(() => error); + } + + switch (true) { + case error.status >= 500 && error.status <= 599: + this.messageService.add({ + severity: 'error', + summary: 'Serverfehler', + detail: + 'Beim Erstellen des Eintrags ist ein Fehler aufgetreten. Bitte versuche es erneut.', + }); + break; + default: + console.error(error); + this.messageService.add({ + severity: 'error', + summary: 'Unerwarteter Fehler', + detail: + 'Beim Erstellen des Eintrags hat der Server eine unerwartete Antwort zurückgegeben.', + }); + break; + } + + return EMPTY; + } + + private handleCreateOrUpdateError(error: unknown): Observable { + if (!(error instanceof HttpErrorResponse)) { + return throwError(() => error); + } + + switch (true) { + case error.status >= 500 && error.status <= 599: + this.messageService.add({ + severity: 'error', + summary: 'Serverfehler', + detail: + 'Beim Erstellen des Eintrags ist ein Fehler aufgetreten. Bitte versuche es erneut.', + }); + break; + case error.status === 400: + this.messageService.add({ + severity: 'error', + summary: 'Clientfehler', + detail: + 'Die Anwendung scheint falsche Daten an den Server zu senden.', + }); + break; + default: + console.error(error); + this.messageService.add({ + severity: 'error', + summary: 'Unerwarteter Fehler', + detail: + 'Beim Erstellen des Eintrags hat der Server eine unerwartete Antwort zurückgegeben.', + }); + break; + } + + return EMPTY; + } + + private dateTimeGreaterThanOrEqualToTodayValidator(control: FormControl): ValidationErrors | null { + const tomorrowStartOfDay = dayjs().add(1, 'day').startOf('day'); + const controlDate = dayjs(control.value); + + if (controlDate.isBefore(tomorrowStartOfDay)) { + return null; + } + + return { dateTimeGreaterThanOrEqualToToday: true }; + } +} diff --git a/src/Vegasco-Web/src/app/modules/entries/entries.routes.ts b/src/Vegasco-Web/src/app/modules/entries/entries.routes.ts new file mode 100644 index 0000000..020bc21 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/entries.routes.ts @@ -0,0 +1,17 @@ +import { Routes } from "@angular/router"; +import { EntriesComponent } from "./entries/entries.component"; + +export const routes: Routes = [ + { + path: '', + component: EntriesComponent + }, + { + path: 'create', + loadComponent: () => import('./edit-entry/edit-entry.component').then(m => m.EditEntryComponent) + }, + { + path: 'edit/:id', + loadComponent: () => import('./edit-entry/edit-entry.component').then(m => m.EditEntryComponent) + } +]; \ No newline at end of file diff --git a/src/Vegasco-Web/src/app/modules/entries/entries/components/entry-card/entry-card.component.html b/src/Vegasco-Web/src/app/modules/entries/entries/components/entry-card/entry-card.component.html new file mode 100644 index 0000000..ae2476a --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/entries/components/entry-card/entry-card.component.html @@ -0,0 +1,49 @@ + + +
+
+
+ +
+
+ +
{{ entry().dateTime | date:"dd.MM.yyyy" }}
+
+
+ +
+
+ +
{{entry().distance }} km
+
+
+ +
+
+ +
{{entry().amount }} ℓ
+
+
+ +
+ @if (formattedLiterPer100Km(); as formattedLiterPer100Km) { +
+ +
+ {{ formattedLiterPer100Km }} + +
+
+ } +
+
+
+
+ + +
+
\ No newline at end of file diff --git a/src/Vegasco-Web/src/app/modules/entries/entries/components/entry-card/entry-card.component.scss b/src/Vegasco-Web/src/app/modules/entries/entries/components/entry-card/entry-card.component.scss new file mode 100644 index 0000000..42ece6f --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/entries/components/entry-card/entry-card.component.scss @@ -0,0 +1,3 @@ +.edit-button { + cursor: pointer; +} diff --git a/src/Vegasco-Web/src/app/modules/entries/entries/components/entry-card/entry-card.component.ts b/src/Vegasco-Web/src/app/modules/entries/entries/components/entry-card/entry-card.component.ts new file mode 100644 index 0000000..9c8558f --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/entries/components/entry-card/entry-card.component.ts @@ -0,0 +1,141 @@ +import { DatePipe } from '@angular/common'; +import { HttpErrorResponse } from '@angular/common/http'; +import { Component, computed, DestroyRef, inject, input, output } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { NgIconComponent, provideIcons } from '@ng-icons/core'; +import { + matCalendarMonthSharp, + matDeleteSharp, + matLocalGasStationSharp, + matSpeedSharp, + matStraightenSharp, +} from '@ng-icons/material-icons/sharp'; +import { ConsumptionClient } from '@vegasco-web/api/consumptions/consumption-client'; +import { RoutingService } from '@vegasco-web/services/routing.service'; +import { ConfirmationService, MessageService } from 'primeng/api'; +import { ButtonModule } from 'primeng/button'; +import { CardModule } from 'primeng/card'; +import { ConfirmDialogModule } from 'primeng/confirmdialog'; +import { catchError, EMPTY, Observable, tap, throwError } from 'rxjs'; +import { FractionComponent } from "../fraction/fraction.component"; + +@Component({ + selector: 'app-entry-card', + imports: [ + ButtonModule, + CardModule, + ConfirmDialogModule, + DatePipe, + NgIconComponent, + FractionComponent + ], + providers: [ + provideIcons({ + matDeleteSharp, + matCalendarMonthSharp, + matSpeedSharp, + matStraightenSharp, + matLocalGasStationSharp, + }), + ConfirmationService, + ], + templateUrl: './entry-card.component.html', + styleUrl: './entry-card.component.scss' +}) +export class EntryCardComponent { + readonly entry = input.required(); + + protected readonly formattedLiterPer100Km = computed(() => { + const entry = this.entry(); + + const formatted = entry.literPer100Km + ?.toFixed(2) + .replace('.', ','); + return formatted; + }) + + readonly entryDeleted = output(); + + private readonly routingService = inject(RoutingService); + private readonly consumptionClient = inject(ConsumptionClient); + private readonly messageService = inject(MessageService); + private readonly confirmationService = inject(ConfirmationService); + + private readonly destroyRef = inject(DestroyRef); + + async navigateToEdit(): Promise { + await this.routingService.navigateToEditEntry(this.entry().id); + } + + confirmDeleteEntry(): void { + const weighedAt = new Date( + Date.parse(this.entry().dateTime), + ).toLocaleString('de-DE', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + }); + + this.confirmationService.confirm({ + closeOnEscape: true, + dismissableMask: true, + header: 'Bist du sicher?', + message: `Möchtest du diesen Eintrag (${weighedAt} für ${this.entry().car.name}) wirklich löschen?`, + acceptButtonProps: { + label: 'Löschen', + severity: 'danger', + }, + rejectButtonProps: { + label: 'Abbrechen', + outlined: true, + }, + accept: () => this.deleteEntry(), + }); + } + + deleteEntry(): void { + this.consumptionClient.delete(this.entry().id) + .pipe( + takeUntilDestroyed(this.destroyRef), + tap(() => this.entryDeleted.emit(this.entry())), + catchError((error) => this.handleError(error)), + ) + .subscribe(); + } + + private handleError(error: unknown): Observable { + if (!(error instanceof HttpErrorResponse)) { + return throwError(() => error); + } + + switch (true) { + case error.status >= 500 && error.status <= 599: + this.messageService.add({ + severity: 'error', + summary: 'Serverfehler', + detail: + 'Beim Löschen des Eintrags ist ein Fehler aufgetreten. Bitte versuche es erneut.', + }); + break; + case error.status === 400: + this.messageService.add({ + severity: 'error', + summary: 'Clientfehler', + detail: + 'Die Anwendung scheint falsche Daten an den Server zu senden.', + }); + break; + default: + console.error(error); + this.messageService.add({ + severity: 'error', + summary: 'Unerwarteter Fehler', + detail: + 'Beim Löschen des Eintrags hat der Server eine unerwartete Antwort zurückgegeben.', + }); + break; + } + + return EMPTY; + } +} diff --git a/src/Vegasco-Web/src/app/modules/entries/entries/components/fraction/fraction.component.html b/src/Vegasco-Web/src/app/modules/entries/entries/components/fraction/fraction.component.html new file mode 100644 index 0000000..896a396 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/entries/components/fraction/fraction.component.html @@ -0,0 +1,9 @@ +
+ + {{ numerator() }} + + + + {{ denominator() }} + +
diff --git a/src/Vegasco-Web/src/app/modules/entries/entries/components/fraction/fraction.component.scss b/src/Vegasco-Web/src/app/modules/entries/entries/components/fraction/fraction.component.scss new file mode 100644 index 0000000..a8afa0b --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/entries/components/fraction/fraction.component.scss @@ -0,0 +1,11 @@ +.separator { + border-bottom-width: 1px; + width: 100% +} + +.text-half-size { + // Specifically use em here to allow the parent to control the font size + // The font size here should be smaller because it is a fraction and would + // otherwise look too large + font-size: 0.75em; +} diff --git a/src/Vegasco-Web/src/app/modules/entries/entries/components/fraction/fraction.component.ts b/src/Vegasco-Web/src/app/modules/entries/entries/components/fraction/fraction.component.ts new file mode 100644 index 0000000..73fc12f --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/entries/components/fraction/fraction.component.ts @@ -0,0 +1,12 @@ +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'app-fraction', + imports: [], + templateUrl: './fraction.component.html', + styleUrl: './fraction.component.scss' +}) +export class FractionComponent { + readonly numerator = input.required(); + readonly denominator = input.required(); +} diff --git a/src/Vegasco-Web/src/app/modules/entries/entries/entries.component.html b/src/Vegasco-Web/src/app/modules/entries/entries/entries.component.html new file mode 100644 index 0000000..2a359cc --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/entries/entries.component.html @@ -0,0 +1,43 @@ +
+ +
+
+ +
+
+ + + +
+
+
+ @if (consumptionEntries$ | async; as entries) { + + +
+ @for (entry of entries; track entry.id) { + + } +
+
+
+ } @else { +
+ @for (_ of skeletonsIterationSource; track $index) { + + } +
+ } +
+
diff --git a/src/Vegasco-Web/src/app/modules/entries/entries/entries.component.scss b/src/Vegasco-Web/src/app/modules/entries/entries/entries.component.scss new file mode 100644 index 0000000..e71c33b --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/entries/entries.component.scss @@ -0,0 +1,3 @@ +th, td { + padding: 0.5rem; +} \ No newline at end of file diff --git a/src/Vegasco-Web/src/app/modules/entries/entries/entries.component.ts b/src/Vegasco-Web/src/app/modules/entries/entries/entries.component.ts new file mode 100644 index 0000000..2429d27 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/entries/entries.component.ts @@ -0,0 +1,171 @@ +import { AsyncPipe, CommonModule } from '@angular/common'; +import { HttpErrorResponse } from '@angular/common/http'; +import { Component, DestroyRef, inject, OnInit } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { RouterLink } from '@angular/router'; +import { NgIconComponent, provideIcons } from '@ng-icons/core'; +import { + matAddSharp, +} from '@ng-icons/material-icons/sharp'; +import { CarClient } from '@vegasco-web/api/cars/car-client'; +import { ConsumptionClient } from '@vegasco-web/api/consumptions/consumption-client'; +import { MessageService } from 'primeng/api'; +import { ButtonModule } from 'primeng/button'; +import { DataViewModule } from 'primeng/dataview'; +import { ScrollTopModule } from 'primeng/scrolltop'; +import { SelectModule } from 'primeng/select'; +import { SkeletonModule } from 'primeng/skeleton'; +import { + BehaviorSubject, + catchError, + combineLatest, + EMPTY, + map, + Observable, + startWith, + tap, + throwError +} from 'rxjs'; +import { SelectedCarService } from '../services/selected-car.service'; +import { EntryCardComponent } from './components/entry-card/entry-card.component'; + +@Component({ + selector: 'app-entries', + imports: [ + AsyncPipe, + ButtonModule, + CommonModule, + DataViewModule, + EntryCardComponent, + NgIconComponent, + ReactiveFormsModule, + RouterLink, + ScrollTopModule, + SelectModule, + SkeletonModule, + ], + providers: [ + provideIcons({ + matAddSharp, + }), + ], + templateUrl: './entries.component.html', + styleUrl: './entries.component.scss' +}) +export class EntriesComponent implements OnInit { + private readonly carClient = inject(CarClient); + private readonly consumptionClient = inject(ConsumptionClient); + private readonly messageService = inject(MessageService); + private readonly selectedCarService = inject(SelectedCarService); + private readonly destroyRef = inject(DestroyRef); + + protected readonly consumptionEntries$: Observable; + protected readonly cars$: Observable; + + protected readonly skeletonsIterationSource = Array(10).fill(0); + + protected readonly selectedCar = new FormControl(null); + + private readonly deletedEntries$ = new BehaviorSubject([]); + + constructor() { + const entries = this.consumptionClient.getAll() + .pipe( + takeUntilDestroyed(), + map(response => response.consumptions.sort((a, b) => b.dateTime.localeCompare(a.dateTime))), + catchError((error) => this.handleGetEntriesError(error)) + ); + + this.consumptionEntries$ = combineLatest([ + entries, + this.selectedCar.valueChanges.pipe(startWith(null)), + this.deletedEntries$, + ]) + .pipe( + takeUntilDestroyed(), + map(([entries, selectedCar, deletedEntries]) => { + const nonDeletedEntries = + deletedEntries.length === 0 + ? entries + : entries.filter(entry => !deletedEntries.includes(entry.id)); + + if (!selectedCar) { + return nonDeletedEntries; + } + + return nonDeletedEntries.filter(entry => entry.car.id === selectedCar.id); + }), + ); + + this.cars$ = this.carClient.getAll() + .pipe( + takeUntilDestroyed(), + map(response => response.cars), + map((cars) => cars + .sort((a, b) => a.name.localeCompare(b.name))), + tap((cars) => { + const selectedCarId = this.selectedCarService.getSelectedCarId(); + + if (selectedCarId === null) { + const firstCar = cars[0]; + this.selectedCar.setValue(firstCar); + this.selectedCarService.setSelectedCarId(firstCar?.id ?? null); + return; + } + + const selectedCar = cars.find(car => car.id === selectedCarId); + this.selectedCar.setValue(selectedCar ?? null); + this.selectedCarService.setSelectedCarId(selectedCar?.id ?? null); + }), + ); + } + + ngOnInit(): void { + this.selectedCar.valueChanges + .pipe( + takeUntilDestroyed(this.destroyRef), + tap((car) => { + this.selectedCarService.setSelectedCarId(car?.id ?? null); + }) + ) + .subscribe(); + } + + onEntryDeleted(entry: GetConsumptionEntriesEntry): void { + this.deletedEntries$.next([...this.deletedEntries$.value, entry.id]); + this.messageService.add({ + severity: 'success', + summary: 'Eintrag gelöscht', + detail: 'Der Eintrag wurde erfolgreich gelöscht.', + }); + } + + private handleGetEntriesError(error: unknown): Observable { + if (!(error instanceof HttpErrorResponse)) { + return throwError(() => new Error('An unexpected error occurred')); + } + + switch (true) { + case error.status >= 500 && error.status <= 599: + this.messageService.add({ + severity: 'error', + summary: 'Serverfehler', + detail: + 'Beim Abrufen der Einträge ist ein Fehler aufgetreten. Bitte versuche es erneut.', + }); + break; + default: + console.error(error); + this.messageService.add({ + severity: 'error', + summary: 'Unerwarteter Fehler', + detail: + 'Beim Abrufen der Einträge hat der Server eine unerwartete Antwort zurückgegeben.', + }); + break; + } + + return EMPTY; + } +} diff --git a/src/Vegasco-Web/src/app/modules/entries/services/selected-car.service.ts b/src/Vegasco-Web/src/app/modules/entries/services/selected-car.service.ts new file mode 100644 index 0000000..e2c66d5 --- /dev/null +++ b/src/Vegasco-Web/src/app/modules/entries/services/selected-car.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { BehaviorSubject, tap } from "rxjs"; + +@Injectable({ + providedIn: "root", +}) +export class SelectedCarService { + static readonly SELECTED_CAR_ID_KEY = "SELECTED_CAR_ID"; + + private selectedCarId: string | null = null; + + constructor() { + this.loadStoredCarId(); + } + + private loadStoredCarId(): void { + this.selectedCarId = localStorage.getItem(SelectedCarService.SELECTED_CAR_ID_KEY); + } + + getSelectedCarId() { + return this.selectedCarId; + } + + setSelectedCarId(carId: string | null): void { + this.selectedCarId = carId; + if (carId === null) { + localStorage.removeItem(SelectedCarService.SELECTED_CAR_ID_KEY); + } else { + localStorage.setItem(SelectedCarService.SELECTED_CAR_ID_KEY, carId); + } + } +} \ No newline at end of file diff --git a/src/Vegasco-Web/src/app/services/routing.service.ts b/src/Vegasco-Web/src/app/services/routing.service.ts new file mode 100644 index 0000000..bc6a961 --- /dev/null +++ b/src/Vegasco-Web/src/app/services/routing.service.ts @@ -0,0 +1,33 @@ +import { inject, Injectable } from "@angular/core"; +import { Router } from "@angular/router"; + +@Injectable({ + providedIn: 'root' +}) +export class RoutingService { + private readonly router = inject(Router); + + async navigateToEntries(): Promise { + await this.router.navigateByUrl('/entries'); + } + + async navigateToEditEntry(entryId: string): Promise { + await this.router.navigate(['entries', 'edit', entryId]); + } + + async navigateToCreateEntry(): Promise { + await this.router.navigate(['entries', 'create']); + } + + async navigateToCars(): Promise { + await this.router.navigateByUrl('/cars'); + } + + async navigateToEditCar(entryId: string): Promise { + await this.router.navigate(['cars', 'edit', entryId]); + } + + async navigateToCreateCar(): Promise { + await this.router.navigate(['cars', 'create']); + } +} \ No newline at end of file diff --git a/src/Vegasco-Web/src/environments/environment.development.ts b/src/Vegasco-Web/src/environments/environment.development.ts new file mode 100644 index 0000000..3135d63 --- /dev/null +++ b/src/Vegasco-Web/src/environments/environment.development.ts @@ -0,0 +1,11 @@ +import { Environment } from "./environment.interface"; + +export const environment: Environment = { + name: "Dev", + isProduction: false, + keycloak: { + host: "https://login.nuyken.dev", + realm: "development", + clientId: "vegasco" + } +}; diff --git a/src/Vegasco-Web/src/environments/environment.interface.ts b/src/Vegasco-Web/src/environments/environment.interface.ts new file mode 100644 index 0000000..ed18e6d --- /dev/null +++ b/src/Vegasco-Web/src/environments/environment.interface.ts @@ -0,0 +1,17 @@ + +/** The app's configuration based on the target environment */ +export interface Environment { + /** A name for this configuration, e.g. 'Prod' */ + name: string; + /** Whether this configuration is for production or not */ + isProduction: boolean; + /** Keycloak login configuration */ + keycloak: { + /** The host under which the keycloak is reachable */ + host: string; + /** The keycloak realm in which the client lives */ + realm: string; + /** The app's client id */ + clientId: string; + } +} diff --git a/src/Vegasco-Web/src/environments/environment.production.ts b/src/Vegasco-Web/src/environments/environment.production.ts new file mode 100644 index 0000000..5c0d939 --- /dev/null +++ b/src/Vegasco-Web/src/environments/environment.production.ts @@ -0,0 +1,11 @@ +import { Environment } from "./environment.interface"; + +export const environment: Environment = { + name: "Prod", + isProduction: true, + keycloak: { + host: "https://login.nuyken.dev", + realm: "apps", + clientId: "vegasco" + } +}; diff --git a/src/Vegasco-Web/src/environments/environment.ts b/src/Vegasco-Web/src/environments/environment.ts new file mode 100644 index 0000000..4f4cb10 --- /dev/null +++ b/src/Vegasco-Web/src/environments/environment.ts @@ -0,0 +1,11 @@ +import { Environment } from "./environment.interface"; + +export const environment: Environment = { + name: "", + isProduction: false, + keycloak: { + host: "", + realm: "", + clientId: "" + } +}; diff --git a/src/Vegasco-Web/src/index.html b/src/Vegasco-Web/src/index.html new file mode 100644 index 0000000..334482a --- /dev/null +++ b/src/Vegasco-Web/src/index.html @@ -0,0 +1,13 @@ + + + + + VegascoWeb + + + + + + + + diff --git a/src/Vegasco-Web/src/main.ts b/src/Vegasco-Web/src/main.ts new file mode 100644 index 0000000..5df75f9 --- /dev/null +++ b/src/Vegasco-Web/src/main.ts @@ -0,0 +1,6 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { App } from './app/app'; + +bootstrapApplication(App, appConfig) + .catch((err) => console.error(err)); diff --git a/src/Vegasco-Web/src/styles.scss b/src/Vegasco-Web/src/styles.scss new file mode 100644 index 0000000..8064e90 --- /dev/null +++ b/src/Vegasco-Web/src/styles.scss @@ -0,0 +1,39 @@ +@use "tailwindcss"; +@plugin "tailwindcss-primeui"; + +html, +body { + height: 100%; + margin: 0; +} + +body { + font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif +} + +.max-content-width { + max-width: 1200px; +} + +.pos-absolute { + position: absolute; +} + +.pos-relative { + position: relative; +} + +.trbl-0 { + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.primary-color-text { + color: var(--primary-color-text); +} + +.visually-hidden { + visibility: hidden; +} diff --git a/src/Vegasco-Web/tsconfig.app.json b/src/Vegasco-Web/tsconfig.app.json new file mode 100644 index 0000000..264f459 --- /dev/null +++ b/src/Vegasco-Web/tsconfig.app.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "src/**/*.spec.ts" + ] +} diff --git a/src/Vegasco-Web/tsconfig.json b/src/Vegasco-Web/tsconfig.json new file mode 100644 index 0000000..7d4ffae --- /dev/null +++ b/src/Vegasco-Web/tsconfig.json @@ -0,0 +1,40 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "paths": { + "@vegasco-web/*": ["src/app/*"], + "@vegasco-web/assets/*": ["assets/*"], + "@vegasco-web/environments/*": ["src/environments/*"] + }, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "ES2022", + "module": "preserve" + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "typeCheckHostBindings": true, + "strictTemplates": true + }, + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/src/Vegasco-Web/tsconfig.spec.json b/src/Vegasco-Web/tsconfig.spec.json new file mode 100644 index 0000000..04df34c --- /dev/null +++ b/src/Vegasco-Web/tsconfig.spec.json @@ -0,0 +1,14 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.ts" + ] +} diff --git a/src/Vegasco-Web/webserver.conf.template b/src/Vegasco-Web/webserver.conf.template new file mode 100644 index 0000000..480e98a --- /dev/null +++ b/src/Vegasco-Web/webserver.conf.template @@ -0,0 +1,12 @@ +server { + listen 80; + + location ~ ^/api/(.*) { + proxy_pass ${apiUrl}/${DOLLAR}1; + } + + location / { + root /usr/share/nginx/html; + try_files ${DOLLAR}uri ${DOLLAR}uri/ /index.html =404; + } +} \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Assembly.cs b/src/Vegasco.Server.Api/Assembly.cs new file mode 100644 index 0000000..d66756a --- /dev/null +++ b/src/Vegasco.Server.Api/Assembly.cs @@ -0,0 +1,3 @@ +using StronglyTypedIds; + +[assembly: StronglyTypedIdDefaults(Template.Guid, "guid-efcore")] \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Authentication/JwtOptions.cs b/src/Vegasco.Server.Api/Authentication/JwtOptions.cs new file mode 100644 index 0000000..bb9073d --- /dev/null +++ b/src/Vegasco.Server.Api/Authentication/JwtOptions.cs @@ -0,0 +1,28 @@ +using FluentValidation; + +namespace Vegasco.Server.Api.Authentication; + +public class JwtOptions +{ + public const string SectionName = "JWT"; + + public string ValidAudience { get; set; } = ""; + + public string MetadataUrl { get; set; } = ""; + + public string? NameClaimType { get; set; } + + public bool AllowHttpMetadataUrl { get; set; } +} + +public class JwtOptionsValidator : AbstractValidator +{ + public JwtOptionsValidator() + { + RuleFor(x => x.ValidAudience) + .NotEmpty(); + + RuleFor(x => x.MetadataUrl) + .NotEmpty(); + } +} \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Authentication/UserAccessor.cs b/src/Vegasco.Server.Api/Authentication/UserAccessor.cs new file mode 100644 index 0000000..989d7bf --- /dev/null +++ b/src/Vegasco.Server.Api/Authentication/UserAccessor.cs @@ -0,0 +1,78 @@ +using Microsoft.Extensions.Options; +using System.Diagnostics.CodeAnalysis; +using System.Security.Claims; + +namespace Vegasco.Server.Api.Authentication; + +public sealed class UserAccessor +{ + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IOptions _jwtOptions; + + /// + /// Stores the username upon first retrieval + /// + private string? _cachedUsername; + + /// + /// Stores the id upon first retrieval + /// + private string? _cachedId; + + public UserAccessor(IHttpContextAccessor httpContextAccessor, IOptions jwtOptions) + { + _httpContextAccessor = httpContextAccessor; + _jwtOptions = jwtOptions; + } + + public string GetUsername() + { + if (string.IsNullOrEmpty(_cachedUsername)) + { + _cachedUsername = GetClaimValue(_jwtOptions.Value.NameClaimType ?? ClaimTypes.Name); + } + + return _cachedUsername; + } + + public string GetUserId() + { + if (string.IsNullOrEmpty(_cachedId)) + { + _cachedId = GetClaimValue(ClaimTypes.NameIdentifier); + } + + return _cachedId; + } + + private string GetClaimValue(string claimType) + { + HttpContext? httpContext = _httpContextAccessor.HttpContext; + + if (httpContext is null) + { + ThrowForMissingHttpContext(); + } + + string? claimValue = httpContext.User.FindFirstValue(claimType); + + if (string.IsNullOrWhiteSpace(claimValue)) + { + ThrowForMissingClaim(claimType); + } + + return claimValue; + } + + [DoesNotReturn] + private static void ThrowForMissingHttpContext() + { + throw new InvalidOperationException("No HttpContext available."); + } + + [DoesNotReturn] + private static void ThrowForMissingClaim(string claimType) + { + throw new InvalidOperationException($"No claim of type '{claimType}' found on the current user."); + } +} diff --git a/src/Vegasco.Server.Api/Cars/Car.cs b/src/Vegasco.Server.Api/Cars/Car.cs new file mode 100644 index 0000000..b506d60 --- /dev/null +++ b/src/Vegasco.Server.Api/Cars/Car.cs @@ -0,0 +1,42 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Vegasco.Server.Api.Consumptions; +using Vegasco.Server.Api.Users; + +namespace Vegasco.Server.Api.Cars; + +public class Car +{ + public CarId Id { get; set; } = CarId.New(); + + public string Name { get; set; } = ""; + + public string UserId { get; set; } = ""; + + public virtual User User { get; set; } = null!; + + public virtual ICollection Consumptions { get; set; } = []; +} + +public class CarTableConfiguration : IEntityTypeConfiguration +{ + public const int NameMaxLength = 50; + + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + + builder.Property(x => x.Id) + .HasConversion(); + + builder.Property(x => x.Name) + .IsRequired() + .HasMaxLength(NameMaxLength); + + builder.Property(x => x.UserId) + .IsRequired(); + + builder.HasOne(x => x.User) + .WithMany(x => x.Cars); + } +} diff --git a/src/Vegasco.Server.Api/Cars/CarId.cs b/src/Vegasco.Server.Api/Cars/CarId.cs new file mode 100644 index 0000000..86ea23e --- /dev/null +++ b/src/Vegasco.Server.Api/Cars/CarId.cs @@ -0,0 +1,6 @@ +using StronglyTypedIds; + +namespace Vegasco.Server.Api.Cars; + +[StronglyTypedId] +public partial struct CarId; diff --git a/src/Vegasco.Server.Api/Cars/CreateCar.cs b/src/Vegasco.Server.Api/Cars/CreateCar.cs new file mode 100644 index 0000000..aac0d7b --- /dev/null +++ b/src/Vegasco.Server.Api/Cars/CreateCar.cs @@ -0,0 +1,82 @@ +using FluentValidation; +using FluentValidation.Results; +using Microsoft.EntityFrameworkCore; +using Vegasco.Server.Api.Authentication; +using Vegasco.Server.Api.Common; +using Vegasco.Server.Api.Persistence; +using Vegasco.Server.Api.Users; + +namespace Vegasco.Server.Api.Cars; + +public static class CreateCar +{ + public record Request(string Name); + public record Response(Guid Id, string Name); + + public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder) + { + return builder + .MapPost("cars", Endpoint) + .WithTags("Cars") + .WithDescription("Creates a new car") + .Produces(201) + .ProducesValidationProblem() + .Produces(409); + } + + public class Validator : AbstractValidator + { + public Validator() + { + RuleFor(x => x.Name) + .NotEmpty() + .MaximumLength(CarTableConfiguration.NameMaxLength); + } + } + + private static async Task Endpoint( + Request request, + IEnumerable> validators, + ApplicationDbContext dbContext, + UserAccessor userAccessor, + CancellationToken cancellationToken) + { + List failedValidations = await validators.ValidateAllAsync(request, cancellationToken: cancellationToken); + if (failedValidations.Count > 0) + { + return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary())); + } + + string userId = userAccessor.GetUserId(); + + User? user = await dbContext.Users.FindAsync([userId], cancellationToken: cancellationToken); + if (user is null) + { + user = new User + { + Id = userId + }; + await dbContext.Users.AddAsync(user, cancellationToken); + } + + Car car = new() + { + Name = request.Name.Trim(), + UserId = userId + }; + + bool isDuplicate = await dbContext.Cars + .AnyAsync(x => x.Name.ToUpper() == request.Name.ToUpper(), cancellationToken); + + if (isDuplicate) + { + return TypedResults.Conflict(); + } + + await dbContext.Cars.AddAsync(car, cancellationToken); + await dbContext.SaveChangesAsync(cancellationToken); + + Response response = new(car.Id.Value, car.Name); + return TypedResults.Created($"/v1/cars/{car.Id}", response); + } +} diff --git a/src/Vegasco.Server.Api/Cars/DeleteCar.cs b/src/Vegasco.Server.Api/Cars/DeleteCar.cs new file mode 100644 index 0000000..3dd1f26 --- /dev/null +++ b/src/Vegasco.Server.Api/Cars/DeleteCar.cs @@ -0,0 +1,41 @@ +using Microsoft.EntityFrameworkCore; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Cars; + +public static class DeleteCar +{ + public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder) + { + return builder + .MapDelete("cars/{id:guid}", Endpoint) + .WithTags("Cars") + .WithDescription("Deletes a car by ID") + .Produces(204) + .Produces(404); + } + + private static async Task Endpoint( + Guid id, + ApplicationDbContext dbContext, + ILoggerFactory loggerFactory, + CancellationToken cancellationToken) + { + int rows = await dbContext.Cars + .Where(x => x.Id == new CarId(id)) + .ExecuteDeleteAsync(cancellationToken); + + if (rows == 0) + { + return TypedResults.NotFound(); + } + + if (rows > 1) + { + ILogger logger = loggerFactory.CreateLogger(nameof(DeleteCar)); + logger.LogWarning("Deleted '{DeletedRowCount}' rows for id '{CarId}'", rows, id); + } + + return TypedResults.NoContent(); + } +} \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Cars/GetCar.cs b/src/Vegasco.Server.Api/Cars/GetCar.cs new file mode 100644 index 0000000..b9fc687 --- /dev/null +++ b/src/Vegasco.Server.Api/Cars/GetCar.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Http.HttpResults; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Cars; + +public static class GetCar +{ + public record Response(Guid Id, string Name); + + public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder) + { + return builder + .MapGet("cars/{id:guid}", Endpoint) + .WithDescription("Returns a single car by ID") + .WithTags("Cars") + .Produces() + .Produces(404); + } + + private static async Task Endpoint( + Guid id, + ApplicationDbContext dbContext, + CancellationToken cancellationToken) + { + Car? car = await dbContext.Cars.FindAsync([new CarId(id)], cancellationToken: cancellationToken); + + if (car is null) + { + return TypedResults.NotFound(); + } + + Response response = new Response(car.Id.Value, car.Name); + return TypedResults.Ok(response); + } +} \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Cars/GetCars.cs b/src/Vegasco.Server.Api/Cars/GetCars.cs new file mode 100644 index 0000000..5a1ed2a --- /dev/null +++ b/src/Vegasco.Server.Api/Cars/GetCars.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Cars; + +public static class GetCars +{ + public class ApiResponse + { + public IEnumerable Cars { get; set; } = []; + } + + public record ResponseDto(Guid Id, string Name); + + public class Request + { + [FromQuery(Name = "page")] public int? Page { get; set; } + [FromQuery(Name = "pageSize")] public int? PageSize { get; set; } + } + + public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder) + { + return builder + .MapGet("cars", Endpoint) + .WithDescription("Returns all cars") + .WithTags("Cars") + .Produces(); + } + + private static async Task Endpoint( + [AsParameters] Request request, + ApplicationDbContext dbContext, + CancellationToken cancellationToken) + { + List cars = await dbContext.Cars + .Select(x => new ResponseDto(x.Id.Value, x.Name)) + .ToListAsync(cancellationToken); + + ApiResponse response = new ApiResponse + { + Cars = cars + }; + return TypedResults.Ok(response); + } +} diff --git a/src/Vegasco.Server.Api/Cars/UpdateCar.cs b/src/Vegasco.Server.Api/Cars/UpdateCar.cs new file mode 100644 index 0000000..e857eb9 --- /dev/null +++ b/src/Vegasco.Server.Api/Cars/UpdateCar.cs @@ -0,0 +1,72 @@ +using FluentValidation; +using FluentValidation.Results; +using Microsoft.EntityFrameworkCore; +using Vegasco.Server.Api.Authentication; +using Vegasco.Server.Api.Common; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Cars; + +public static class UpdateCar +{ + public record Request(string Name); + public record Response(Guid Id, string Name); + + public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder) + { + return builder + .MapPut("cars/{id:guid}", Endpoint) + .WithTags("Cars") + .WithDescription("Updates a car by ID") + .Produces() + .ProducesValidationProblem() + .Produces(404) + .Produces(409); + } + + public class Validator : AbstractValidator + { + public Validator() + { + RuleFor(x => x.Name) + .NotEmpty() + .MaximumLength(CarTableConfiguration.NameMaxLength); + } + } + + private static async Task Endpoint( + Guid id, + Request request, + IEnumerable> validators, + ApplicationDbContext dbContext, + UserAccessor userAccessor, + CancellationToken cancellationToken) + { + List failedValidations = await validators.ValidateAllAsync(request, cancellationToken); + if (failedValidations.Count > 0) + { + return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary())); + } + + Car? car = await dbContext.Cars.FindAsync([new CarId(id)], cancellationToken: cancellationToken); + + if (car is null) + { + return TypedResults.NotFound(); + } + + bool isDuplicate = await dbContext.Cars + .AnyAsync(x => x.Name.ToUpper() == request.Name.ToUpper(), cancellationToken); + + if (isDuplicate) + { + return TypedResults.Conflict(); + } + + car.Name = request.Name.Trim(); + await dbContext.SaveChangesAsync(cancellationToken); + + Response response = new(car.Id.Value, car.Name); + return TypedResults.Ok(response); + } +} diff --git a/src/Vegasco.Server.Api/Common/Constants.cs b/src/Vegasco.Server.Api/Common/Constants.cs new file mode 100644 index 0000000..5e4b521 --- /dev/null +++ b/src/Vegasco.Server.Api/Common/Constants.cs @@ -0,0 +1,9 @@ +namespace Vegasco.Server.Api.Common; + +public static class Constants +{ + public static class Authorization + { + public const string RequireAuthenticatedUserPolicy = "RequireAuthenticatedUser"; + } +} \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Common/DependencyInjectionExtensions.cs b/src/Vegasco.Server.Api/Common/DependencyInjectionExtensions.cs new file mode 100644 index 0000000..ccdbd30 --- /dev/null +++ b/src/Vegasco.Server.Api/Common/DependencyInjectionExtensions.cs @@ -0,0 +1,157 @@ +using Asp.Versioning; +using FluentValidation; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.Extensions.Options; +using System.Diagnostics; +using System.Reflection; +using Vegasco.Server.Api.Authentication; +using Vegasco.Server.Api.Common; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Common; + +public static class DependencyInjectionExtensions +{ + /// + /// Adds all the Api related services to the Dependency Injection container. + /// + /// + public static void AddApiServices(this IHostApplicationBuilder builder) + { + builder.Services + .AddMiscellaneousServices() + .AddCustomOpenApi() + .AddApiVersioning() + .AddAuthenticationAndAuthorization(builder.Environment); + + builder.AddDbContext(); + } + + private static IServiceCollection AddMiscellaneousServices(this IServiceCollection services) + { + services.AddSingleton(() => + { + string assemblyName = Assembly.GetExecutingAssembly() + .GetName() + .Name ?? "Vegasco.Server.Api"; + return new ActivitySource(assemblyName); + }); + + services.AddResponseCompression(); + + services.AddValidatorsFromAssemblies( + [ + typeof(IApiMarker).Assembly + ], ServiceLifetime.Singleton); + + services.AddHealthChecks(); + + services.AddHttpContextAccessor(); + + services.AddHostedService(); + + services.AddRequestLocalization(o => + { + string[] cultures = + [ + "en-US", + "en", + "de-DE", + "de" + ]; + + o.SetDefaultCulture(cultures[0]) + .AddSupportedCultures(cultures) + .AddSupportedUICultures(cultures); + }); + + return services; + } + + private static IServiceCollection AddCustomOpenApi(this IServiceCollection services) + { + services.AddEndpointsApiExplorer(); + services.AddOpenApi(o => + { + o.CreateSchemaReferenceId = jsonTypeInfo => + { + if (string.IsNullOrEmpty(jsonTypeInfo.Type.FullName)) + { + return jsonTypeInfo.Type.Name; + } + + string? fullClassName = jsonTypeInfo.Type.FullName; + + if (!string.IsNullOrEmpty(jsonTypeInfo.Type.Namespace)) + { + fullClassName = fullClassName + .Replace(jsonTypeInfo.Type.Namespace, "") + .TrimStart('.'); + } + + fullClassName = fullClassName.Replace('+', '_'); + return fullClassName; + }; + }); + + return services; + } + + private static IServiceCollection AddApiVersioning(this IServiceCollection services) + { + services.AddApiVersioning(o => + { + o.DefaultApiVersion = new ApiVersion(1); + o.ApiVersionReader = new UrlSegmentApiVersionReader(); + o.ReportApiVersions = true; + }) + .AddApiExplorer(o => + { + o.GroupNameFormat = "'v'V"; + o.SubstituteApiVersionInUrl = true; + }); + + return services; + } + + private static IServiceCollection AddAuthenticationAndAuthorization(this IServiceCollection services, IHostEnvironment environment) + { + services.AddOptions() + .BindConfiguration(JwtOptions.SectionName) + .ValidateFluently() + .ValidateOnStart(); + + IOptions jwtOptions = services.BuildServiceProvider().GetRequiredService>(); + + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, o => + { + o.MetadataAddress = jwtOptions.Value.MetadataUrl; + + o.TokenValidationParameters.ValidAudience = jwtOptions.Value.ValidAudience; + o.TokenValidationParameters.ValidateAudience = true; + + if (!string.IsNullOrWhiteSpace(jwtOptions.Value.NameClaimType)) + { + o.TokenValidationParameters.NameClaimType = jwtOptions.Value.NameClaimType; + } + + o.RequireHttpsMetadata = !jwtOptions.Value.AllowHttpMetadataUrl && !environment.IsDevelopment(); + }); + + services.AddAuthorizationBuilder() + .AddPolicy(Constants.Authorization.RequireAuthenticatedUserPolicy, p => p + .RequireAuthenticatedUser() + .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)); + + services.AddScoped(); + + return services; + } + + private static IHostApplicationBuilder AddDbContext(this IHostApplicationBuilder builder) + { + builder.AddNpgsqlDbContext(AppHost.Shared.Constants.Database.Name); + return builder; + } +} diff --git a/src/Vegasco.Server.Api/Common/FluentValidationOptions.cs b/src/Vegasco.Server.Api/Common/FluentValidationOptions.cs new file mode 100644 index 0000000..eaa3164 --- /dev/null +++ b/src/Vegasco.Server.Api/Common/FluentValidationOptions.cs @@ -0,0 +1,37 @@ +using FluentValidation; +using FluentValidation.Results; +using Microsoft.Extensions.Options; + +namespace Vegasco.Server.Api.Common; + +public class FluentValidationOptions : IValidateOptions + where TOptions : class +{ + private readonly IEnumerable> _validators; + + public string? Name { get; set; } + + public FluentValidationOptions(string? name, IEnumerable> validators) + { + Name = name; + _validators = validators; + } + + public ValidateOptionsResult Validate(string? name, TOptions options) + { + if (name is not null && name != Name) + { + return ValidateOptionsResult.Skip; + } + + ArgumentNullException.ThrowIfNull(options); + + List failedValidations = _validators.ValidateAllAsync(options).Result; + if (failedValidations.Count == 0) + { + return ValidateOptionsResult.Success; + } + + return ValidateOptionsResult.Fail(failedValidations.SelectMany(x => x.Errors.Select(x => x.ErrorMessage))); + } +} \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Common/IApiMarker.cs b/src/Vegasco.Server.Api/Common/IApiMarker.cs new file mode 100644 index 0000000..643d2b3 --- /dev/null +++ b/src/Vegasco.Server.Api/Common/IApiMarker.cs @@ -0,0 +1,3 @@ +namespace Vegasco.Server.Api.Common; + +public interface IApiMarker; diff --git a/src/Vegasco.Server.Api/Common/StartupExtensions.cs b/src/Vegasco.Server.Api/Common/StartupExtensions.cs new file mode 100644 index 0000000..ac96a37 --- /dev/null +++ b/src/Vegasco.Server.Api/Common/StartupExtensions.cs @@ -0,0 +1,46 @@ +using Asp.Versioning.ApiExplorer; +using Scalar.AspNetCore; +using Vegasco.Server.Api.Endpoints; +using Vegasco.Server.ServiceDefaults; + +namespace Vegasco.Server.Api.Common; + +internal static class StartupExtensions +{ + internal static WebApplication ConfigureServices(this WebApplicationBuilder builder) + { + builder.AddServiceDefaults(); + + builder.Configuration.AddEnvironmentVariables("Vegasco_"); + + builder.AddApiServices(); + + WebApplication app = builder.Build(); + return app; + } + + internal static WebApplication ConfigureRequestPipeline(this WebApplication app) + { + app.UseRequestLocalization(o => + { + o.ApplyCurrentCultureToResponseHeaders = true; + }); + + app.UseHttpsRedirection(); + + app.MapHealthChecks("/health"); + + app.UseAuthentication(); + app.UseAuthorization(); + + app.MapEndpoints(); + + if (app.Environment.IsDevelopment()) + { + app.MapOpenApi(); + app.MapScalarApiReference(); + } + + return app; + } +} diff --git a/src/Vegasco.Server.Api/Common/ValidatorExtensions.cs b/src/Vegasco.Server.Api/Common/ValidatorExtensions.cs new file mode 100644 index 0000000..b93bb68 --- /dev/null +++ b/src/Vegasco.Server.Api/Common/ValidatorExtensions.cs @@ -0,0 +1,62 @@ +using FluentValidation; +using FluentValidation.Results; +using Microsoft.Extensions.Options; + +namespace Vegasco.Server.Api.Common; + +public static class ValidatorExtensions +{ + /// + /// Asynchronously validates an instance of against all instances in . + /// + /// + /// + /// + /// The failed validation results. + public static async Task> ValidateAllAsync(this IEnumerable> validators, T instance, CancellationToken cancellationToken = default) + { + List> validationTasks = validators + .Select(validator => validator.ValidateAsync(instance, cancellationToken)) + .ToList(); + + await Task.WhenAll(validationTasks); + + List failedValidations = validationTasks + .Select(x => x.Result) + .Where(x => !x.IsValid) + .ToList(); + + return failedValidations; + } + + public static Dictionary ToCombinedDictionary(this IEnumerable validationResults) + { + // Use a hash set to avoid duplicate error messages. + Dictionary> combinedErrors = []; + + foreach (ValidationFailure? error in validationResults.SelectMany(x => x.Errors)) + { + if (!combinedErrors.TryGetValue(error.PropertyName, out HashSet? value)) + { + value = [error.ErrorMessage]; + combinedErrors[error.PropertyName] = value; + continue; + } + + value.Add(error.ErrorMessage); + } + + return combinedErrors.ToDictionary(x => x.Key, x => x.Value.ToArray()); + } + + public static OptionsBuilder ValidateFluently(this OptionsBuilder builder) + where T : class + { + builder.Services.AddTransient>(serviceProvider => + { + IEnumerable> validators = serviceProvider.GetServices>() ?? []; + return new FluentValidationOptions(builder.Name, validators); + }); + return builder; + } +} diff --git a/src/Vegasco.Server.Api/Consumptions/Consumption.cs b/src/Vegasco.Server.Api/Consumptions/Consumption.cs new file mode 100644 index 0000000..ae2024f --- /dev/null +++ b/src/Vegasco.Server.Api/Consumptions/Consumption.cs @@ -0,0 +1,47 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Vegasco.Server.Api.Cars; + +namespace Vegasco.Server.Api.Consumptions; + +public class Consumption +{ + public ConsumptionId Id { get; set; } = ConsumptionId.New(); + + public DateTimeOffset DateTime { get; set; } + + public double Distance { get; set; } + + public double Amount { get; set; } + + public CarId CarId { get; set; } + + public virtual Car Car { get; set; } = null!; +} + +public class ConsumptionTableConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + + builder.Property(x => x.Id) + .HasConversion(); + + builder.Property(x => x.DateTime) + .IsRequired(); + + builder.Property(x => x.Distance) + .IsRequired(); + + builder.Property(x => x.Amount) + .IsRequired(); + + builder.Property(x => x.CarId) + .IsRequired() + .HasConversion(); + + builder.HasOne(x => x.Car) + .WithMany(x => x.Consumptions); + } +} diff --git a/src/Vegasco.Server.Api/Consumptions/ConsumptionId.cs b/src/Vegasco.Server.Api/Consumptions/ConsumptionId.cs new file mode 100644 index 0000000..e4936fb --- /dev/null +++ b/src/Vegasco.Server.Api/Consumptions/ConsumptionId.cs @@ -0,0 +1,7 @@ +using StronglyTypedIds; + +namespace Vegasco.Server.Api.Consumptions; + + +[StronglyTypedId] +public partial struct ConsumptionId; diff --git a/src/Vegasco.Server.Api/Consumptions/CreateConsumption.cs b/src/Vegasco.Server.Api/Consumptions/CreateConsumption.cs new file mode 100644 index 0000000..9b236d3 --- /dev/null +++ b/src/Vegasco.Server.Api/Consumptions/CreateConsumption.cs @@ -0,0 +1,81 @@ +using FluentValidation; +using FluentValidation.Results; +using Vegasco.Server.Api.Cars; +using Vegasco.Server.Api.Common; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Consumptions; + +public static class CreateConsumption +{ + public record Request(DateTimeOffset DateTime, double Distance, double Amount, Guid CarId); + + public record Response(Guid Id, DateTimeOffset DateTime, double Distance, double Amount, Guid CarId); + + public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder) + { + return builder + .MapPost("consumptions", Endpoint) + .WithTags("Consumptions") + .WithDescription("Creates a new consumption entry") + .Produces(201); + } + + public class Validator : AbstractValidator + { + public Validator(TimeProvider timeProvider) + { + DateTime todayEndOfDay = timeProvider.GetUtcNow() + .Date + .AddDays(1) + .AddTicks(-1); + + RuleFor(x => x.DateTime.ToUniversalTime()) + .LessThanOrEqualTo(todayEndOfDay) + .WithName(nameof(Request.DateTime)); + + RuleFor(x => x.Distance) + .GreaterThan(0); + + RuleFor(x => x.Amount) + .GreaterThan(0); + + RuleFor(x => x.CarId) + .NotEmpty(); + } + } + + private static async Task Endpoint( + ApplicationDbContext dbContext, + Request request, + IEnumerable> validators, + CancellationToken cancellationToken) + { + List failedValidations = await validators.ValidateAllAsync(request, cancellationToken); + if (failedValidations.Count > 0) + { + return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary())); + } + + Car? car = await dbContext.Cars.FindAsync([new CarId(request.CarId)], cancellationToken); + if (car is null) + { + return TypedResults.NotFound(); + } + + Consumption consumption = new() + { + DateTime = request.DateTime.ToUniversalTime(), + Distance = request.Distance, + Amount = request.Amount, + CarId = new CarId(request.CarId) + }; + + dbContext.Consumptions.Add(consumption); + await dbContext.SaveChangesAsync(cancellationToken); + + return TypedResults.Created($"consumptions/{consumption.Id.Value}", + new Response(consumption.Id.Value, consumption.DateTime, consumption.Distance, consumption.Amount, + consumption.CarId.Value)); + } +} \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Consumptions/DeleteConsumptions.cs b/src/Vegasco.Server.Api/Consumptions/DeleteConsumptions.cs new file mode 100644 index 0000000..48dae75 --- /dev/null +++ b/src/Vegasco.Server.Api/Consumptions/DeleteConsumptions.cs @@ -0,0 +1,41 @@ +using Microsoft.EntityFrameworkCore; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Consumptions; + +public static class DeleteConsumption +{ + public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder) + { + return builder + .MapDelete("consumptions/{id:guid}", Endpoint) + .WithTags("Consumptions") + .WithDescription("Deletes a consumption entry by ID") + .Produces(204) + .Produces(404); + } + + private static async Task Endpoint( + Guid id, + ApplicationDbContext dbContext, + ILoggerFactory loggerFactory, + CancellationToken cancellationToken) + { + int rows = await dbContext.Consumptions + .Where(x => x.Id == new ConsumptionId(id)) + .ExecuteDeleteAsync(cancellationToken); + + if (rows == 0) + { + return TypedResults.NotFound(); + } + + if (rows > 1) + { + ILogger logger = loggerFactory.CreateLogger(nameof(DeleteConsumption)); + logger.LogWarning("Deleted '{DeletedRowCount}' rows for id '{ConsumptionId}'", rows, id); + } + + return TypedResults.NoContent(); + } +} \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Consumptions/GetConsumption.cs b/src/Vegasco.Server.Api/Consumptions/GetConsumption.cs new file mode 100644 index 0000000..82b8dc7 --- /dev/null +++ b/src/Vegasco.Server.Api/Consumptions/GetConsumption.cs @@ -0,0 +1,39 @@ +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Consumptions; + +public static class GetConsumption +{ + public record Response(Guid Id, DateTimeOffset DateTime, double Distance, double Amount, Guid CarId); + + public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder) + { + return builder + .MapGet("consumptions/{id:guid}", Endpoint) + .WithTags("Consumptions") + .WithDescription("Returns a single consumption entry by ID") + .Produces() + .Produces(404); + } + + private static async Task Endpoint( + ApplicationDbContext dbContext, + Guid id, + CancellationToken cancellationToken) + { + Consumption? consumption = await dbContext.Consumptions.FindAsync([new ConsumptionId(id)], cancellationToken); + + if (consumption is null) + { + return TypedResults.NotFound(); + } + + Response response = new( + consumption.Id.Value, + consumption.DateTime, + consumption.Distance, + consumption.Amount, + consumption.CarId.Value); + return TypedResults.Ok(response); + } +} \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Consumptions/GetConsumptions.cs b/src/Vegasco.Server.Api/Consumptions/GetConsumptions.cs new file mode 100644 index 0000000..37b1169 --- /dev/null +++ b/src/Vegasco.Server.Api/Consumptions/GetConsumptions.cs @@ -0,0 +1,93 @@ +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Vegasco.Server.Api.Cars; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Consumptions; + +public static class GetConsumptions +{ + public class ApiResponse + { + public IEnumerable Consumptions { get; set; } = []; + } + + public record ResponseDto( + Guid Id, + DateTimeOffset DateTime, + double Distance, + double Amount, + CarDto Car, + double? LiterPer100Km); + + public record CarDto( + Guid Id, + string Name) + { + public static CarDto FromCar(Car car) + { + return new CarDto(car.Id.Value, car.Name); + } + } + + public class Request + { + [FromQuery(Name = "page")] public int? Page { get; set; } + [FromQuery(Name = "pageSize")] public int? PageSize { get; set; } + } + + public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder) + { + return builder + .MapGet("consumptions", Endpoint) + .WithDescription("Returns all consumption entries") + .WithTags("Consumptions") + .Produces(); + } + + private static async Task> Endpoint( + [AsParameters] Request request, + ApplicationDbContext dbContext, + CancellationToken cancellationToken) + { + Dictionary> consumptionsByCar = await dbContext.Consumptions + .Include(x => x.Car) + .GroupBy(x => x.CarId) + .ToDictionaryAsync(x => x.Key, x => x.OrderByDescending(x => x.DateTime).ToList(), cancellationToken); + + List responses = []; + + foreach (List consumptions in consumptionsByCar.Select(x => x.Value)) + { + for (int i = 0; i < consumptions.Count; i++) + { + Consumption consumption = consumptions[i]; + + double? literPer100Km = null; + + bool isLast = i == consumptions.Count - 1; + if (!isLast) + { + Consumption previousConsumption = consumptions[i + 1]; + double distanceDiff = consumption.Distance - previousConsumption.Distance; + literPer100Km = consumption.Amount / (distanceDiff / 100); + } + + responses.Add(new ResponseDto( + consumption.Id.Value, + consumption.DateTime, + consumption.Distance, + consumption.Amount, + CarDto.FromCar(consumption.Car), + literPer100Km)); + } + } + + ApiResponse apiResponse = new() + { + Consumptions = responses + }; + return TypedResults.Ok(apiResponse); + } +} \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Consumptions/UpdateConsumption.cs b/src/Vegasco.Server.Api/Consumptions/UpdateConsumption.cs new file mode 100644 index 0000000..80553c2 --- /dev/null +++ b/src/Vegasco.Server.Api/Consumptions/UpdateConsumption.cs @@ -0,0 +1,73 @@ +using FluentValidation; +using FluentValidation.Results; +using Vegasco.Server.Api.Common; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Consumptions; + +public static class UpdateConsumption +{ + public record Request(DateTimeOffset DateTime, double Distance, double Amount); + + public record Response(Guid Id, DateTimeOffset DateTime, double Distance, double Amount, Guid CarId); + + public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder) + { + return builder + .MapPut("consumptions/{id:guid}", Endpoint) + .WithTags("Consumptions") + .WithDescription("Updates a consumption entry by ID") + .Produces() + .ProducesValidationProblem() + .Produces(404); + } + + public class Validator : AbstractValidator + { + public Validator(TimeProvider timeProvider) + { + DateTime todayEndOfDay = timeProvider.GetUtcNow() + .Date + .AddDays(1) + .AddTicks(-1); + + RuleFor(x => x.DateTime.ToUniversalTime()) + .LessThanOrEqualTo(todayEndOfDay) + .WithName(nameof(Request.DateTime)); + + RuleFor(x => x.Distance) + .GreaterThan(0); + + RuleFor(x => x.Amount) + .GreaterThan(0); + } + } + + private static async Task Endpoint( + ApplicationDbContext dbContext, + Guid id, + Request request, + IEnumerable> validators, + CancellationToken cancellationToken) + { + List failedValidations = await validators.ValidateAllAsync(request, cancellationToken); + if (failedValidations.Count > 0) + { + return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary())); + } + + Consumption? consumption = await dbContext.Consumptions.FindAsync([new ConsumptionId(id)], cancellationToken); + if (consumption is null) + { + return TypedResults.NotFound(); + } + + consumption.DateTime = request.DateTime.ToUniversalTime(); + consumption.Distance = request.Distance; + consumption.Amount = request.Amount; + + await dbContext.SaveChangesAsync(cancellationToken); + + return TypedResults.Ok(new Response(consumption.Id.Value, consumption.DateTime, consumption.Distance, consumption.Amount, consumption.CarId.Value)); + } +} \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Endpoints/EndpointExtensions.cs b/src/Vegasco.Server.Api/Endpoints/EndpointExtensions.cs new file mode 100644 index 0000000..0b5901d --- /dev/null +++ b/src/Vegasco.Server.Api/Endpoints/EndpointExtensions.cs @@ -0,0 +1,45 @@ +using Asp.Versioning.Builder; +using Asp.Versioning.Conventions; +using Vegasco.Server.Api.Cars; +using Vegasco.Server.Api.Common; +using Vegasco.Server.Api.Consumptions; +using Vegasco.Server.Api.Info; + +namespace Vegasco.Server.Api.Endpoints; + +public static class EndpointExtensions +{ + public static void MapEndpoints(this IEndpointRouteBuilder builder) + { + ApiVersionSet apiVersionSet = builder.NewApiVersionSet() + .HasApiVersion(1.0) + .Build(); + + RouteGroupBuilder versionedApis = builder.MapGroup("/v{apiVersion:apiVersion}") + .WithApiVersionSet(apiVersionSet); + + GetCar.MapEndpoint(versionedApis) + .RequireAuthorization(Constants.Authorization.RequireAuthenticatedUserPolicy); + GetCars.MapEndpoint(versionedApis) + .RequireAuthorization(Constants.Authorization.RequireAuthenticatedUserPolicy); + CreateCar.MapEndpoint(versionedApis) + .RequireAuthorization(Constants.Authorization.RequireAuthenticatedUserPolicy); + UpdateCar.MapEndpoint(versionedApis) + .RequireAuthorization(Constants.Authorization.RequireAuthenticatedUserPolicy); + DeleteCar.MapEndpoint(versionedApis) + .RequireAuthorization(Constants.Authorization.RequireAuthenticatedUserPolicy); + + GetConsumptions.MapEndpoint(versionedApis) + .RequireAuthorization(Constants.Authorization.RequireAuthenticatedUserPolicy); + GetConsumption.MapEndpoint(versionedApis) + .RequireAuthorization(Constants.Authorization.RequireAuthenticatedUserPolicy); + CreateConsumption.MapEndpoint(versionedApis) + .RequireAuthorization(Constants.Authorization.RequireAuthenticatedUserPolicy); + UpdateConsumption.MapEndpoint(versionedApis) + .RequireAuthorization(Constants.Authorization.RequireAuthenticatedUserPolicy); + DeleteConsumption.MapEndpoint(versionedApis) + .RequireAuthorization(Constants.Authorization.RequireAuthenticatedUserPolicy); + + GetServerInfo.MapEndpoint(versionedApis); + } +} diff --git a/src/Vegasco.Server.Api/Info/GetServerInfo.cs b/src/Vegasco.Server.Api/Info/GetServerInfo.cs new file mode 100644 index 0000000..e188769 --- /dev/null +++ b/src/Vegasco.Server.Api/Info/GetServerInfo.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Http.HttpResults; + +namespace Vegasco.Server.Api.Info; + +public class GetServerInfo +{ + public record Response( + string FullVersion, + string CommitId, + DateTimeOffset CommitDate, + string Environment); + + public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder) + { + return builder + .MapGet("info/server", Endpoint) + .WithTags("Info"); + } + + private static Ok Endpoint( + IHostEnvironment environment) + { + return TypedResults.Ok(new Response( + ThisAssembly.AssemblyInformationalVersion, + ThisAssembly.GitCommitId, + new DateTimeOffset(ThisAssembly.GitCommitDate, TimeSpan.Zero), + environment.EnvironmentName)); + } +} \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Persistence/ApplicationDbContext.cs b/src/Vegasco.Server.Api/Persistence/ApplicationDbContext.cs new file mode 100644 index 0000000..1b35885 --- /dev/null +++ b/src/Vegasco.Server.Api/Persistence/ApplicationDbContext.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore; +using Vegasco.Server.Api.Cars; +using Vegasco.Server.Api.Common; +using Vegasco.Server.Api.Consumptions; +using Vegasco.Server.Api.Users; + +namespace Vegasco.Server.Api.Persistence; + +public class ApplicationDbContext(DbContextOptions options) : DbContext(options) +{ + public DbSet Cars { get; set; } + + public DbSet Users { get; set; } + + public DbSet Consumptions { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.ApplyConfigurationsFromAssembly(typeof(IApiMarker).Assembly); + } +} diff --git a/src/Vegasco.Server.Api/Persistence/ApplyMigrationsService.cs b/src/Vegasco.Server.Api/Persistence/ApplyMigrationsService.cs new file mode 100644 index 0000000..6fd5afb --- /dev/null +++ b/src/Vegasco.Server.Api/Persistence/ApplyMigrationsService.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore; +using System.Diagnostics; + +namespace Vegasco.Server.Api.Persistence; + +public class ApplyMigrationsService( + ILogger logger, + IServiceScopeFactory scopeFactory, + ActivitySource activitySource) + : IHostedService +{ + public async Task StartAsync(CancellationToken cancellationToken) + { + using Activity? activity = activitySource.StartActivity("ApplyMigrations"); + + logger.LogInformation("Starting migrations"); + + using IServiceScope scope = scopeFactory.CreateScope(); + await using ApplicationDbContext dbContext = scope.ServiceProvider.GetRequiredService(); + await dbContext.Database.MigrateAsync(cancellationToken); + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; +} \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Persistence/Migrations/20240818105918_Initial.Designer.cs b/src/Vegasco.Server.Api/Persistence/Migrations/20240818105918_Initial.Designer.cs new file mode 100644 index 0000000..9e48148 --- /dev/null +++ b/src/Vegasco.Server.Api/Persistence/Migrations/20240818105918_Initial.Designer.cs @@ -0,0 +1,121 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Vegasco.Server.Api.Persistence; + + +#nullable disable + +namespace Vegasco.Server.Api.Persistence.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240818105918_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Vegasco.Server.Api.Cars.Car", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Consumptions.Consumption", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("double precision"); + + b.Property("CarId") + .HasColumnType("uuid"); + + b.Property("DateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Distance") + .HasColumnType("double precision"); + + b.Property("IgnoreInCalculation") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("Consumptions"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Users.User", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Cars.Car", b => + { + b.HasOne("Vegasco.Server.Api.Users.User", "User") + .WithMany("Cars") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Consumptions.Consumption", b => + { + b.HasOne("Vegasco.Server.Api.Cars.Car", "Car") + .WithMany("Consumptions") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Cars.Car", b => + { + b.Navigation("Consumptions"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Users.User", b => + { + b.Navigation("Cars"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Vegasco.Server.Api/Persistence/Migrations/20240818105918_Initial.cs b/src/Vegasco.Server.Api/Persistence/Migrations/20240818105918_Initial.cs new file mode 100644 index 0000000..039e09b --- /dev/null +++ b/src/Vegasco.Server.Api/Persistence/Migrations/20240818105918_Initial.cs @@ -0,0 +1,89 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Vegasco.Server.Api.Persistence.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Cars", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + UserId = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Cars", x => x.Id); + table.ForeignKey( + name: "FK_Cars_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Consumptions", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + DateTime = table.Column(type: "timestamp with time zone", nullable: false), + Distance = table.Column(type: "double precision", nullable: false), + Amount = table.Column(type: "double precision", nullable: false), + IgnoreInCalculation = table.Column(type: "boolean", nullable: false), + CarId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Consumptions", x => x.Id); + table.ForeignKey( + name: "FK_Consumptions_Cars_CarId", + column: x => x.CarId, + principalTable: "Cars", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Cars_UserId", + table: "Cars", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_Consumptions_CarId", + table: "Consumptions", + column: "CarId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Consumptions"); + + migrationBuilder.DropTable( + name: "Cars"); + + migrationBuilder.DropTable( + name: "Users"); + } + } +} diff --git a/src/Vegasco.Server.Api/Persistence/Migrations/20250622085121_DropIgnoreInCalculation.Designer.cs b/src/Vegasco.Server.Api/Persistence/Migrations/20250622085121_DropIgnoreInCalculation.Designer.cs new file mode 100644 index 0000000..0fefbc7 --- /dev/null +++ b/src/Vegasco.Server.Api/Persistence/Migrations/20250622085121_DropIgnoreInCalculation.Designer.cs @@ -0,0 +1,117 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Vegasco.Server.Api.Persistence; + +#nullable disable + +namespace Vegasco.Server.Api.Persistence.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250622085121_DropIgnoreInCalculation")] + partial class DropIgnoreInCalculation + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Vegasco.Server.Api.Cars.Car", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Consumptions.Consumption", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("double precision"); + + b.Property("CarId") + .HasColumnType("uuid"); + + b.Property("DateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Distance") + .HasColumnType("double precision"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("Consumptions"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Users.User", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Cars.Car", b => + { + b.HasOne("Vegasco.Server.Api.Users.User", "User") + .WithMany("Cars") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Consumptions.Consumption", b => + { + b.HasOne("Vegasco.Server.Api.Cars.Car", "Car") + .WithMany("Consumptions") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Cars.Car", b => + { + b.Navigation("Consumptions"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Users.User", b => + { + b.Navigation("Cars"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Vegasco.Server.Api/Persistence/Migrations/20250622085121_DropIgnoreInCalculation.cs b/src/Vegasco.Server.Api/Persistence/Migrations/20250622085121_DropIgnoreInCalculation.cs new file mode 100644 index 0000000..0a82444 --- /dev/null +++ b/src/Vegasco.Server.Api/Persistence/Migrations/20250622085121_DropIgnoreInCalculation.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Vegasco.Server.Api.Persistence.Migrations +{ + /// + public partial class DropIgnoreInCalculation : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IgnoreInCalculation", + table: "Consumptions"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IgnoreInCalculation", + table: "Consumptions", + type: "boolean", + nullable: false, + defaultValue: false); + } + } +} diff --git a/src/Vegasco.Server.Api/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Vegasco.Server.Api/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 0000000..6c23abf --- /dev/null +++ b/src/Vegasco.Server.Api/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,114 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Vegasco.Server.Api.Persistence; + +#nullable disable + +namespace Vegasco.Server.Api.Persistence.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Vegasco.Server.Api.Cars.Car", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Cars"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Consumptions.Consumption", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("double precision"); + + b.Property("CarId") + .HasColumnType("uuid"); + + b.Property("DateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Distance") + .HasColumnType("double precision"); + + b.HasKey("Id"); + + b.HasIndex("CarId"); + + b.ToTable("Consumptions"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Users.User", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Cars.Car", b => + { + b.HasOne("Vegasco.Server.Api.Users.User", "User") + .WithMany("Cars") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Consumptions.Consumption", b => + { + b.HasOne("Vegasco.Server.Api.Cars.Car", "Car") + .WithMany("Consumptions") + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Car"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Cars.Car", b => + { + b.Navigation("Consumptions"); + }); + + modelBuilder.Entity("Vegasco.Server.Api.Users.User", b => + { + b.Navigation("Cars"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Vegasco.Server.Api/Program.cs b/src/Vegasco.Server.Api/Program.cs new file mode 100644 index 0000000..6a2b441 --- /dev/null +++ b/src/Vegasco.Server.Api/Program.cs @@ -0,0 +1,6 @@ +using Vegasco.Server.Api.Common; + +WebApplication.CreateBuilder(args) + .ConfigureServices() + .ConfigureRequestPipeline() + .Run(); diff --git a/src/Vegasco.Server.Api/Properties/launchSettings.json b/src/Vegasco.Server.Api/Properties/launchSettings.json new file mode 100644 index 0000000..474c37d --- /dev/null +++ b/src/Vegasco.Server.Api/Properties/launchSettings.json @@ -0,0 +1,15 @@ +{ + "profiles": { + "https": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "scalar/v1", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7098;http://localhost:5076" + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json" +} \ No newline at end of file diff --git a/src/Vegasco.Server.Api/Users/User.cs b/src/Vegasco.Server.Api/Users/User.cs new file mode 100644 index 0000000..e114b4e --- /dev/null +++ b/src/Vegasco.Server.Api/Users/User.cs @@ -0,0 +1,10 @@ +using Vegasco.Server.Api.Cars; + +namespace Vegasco.Server.Api.Users; + +public class User +{ + public string Id { get; set; } = ""; + + public virtual IList Cars { get; set; } = []; +} diff --git a/src/Vegasco.Server.Api/Users/UserTableConfiguration.cs b/src/Vegasco.Server.Api/Users/UserTableConfiguration.cs new file mode 100644 index 0000000..6abe0ee --- /dev/null +++ b/src/Vegasco.Server.Api/Users/UserTableConfiguration.cs @@ -0,0 +1,12 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Vegasco.Server.Api.Users; + +public class UserTableConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(user => user.Id); + } +} diff --git a/src/Vegasco.Server.Api/Vegasco.Server.Api.csproj b/src/Vegasco.Server.Api/Vegasco.Server.Api.csproj new file mode 100644 index 0000000..86548ed --- /dev/null +++ b/src/Vegasco.Server.Api/Vegasco.Server.Api.csproj @@ -0,0 +1,46 @@ + + + + net9.0 + enable + enable + 4bf893d3-0c16-41ec-8b46-2768d841215d + Linux + ..\.. + Vegasco.Server.Api + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Vegasco.Server.Api/appsettings.Development.json b/src/Vegasco.Server.Api/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/src/Vegasco.Server.Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Vegasco.Server.Api/appsettings.json b/src/Vegasco.Server.Api/appsettings.json new file mode 100644 index 0000000..fb6c195 --- /dev/null +++ b/src/Vegasco.Server.Api/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning", + "Vegasco": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Vegasco.Server.Api/migrations/migration.sql b/src/Vegasco.Server.Api/migrations/migration.sql new file mode 100644 index 0000000..545aaff --- /dev/null +++ b/src/Vegasco.Server.Api/migrations/migration.sql @@ -0,0 +1,85 @@ +CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" ( + "MigrationId" character varying(150) NOT NULL, + "ProductVersion" character varying(32) NOT NULL, + CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId") +); + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20240818105918_Initial') THEN + CREATE TABLE "Users" ( + "Id" text NOT NULL, + CONSTRAINT "PK_Users" PRIMARY KEY ("Id") + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20240818105918_Initial') THEN + CREATE TABLE "Cars" ( + "Id" uuid NOT NULL, + "Name" character varying(50) NOT NULL, + "UserId" text NOT NULL, + CONSTRAINT "PK_Cars" PRIMARY KEY ("Id"), + CONSTRAINT "FK_Cars_Users_UserId" FOREIGN KEY ("UserId") REFERENCES "Users" ("Id") ON DELETE CASCADE + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20240818105918_Initial') THEN + CREATE TABLE "Consumptions" ( + "Id" uuid NOT NULL, + "DateTime" timestamp with time zone NOT NULL, + "Distance" double precision NOT NULL, + "Amount" double precision NOT NULL, + "IgnoreInCalculation" boolean NOT NULL, + "CarId" uuid NOT NULL, + CONSTRAINT "PK_Consumptions" PRIMARY KEY ("Id"), + CONSTRAINT "FK_Consumptions_Cars_CarId" FOREIGN KEY ("CarId") REFERENCES "Cars" ("Id") ON DELETE CASCADE + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20240818105918_Initial') THEN + CREATE INDEX "IX_Cars_UserId" ON "Cars" ("UserId"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20240818105918_Initial') THEN + CREATE INDEX "IX_Consumptions_CarId" ON "Consumptions" ("CarId"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20240818105918_Initial') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20240818105918_Initial', '9.0.5'); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250622085121_DropIgnoreInCalculation') THEN + ALTER TABLE "Consumptions" DROP COLUMN "IgnoreInCalculation"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250622085121_DropIgnoreInCalculation') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20250622085121_DropIgnoreInCalculation', '9.0.5'); + END IF; +END $EF$; +COMMIT; + diff --git a/src/Vegasco.Server.AppHost.Shared/Constants.cs b/src/Vegasco.Server.AppHost.Shared/Constants.cs new file mode 100644 index 0000000..4b18f9b --- /dev/null +++ b/src/Vegasco.Server.AppHost.Shared/Constants.cs @@ -0,0 +1,16 @@ +namespace Vegasco.Server.AppHost.Shared; + +public static class Constants +{ + public static class Projects + { + public const string Api = "Api"; + } + + public static class Database + { + public const string ServiceName = "postgres"; + + public const string Name = "vegasco-database"; + } +} diff --git a/src/Vegasco.Server.AppHost.Shared/Vegasco.Server.AppHost.Shared.csproj b/src/Vegasco.Server.AppHost.Shared/Vegasco.Server.AppHost.Shared.csproj new file mode 100644 index 0000000..5808fe7 --- /dev/null +++ b/src/Vegasco.Server.AppHost.Shared/Vegasco.Server.AppHost.Shared.csproj @@ -0,0 +1,15 @@ + + + + net9.0 + enable + enable + + + + + 3.7.115 + + + + diff --git a/src/Vegasco.Server.AppHost/Program.cs b/src/Vegasco.Server.AppHost/Program.cs new file mode 100644 index 0000000..c4debdf --- /dev/null +++ b/src/Vegasco.Server.AppHost/Program.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.Hosting; +using Vegasco.Server.AppHost.Shared; + +IDistributedApplicationBuilder builder = DistributedApplication.CreateBuilder(args); + +IResourceBuilder postgresBuilder = builder.AddPostgres(Constants.Database.ServiceName) + .WithLifetime(ContainerLifetime.Persistent) + .WithDataVolume(); + +if (builder.Environment.IsDevelopment()) +{ + postgresBuilder = postgresBuilder + .WithPgWeb() + .WithPgAdmin(); +} + +IResourceBuilder postgres = postgresBuilder + .AddDatabase(Constants.Database.Name); + +IResourceBuilder api = builder + .AddProject(Constants.Projects.Api) + .WithReference(postgres) + .WaitFor(postgres); + +builder + .AddNpmApp("Vegasco-Web", "../Vegasco-Web", scriptName: "start:withInstall") + .WithReference(api) + .WaitFor(api) + .WithHttpEndpoint(port: 44200, env: "PORT", isProxied: false) + .WithExternalHttpEndpoints() + .WithHttpHealthCheck("/", 200); + +builder.Build().Run(); diff --git a/src/Vegasco.Server.AppHost/Properties/launchSettings.json b/src/Vegasco.Server.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000..5a2b722 --- /dev/null +++ b/src/Vegasco.Server.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17055;http://localhost:15102", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21122", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22235" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15102", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19222", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20257" + } + } + } +} diff --git a/src/Vegasco.Server.AppHost/Vegasco.Server.AppHost.csproj b/src/Vegasco.Server.AppHost/Vegasco.Server.AppHost.csproj new file mode 100644 index 0000000..dc8b747 --- /dev/null +++ b/src/Vegasco.Server.AppHost/Vegasco.Server.AppHost.csproj @@ -0,0 +1,28 @@ + + + + + + Exe + net9.0 + enable + enable + true + bb714834-9872-4af6-b154-0b98b14fcca2 + + + + + + + + 3.7.115 + + + + + + + + + diff --git a/src/Vegasco.Server.AppHost/appsettings.Development.json b/src/Vegasco.Server.AppHost/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/src/Vegasco.Server.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Vegasco.Server.AppHost/appsettings.json b/src/Vegasco.Server.AppHost/appsettings.json new file mode 100644 index 0000000..31c092a --- /dev/null +++ b/src/Vegasco.Server.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/src/Vegasco.Server.ServiceDefaults/Extensions.cs b/src/Vegasco.Server.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000..f602d29 --- /dev/null +++ b/src/Vegasco.Server.ServiceDefaults/Extensions.cs @@ -0,0 +1,119 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Vegasco.Server.ServiceDefaults; + +// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + // Uncomment the following to restrict the allowed schemes for service discovery. + // builder.Services.Configure(options => + // { + // options.AllowedSchemes = ["https"]; + // }); + + return builder; + } + + public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddSource(builder.Environment.ApplicationName) + .AddAspNetCoreInstrumentation() + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + bool useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Services.AddHealthChecks() + // Add a default liveness check to ensure app is responsive + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks("/health"); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks("/alive", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/src/Vegasco.Server.ServiceDefaults/Vegasco.Server.ServiceDefaults.csproj b/src/Vegasco.Server.ServiceDefaults/Vegasco.Server.ServiceDefaults.csproj new file mode 100644 index 0000000..f5a831a --- /dev/null +++ b/src/Vegasco.Server.ServiceDefaults/Vegasco.Server.ServiceDefaults.csproj @@ -0,0 +1,25 @@ + + + + net9.0 + enable + enable + true + + + + + + + + + + + + + + 3.7.115 + + + + diff --git a/tests/Vegasco.Server.Api.Tests.Integration/CarFaker.cs b/tests/Vegasco.Server.Api.Tests.Integration/CarFaker.cs new file mode 100644 index 0000000..0c21d4d --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/CarFaker.cs @@ -0,0 +1,19 @@ +using Bogus; +using Vegasco.Server.Api.Cars; + +namespace Vegasco.Server.Api.Tests.Integration; + +internal class CarFaker +{ + internal CreateCar.Request CreateCarRequest() + { + Faker faker = new(); + return new CreateCar.Request(faker.Person.FirstName); + } + + internal UpdateCar.Request UpdateCarRequest() + { + Faker faker = new(); + return new UpdateCar.Request(faker.Person.FirstName); + } +} \ No newline at end of file diff --git a/tests/Vegasco.Server.Api.Tests.Integration/Cars/CreateCarTests.cs b/tests/Vegasco.Server.Api.Tests.Integration/Cars/CreateCarTests.cs new file mode 100644 index 0000000..95c076c --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/Cars/CreateCarTests.cs @@ -0,0 +1,71 @@ +using FluentAssertions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using System.Net; +using System.Net.Http.Json; +using Vegasco.Server.Api.Cars; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Tests.Integration.Cars; + +[Collection(SharedTestCollection.Name)] +public class CreateCarTests : IAsyncLifetime +{ + private readonly WebAppFactory _factory; + private readonly IServiceScope _scope; + private readonly ApplicationDbContext _dbContext; + + private readonly CarFaker _carFaker = new(); + + public CreateCarTests(WebAppFactory factory) + { + _factory = factory; + _scope = _factory.Services.CreateScope(); + _dbContext = _scope.ServiceProvider.GetRequiredService(); + } + + [Fact] + public async Task CreateCar_ShouldCreateCar_WhenRequestIsValid() + { + // Arrange + CreateCar.Request createCarRequest = _carFaker.CreateCarRequest(); + + // Act + HttpResponseMessage response = await _factory.HttpClient.PostAsJsonAsync("v1/cars", createCarRequest); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.Created); + CreateCar.Response? createdCar = await response.Content.ReadFromJsonAsync(); + createdCar.Should().BeEquivalentTo(createCarRequest, o => o.ExcludingMissingMembers()); + + _dbContext.Cars.Should().ContainEquivalentOf(createdCar, o => o.Excluding(x => x!.Id)) + .Which.Id.Value.Should().Be(createdCar!.Id); + } + + [Fact] + public async Task CreateCar_ShouldReturnValidationProblems_WhenRequestIsNotValid() + { + // Arrange + CreateCar.Request createCarRequest = new CreateCar.Request(""); + + // Act + HttpResponseMessage response = await _factory.HttpClient.PostAsJsonAsync("v1/cars", createCarRequest); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.BadRequest); + ValidationProblemDetails? validationProblemDetails = await response.Content.ReadFromJsonAsync(); + validationProblemDetails!.Errors.Keys.Should().Contain(x => + x.Equals(nameof(CreateCar.Request.Name), StringComparison.OrdinalIgnoreCase)); + + _dbContext.Cars.Should().NotContainEquivalentOf(createCarRequest, o => o.ExcludingMissingMembers()); + } + + public Task InitializeAsync() => Task.CompletedTask; + + public async Task DisposeAsync() + { + _scope.Dispose(); + await _dbContext.DisposeAsync(); + await _factory.ResetDatabaseAsync(); + } +} diff --git a/tests/Vegasco.Server.Api.Tests.Integration/Cars/DeleteCarTests.cs b/tests/Vegasco.Server.Api.Tests.Integration/Cars/DeleteCarTests.cs new file mode 100644 index 0000000..3348ded --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/Cars/DeleteCarTests.cs @@ -0,0 +1,64 @@ +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using System.Net; +using System.Net.Http.Json; +using Vegasco.Server.Api.Cars; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Tests.Integration.Cars; + +[Collection(SharedTestCollection.Name)] +public class DeleteCarTests : IAsyncLifetime +{ + private readonly WebAppFactory _factory; + private readonly IServiceScope _scope; + private readonly ApplicationDbContext _dbContext; + + private readonly CarFaker _carFaker = new(); + + public DeleteCarTests(WebAppFactory factory) + { + _factory = factory; + _scope = _factory.Services.CreateScope(); + _dbContext = _scope.ServiceProvider.GetRequiredService(); + } + + [Fact] + public async Task DeleteCar_ShouldReturnNotFound_WhenCarDoesNotExist() + { + // Arrange + Guid randomCarId = Guid.NewGuid(); + + // Act + HttpResponseMessage response = await _factory.HttpClient.DeleteAsync($"v1/cars/{randomCarId}"); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.NotFound); + } + + [Fact] + public async Task DeleteCar_ShouldDeleteCar_WhenCarExists() + { + // Arrange + CreateCar.Request createCarRequest = _carFaker.CreateCarRequest(); + HttpResponseMessage createCarResponse = await _factory.HttpClient.PostAsJsonAsync("v1/cars", createCarRequest); + createCarResponse.EnsureSuccessStatusCode(); + CreateCar.Response? createdCar = await createCarResponse.Content.ReadFromJsonAsync(); + + // Act + HttpResponseMessage response = await _factory.HttpClient.DeleteAsync($"v1/cars/{createdCar!.Id}"); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.NoContent); + _dbContext.Cars.Should().BeEmpty(); + } + + public Task InitializeAsync() => Task.CompletedTask; + + public async Task DisposeAsync() + { + _scope.Dispose(); + await _dbContext.DisposeAsync(); + await _factory.ResetDatabaseAsync(); + } +} diff --git a/tests/Vegasco.Server.Api.Tests.Integration/Cars/GetCarTests.cs b/tests/Vegasco.Server.Api.Tests.Integration/Cars/GetCarTests.cs new file mode 100644 index 0000000..8a69239 --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/Cars/GetCarTests.cs @@ -0,0 +1,57 @@ +using FluentAssertions; +using System.Net; +using System.Net.Http.Json; +using Vegasco.Server.Api.Cars; + +namespace Vegasco.Server.Api.Tests.Integration.Cars; + +[Collection(SharedTestCollection.Name)] +public class GetCarTests : IAsyncLifetime +{ + private readonly WebAppFactory _factory; + + private readonly CarFaker _carFaker = new(); + + public GetCarTests(WebAppFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task GetCar_ShouldReturnNotFound_WhenCarDoesNotExist() + { + // Arrange + Guid randomCarId = Guid.NewGuid(); + + // Act + HttpResponseMessage response = await _factory.HttpClient.GetAsync($"v1/cars/{randomCarId}"); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.NotFound); + } + + [Fact] + public async Task GetCar_ShouldReturnCar_WhenCarExists() + { + // Arrange + CreateCar.Request createCarRequest = _carFaker.CreateCarRequest(); + HttpResponseMessage createCarResponse = await _factory.HttpClient.PostAsJsonAsync("v1/cars", createCarRequest); + createCarResponse.EnsureSuccessStatusCode(); + CreateCar.Response? createdCar = await createCarResponse.Content.ReadFromJsonAsync(); + + // Act + HttpResponseMessage response = await _factory.HttpClient.GetAsync($"v1/cars/{createdCar!.Id}"); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); + GetCar.Response? car = await response.Content.ReadFromJsonAsync(); + car.Should().BeEquivalentTo(createdCar); + } + + public Task InitializeAsync() => Task.CompletedTask; + + public async Task DisposeAsync() + { + await _factory.ResetDatabaseAsync(); + } +} diff --git a/tests/Vegasco.Server.Api.Tests.Integration/Cars/GetCarsTests.cs b/tests/Vegasco.Server.Api.Tests.Integration/Cars/GetCarsTests.cs new file mode 100644 index 0000000..6ac7d57 --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/Cars/GetCarsTests.cs @@ -0,0 +1,66 @@ +using FluentAssertions; +using System.Net; +using System.Net.Http.Json; +using Vegasco.Server.Api.Cars; + +namespace Vegasco.Server.Api.Tests.Integration.Cars; + +[Collection(SharedTestCollection.Name)] +public class GetCarsTests : IAsyncLifetime +{ + private readonly WebAppFactory _factory; + + private readonly CarFaker _carFaker = new(); + + public GetCarsTests(WebAppFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task GetCars_ShouldReturnEmptyList_WhenNoEntriesExist() + { + // Arrange + + // Act + using HttpResponseMessage response = await _factory.HttpClient.GetAsync("v1/cars"); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); + GetCars.ApiResponse? apiResponse = await response.Content.ReadFromJsonAsync(); + apiResponse!.Cars.Should().BeEmpty(); + } + + [Fact] + public async Task GetCars_ShouldReturnEntries_WhenEntriesExist() + { + // Arrange + List createdCars = []; + + const int numberOfCars = 5; + for (int i = 0; i < numberOfCars; i++) + { + CreateCar.Request createCarRequest = _carFaker.CreateCarRequest(); + HttpResponseMessage createCarResponse = await _factory.HttpClient.PostAsJsonAsync("v1/cars", createCarRequest); + createCarResponse.EnsureSuccessStatusCode(); + + CreateCar.Response? createdCar = await createCarResponse.Content.ReadFromJsonAsync(); + createdCars.Add(createdCar!); + } + + // Act + using HttpResponseMessage response = await _factory.HttpClient.GetAsync("v1/cars"); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); + GetCars.ApiResponse? apiResponse = await response.Content.ReadFromJsonAsync(); + apiResponse!.Cars.Should().BeEquivalentTo(createdCars); + } + + public Task InitializeAsync() => Task.CompletedTask; + + public async Task DisposeAsync() + { + await _factory.ResetDatabaseAsync(); + } +} diff --git a/tests/Vegasco.Server.Api.Tests.Integration/Cars/UpdateCarTests.cs b/tests/Vegasco.Server.Api.Tests.Integration/Cars/UpdateCarTests.cs new file mode 100644 index 0000000..f97ca4b --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/Cars/UpdateCarTests.cs @@ -0,0 +1,102 @@ +using FluentAssertions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using System.Net; +using System.Net.Http.Json; +using Vegasco.Server.Api.Cars; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Tests.Integration.Cars; + +[Collection(SharedTestCollection.Name)] +public class UpdateCarTests : IAsyncLifetime +{ + private readonly WebAppFactory _factory; + private readonly IServiceScope _scope; + private readonly ApplicationDbContext _dbContext; + + private readonly CarFaker _carFaker = new(); + + public UpdateCarTests(WebAppFactory factory) + { + _factory = factory; + _scope = _factory.Services.CreateScope(); + _dbContext = _scope.ServiceProvider.GetRequiredService(); + } + + [Fact] + public async Task UpdateCar_ShouldUpdateCar_WhenCarExistsAndRequestIsValid() + { + // Arrange + CreateCar.Request createCarRequest = _carFaker.CreateCarRequest(); + HttpResponseMessage createCarResponse = await _factory.HttpClient.PostAsJsonAsync("v1/cars", createCarRequest); + createCarResponse.EnsureSuccessStatusCode(); + CreateCar.Response? createdCar = await createCarResponse.Content.ReadFromJsonAsync(); + + UpdateCar.Request updateCarRequest = _carFaker.UpdateCarRequest(); + + // Act + HttpResponseMessage response = await _factory.HttpClient.PutAsJsonAsync($"v1/cars/{createdCar!.Id}", updateCarRequest); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); + CreateCar.Response? updatedCar = await response.Content.ReadFromJsonAsync(); + updatedCar!.Id.Should().Be(createdCar.Id); + updatedCar.Should().BeEquivalentTo(updateCarRequest, o => o.ExcludingMissingMembers()); + + _dbContext.Cars.Should().ContainEquivalentOf(updatedCar, o => + o.ExcludingMissingMembers() + .Excluding(x => x.Id)) + .Which.Id.Value.Should().Be(updatedCar.Id); + } + + [Fact] + public async Task UpdateCar_ShouldReturnValidationProblems_WhenRequestIsNotValid() + { + // Arrange + CreateCar.Request createCarRequest = _carFaker.CreateCarRequest(); + HttpResponseMessage createCarResponse = await _factory.HttpClient.PostAsJsonAsync("v1/cars", createCarRequest); + createCarResponse.EnsureSuccessStatusCode(); + CreateCar.Response? createdCar = await createCarResponse.Content.ReadFromJsonAsync(); + + UpdateCar.Request updateCarRequest = new UpdateCar.Request(""); + + // Act + HttpResponseMessage response = await _factory.HttpClient.PutAsJsonAsync($"v1/cars/{createdCar!.Id}", updateCarRequest); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.BadRequest); + ValidationProblemDetails? validationProblemDetails = await response.Content.ReadFromJsonAsync(); + validationProblemDetails!.Errors.Keys.Should().Contain(x => + x.Equals(nameof(CreateCar.Request.Name), StringComparison.OrdinalIgnoreCase)); + + _dbContext.Cars.Should().ContainSingle(x => x.Id.Value == createdCar.Id) + .Which + .Should().NotBeEquivalentTo(updateCarRequest, o => o.ExcludingMissingMembers()); + } + + [Fact] + public async Task UpdateCar_ShouldReturnNotFound_WhenNoCarWithIdExists() + { + // Arrange + UpdateCar.Request updateCarRequest = _carFaker.UpdateCarRequest(); + Guid randomCarId = Guid.NewGuid(); + + // Act + HttpResponseMessage response = await _factory.HttpClient.PutAsJsonAsync($"v1/cars/{randomCarId}", updateCarRequest); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.NotFound); + + _dbContext.Cars.Should().BeEmpty(); + } + + public Task InitializeAsync() => Task.CompletedTask; + + public async Task DisposeAsync() + { + _scope.Dispose(); + await _dbContext.DisposeAsync(); + await _factory.ResetDatabaseAsync(); + } +} diff --git a/tests/Vegasco.Server.Api.Tests.Integration/ConsumptionFaker.cs b/tests/Vegasco.Server.Api.Tests.Integration/ConsumptionFaker.cs new file mode 100644 index 0000000..36747c0 --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/ConsumptionFaker.cs @@ -0,0 +1,26 @@ +using Bogus; +using Vegasco.Server.Api.Consumptions; + +namespace Vegasco.Server.Api.Tests.Integration; + +internal class ConsumptionFaker +{ + internal CreateConsumption.Request CreateConsumptionRequest(Guid carId) + { + Faker faker = new(); + return new CreateConsumption.Request( + faker.Date.RecentOffset(), + faker.Random.Int(1, 1_000), + faker.Random.Int(20, 70), + carId); + } + + internal UpdateConsumption.Request UpdateConsumptionRequest() + { + CreateConsumption.Request createRequest = CreateConsumptionRequest(Guid.Empty); + return new UpdateConsumption.Request( + createRequest.DateTime, + createRequest.Distance, + createRequest.Amount); + } +} \ No newline at end of file diff --git a/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/CreateConsumptionTests.cs b/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/CreateConsumptionTests.cs new file mode 100644 index 0000000..6783011 --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/CreateConsumptionTests.cs @@ -0,0 +1,95 @@ +using FluentAssertions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using System.Net; +using System.Net.Http.Json; +using Vegasco.Server.Api.Cars; +using Vegasco.Server.Api.Consumptions; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Tests.Integration.Consumptions; + +[Collection(SharedTestCollection.Name)] +public class CreateConsumptionTests : IAsyncLifetime +{ + private readonly WebAppFactory _factory; + private readonly IServiceScope _scope; + private readonly ApplicationDbContext _dbContext; + + private readonly CarFaker _carFaker = new(); + private readonly ConsumptionFaker _consumptionFaker = new(); + + public CreateConsumptionTests(WebAppFactory factory) + { + _factory = factory; + _scope = _factory.Services.CreateScope(); + _dbContext = _scope.ServiceProvider.GetRequiredService(); + } + + [Fact] + public async Task CreateConsumption_ShouldCreateConsumption_WhenRequestIsValid() + { + // Arrange + CreateCar.Response createdCarResponse = await CreateCarAsync(); + + CreateConsumption.Request createConsumptionRequest = _consumptionFaker.CreateConsumptionRequest(createdCarResponse.Id); + + // Act + using HttpResponseMessage response = await _factory.HttpClient.PostAsJsonAsync("v1/consumptions", createConsumptionRequest); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.Created); + CreateConsumption.Response? createdConsumption = await response.Content.ReadFromJsonAsync(); + createdConsumption.Should().BeEquivalentTo(createConsumptionRequest, o => o.ExcludingMissingMembers()); + + _dbContext.Consumptions.Should().HaveCount(1) + .And.ContainEquivalentOf(createdConsumption, o => + o.ExcludingMissingMembers() + .Excluding(x => x!.Id) + .Excluding(x => x!.CarId)); + + Consumption singleConsumption = _dbContext.Consumptions.Single(); + singleConsumption.Id.Value.Should().Be(createdConsumption!.Id); + singleConsumption.CarId.Value.Should().Be(createdConsumption.CarId); + } + + [Fact] + public async Task CreateConsumption_ShouldReturnValidationProblems_WhenRequestIsInvalid() + { + // Arrange + CreateConsumption.Request createConsumptionRequest = _consumptionFaker.CreateConsumptionRequest(Guid.Empty); + + // Act + using HttpResponseMessage response = await _factory.HttpClient.PostAsJsonAsync("v1/consumptions", createConsumptionRequest); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.BadRequest); + ValidationProblemDetails? validationProblemDetails = await response.Content.ReadFromJsonAsync(); + validationProblemDetails!.Errors.Keys.Should().Contain(x => + x.Equals(nameof(createConsumptionRequest.CarId), StringComparison.OrdinalIgnoreCase)); + + _dbContext.Consumptions.Should().NotContainEquivalentOf(createConsumptionRequest, o => o.ExcludingMissingMembers()); + } + + private async Task CreateCarAsync() + { + CreateCar.Request createCarRequest = new CarFaker().CreateCarRequest(); + using HttpResponseMessage createCarResponse = await _factory.HttpClient.PostAsJsonAsync("v1/cars", createCarRequest); + createCarResponse.EnsureSuccessStatusCode(); + CreateCar.Response? createdCarResponse = await createCarResponse.Content.ReadFromJsonAsync(); + return createdCarResponse!; + } + + public Task InitializeAsync() + { + FluentAssertionConfiguration.SetupGlobalConfig(); + return Task.CompletedTask; + } + + public async Task DisposeAsync() + { + _scope.Dispose(); + await _dbContext.DisposeAsync(); + await _factory.ResetDatabaseAsync(); + } +} \ No newline at end of file diff --git a/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/DeleteConsumptionTests.cs b/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/DeleteConsumptionTests.cs new file mode 100644 index 0000000..9091ccd --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/DeleteConsumptionTests.cs @@ -0,0 +1,82 @@ +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using System.Net; +using System.Net.Http.Json; +using Vegasco.Server.Api.Cars; +using Vegasco.Server.Api.Consumptions; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Tests.Integration.Consumptions; + +[Collection(SharedTestCollection.Name)] +public class DeleteConsumptionTests : IAsyncLifetime +{ + private readonly WebAppFactory _factory; + private readonly IServiceScope _scope; + private readonly ApplicationDbContext _dbContext; + + private readonly CarFaker _carFaker = new(); + private readonly ConsumptionFaker _consumptionFaker = new(); + + public DeleteConsumptionTests(WebAppFactory factory) + { + _factory = factory; + _scope = _factory.Services.CreateScope(); + _dbContext = _scope.ServiceProvider.GetRequiredService(); + } + + [Fact] + public async Task DeleteConsumption_ShouldDeleteConsumption_WhenConsumptionExists() + { + // Arrange + CreateConsumption.Response createdConsumption = await CreateConsumptionAsync(); + + // Act + using HttpResponseMessage response = await _factory.HttpClient.DeleteAsync($"v1/consumptions/{createdConsumption.Id}"); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.NoContent); + _dbContext.Consumptions.Should().NotContain(x => x.Id.Value == createdConsumption.Id); + } + + [Fact] + public async Task DeleteConsumption_ShouldReturnNotFound_WhenConsumptionDoesNotExist() + { + // Arrange + Guid consumptionId = Guid.NewGuid(); + + // Act + using HttpResponseMessage response = await _factory.HttpClient.DeleteAsync($"v1/consumptions/{consumptionId}"); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.NotFound); + } + + private async Task CreateConsumptionAsync() + { + CreateCar.Response createdCarResponse = await CreateCarAsync(); + CreateConsumption.Request createConsumptionRequest = _consumptionFaker.CreateConsumptionRequest(createdCarResponse.Id); + using HttpResponseMessage response = await _factory.HttpClient.PostAsJsonAsync("v1/consumptions", createConsumptionRequest); + response.EnsureSuccessStatusCode(); + CreateConsumption.Response? createdConsumption = await response.Content.ReadFromJsonAsync(); + return createdConsumption!; + } + + private async Task CreateCarAsync() + { + CreateCar.Request createCarRequest = new CarFaker().CreateCarRequest(); + using HttpResponseMessage createCarResponse = await _factory.HttpClient.PostAsJsonAsync("v1/cars", createCarRequest); + createCarResponse.EnsureSuccessStatusCode(); + CreateCar.Response? createdCarResponse = await createCarResponse.Content.ReadFromJsonAsync(); + return createdCarResponse!; + } + + public Task InitializeAsync() => Task.CompletedTask; + + public async Task DisposeAsync() + { + _scope.Dispose(); + await _dbContext.DisposeAsync(); + await _factory.ResetDatabaseAsync(); + } +} diff --git a/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/GetConsumptionTests.cs b/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/GetConsumptionTests.cs new file mode 100644 index 0000000..14dd412 --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/GetConsumptionTests.cs @@ -0,0 +1,88 @@ +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using System.Net; +using System.Net.Http.Json; +using Vegasco.Server.Api.Cars; +using Vegasco.Server.Api.Consumptions; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Tests.Integration.Consumptions; + +[Collection(SharedTestCollection.Name)] +public class GetConsumptionTests : IAsyncLifetime +{ + private readonly WebAppFactory _factory; + private readonly IServiceScope _scope; + private readonly ApplicationDbContext _dbContext; + + private readonly CarFaker _carFaker = new(); + private readonly ConsumptionFaker _consumptionFaker = new(); + + public GetConsumptionTests(WebAppFactory factory) + { + _factory = factory; + _scope = _factory.Services.CreateScope(); + _dbContext = _scope.ServiceProvider.GetRequiredService(); + } + + [Fact] + public async Task GetConsumption_ShouldReturnConsumption_WhenConsumptionExist() + { + // Arrange + CreateConsumption.Response createdConsumption = await CreateConsumptionAsync(); + + // Act + using HttpResponseMessage response = await _factory.HttpClient.GetAsync($"v1/consumptions/{createdConsumption.Id}"); + + // Assert + string content = await response.Content.ReadAsStringAsync(); + response.StatusCode.Should().Be(HttpStatusCode.OK); + GetConsumption.Response? consumption = await response.Content.ReadFromJsonAsync(); + consumption.Should().BeEquivalentTo(createdConsumption); + } + + [Fact] + public async Task GetConsumptions_ShouldReturnNotFound_WhenConsumptionDoesNotExist() + { + // Arrange + Guid consumptionId = Guid.NewGuid(); + + // Act + using HttpResponseMessage response = await _factory.HttpClient.GetAsync($"v1/consumptions{consumptionId}"); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.NotFound); + } + + private async Task CreateConsumptionAsync() + { + CreateCar.Response createdCarResponse = await CreateCarAsync(); + CreateConsumption.Request createConsumptionRequest = _consumptionFaker.CreateConsumptionRequest(createdCarResponse.Id); + using HttpResponseMessage response = await _factory.HttpClient.PostAsJsonAsync("v1/consumptions", createConsumptionRequest); + response.EnsureSuccessStatusCode(); + CreateConsumption.Response? createdConsumption = await response.Content.ReadFromJsonAsync(); + return createdConsumption!; + } + + private async Task CreateCarAsync() + { + CreateCar.Request createCarRequest = new CarFaker().CreateCarRequest(); + using HttpResponseMessage createCarResponse = await _factory.HttpClient.PostAsJsonAsync("v1/cars", createCarRequest); + createCarResponse.EnsureSuccessStatusCode(); + CreateCar.Response? createdCarResponse = await createCarResponse.Content.ReadFromJsonAsync(); + return createdCarResponse!; + } + + public Task InitializeAsync() + { + FluentAssertionConfiguration.SetupGlobalConfig(); + return Task.CompletedTask; + } + + public async Task DisposeAsync() + { + _scope.Dispose(); + await _dbContext.DisposeAsync(); + await _factory.ResetDatabaseAsync(); + } +} \ No newline at end of file diff --git a/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/GetConsumptionsTests.cs b/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/GetConsumptionsTests.cs new file mode 100644 index 0000000..66ac6bb --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/GetConsumptionsTests.cs @@ -0,0 +1,108 @@ +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using System.Net; +using System.Net.Http.Json; +using Vegasco.Server.Api.Cars; +using Vegasco.Server.Api.Consumptions; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Tests.Integration.Consumptions; + +[Collection(SharedTestCollection.Name)] +public class GetConsumptionsTests : IAsyncLifetime +{ + private readonly WebAppFactory _factory; + private readonly IServiceScope _scope; + private readonly ApplicationDbContext _dbContext; + + private readonly CarFaker _carFaker = new(); + private readonly ConsumptionFaker _consumptionFaker = new(); + + public GetConsumptionsTests(WebAppFactory factory) + { + _factory = factory; + _scope = _factory.Services.CreateScope(); + _dbContext = _scope.ServiceProvider.GetRequiredService(); + } + + [Fact] + public async Task GetConsumptions_ShouldReturnConsumptions_WhenConsumptionsExist() + { + // Arrange + List createdConsumptions = []; + const int numberOfConsumptions = 3; + for (int i = 0; i < numberOfConsumptions; i++) + { + CreateConsumption.Response createdConsumption = await CreateConsumptionAsync(); + createdConsumptions.Add(createdConsumption); + } + + // Act + using HttpResponseMessage response = await _factory.HttpClient.GetAsync("v1/consumptions"); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); + GetConsumptions.ApiResponse? apiResponse = + await response.Content.ReadFromJsonAsync(); + + apiResponse.Should().NotBeNull(); + apiResponse.Consumptions.Should().HaveCount(createdConsumptions.Count); + apiResponse.Consumptions.Should().BeEquivalentTo(createdConsumptions, o => o.ExcludingMissingMembers()); + apiResponse.Consumptions + .Select(x => x.Car.Id) + .Should() + .BeEquivalentTo(createdConsumptions, o => o.ExcludingMissingMembers()); + } + + [Fact] + public async Task GetConsumptions_ShouldReturnEmptyList_WhenNoConsumptionsExist() + { + // Arrange + + // Act + using HttpResponseMessage response = await _factory.HttpClient.GetAsync("v1/consumptions"); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); + GetConsumptions.ApiResponse? apiResponse = + await response.Content.ReadFromJsonAsync(); + apiResponse!.Consumptions.Should().BeEmpty(); + } + + private async Task CreateConsumptionAsync() + { + CreateCar.Response createdCarResponse = await CreateCarAsync(); + CreateConsumption.Request createConsumptionRequest = + _consumptionFaker.CreateConsumptionRequest(createdCarResponse.Id); + using HttpResponseMessage response = + await _factory.HttpClient.PostAsJsonAsync("v1/consumptions", createConsumptionRequest); + response.EnsureSuccessStatusCode(); + CreateConsumption.Response? createdConsumption = + await response.Content.ReadFromJsonAsync(); + return createdConsumption!; + } + + private async Task CreateCarAsync() + { + CreateCar.Request createCarRequest = new CarFaker().CreateCarRequest(); + using HttpResponseMessage createCarResponse = + await _factory.HttpClient.PostAsJsonAsync("v1/cars", createCarRequest); + createCarResponse.EnsureSuccessStatusCode(); + CreateCar.Response? createdCarResponse = + await createCarResponse.Content.ReadFromJsonAsync(); + return createdCarResponse!; + } + + public Task InitializeAsync() + { + FluentAssertionConfiguration.SetupGlobalConfig(); + return Task.CompletedTask; + } + + public async Task DisposeAsync() + { + _scope.Dispose(); + await _dbContext.DisposeAsync(); + await _factory.ResetDatabaseAsync(); + } +} \ No newline at end of file diff --git a/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/UpdateConsumptionTests.cs b/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/UpdateConsumptionTests.cs new file mode 100644 index 0000000..09d0964 --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/Consumptions/UpdateConsumptionTests.cs @@ -0,0 +1,126 @@ +using FluentAssertions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using System.Net; +using System.Net.Http.Json; +using Vegasco.Server.Api.Cars; +using Vegasco.Server.Api.Consumptions; +using Vegasco.Server.Api.Persistence; + +namespace Vegasco.Server.Api.Tests.Integration.Consumptions; + +[Collection(SharedTestCollection.Name)] +public class UpdateConsumptionTests : IAsyncLifetime +{ + private readonly WebAppFactory _factory; + private readonly IServiceScope _scope; + private readonly ApplicationDbContext _dbContext; + + private readonly CarFaker _carFaker = new(); + private readonly ConsumptionFaker _consumptionFaker = new(); + + public UpdateConsumptionTests(WebAppFactory factory) + { + _factory = factory; + _scope = _factory.Services.CreateScope(); + _dbContext = _scope.ServiceProvider.GetRequiredService(); + } + + [Fact] + public async Task UpdateConsumption_ShouldCreateConsumption_WhenRequestIsValid() + { + // Arrange + CreateConsumption.Response createdConsumption = await CreateConsumptionAsync(); + UpdateConsumption.Request updateConsumptionRequest = _consumptionFaker.UpdateConsumptionRequest(); + + // Act + using HttpResponseMessage response = await _factory.HttpClient.PutAsJsonAsync($"v1/consumptions/{createdConsumption.Id}", updateConsumptionRequest); + + // Assert + string content = await response.Content.ReadAsStringAsync(); + response.StatusCode.Should().Be(HttpStatusCode.OK); + UpdateConsumption.Response? updatedConsumption = await response.Content.ReadFromJsonAsync(); + updatedConsumption.Should().BeEquivalentTo(updateConsumptionRequest, o => o.ExcludingMissingMembers()); + + _dbContext.Consumptions.Should().HaveCount(1) + .And.ContainEquivalentOf(updatedConsumption, o => + o.ExcludingMissingMembers() + .Excluding(x => x!.Id) + .Excluding(x => x!.CarId)); + + Consumption singleConsumption = _dbContext.Consumptions.Single(); + singleConsumption.Id.Value.Should().Be(updatedConsumption!.Id); + singleConsumption.CarId.Value.Should().Be(updatedConsumption.CarId); + } + + [Fact] + public async Task UpdateConsumption_ShouldReturnValidationProblems_WhenRequestIsInvalid() + { + // Arrange + CreateConsumption.Response createdConsumption = await CreateConsumptionAsync(); + UpdateConsumption.Request updateConsumptionRequest = _consumptionFaker.UpdateConsumptionRequest() with { Distance = -42 }; + Guid randomGuid = Guid.NewGuid(); + + // Act + using HttpResponseMessage response = await _factory.HttpClient.PutAsJsonAsync($"v1/consumptions/{randomGuid}", updateConsumptionRequest); + + // Assert + string content = await response.Content.ReadAsStringAsync(); + response.StatusCode.Should().Be(HttpStatusCode.BadRequest); + ValidationProblemDetails? validationProblemDetails = await response.Content.ReadFromJsonAsync(); + validationProblemDetails!.Errors.Keys.Should().Contain(x => + x.Equals(nameof(updateConsumptionRequest.Distance), StringComparison.OrdinalIgnoreCase)); + + _dbContext.Consumptions.Should().NotContainEquivalentOf(updateConsumptionRequest); + } + + [Fact] + public async Task UpdateConsumption_ShouldReturnNotFound_WhenConsumptionDoesNotExist() + { + // Arrange + CreateConsumption.Response createdConsumption = await CreateConsumptionAsync(); + UpdateConsumption.Request updateConsumptionRequest = _consumptionFaker.UpdateConsumptionRequest(); + Guid randomGuid = Guid.NewGuid(); + + // Act + using HttpResponseMessage response = await _factory.HttpClient.PutAsJsonAsync($"v1/consumptions/{randomGuid}", updateConsumptionRequest); + + // Assert + string content = await response.Content.ReadAsStringAsync(); + response.StatusCode.Should().Be(HttpStatusCode.NotFound); + + _dbContext.Consumptions.Should().NotContainEquivalentOf(updateConsumptionRequest); + } + + private async Task CreateConsumptionAsync() + { + CreateCar.Response createdCarResponse = await CreateCarAsync(); + CreateConsumption.Request createConsumptionRequest = _consumptionFaker.CreateConsumptionRequest(createdCarResponse.Id); + using HttpResponseMessage response = await _factory.HttpClient.PostAsJsonAsync("v1/consumptions", createConsumptionRequest); + response.EnsureSuccessStatusCode(); + CreateConsumption.Response? createdConsumption = await response.Content.ReadFromJsonAsync(); + return createdConsumption!; + } + + private async Task CreateCarAsync() + { + CreateCar.Request createCarRequest = new CarFaker().CreateCarRequest(); + using HttpResponseMessage createCarResponse = await _factory.HttpClient.PostAsJsonAsync("v1/cars", createCarRequest); + createCarResponse.EnsureSuccessStatusCode(); + CreateCar.Response? createdCarResponse = await createCarResponse.Content.ReadFromJsonAsync(); + return createdCarResponse!; + } + + public Task InitializeAsync() + { + FluentAssertionConfiguration.SetupGlobalConfig(); + return Task.CompletedTask; + } + + public async Task DisposeAsync() + { + _scope.Dispose(); + await _dbContext.DisposeAsync(); + await _factory.ResetDatabaseAsync(); + } +} \ No newline at end of file diff --git a/tests/Vegasco.Server.Api.Tests.Integration/FluentAssertionConfiguration.cs b/tests/Vegasco.Server.Api.Tests.Integration/FluentAssertionConfiguration.cs new file mode 100644 index 0000000..235018a --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/FluentAssertionConfiguration.cs @@ -0,0 +1,19 @@ +using FluentAssertions; + +namespace Vegasco.Server.Api.Tests.Integration; + +internal static class FluentAssertionConfiguration +{ + private const int DateTimeComparisonPrecision = 100; + + internal static void SetupGlobalConfig() + { + AssertionOptions.AssertEquivalencyUsing(options => options + .Using(ctx => ctx.Subject.ToUniversalTime().Should().BeCloseTo(ctx.Expectation.ToUniversalTime(), TimeSpan.FromMilliseconds(DateTimeComparisonPrecision))) + .WhenTypeIs()); + + AssertionOptions.AssertEquivalencyUsing(options => options + .Using(ctx => ctx.Subject.ToUniversalTime().Should().BeCloseTo(ctx.Expectation.ToUniversalTime(), TimeSpan.FromMilliseconds(DateTimeComparisonPrecision))) + .WhenTypeIs()); + } +} diff --git a/tests/Vegasco.Server.Api.Tests.Integration/Info/GetServerInfoTests.cs b/tests/Vegasco.Server.Api.Tests.Integration/Info/GetServerInfoTests.cs new file mode 100644 index 0000000..02cd58e --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/Info/GetServerInfoTests.cs @@ -0,0 +1,35 @@ +using FluentAssertions; +using FluentAssertions.Extensions; +using System.Net.Http.Json; +using Vegasco.Server.Api.Info; + +namespace Vegasco.Server.Api.Tests.Integration.Info; + +[Collection(SharedTestCollection.Name)] +public class GetServerInfoTests +{ + private readonly WebAppFactory _factory; + + public GetServerInfoTests(WebAppFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task GetServerInfo_ShouldReturnServerInfo_WhenCalled() + { + // Arrange + + // Act + using HttpResponseMessage response = await _factory.HttpClient.GetAsync("/v1/info/server"); + + // Assert + response.IsSuccessStatusCode.Should().BeTrue(); + GetServerInfo.Response? serverInfo = await response.Content.ReadFromJsonAsync(); + serverInfo!.Environment.Should().NotBeEmpty(); + serverInfo.CommitDate.Should().BeAfter(23.August(2024)) + .And.NotBeAfter(DateTime.Now); + serverInfo.CommitId.Should().MatchRegex(@"[0-9a-f]{40}"); + serverInfo.FullVersion.Should().MatchRegex(@"\d\.\d\.\d(-[0-9a-zA-Z]+)?(\+g?[0-9a-f]{10})?"); + } +} \ No newline at end of file diff --git a/tests/Vegasco.Server.Api.Tests.Integration/PostgresRespawner.cs b/tests/Vegasco.Server.Api.Tests.Integration/PostgresRespawner.cs new file mode 100644 index 0000000..eed3a8d --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/PostgresRespawner.cs @@ -0,0 +1,40 @@ +using Npgsql; +using Respawn; +using System.Data.Common; + +namespace Vegasco.Server.Api.Tests.Integration; +internal sealed class PostgresRespawner : IDisposable +{ + private readonly DbConnection _connection; + private readonly Respawner _respawner; + + private PostgresRespawner(Respawner respawner, DbConnection connection) + { + _respawner = respawner; + _connection = connection; + } + + public static async Task CreateAsync(string connectionString) + { + DbConnection connection = new NpgsqlConnection(connectionString); + await connection.OpenAsync(); + + Respawner respawner = await Respawner.CreateAsync(connection, + new RespawnerOptions + { + SchemasToInclude = ["public"], + DbAdapter = DbAdapter.Postgres + }); + return new PostgresRespawner(respawner, connection); + } + + public async Task ResetDatabaseAsync() + { + await _respawner.ResetAsync(_connection); + } + + public void Dispose() + { + _connection.Dispose(); + } +} diff --git a/tests/Vegasco.Server.Api.Tests.Integration/SharedTestCollection.cs b/tests/Vegasco.Server.Api.Tests.Integration/SharedTestCollection.cs new file mode 100644 index 0000000..f8ebc6d --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/SharedTestCollection.cs @@ -0,0 +1,7 @@ +namespace Vegasco.Server.Api.Tests.Integration; + +[CollectionDefinition(Name)] +public class SharedTestCollection : ICollectionFixture +{ + public const string Name = nameof(SharedTestCollection); +} diff --git a/tests/Vegasco.Server.Api.Tests.Integration/TestUserAlwaysAuthorizedPolicyEvaluator.cs b/tests/Vegasco.Server.Api.Tests.Integration/TestUserAlwaysAuthorizedPolicyEvaluator.cs new file mode 100644 index 0000000..7c9deac --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/TestUserAlwaysAuthorizedPolicyEvaluator.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization.Policy; +using Microsoft.AspNetCore.Http; +using System.Security.Claims; + +namespace Vegasco.Server.Api.Tests.Integration; + +public sealed class TestUserAlwaysAuthorizedPolicyEvaluator : IPolicyEvaluator +{ + public const string Username = "Test user"; + public static readonly string UserId = Guid.NewGuid().ToString(); + + public Task AuthenticateAsync(AuthorizationPolicy policy, HttpContext context) + { + Claim[] claims = + [ + + new Claim(ClaimTypes.Name, Username), + new Claim("name", Username), + new Claim(ClaimTypes.NameIdentifier, UserId), + new Claim("aud", "https://localhost") + ]; + + ClaimsIdentity identity = new(claims, JwtBearerDefaults.AuthenticationScheme); + ClaimsPrincipal principal = new(identity); + AuthenticationTicket ticket = new(principal, JwtBearerDefaults.AuthenticationScheme); + AuthenticateResult result = AuthenticateResult.Success(ticket); + return Task.FromResult(result); ; + } + + public Task AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, + object? resource) + { + return Task.FromResult(PolicyAuthorizationResult.Success()); + } +} \ No newline at end of file diff --git a/tests/Vegasco.Server.Api.Tests.Integration/Vegasco.Server.Api.Tests.Integration.csproj b/tests/Vegasco.Server.Api.Tests.Integration/Vegasco.Server.Api.Tests.Integration.csproj new file mode 100644 index 0000000..8007836 --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/Vegasco.Server.Api.Tests.Integration.csproj @@ -0,0 +1,46 @@ + + + + net9.0 + enable + enable + + false + true + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + diff --git a/tests/Vegasco.Server.Api.Tests.Integration/WebAppFactory.cs b/tests/Vegasco.Server.Api.Tests.Integration/WebAppFactory.cs new file mode 100644 index 0000000..3264e46 --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Integration/WebAppFactory.cs @@ -0,0 +1,72 @@ +using DotNet.Testcontainers.Images; +using Microsoft.AspNetCore.Authorization.Policy; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Testcontainers.PostgreSql; +using Vegasco.Server.Api.Common; + +namespace Vegasco.Server.Api.Tests.Integration; + +public sealed class WebAppFactory : WebApplicationFactory, IAsyncLifetime +{ + private readonly PostgreSqlContainer _database = new PostgreSqlBuilder() + .WithImage(DockerImage) + .WithImagePullPolicy(PullPolicy.Always) + .Build(); + + private const string DockerImage = "postgres:alpine"; + + public HttpClient HttpClient => CreateClient(); + + private PostgresRespawner? _postgresRespawner; + + public async Task InitializeAsync() + { + await _database.StartAsync(); + + // Force application startup (i.e. initialization and validation) + _ = CreateClient(); + + _postgresRespawner = await PostgresRespawner.CreateAsync(_database.GetConnectionString()); + } + + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + IEnumerable> customConfig = + [ + new KeyValuePair($"ConnectionStrings:{AppHost.Shared.Constants.Database.Name}", _database.GetConnectionString()), + new KeyValuePair("JWT:ValidAudience", "https://localhost"), + new KeyValuePair("JWT:MetadataUrl", "https://localhost"), + new KeyValuePair("JWT:NameClaimType", null), + ]; + + builder.UseConfiguration(new ConfigurationBuilder() + .AddInMemoryCollection(customConfig) + .Build()); + + builder.ConfigureServices(services => + { + }); + + builder.ConfigureTestServices(services => + { + services.RemoveAll(); + services.AddSingleton(); + }); + } + + public async Task ResetDatabaseAsync() + { + await _postgresRespawner!.ResetDatabaseAsync(); + } + + async Task IAsyncLifetime.DisposeAsync() + { + _postgresRespawner!.Dispose(); + await _database.DisposeAsync(); + } +} \ No newline at end of file diff --git a/tests/Vegasco.Server.Api.Tests.Unit/Authentication/UserAccessorTests.cs b/tests/Vegasco.Server.Api.Tests.Unit/Authentication/UserAccessorTests.cs new file mode 100644 index 0000000..138819f --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Unit/Authentication/UserAccessorTests.cs @@ -0,0 +1,179 @@ +using FluentAssertions; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; +using NSubstitute; +using System.Security.Claims; +using Vegasco.Server.Api.Authentication; + +namespace Vegasco.Server.Api.Tests.Unit.Authentication; +public sealed class UserAccessorTests +{ + private readonly UserAccessor _sut; + private readonly IHttpContextAccessor _httpContextAccessor; + + private static readonly string _nameClaimType = "name"; + private readonly JwtOptions _jwtOptions = new() + { + NameClaimType = _nameClaimType + }; + + private readonly IOptions _options = Substitute.For>(); + + private static readonly string _defaultUsername = "username"; + private static readonly string _defaultId = "id"; + private readonly ClaimsPrincipal _defaultUser = new(new ClaimsIdentity( + [ + new Claim(_nameClaimType, _defaultUsername), + new Claim(ClaimTypes.NameIdentifier, _defaultId) + ])); + + public UserAccessorTests() + { + _httpContextAccessor = new HttpContextAccessor + { + HttpContext = new DefaultHttpContext() + { + User = _defaultUser + } + }; + + _options.Value.Returns(_jwtOptions); + + _sut = new UserAccessor(_httpContextAccessor, _options); + } + + #region GetUsername + + [Fact] + public void GetUsername_ShouldReturnUsername_WhenOptionsNameClaimTypeMatches() + { + // Arrange + + // Act + string result = _sut.GetUsername(); + + // Assert + result.Should().Be(_defaultUsername); + } + + [Fact] + public void GetUsername_ShouldReturnUsername_WhenNameClaimTypeIsNotSetAndUsernameIsInUriNameClaimType() + { + // Arrange + _jwtOptions.NameClaimType = null; + _httpContextAccessor.HttpContext!.User = new ClaimsPrincipal(new ClaimsIdentity( + [ + new Claim(ClaimTypes.Name, _defaultUsername) + ])); + + // Act + string result = _sut.GetUsername(); + + // Assert + result.Should().Be(_defaultUsername); + } + + [Fact] + public void GetUsername_ShouldCacheUsername_WhenFirstCalled() + { + // Arrange + _ = _sut.GetUsername(); + _options.ClearReceivedCalls(); + + // Act + string result = _sut.GetUsername(); + + // Assert + result.Should().Be(_defaultUsername); + _ = _options.Received(0).Value; + } + + [Fact] + public void GetUsername_ShouldThrowInvalidOperationException_WhenHttpContextIsNull() + { + // Arrange + _httpContextAccessor.HttpContext = null; + + // Act + Func action = () => _sut.GetUsername(); + + // Assert + action.Should().ThrowExactly() + .Which.Message.Should().Be("No HttpContext available."); + } + + [Fact] + public void GetUsername_ShouldThrowInvalidOperationException_WhenNameClaimIsNotFound() + { + // Arrange + _httpContextAccessor.HttpContext!.User = new ClaimsPrincipal(); + + // Act + Func action = () => _sut.GetUsername(); + + // Assert + action.Should().ThrowExactly() + .Which.Message.Should().Be($"No claim of type '{_nameClaimType}' found on the current user."); + } + + #endregion + + #region GetUserId + + [Fact] + public void GetUserId_ShouldReturnUserId_WhenUserIdClaimExists() + { + // Arrange + + // Act + string result = _sut.GetUserId(); + + // Assert + result.Should().Be(_defaultId); + } + + [Fact] + public void GetUserId_ShouldCacheUserId_WhenFirstCalled() + { + // Arrange + _ = _sut.GetUserId(); + _options.ClearReceivedCalls(); + + // Act + string result = _sut.GetUserId(); + + // Assert + result.Should().Be(_defaultId); + _ = _options.Received(0).Value; + } + + [Fact] + public void GetUserId_ShouldThrowInvalidOperationException_WhenHttpContextIsNull() + { + // Arrange + _httpContextAccessor.HttpContext = null; + + // Act + Func action = () => _sut.GetUserId(); + + // Assert + action.Should().ThrowExactly() + .Which.Message.Should().Be("No HttpContext available."); + } + + [Fact] + public void GetUserId_ShouldThrowInvalidOperationException_WhenIdClaimIsNotFound() + { + // Arrange + _httpContextAccessor.HttpContext!.User = new ClaimsPrincipal(); + + // Act + Func action = () => _sut.GetUserId(); + + // Assert + action.Should().ThrowExactly() + .Which.Message.Should().Be($"No claim of type '{ClaimTypes.NameIdentifier}' found on the current user."); + } + + #endregion +} diff --git a/tests/Vegasco.Server.Api.Tests.Unit/Cars/CreateCarRequestValidatorTests.cs b/tests/Vegasco.Server.Api.Tests.Unit/Cars/CreateCarRequestValidatorTests.cs new file mode 100644 index 0000000..d65f8af --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Unit/Cars/CreateCarRequestValidatorTests.cs @@ -0,0 +1,72 @@ +using FluentAssertions; +using FluentValidation.Results; +using Vegasco.Server.Api.Cars; + +namespace Vegasco.Server.Api.Tests.Unit.Cars; + +public sealed class CreateCarRequestValidatorTests +{ + private readonly CreateCar.Validator _sut = new(); + + private readonly CreateCar.Request _validRequest = new("Ford Focus"); + + [Fact] + public async Task ValidateAsync_ShouldBeValid_WhenRequestIsValid() + { + // Arrange + + // Act + ValidationResult? result = await _sut.ValidateAsync(_validRequest); + + // Assert + result.IsValid.Should().BeTrue(); + } + + [Theory] + [InlineData(1)] + [InlineData(50)] + public async Task ValidateAsync_ShouldBeValid_WhenNameIsJustWithinTheLimits(int nameLength) + { + // Arrange + CreateCar.Request request = _validRequest with { Name = new string('s', nameLength) }; + + // Act + ValidationResult? result = await _sut.ValidateAsync(request); + + // Assert + result.IsValid.Should().BeTrue(); + } + + [Fact] + public async Task ValidateAsync_ShouldNotBeValid_WhenNameIsEmpty() + { + // Arrange + CreateCar.Request request = _validRequest with { Name = "" }; + + // Act + ValidationResult? result = await _sut.ValidateAsync(request); + + // Assert + result.IsValid.Should().BeFalse(); + result.Errors.Should().ContainSingle() + .Which + .PropertyName.Should().Be(nameof(CreateCar.Request.Name)); + } + + [Fact] + public async Task ValidateAsync_ShouldNotBeValid_WhenNameIsTooLong() + { + // Arrange + const int nameMaxLength = 50; + CreateCar.Request request = _validRequest with { Name = new string('s', nameMaxLength + 1) }; + + // Act + ValidationResult? result = await _sut.ValidateAsync(request); + + // Assert + result.IsValid.Should().BeFalse(); + result.Errors.Should().ContainSingle() + .Which + .PropertyName.Should().Be(nameof(CreateCar.Request.Name)); + } +} diff --git a/tests/Vegasco.Server.Api.Tests.Unit/Cars/UpdateCarRequestValidatorTests.cs b/tests/Vegasco.Server.Api.Tests.Unit/Cars/UpdateCarRequestValidatorTests.cs new file mode 100644 index 0000000..dfbdc03 --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Unit/Cars/UpdateCarRequestValidatorTests.cs @@ -0,0 +1,72 @@ +using FluentAssertions; +using FluentValidation.Results; +using Vegasco.Server.Api.Cars; + +namespace Vegasco.Server.Api.Tests.Unit.Cars; + +public sealed class UpdateCarRequestValidatorTests +{ + private readonly UpdateCar.Validator _sut = new(); + + private readonly UpdateCar.Request _validRequest = new("Ford Focus"); + + [Fact] + public async Task ValidateAsync_ShouldBeValid_WhenRequestIsValid() + { + // Arrange + + // Act + ValidationResult? result = await _sut.ValidateAsync(_validRequest); + + // Assert + result.IsValid.Should().BeTrue(); + } + + [Theory] + [InlineData(1)] + [InlineData(50)] + public async Task ValidateAsync_ShouldBeValid_WhenNameIsJustWithinTheLimits(int nameLength) + { + // Arrange + UpdateCar.Request request = _validRequest with { Name = new string('s', nameLength) }; + + // Act + ValidationResult? result = await _sut.ValidateAsync(request); + + // Assert + result.IsValid.Should().BeTrue(); + } + + [Fact] + public async Task ValidateAsync_ShouldNotBeValid_WhenNameIsEmpty() + { + // Arrange + UpdateCar.Request request = _validRequest with { Name = "" }; + + // Act + ValidationResult? result = await _sut.ValidateAsync(request); + + // Assert + result.IsValid.Should().BeFalse(); + result.Errors.Should().ContainSingle() + .Which + .PropertyName.Should().Be(nameof(UpdateCar.Request.Name)); + } + + [Fact] + public async Task ValidateAsync_ShouldNotBeValid_WhenNameIsTooLong() + { + // Arrange + const int nameMaxLength = 50; + UpdateCar.Request request = _validRequest with { Name = new string('s', nameMaxLength + 1) }; + + // Act + ValidationResult? result = await _sut.ValidateAsync(request); + + // Assert + result.IsValid.Should().BeFalse(); + result.Errors.Should().ContainSingle() + .Which + .PropertyName.Should().Be(nameof(UpdateCar.Request.Name)); + } +} \ No newline at end of file diff --git a/tests/Vegasco.Server.Api.Tests.Unit/Consumptions/CreateConsumptionRequestValidatorTests.cs b/tests/Vegasco.Server.Api.Tests.Unit/Consumptions/CreateConsumptionRequestValidatorTests.cs new file mode 100644 index 0000000..e60dcca --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Unit/Consumptions/CreateConsumptionRequestValidatorTests.cs @@ -0,0 +1,99 @@ +using FluentAssertions; +using FluentValidation.Results; +using NSubstitute; +using Vegasco.Server.Api.Consumptions; + +namespace Vegasco.Server.Api.Tests.Unit.Consumptions; +public class CreateConsumptionRequestValidatorTests +{ + private readonly CreateConsumption.Validator _sut; + private readonly TimeProvider _timeProvider = Substitute.For(); + + private readonly DateTimeOffset _utcNow = new DateTimeOffset(2024, 8, 18, 13, 2, 53, TimeSpan.Zero); + + private readonly CreateConsumption.Request _validRequest; + + public CreateConsumptionRequestValidatorTests() + { + _timeProvider.GetUtcNow().Returns(_utcNow); + _sut = new CreateConsumption.Validator(_timeProvider); + + _validRequest = new CreateConsumption.Request( + _utcNow.Date.AddDays(1).AddTicks(-1), + 1, + 1, + Guid.NewGuid()); + } + + [Fact] + public async Task ValidateAsync_ShouldBeValid_WhenRequestIsValid() + { + // Arrange + + // Act + ValidationResult? result = await _sut.ValidateAsync(_validRequest); + + // Assert + result.IsValid.Should().BeTrue(); + } + + [Fact] + public async Task ValidateAsync_ShouldBeInvalid_WhenDateTimeIsGreaterThanUtcToday() + { + // Arrange + CreateConsumption.Request request = _validRequest with { DateTime = _utcNow.Date.AddDays(1) }; + + // Act + ValidationResult? result = await _sut.ValidateAsync(request); + + // Assert + result.IsValid.Should().BeFalse(); + result.Errors.Should().ContainSingle(x => x.PropertyName == nameof(CreateConsumption.Request.DateTime)); + } + + [Theory] + [InlineData(0)] + [InlineData(-1)] + public async Task ValidateAsync_ShouldBeInvalid_WhenDistanceIsLessThanOrEqualToZero(double distance) + { + // Arrange + CreateConsumption.Request request = _validRequest with { Distance = distance }; + + // Act + ValidationResult? result = await _sut.ValidateAsync(request); + + // Assert + result.IsValid.Should().BeFalse(); + result.Errors.Should().ContainSingle(x => x.PropertyName == nameof(CreateConsumption.Request.Distance)); + } + + [Theory] + [InlineData(0)] + [InlineData(-1)] + public async Task ValidateAsync_ShouldBeInvalid_WhenAmountIsLessThanOrEqualToZero(double amount) + { + // Arrange + CreateConsumption.Request request = _validRequest with { Amount = amount }; + + // Act + ValidationResult? result = await _sut.ValidateAsync(request); + + // Assert + result.IsValid.Should().BeFalse(); + result.Errors.Should().ContainSingle(x => x.PropertyName == nameof(CreateConsumption.Request.Amount)); + } + + [Fact] + public async Task ValidateAsync_ShouldBeInvalid_WhenCarIdIsEmpty() + { + // Arrange + CreateConsumption.Request request = _validRequest with { CarId = Guid.Empty }; + + // Act + ValidationResult? result = await _sut.ValidateAsync(request); + + // Assert + result.IsValid.Should().BeFalse(); + result.Errors.Should().ContainSingle(x => x.PropertyName == nameof(CreateConsumption.Request.CarId)); + } +} \ No newline at end of file diff --git a/tests/Vegasco.Server.Api.Tests.Unit/Consumptions/UpdateConsumptionRequestValidatorTests.cs b/tests/Vegasco.Server.Api.Tests.Unit/Consumptions/UpdateConsumptionRequestValidatorTests.cs new file mode 100644 index 0000000..982458d --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Unit/Consumptions/UpdateConsumptionRequestValidatorTests.cs @@ -0,0 +1,85 @@ +using FluentAssertions; +using FluentValidation.Results; +using NSubstitute; +using Vegasco.Server.Api.Consumptions; + +namespace Vegasco.Server.Api.Tests.Unit.Consumptions; + +public class UpdateConsumptionRequestValidatorTests +{ + private readonly UpdateConsumption.Validator _sut; + private readonly TimeProvider _timeProvider = Substitute.For(); + + private readonly DateTimeOffset _utcNow = new DateTimeOffset(2024, 8, 18, 13, 2, 53, TimeSpan.Zero); + + private readonly UpdateConsumption.Request _validRequest; + + public UpdateConsumptionRequestValidatorTests() + { + _timeProvider.GetUtcNow().Returns(_utcNow); + _sut = new UpdateConsumption.Validator(_timeProvider); + + _validRequest = new UpdateConsumption.Request( + _utcNow.Date.AddDays(1).AddTicks(-1), + 1, + 1); + } + + [Fact] + public async Task ValidateAsync_ShouldBeValid_WhenRequestIsValid() + { + // Arrange + + // Act + ValidationResult? result = await _sut.ValidateAsync(_validRequest); + + // Assert + result.IsValid.Should().BeTrue(); + } + + [Fact] + public async Task ValidateAsync_ShouldBeInvalid_WhenDateTimeIsGreaterThanUtcToday() + { + // Arrange + UpdateConsumption.Request request = _validRequest with { DateTime = _utcNow.Date.AddDays(1) }; + + // Act + ValidationResult? result = await _sut.ValidateAsync(request); + + // Assert + result.IsValid.Should().BeFalse(); + result.Errors.Should().ContainSingle(x => x.PropertyName == nameof(UpdateConsumption.Request.DateTime)); + } + + [Theory] + [InlineData(0)] + [InlineData(-1)] + public async Task ValidateAsync_ShouldBeInvalid_WhenDistanceIsLessThanOrEqualToZero(double distance) + { + // Arrange + UpdateConsumption.Request request = _validRequest with { Distance = distance }; + + // Act + ValidationResult? result = await _sut.ValidateAsync(request); + + // Assert + result.IsValid.Should().BeFalse(); + result.Errors.Should().ContainSingle(x => x.PropertyName == nameof(UpdateConsumption.Request.Distance)); + } + + [Theory] + [InlineData(0)] + [InlineData(-1)] + public async Task ValidateAsync_ShouldBeInvalid_WhenAmountIsLessThanOrEqualToZero(double amount) + { + // Arrange + UpdateConsumption.Request request = _validRequest with { Amount = amount }; + + // Act + ValidationResult? result = await _sut.ValidateAsync(request); + + // Assert + result.IsValid.Should().BeFalse(); + result.Errors.Should().ContainSingle(x => x.PropertyName == nameof(UpdateConsumption.Request.Amount)); + } +} \ No newline at end of file diff --git a/tests/Vegasco.Server.Api.Tests.Unit/Vegasco.Server.Api.Tests.Unit.csproj b/tests/Vegasco.Server.Api.Tests.Unit/Vegasco.Server.Api.Tests.Unit.csproj new file mode 100644 index 0000000..a2a53a7 --- /dev/null +++ b/tests/Vegasco.Server.Api.Tests.Unit/Vegasco.Server.Api.Tests.Unit.csproj @@ -0,0 +1,40 @@ + + + + net9.0 + enable + enable + + false + true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + diff --git a/vegasco-server.sln.DotSettings b/vegasco-server.sln.DotSettings new file mode 100644 index 0000000..d8d7c4c --- /dev/null +++ b/vegasco-server.sln.DotSettings @@ -0,0 +1,3 @@ + + True + True \ No newline at end of file diff --git a/vegasco-server.slnx b/vegasco-server.slnx new file mode 100644 index 0000000..df254b8 --- /dev/null +++ b/vegasco-server.slnx @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/version.json b/version.json new file mode 100644 index 0000000..656cc93 --- /dev/null +++ b/version.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", + "version": "2.0-beta", + "cloudBuild": { + "setAllVariables": true + }, + "publicReleaseRefSpec": [ + "prod" + ] +} \ No newline at end of file