[a-z]*))?
- $
- }xi
-
- match = descriptor.strip.match(regex)
- if match.nil?
- STDERR.puts "Error: Invalid descriptor #{descriptor}"
- @parsed_descriptor = nil
- else
- match = match.named_captures.transform_keys(&:to_sym)
-
- @parsed_descriptor = match
- end
- end
- end
-
- def compiled_welcome_page
- binding_hash = {
- domain: self,
- NAConfig: NAConfig
- }
-
- ERBBinding.new('/var/www/default/index.html.erb', binding_hash).compile
- end
-end
diff --git a/fs_overlay/var/lib/crontab.erb b/fs_overlay/var/lib/crontab.erb
deleted file mode 100644
index 6572b23..0000000
--- a/fs_overlay/var/lib/crontab.erb
+++ /dev/null
@@ -1,17 +0,0 @@
-# THIS FILE IS AUTO-GENERATED FROM /var/lib/crontab.erb ON FIRST RUN.
-# Certificate renewal is set to start at a random fractional second evenly
-# throughout the day on container creation, to avoid Let's Encrypt workload
-# peaks.
-#
-# /etc/crontab: system-wide crontab
-# Unlike any other crontab you don't have to run the `crontab'
-# command to install the new version when you edit this file
-# and files in /etc/cron.d. These files also have username fields,
-# that none of the other crontabs do.
-
-SHELL=/bin/sh
-PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-
-# m h dom mon dow user command
-<%= rand 60 %> <%= rand 24 %> * * * root /bin/sleep <%= '%.3f' % (rand * 60) %>; /usr/bin/with-contenv /bin/renew_certs > /proc/$(cat /var/run/crond.pid)/fd/1 2>&1
-45 3 * * * root /usr/sbin/logrotate -s /var/lib/logrotate/logrotate.status /etc/logrotate.d/nginx -l /var/log/nginx/logrotate.log
diff --git a/fs_overlay/var/lib/nginx-conf/default.conf.erb b/fs_overlay/var/lib/nginx-conf/default.conf.erb
deleted file mode 100644
index 2c398ea..0000000
--- a/fs_overlay/var/lib/nginx-conf/default.conf.erb
+++ /dev/null
@@ -1,18 +0,0 @@
-server {
- listen 80;
- <% if ENV['LISTEN_IPV6'] && ENV['LISTEN_IPV6'].downcase == 'true' %>
- listen [::]:80;
- <% end %>
- server_name <%= domain.name %>;
-
- location /dappnode-health-check {
- access_log off;
- return 200 "I am healthy!\n";
- }
-
- location / {
- return 301 https://$server_name$request_uri;
- }
-
- <%= acme_challenge_location %>
-}
diff --git a/fs_overlay/var/lib/nginx-conf/default.ssl.conf.erb b/fs_overlay/var/lib/nginx-conf/default.ssl.conf.erb
deleted file mode 100644
index 7d85fa1..0000000
--- a/fs_overlay/var/lib/nginx-conf/default.ssl.conf.erb
+++ /dev/null
@@ -1,85 +0,0 @@
-<% if domain.multiple_upstreams? %>
-upstream <%= domain.upstream_backend_name %> {
- <% domain.upstreams.each do |upstream| %>
- server <%= upstream[:address] %> <%= upstream[:parameters] %>;
- <% end %>
-}
-<% end %>
-
-server {
- listen 443 ssl http2;
- <% if ENV['LISTEN_IPV6'] && ENV['LISTEN_IPV6'].downcase == 'true' %>
- listen [::]:443 ssl http2;
- <% end %>
- server_name <%= domain.name %>;
-
- ssl_certificate <%= domain.chained_cert_path %>;
- ssl_certificate_key <%= domain.key_path %>;
-
- ssl_session_cache shared:SSL:50m;
- ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA;
- ssl_prefer_server_ciphers on;
-
- <%# ssl_dhparam <%= dhparam_path %>
-
- # Send HSTS header if configured
- <% if ENV['HSTS_MAX_AGE'] %>
- add_header Strict-Transport-Security "max-age=<%= ENV['HSTS_MAX_AGE'] %>" always;
- <% end %>
-
- <% if domain.access_restriction %>
- <% domain.access_restriction.each do |ip| %>
- allow <%= ip %>;
- <% end %>
- deny all;
- <% end %>
-
- <% if domain.basic_auth_enabled? %>
- auth_basic "Password";
- auth_basic_user_file <%= domain.htaccess_path %>;
- <% end %>
-
- <% if ENV['CUSTOM_NGINX_SERVER_CONFIG_BLOCK'] %>
- <%= ENV['CUSTOM_NGINX_SERVER_CONFIG_BLOCK'] %>
- <% end %>
-
- <% if ENV["CUSTOM_NGINX_#{domain.env_format_name}_CONFIG_BLOCK"] %>
- <%= ENV["CUSTOM_NGINX_#{domain.env_format_name}_CONFIG_BLOCK"] %>
- <% end %>
-
- location /dappnode-health-check {
- access_log off;
- return 200 "I am healthy!\n";
- }
-
- <% if domain.upstream %>
- location / {
- <% if ENV['DYNAMIC_UPSTREAM'] && ENV['DYNAMIC_UPSTREAM'].downcase == 'true' %>
- set $backend <%= domain.multiple_upstreams? ? domain.upstream_proto + domain.upstream_backend_name : domain.upstream %>;
- proxy_pass $backend;
- <% else %>
- proxy_pass <%= domain.multiple_upstreams? ? domain.upstream_proto + domain.upstream_backend_name : domain.upstream %>;
- <% end %>
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
-
- <% if ENV['WEBSOCKET'] && ENV['WEBSOCKET'].downcase == 'true' %>
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection $connection_upgrade;
- proxy_read_timeout 2h;
- <% end %>
- }
- <% elsif domain.redirect_target_url %>
- location / {
- return 301 <%= domain.redirect_target_url %>$request_uri;
- }
- <% else %>
- location / {
- root <%= domain.www_root %>;
- index index.html;
- }
- <% end %>
-}
diff --git a/fs_overlay/var/lib/nginx-conf/nginx.conf.erb b/fs_overlay/var/lib/nginx-conf/nginx.conf.erb
deleted file mode 100644
index 13d5891..0000000
--- a/fs_overlay/var/lib/nginx-conf/nginx.conf.erb
+++ /dev/null
@@ -1,147 +0,0 @@
-# This file will be compiled into /etc/nginx/nginx.conf
-
-user nginx;
-worker_processes <%= ENV['WORKER_PROCESSES'] || 1 %>;
-
-pid /var/run/nginx.pid;
-
-events {
- worker_connections <%= ENV['WORKER_CONNECTIONS'] || 1024 %>;
-}
-
-http {
- include /etc/nginx/mime.types;
- default_type application/octet-stream;
- ssl_protocols TLSv1.2 TLSv1.3;
-
- <% if ENV['ACCESS_LOG_INCLUDE_HOST'] == 'on' %>
- log_format main '$host:$server_port $remote_addr - $remote_user [$time_local] "$request" '
- '$status $body_bytes_sent "$http_referer" '
- '"$http_user_agent" "$http_x_forwarded_for"';
- <% else %>
- log_format main '$remote_addr - $remote_user [$time_local] "$request" '
- '$status $body_bytes_sent "$http_referer" '
- '"$http_user_agent" "$http_x_forwarded_for"';
- <% end %>
-
- <% if ENV['WEBSOCKET'] && ENV['WEBSOCKET'].downcase == 'true' %>
- map $http_upgrade $connection_upgrade {
- default upgrade;
- '' close;
- }
- <% end %>
-
- <% if !ENV['ACCESS_LOG'] || ENV['ACCESS_LOG'] == '' || ENV['ACCESS_LOG'] == 'off' %>
- access_log off;
- <% elsif ENV['ACCESS_LOG'] == 'stdout' %>
- access_log /dev/stdout main <%= ENV['ACCESS_LOG_BUFFER'] && "buffer=#{ENV['ACCESS_LOG_BUFFER']}" %>;
- <% elsif ENV['ACCESS_LOG'] == 'stderr' %>
- access_log /dev/stderr main <%= ENV['ACCESS_LOG_BUFFER'] && "buffer=#{ENV['ACCESS_LOG_BUFFER']}" %>;
- <% elsif ENV['ACCESS_LOG'] == 'default' %>
- access_log /var/log/nginx/access.log main <%= ENV['ACCESS_LOG_BUFFER'] && "buffer=#{ENV['ACCESS_LOG_BUFFER']}" %>;
- <% else %>
- access_log <%= ENV['ACCESS_LOG'] %> main <%= ENV['ACCESS_LOG_BUFFER'] && "buffer=#{ENV['ACCESS_LOG_BUFFER']}" %>;
- <% end %>
-
- <% if !ENV['ERROR_LOG'] || ENV['ERROR_LOG'] == '' || ENV['ERROR_LOG'] == 'stderr' %>
- error_log /dev/stderr <%= ENV['ERROR_LOG_LEVEL'] || "error" %>;
- <% elsif ENV['ERROR_LOG'] == 'off' %>
- error_log off;
- <% elsif ENV['ERROR_LOG'] == 'stdout' %>
- error_log /dev/stdout <%= ENV['ERROR_LOG_LEVEL'] || "error" %>;
- <% elsif ENV['ERROR_LOG'] == 'default' %>
- error_log /var/log/nginx/error.log <%= ENV['ERROR_LOG_LEVEL'] || "error" %>;
- <% else %>
- error_log <%= ENV['ERROR_LOG'] || "/var/log/nginx/error.log" %> <%= ENV['ERROR_LOG_LEVEL'] || "error" %>;
- <% end %>
-
- sendfile on;
-
- keepalive_timeout <%= ENV['KEEPALIVE_TIMEOUT'] || 65 %>;
-
- <% unless ENV['GZIP'] == 'off' %>
- gzip on;
- gzip_disable "msie6";
- gzip_vary on;
- gzip_proxied any;
- gzip_comp_level 6;
- gzip_buffers 16 8k;
- gzip_http_version 1.1;
- gzip_types application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font application/x-font-opentype application/x-font-otf application/x-font-truetype application/x-font-ttf application/x-javascript application/xhtml+xml application/xml font/opentype font/otf font/ttf image/svg+xml image/x-icon text/css text/javascript text/plain text/xml;
- <% end %>
-
- server_tokens <%= ENV['SERVER_TOKENS'] || 'off' %>;
-
- server_names_hash_max_size <%= ENV['SERVER_NAMES_HASH_MAX_SIZE'] || 4096 %>;
-
- server_names_hash_bucket_size <%= ENV['SERVER_NAMES_HASH_BUCKET_SIZE'] || 128 %>;
-
- <% if ENV['CLIENT_MAX_BODY_SIZE'] %>
- client_max_body_size <%= ENV['CLIENT_MAX_BODY_SIZE'] %>;
- <% end %>
-
- <% if ENV['PROXY_BUFFERS'] %>
- proxy_buffers <%= ENV['PROXY_BUFFERS'] %>;
- <% end %>
-
- <% if ENV['PROXY_BUFFER_SIZE'] %>
- proxy_buffer_size <%= ENV['PROXY_BUFFER_SIZE'] %>;
- <% end %>
-
- <% if ENV['RESOLVER'] %>
- resolver <%= ENV['RESOLVER'] %>;
- <% end %>
-
- <% if ENV['PROXY_CONNECT_TIMEOUT'] %>
- proxy_connect_timeout <%= ENV['PROXY_CONNECT_TIMEOUT'] %>;
- <% end %>
-
- <% if ENV['PROXY_SEND_TIMEOUT'] %>
- proxy_send_timeout <%= ENV['PROXY_SEND_TIMEOUT'] %>;
- <% end %>
-
- <% if ENV['PROXY_READ_TIMEOUT'] %>
- proxy_read_timeout <%= ENV['PROXY_READ_TIMEOUT'] %>;
- <% end %>
-
- <% if ENV['ACCESS_RESTRICTION'] %>
- <% ENV['ACCESS_RESTRICTION'].split(' ').each do |ip| %>
- allow <%= ip %>;
- <% end %>
- deny all;
- <% end %>
-
-
- <% if ENV['LOCAL_PROXYING'].downcase == 'true' && ENV['LOCAL_PROXY_DOMAIN'] %>
- server {
- server_name <%=ENV['LOCAL_PROXY_DOMAIN']%>.local;
- listen 80;
- listen [::]:80;
- resolver <%= ENV['GLOBAL_RESOLVER'] %>;
- allow 10.0.0.0/8;
- allow 172.16.0.0/12;
- allow 192.168.0.0/16;
- allow fc00::/7;
- allow fe80::/10;
- deny all;
- location / {
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_pass http://my.dappnode;
- }
- }
- <% end %>
-
- include /etc/nginx/conf.d/*.conf;
-
- # Prevent Nginx from leaking other server configurations on the same machine
- server {
- listen 80 default_server;
- listen 443 ssl default_server;
-
- ssl_certificate <%= NAConfig.portal_base_dir %>/default_server/default_server.crt;
- ssl_certificate_key <%= NAConfig.portal_base_dir %>/default_server/default_server.key;
- server_name _;
- return 444;
- }
-}
diff --git a/fs_overlay/var/www/default/challenges/.gitkeep b/fs_overlay/var/www/default/challenges/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/fs_overlay/var/www/default/index.html.erb b/fs_overlay/var/www/default/index.html.erb
deleted file mode 100644
index 66e50b3..0000000
--- a/fs_overlay/var/www/default/index.html.erb
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-Welcome to HTTPS-PORTAL!
-
-
-
-Welcome to HTTPS-PORTAL!
-
-<% if domain.stage == 'production' %>
-
- Your site is available in HTTPS with a certificate issued by
- Let's Encrypt.
-
-<% elsif domain.stage == 'staging' %>
-
- Your site is available in HTTPS with a certificate issued by
- Let's Encrypt staging server.
-
-
-
- This certificate is for testing your setup, and might not be trusted by your browser.
-
- To run the your website with Let's Encrypt production server, add
- `STAGE=production` to the container environment.
-
-<% elsif domain.stage == 'local' %>
-
- Your site is available in HTTPS with a self-signed certificate.
-
-
-
- You can now run the same Docker compose file on your server with Let's Encrypt certificated.
- Don't forget to set up your DNS and use `STAGE=staging`!
-
-<% end %>
-
-
- You can replace this page with your own static files, by mounting your own www
- root directory as a data volume at <%= domain.www_root %>.
-
-
-
- For more detailed information about static sites, check
- Serving
- Static Sites section of the documents.
-
-
-
- Documents are available at
- https://github.com/SteveLTN/https-portal.
-
-
-
diff --git a/fs_overlay/var/www/vhosts/.gitkeep b/fs_overlay/var/www/vhosts/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..f80c898
--- /dev/null
+++ b/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "dnp_https",
+ "version": "1.0.0",
+ "description": "DNP_HTTPS DAppNode package",
+ "main": "bin/index.js",
+ "license": "MIT",
+ "dependencies": {
+ "axios": "^1.2.0",
+ "ejs": "^3.1.8",
+ "express": "^4.18.2",
+ "morgan": "^1.10.0"
+ },
+ "devDependencies": {
+ "@types/ejs": "^3.1.1",
+ "@types/express": "^4.17.14",
+ "@types/morgan": "^1.9.3",
+ "@types/node": "^18.11.9",
+ "@typescript-eslint/eslint-plugin": "^5.54.0",
+ "@typescript-eslint/parser": "^5.54.0",
+ "ejs-lint": "^2.0.0",
+ "eslint": "^8.35.0",
+ "prettier": "^2.8.0",
+ "ts-node": "^10.9.1",
+ "typescript": "^4.9.3"
+ },
+ "scripts": {
+ "build": "tsc",
+ "start": "node ./build/index.js",
+ "dev": "ts-node ./src/index.ts",
+ "lint:ejs": "ejslint ./templates/*.ejs",
+ "lint": "eslint . --ext .ts --fix",
+ "prettier": "prettier --write 'src/**/*.*'"
+ }
+}
diff --git a/releases.json b/releases.json
index c6ddbe6..f21d0af 100644
--- a/releases.json
+++ b/releases.json
@@ -1,8 +1,14 @@
{
"0.1.1": {
- "hash": "/ipfs/QmTDtAF9P9ZX5eFV11W5vi6W411u3mX5F6hnsYSn1AwjeU",
+ "hash": "/ipfs/Qmbobj8bDkd8oH1yyW8Ra9r7rHbWzKF79jNBDMH45pSKAG",
"uploadedTo": {
- "dappnode": "Thu, 10 Jun 2021 10:01:46 GMT"
+ "remote": "Wed, 21 Dec 2022 00:46:59 GMT"
+ }
+ },
+ "0.2.0": {
+ "hash": "/ipfs/QmU1KRM9s2Y6GrJXHE1Wzs1ZyfUqoWYe8xnApJB8uZKjwX",
+ "uploadedTo": {
+ "remote": "Wed, 15 Feb 2023 08:54:51 GMT"
}
}
}
diff --git a/spec/compositions/auto-discovery/docker-compose.yml b/spec/compositions/auto-discovery/docker-compose.yml
deleted file mode 100644
index 2231cf7..0000000
--- a/spec/compositions/auto-discovery/docker-compose.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-https-portal:
- build: ../../..
- ports:
- - '80:80'
- - '443:443'
- environment:
- FORCE_RENEW: $FORCE_RENEW
- volumes:
- - /var/run/docker.sock:/var/run/docker.sock:ro
- links:
- - wordpress # to make sure wordpress starts first
-
-wordpress:
- image: wordpress
- ports:
- - '8080:80'
- expose:
- - '80'
- - '81'
- links:
- - db:mysql
- environment:
- VIRTUAL_HOST: $TEST_DOMAIN
- WORDPRESS_DB_HOST: mysql
- WORDPRESS_DB_PASSWORD: asecretpassword
-
-db:
- image: mariadb
- environment:
- MYSQL_ROOT_PASSWORD: asecretpassword
diff --git a/spec/compositions/linked-containers/docker-compose.yml b/spec/compositions/linked-containers/docker-compose.yml
deleted file mode 100644
index 5dace25..0000000
--- a/spec/compositions/linked-containers/docker-compose.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-version: '2'
-
-services:
- https-portal:
- build: ../../..
- ports:
- - 80:80
- - 443:443
- links:
- - wordpress
- environment:
- DOMAINS: $TEST_DOMAIN->http://wordpress
- FORCE_RENEW: $FORCE_RENEW
-
- wordpress:
- image: wordpress
- links:
- - db:mysql
- environment:
- WORDPRESS_DB_HOST: mysql
- WORDPRESS_DB_PASSWORD: asecretpassword
-
-
- db:
- image: mariadb
- environment:
- MYSQL_ROOT_PASSWORD: asecretpassword
diff --git a/spec/compositions/local/docker-compose.yml b/spec/compositions/local/docker-compose.yml
deleted file mode 100644
index 18b53d7..0000000
--- a/spec/compositions/local/docker-compose.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-https-portal:
- build: ../../..
- ports:
- - 80:80
- - 443:443
- environment:
- DOMAINS: $TEST_DOMAIN
- FORCE_RENEW: $FORCE_RENEW
- STAGE: local
diff --git a/spec/compositions/minimal-setup/docker-compose.yml b/spec/compositions/minimal-setup/docker-compose.yml
deleted file mode 100644
index 6e30779..0000000
--- a/spec/compositions/minimal-setup/docker-compose.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-https-portal:
- build: ../../..
- ports:
- - 80:80
- - 443:443
- environment:
- DOMAINS: $TEST_DOMAIN
- FORCE_RENEW: $FORCE_RENEW
diff --git a/spec/compositions/static-site/docker-compose.yml b/spec/compositions/static-site/docker-compose.yml
deleted file mode 100644
index 822dc66..0000000
--- a/spec/compositions/static-site/docker-compose.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-version: '2'
-
-services:
- https-portal:
- build: ../../..
- ports:
- - 80:80
- - 443:443
- environment:
- DOMAINS: $TEST_DOMAIN
- volumes:
- - /data/https-portal/vhosts:/var/www/vhosts
diff --git a/spec/compositions/static-site/index.html b/spec/compositions/static-site/index.html
deleted file mode 100644
index 8e56c47..0000000
--- a/spec/compositions/static-site/index.html
+++ /dev/null
@@ -1 +0,0 @@
-Welcome to my awesome static site powered by HTTPS-PORTAL!
diff --git a/spec/features/auto_discovery_spec.rb b/spec/features/auto_discovery_spec.rb
deleted file mode 100644
index 8e76ce2..0000000
--- a/spec/features/auto_discovery_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require 'spec_helper'
-
-RSpec.describe 'Auto discovery', composition: 'auto-discovery', type: :feature do
- context 'when no certificates are stored' do
- it 'should forward request to auto discovered WordPress container' do
- docker_compose :up, env: { 'FORCE_RENEW' => 'true' }
-
- page = read_https_content
- expect(page).to include 'WordPress'
- end
- end
-
- context 'when certificates are stored in a data volume' do
- it 'should forward request to auto discovered WordPress container' do
- docker_compose :up
-
- page = read_https_content
- expect(page).to include 'WordPress'
- end
- end
-end
diff --git a/spec/features/linked_containers_spec.rb b/spec/features/linked_containers_spec.rb
deleted file mode 100644
index d66f816..0000000
--- a/spec/features/linked_containers_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require 'spec_helper'
-
-RSpec.describe 'Linked containers', composition: 'linked-containers', type: :feature do
- context 'when no certificates are stored' do
- it 'should forward request to linked WordPress container' do
- docker_compose :up, env: { 'FORCE_RENEW' => 'true' }
-
- page = read_https_content
- expect(page).to include 'WordPress'
- end
- end
-
- context 'when certificates are stored in a data volume' do
- it 'should forward request to linked WordPress container' do
- docker_compose :up
-
- page = read_https_content
- expect(page).to include 'WordPress'
- end
- end
-end
diff --git a/spec/features/local_stage_spec.rb b/spec/features/local_stage_spec.rb
deleted file mode 100644
index a3e07a1..0000000
--- a/spec/features/local_stage_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require 'spec_helper'
-
-RSpec.describe 'Local stage', composition: 'local', type: :feature do
- context 'when no certificates are stored' do
- it 'should serve a welcome page' do
- docker_compose :up, env: { 'FORCE_RENEW' => 'true', 'STAGE' => 'local' }
-
- page = read_https_content
- expect(page).to include 'Welcome to HTTPS-PORTAL!'
- end
- end
-
- context 'when certificates are stored in a data volume' do
- it 'should serve a welcome page' do
- docker_compose :up, env: { 'STAGE' => 'local' }
-
- page = read_https_content
- expect(page).to include 'Welcome to HTTPS-PORTAL!'
- end
- end
-end
diff --git a/spec/features/minimal_setup_spec.rb b/spec/features/minimal_setup_spec.rb
deleted file mode 100644
index 97e8456..0000000
--- a/spec/features/minimal_setup_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require 'spec_helper'
-
-RSpec.describe 'Minimal setup', composition: 'minimal-setup', type: :feature do
- context 'when no certificates are stored' do
- it 'should serve a welcome page' do
- docker_compose :up, env: { 'FORCE_RENEW' => 'true' }
-
- page = read_https_content
- expect(page).to include 'Welcome to HTTPS-PORTAL!'
- end
- end
-
- context 'when certificates are stored in a data volume' do
- it 'should serve a welcome page' do
- docker_compose :up
-
- page = read_https_content
- expect(page).to include 'Welcome to HTTPS-PORTAL!'
- end
- end
-end
diff --git a/spec/features/renewal_spec.rb b/spec/features/renewal_spec.rb
deleted file mode 100644
index 39cb492..0000000
--- a/spec/features/renewal_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require 'spec_helper'
-
-# This spec intentionally reuse containers created by previous example group.
-# Since we don't retry the docker command here, to ensure it success, an
-# already initialized https-portal instance is required.
-RSpec.describe 'Renewal', :reuse_container, composition: 'minimal-setup', type: :feature do
- let(:docker_command) do
- 'docker exec portalspec_https-portal_1 bash -c ' \
- "'test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )'"
- end
-
- context 'when certs already signed and no FORCE_RENEW specified' do
- it 'should not renew certs' do
- docker_compose :up
-
- read_https_content
- output = `#{docker_command}`
-
- expect(output).to include "No need to renew certs for #{ENV['TEST_DOMAIN']}"
- end
- end
-
- context 'when certs already signed and FORCE_RENEW specified' do
- it 'should force renew the certs' do
- docker_compose :up, env: { 'FORCE_RENEW' => 'true' }
-
- read_https_content
- output = `#{docker_command}`
-
- expect(output).to include "Renewed certs for #{ENV['TEST_DOMAIN']}"
- end
- end
-end
diff --git a/spec/features/static_site_spec.rb b/spec/features/static_site_spec.rb
deleted file mode 100644
index a9f24fc..0000000
--- a/spec/features/static_site_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require 'spec_helper'
-
-RSpec.describe 'Serving static site', composition: 'static-site', type: :feature do
- before :all do
- system 'docker-machine ssh $DOCKER_MACHINE_NAME rm -rf /data/https-portal'
- end
-
- it 'should serve a custom index page' do
- docker_compose :up
- system "docker-machine scp index.html $DOCKER_MACHINE_NAME:/data/https-portal/vhosts/#{ENV['TEST_DOMAIN']}/"
-
- page = read_https_content
- expect(page).to include 'Welcome to my awesome static site powered by HTTPS-PORTAL!'
- end
-
- it 'should serve a custom page' do
- docker_compose :up
- system "docker-machine scp index.html $DOCKER_MACHINE_NAME:/data/https-portal/vhosts/#{ENV['TEST_DOMAIN']}/welcome"
-
- page = read_https_content('welcome')
- expect(page).to include 'Welcome to my awesome static site powered by HTTPS-PORTAL!'
- end
-end
diff --git a/spec/models/domain_spec.rb b/spec/models/domain_spec.rb
deleted file mode 100644
index 076e987..0000000
--- a/spec/models/domain_spec.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-require 'spec_helper'
-require_relative '../../fs_overlay/opt/certs_manager/certs_manager'
-
-RSpec.describe Domain do
- before do
- allow(NAConfig).to receive(:stage).and_return('local')
- allow(FileUtils).to receive(:mkdir_p)
- end
-
- it 'returns correct names, upstream. redirect_target_url, stage etc.' do
- keys = [:descriptor, :name, :env_format_name, :upstream_proto, :upstreams, :redirect_target_url, :stage, :basic_auth_username, :basic_auth_password, :access_restriction]
-
- domain_configs = [
- ['example.com', 'example.com', 'EXAMPLE_COM', nil, [], nil, 'local', nil, nil, nil],
- ['4example.com', '4example.com', '4EXAMPLE_COM', nil, [], nil, 'local', nil, nil, nil],
- [' example.com ', 'example.com', 'EXAMPLE_COM', nil, [], nil, 'local', nil, nil, nil],
- ['example.com #staging', 'example.com', 'EXAMPLE_COM', nil, [], nil, 'staging', nil, nil, nil],
- ['example.com -> http://target ', 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'target', :parameters => nil}], nil, 'local', nil, nil, nil],
- ["example.com \n-> http://target \n", 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'target', :parameters => nil}], nil, 'local', nil, nil, nil],
- ["example.com\n-> http://target ", 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'target', :parameters => nil}], nil, 'local', nil, nil, nil],
- ['example.com -> http://target:8000', 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'target:8000', :parameters => nil}], nil, 'local', nil, nil, nil],
- ['example.com -> target:8000', 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'target:8000', :parameters => nil}], nil, 'local', nil, nil, nil],
- ['example.com => http://target', 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'target', :parameters => nil}], 'http://target', 'local', nil, nil, nil],
- ['example.com => https://target', 'example.com', 'EXAMPLE_COM', 'https://', [{:address => 'target', :parameters => nil}], 'https://target', 'local', nil, nil, nil],
- ['example.com => target', 'example.com', 'EXAMPLE_COM', 'https://', [{:address => 'target', :parameters => nil}], 'https://target', 'local', nil, nil, nil],
- ['example.com=>http://target', 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'target', :parameters => nil}], 'http://target', 'local', nil, nil, nil],
- ['example.com -> http://target #staging', 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'target', :parameters => nil}], nil, 'staging', nil, nil, nil],
- ['example.com => http://target #staging', 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'target', :parameters => nil}], 'http://target', 'staging', nil, nil, nil],
- ['example.com->http://target #staging', 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'target', :parameters => nil}], nil, 'staging', nil, nil, nil],
- ['exam-ple.com->http://tar-get #staging', 'exam-ple.com', 'EXAM_PLE_COM', 'http://', [{:address => 'tar-get', :parameters => nil}], nil, 'staging', nil, nil, nil],
- ['example_.com->http://target #staging', 'example_.com', 'EXAMPLE__COM', 'http://', [{:address => 'target', :parameters => nil}], nil, 'staging', nil, nil, nil],
- ['example.com->http://tar_get_ #staging', 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'tar_get_', :parameters => nil}], nil, 'staging', nil, nil, nil],
- ['username:password@example.com', 'example.com', 'EXAMPLE_COM', nil, [], nil, 'local', 'username', 'password', nil],
- ['username:password@example.com -> http://target #staging', 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'target', :parameters => nil}], nil, 'staging', 'username', 'password', nil],
- ['[1.2.3.4/24]username:password@example.com -> http://target #staging', 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'target', :parameters => nil}], nil, 'staging', 'username', 'password', %w(1.2.3.4/24)],
- [' [ 1.2.3.4 4.3.2.1/24 ] username:password@example.com -> http://target #staging', 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'target', :parameters => nil}], nil, 'staging', 'username', 'password', %w(1.2.3.4 4.3.2.1/24)],
- ['example.com -> https://target1|target2:8000', 'example.com', 'EXAMPLE_COM', 'https://', [{:address => 'target1', :parameters => nil}, {:address => 'target2:8000', :parameters => nil}], nil, 'local', nil, nil, nil],
- ['example.com -> http://target1:8000|target2:8001[backup max_conns=100]', 'example.com', 'EXAMPLE_COM', 'http://', [{:address => 'target1:8000', :parameters => nil}, {:address => 'target2:8001', :parameters => 'backup max_conns=100'}], nil, 'local', nil, nil, nil],
- ]
-
- domain_configs.map { |config|
- Hash[keys.zip(config)]
- }.each do |config|
- domain = Domain.new(config[:descriptor])
-
- expect(domain.name).to eq(config[:name]), lambda { "Parsing failed on #{config[:descriptor].inspect} method :name, expected #{config[:name].inspect}, got #{domain.name.inspect}" }
- expect(domain.env_format_name).to eq(config[:env_format_name]), lambda { "Parsing failed on #{config[:descriptor].inspect} method :env_format_name, expected #{config[:env_format_name].inspect}, got #{domain.env_format_name.inspect}" }
- expect(domain.upstream_proto).to eq(config[:upstream_proto]), lambda { "Parsing failed on #{config[:descriptor].inspect} method :upstream_proto, expected #{config[:upstream_proto].inspect}, got #{domain.upstream_proto.inspect}" }
- expect(domain.upstreams).to eq(config[:upstreams]), lambda { "Parsing failed on #{config[:descriptor].inspect} method :upstreams, expected #{config[:upstreams].inspect}, got #{domain.upstreams.inspect}" }
- expect(domain.redirect_target_url).to eq(config[:redirect_target_url]), lambda { "Parsing failed on #{config[:descriptor].inspect} method :redirect_target_url, expected #{config[:redirect_target_url].inspect}, got #{domain.redirect_target_url.inspect}" }
- expect(domain.stage).to eq(config[:stage]), lambda { "Parsing failed on #{config[:descriptor].inspect} method :stage, expected #{config[:stage].inspect}, got #{domain.stage.inspect}" }
- expect(domain.basic_auth_username).to eq(config[:basic_auth_username]), lambda { "Parsing failed on #{config[:descriptor].inspect} method :basic_auth_username, expected #{config[:basic_auth_username].inspect}, got #{domain.basic_auth_username.inspect}" }
- expect(domain.basic_auth_password).to eq(config[:basic_auth_password]), lambda { "Parsing failed on #{config[:descriptor].inspect} method :basic_auth_password, expected #{config[:basic_auth_password].inspect}, got #{domain.basic_auth_password.inspect}" }
- expect(domain.access_restriction).to eq(config[:access_restriction]), lambda { "Parsing failed on #{config[:descriptor].inspect} method :access_restriction, expected #{config[:access_restriction].inspect}, got #{domain.access_restriction.inspect}" }
- end
- end
-end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
deleted file mode 100644
index 699fd3a..0000000
--- a/spec/spec_helper.rb
+++ /dev/null
@@ -1,132 +0,0 @@
-# This file was generated by the `rspec --init` command. Conventionally, all
-# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
-# The generated `.rspec` file contains `--require spec_helper` which will cause
-# this file to always be loaded, without a need to explicitly require it in any
-# files.
-#
-# Given that it is always loaded, you are encouraged to keep this file as
-# light-weight as possible. Requiring heavyweight dependencies from this file
-# will add to the boot time of your test suite on EVERY test run, even for an
-# individual file that may not need all of that loaded. Instead, consider making
-# a separate helper file that requires the additional dependencies and performs
-# the additional setup, and require it from the spec files that actually need
-# it.
-#
-# The `.rspec` file also contains a few flags that are not defaults but that
-# users commonly want.
-#
-# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
-
-require 'pathname'
-
-ENV['TEST_DOMAIN'] ||= 'test.nginx-acme.site'
-ENV['FORCE_RENEW'] ||= 'false'
-
-RootPath = Pathname(File.expand_path('../..', __FILE__))
-CompositionsPath = RootPath.join('spec/compositions')
-
-Dir[RootPath.join('spec/support/**/*.rb')].each { |f| require f }
-
-RSpec.configure do |config|
- # rspec-expectations config goes here. You can use an alternate
- # assertion/expectation library such as wrong or the stdlib/minitest
- # assertions if you prefer.
- config.expect_with :rspec do |expectations|
- # This option will default to `true` in RSpec 4. It makes the `description`
- # and `failure_message` of custom matchers include text for helper methods
- # defined using `chain`, e.g.:
- # be_bigger_than(2).and_smaller_than(4).description
- # # => 'be bigger than 2 and smaller than 4'
- # ...rather than:
- # # => 'be bigger than 2'
- expectations.include_chain_clauses_in_custom_matcher_descriptions = true
- end
-
- # rspec-mocks config goes here. You can use an alternate test double
- # library (such as bogus or mocha) by changing the `mock_with` option here.
- config.mock_with :rspec do |mocks|
- # Prevents you from mocking or stubbing a method that does not exist on
- # a real object. This is generally recommended, and will default to
- # `true` in RSpec 4.
- mocks.verify_partial_doubles = true
- end
-
- # The settings below are suggested to provide a good initial experience
- # with RSpec, but feel free to customize to your heart's content.
-
- # These two settings work together to allow you to limit a spec run
- # to individual examples or groups you care about by tagging them with
- # `:focus` metadata. When nothing is tagged with `:focus`, all examples
- # get run.
- config.filter_run :focus
- config.run_all_when_everything_filtered = true
-
- # Allows RSpec to persist some state between runs in order to support
- # the `--only-failures` and `--next-failure` CLI options. We recommend
- # you configure your source control system to ignore this file.
- config.example_status_persistence_file_path = 'spec/examples.txt'
-
- # Limits the available syntax to the non-monkey patched syntax that is
- # recommended. For more details, see:
- # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
- # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
- # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
- config.disable_monkey_patching!
-
- # This setting enables warnings. It's recommended, but in some cases may
- # be too noisy due to issues in dependencies.
- config.warnings = true
-
- # Many RSpec users commonly either run the entire suite or an individual
- # file, and it's useful to allow more verbose output when running an
- # individual spec file.
- if config.files_to_run.one?
- # Use the documentation formatter for detailed output,
- # unless a formatter has already been configured
- # (e.g. via a command-line flag).
- config.default_formatter = 'doc'
- end
-
- # Print the 10 slowest examples and example groups at the
- # end of the spec run, to help surface which specs are running
- # particularly slow.
- config.profile_examples = 10
-
- # Intentional configure RSpec to run ours specs in defined order.
- # This is important since some example groups reuse existing containers
- # created by previous example group.
- config.order = :defined
-
- config.include PortalHelpers
-
- config.before :suite do
- unless ENV['SKIP_BUILD']
- puts "TEST_DOMAIN: #{ENV['TEST_DOMAIN']}"
-
- # Ensure the build process have existing cached layers to reuse, by
- # explicitly rebuild the docker image for spec.
- puts 'Rebuilding docker image for spec...'
- Dir.chdir CompositionsPath.children.first do
- PortalHelpers.docker_compose :build
- end
- end
- end
-
- config.before :all, type: :feature do |example|
- unless example.class.metadata[:reuse_container]
- Dir.chdir CompositionsPath.join(example.class.metadata[:composition]) do
- purge_existing_containers
- end
- end
- end
-
- config.around :example, type: :feature do |example|
- Dir.chdir CompositionsPath.join(example.metadata[:composition]) do
- example.run
- end
- end
-
- config.after :example, type: :feature do
- docker_compose :stop
- end
-end
diff --git a/spec/support/portal_helpers.rb b/spec/support/portal_helpers.rb
deleted file mode 100644
index eadb77a..0000000
--- a/spec/support/portal_helpers.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'open-uri'
-require 'openssl'
-
-module PortalHelpers
- extend self
-
- def docker_compose(command, env: {})
- case command.to_sym
- when :up
- command = 'up -d'
- end
-
- system(env, "docker-compose --project-name portalspec #{command}")
- end
-
- def purge_existing_containers
- system 'docker rm --force --volumes $(docker-compose --project-name portalspec ps -q) &> /dev/null'
- end
-
- def read_https_content(path = nil)
- tries = 60
- open("https://#{ENV['TEST_DOMAIN']}/#{path}", ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE, &:read)
- rescue Errno::ECONNREFUSED
- if (tries -= 1) > 0
- sleep 10
- retry
- end
- end
-end
diff --git a/api/src/app.ts b/src/api/app.ts
similarity index 65%
rename from api/src/app.ts
rename to src/api/app.ts
index d64728d..f2d1126 100644
--- a/api/src/app.ts
+++ b/src/api/app.ts
@@ -2,10 +2,9 @@ import express, { ErrorRequestHandler, Request, Response } from "express";
import morgan from "morgan";
import { HttpError, BadRequestError, asyncHandler } from "./utils/asyncHandler";
import { entriesDb } from "./db";
-import { reconfigureNGINX } from "./nginx";
-import { sanitizeFrom, sanitizeTo } from "./utils/sanitize";
-import { config } from "./config";
-import { getDAppNodeDomain } from "./utils/getDAppNodeDomain";
+import { reconfigureNGINX } from "./utils/nginx";
+import { sanitizeExternal, sanitizeFrom, sanitizeTo } from "./utils/sanitize";
+import { config } from "../config";
const app = express();
@@ -13,38 +12,44 @@ app.use(morgan("tiny"));
app.get(
"/add",
- asyncHandler(async req => {
+ asyncHandler(async (req) => {
const from = await sanitizeFrom(req.query.from as string);
- const to = sanitizeTo(req.query.to as string);
+ const to = await sanitizeTo(req.query.to as string);
+ const external = sanitizeExternal(req.query.external as string); //true if not set, we should swap this, but it left like this for backwards compatibility
const entries = entriesDb.read();
- if (entries.some(entry => entry.from === from)) {
+ if (entries.some((entry) => entry.from === from)) {
throw new BadRequestError("External endpoint already exists");
}
// NGINX will crash in loop if a domain is longer than `server_names_hash_bucket_size`
// Force that from has only ASCII characters to make sure the char length = bytes lenght
// fulldomain = from + "." + dappnodeDomain
- const dappnodeDomain = await getDAppNodeDomain();
+ const dappnodeDomain = process.env._DAPPNODE_GLOBAL_DOMAIN;
const maxLen = config.maximum_domain_length - dappnodeDomain.length - 1;
if (from.length > maxLen) {
throw new BadRequestError(`'from' ${from} exceeds max length of ${from}`);
}
- entries.push({ from, to });
+ entries.push({ from, to, external });
entriesDb.write(entries);
- await reconfigureNGINX();
+ const reconfigured = await reconfigureNGINX();
+ if (!reconfigured) {
+ entriesDb.write(entries.filter((e) => e.from !== from)); // rollback
+ await reconfigureNGINX();
+ throw new HttpError("Unable to add mapping", 500);
+ }
})
);
app.get(
"/remove",
- asyncHandler(async req => {
+ asyncHandler(async (req) => {
const from = await sanitizeFrom(req.query.from as string);
const entries = entriesDb.read();
- entriesDb.write(entries.filter(e => e.from !== from));
+ entriesDb.write(entries.filter((e) => e.from !== from));
await reconfigureNGINX();
})
diff --git a/src/api/db.ts b/src/api/db.ts
new file mode 100644
index 0000000..1c961d4
--- /dev/null
+++ b/src/api/db.ts
@@ -0,0 +1,8 @@
+import { DomainMapping } from "../types";
+import { config } from "../config";
+import { JsonFileDb } from "../utils/fileDb";
+
+export const entriesDb = new JsonFileDb(
+ config.db_filepath,
+ []
+);
diff --git a/src/api/index.ts b/src/api/index.ts
new file mode 100644
index 0000000..1ed9db0
--- /dev/null
+++ b/src/api/index.ts
@@ -0,0 +1,25 @@
+import { app } from "./app";
+import { AddressInfo } from "net";
+import { reconfigureNGINX } from "./utils/nginx";
+import { entriesDb } from "./db";
+
+function dbMigration() {
+ const mappings = entriesDb.read();
+ entriesDb.write(
+ mappings.map((m) => {
+ return {
+ ...m,
+ external: m.external ?? true,
+ };
+ })
+ );
+}
+
+export default async function startAPI() {
+ dbMigration();
+ await reconfigureNGINX();
+ const server = app.listen(5000, "0.0.0.0", () => {
+ const { port, address } = server.address() as AddressInfo;
+ console.log("Server listening on:", "http://" + address + ":" + port);
+ });
+}
diff --git a/api/src/utils/asyncHandler.ts b/src/api/utils/asyncHandler.ts
similarity index 85%
rename from api/src/utils/asyncHandler.ts
rename to src/api/utils/asyncHandler.ts
index 3695fce..77c30b2 100644
--- a/api/src/utils/asyncHandler.ts
+++ b/src/api/utils/asyncHandler.ts
@@ -19,7 +19,7 @@ export function asyncHandler(
): RequestHandler {
return (req, res, next) => {
handler(req)
- .then(result => res.json(result || {}))
- .catch(e => next(e));
+ .then((result) => res.json(result || {}))
+ .catch((e) => next(e));
};
}
diff --git a/src/api/utils/nginx.ts b/src/api/utils/nginx.ts
new file mode 100644
index 0000000..0f1859b
--- /dev/null
+++ b/src/api/utils/nginx.ts
@@ -0,0 +1,22 @@
+import { entriesDb } from "../db";
+import shell from "../../utils/shell";
+import { updateServerConfigs } from "../../nginx";
+
+const maxRetries = 3;
+
+export async function reconfigureNGINX(
+ force = false
+): Promise {
+ await updateServerConfigs(entriesDb.read(), force);
+ for (let i = 0; i < maxRetries; i++) {
+ try {
+ await shell("nginx -s reload");
+ console.log("Reconfigured NGINX");
+ return true;
+ } catch (e) {
+ console.log("Failed to reconfigure NGINX");
+ }
+ await new Promise((r) => setTimeout(r, 3000));
+ }
+ return false;
+}
diff --git a/api/src/utils/sanitize.ts b/src/api/utils/sanitize.ts
similarity index 50%
rename from api/src/utils/sanitize.ts
rename to src/api/utils/sanitize.ts
index c063cd1..4398b3b 100644
--- a/api/src/utils/sanitize.ts
+++ b/src/api/utils/sanitize.ts
@@ -1,5 +1,6 @@
+import { config } from "../../config";
import { BadRequestError } from "./asyncHandler";
-
+import axios from "axios"
/**
* from param must be a subdomain
*/
@@ -18,16 +19,38 @@ export async function sanitizeFrom(from: string): Promise {
/**
* to param must be a host with maybe a port number
*/
-export function sanitizeTo(to: string): string {
+export async function sanitizeTo(to: string): Promise {
try {
if (!to) throw Error("not defined");
} catch (e) {
throw new BadRequestError(`Bad param 'to': ${e.message}`);
}
+ // probe target with axios. We are actually not interested whether it returns something or not, just that target package is available and that port is correct
+
+ try {
+ await axios.get(to, {timeout: 100});
+ } catch (e) {
+ if(e.message === "ECONNREFUSED") {
+ throw new BadRequestError(`Unable to add mapping! Make sure that you are pointing to the open port`);
+ }
+ if(e.code === "ENOTFOUND") {
+ throw new BadRequestError(`Unable to add mapping! Make sure that target package is up and running`);
+ }
+ }
return to;
}
+export function sanitizeExternal(external: string): boolean {
+ if (!external) {
+ return config.mappingExternalByDefault;
+ }
+ if (parseInt(external) > 0 || external.toLowerCase() === "true") {
+ return true;
+ }
+ return false;
+}
+
function assertIsSubdomain(subdomain: string): void {
if (subdomain.includes(".")) {
throw Error("Must not be FQDN nor contain any subdomains");
diff --git a/src/certificates/apiSign.ts b/src/certificates/apiSign.ts
new file mode 100644
index 0000000..2c64a2a
--- /dev/null
+++ b/src/certificates/apiSign.ts
@@ -0,0 +1,52 @@
+import { config } from "../config";
+import fs from "fs";
+import axios from "axios";
+import shell from "../utils/shell";
+
+async function getEthSignature(timestamp: number): Promise<[string, string]> {
+ try {
+ const response = await axios.post(
+ process.env.DAPPMANAGER_SIGN,
+ timestamp.toString(),
+ {
+ headers: { "Content-Type": "text/plain" },
+ }
+ );
+ return [response.data.signature, response.data.address];
+ } catch (e) {
+ console.warn(e);
+ }
+}
+
+async function executeAPISign(
+ signature: string,
+ address: string,
+ timestamp: number,
+ force: boolean
+) {
+ const certapi_url = process.env["CERTAPI_URL"];
+ const url = `http://${certapi_url}/?signature=${signature}&address=${address}×tamp=${timestamp}&force=${
+ force ? 1 : 0
+ }`;
+ const file = fs.createReadStream(config.csrPath);
+ const response = await axios.postForm(url, {
+ csr: file,
+ });
+ await fs.writeFileSync(config.certPath, response.data);
+}
+
+export default async function apiSign() {
+ const timestamp = Math.floor(Date.now() / 1000);
+ const [signature, address] = await getEthSignature(timestamp);
+ const force = process.env["FORCE"] === "true" || false;
+
+ await executeAPISign(signature, address, timestamp, force);
+ const cert_pubkey = shell(
+ `openssl x509 -pubkey -noout -in ${config.certPath}`
+ );
+ const priv_pubkey = shell(`openssl rsa -in ${config.keyPath} -pubout`);
+ if (cert_pubkey !== priv_pubkey) {
+ console.warn("Keys do not match, trying forcing certification service");
+ await executeAPISign(signature, address, timestamp, true);
+ }
+}
diff --git a/src/certificates/index.ts b/src/certificates/index.ts
new file mode 100644
index 0000000..702b8bc
--- /dev/null
+++ b/src/certificates/index.ts
@@ -0,0 +1,46 @@
+import { config } from "../config";
+import apiSign from "./apiSign";
+import {
+ createCSR,
+ generateDHParam,
+ generateDomainKey,
+ generateDummyCert,
+ shouldRenew,
+} from "./openssl";
+
+export async function ensureValidCert(createIfNotExists = false) {
+ if (await shouldRenew(createIfNotExists)) {
+ try {
+ await signCert();
+ } catch (e) {
+ console.log(e);
+ }
+ } else {
+ console.log("Certificate valid or not necessary, skip signing");
+ }
+}
+
+export default async function initCertificateProvider() {
+ console.log("Certificates provider initializing");
+ try {
+ console.log("- Creating Dummy certificate");
+ await generateDummyCert();
+ console.log("- Generating domain key");
+ await generateDomainKey();
+ console.log("- Generating DH parameters");
+ await generateDHParam();
+ console.log("- Creating Certificate signing request");
+ await createCSR();
+ console.log("Certificates provider initialized");
+ } catch (e) {
+ console.log(e);
+ }
+
+ console.log("Setting cronjob for cert updates");
+ setInterval(ensureValidCert, config.certCheckInterval);
+}
+
+async function signCert() {
+ console.log("- Calling DAppNode API");
+ await apiSign();
+}
diff --git a/src/certificates/openssl.ts b/src/certificates/openssl.ts
new file mode 100644
index 0000000..2df4ac6
--- /dev/null
+++ b/src/certificates/openssl.ts
@@ -0,0 +1,75 @@
+import path from "path";
+import { config } from "../config";
+import shell from "../utils/shell";
+import fs from "fs";
+
+const keyLength = 2048;
+
+async function isDHParamValid() {
+ try {
+ await shell(`openssl dhparam -check < ${path}`);
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+async function certExpiringDate(pem: string) {
+ const date = (
+ await shell(`openssl x509 -enddate -noout -in ${pem}`)
+ ).substring(9);
+ return new Date(date);
+}
+
+async function generateDHParam() {
+ if (fs.existsSync(config.dhparamPath) && isDHParamValid()) {
+ console.log("Valid, skipping");
+ return;
+ }
+ await shell(`openssl dhparam -out ${config.dhparamPath} ${keyLength}`);
+}
+
+async function generateDomainKey() {
+ if (fs.existsSync(config.keyPath)) {
+ console.log("Exists, skipping");
+ return;
+ }
+ await shell(`openssl genrsa ${keyLength} > ${config.keyPath}`);
+}
+
+async function createCSR() {
+ if (fs.existsSync(config.csrPath)) {
+ console.log("Exists, skipping");
+ return;
+ }
+ const publicDomain = process.env._DAPPNODE_GLOBAL_DOMAIN;
+ await shell(
+ `openssl req -new -sha256 -key ${config.keyPath} -subj '/CN=${publicDomain}' -addext 'subjectAltName = DNS:*.${publicDomain}' > ${config.csrPath}`
+ );
+}
+
+async function shouldRenew(createIfNotExists: boolean) {
+ if (!fs.existsSync(config.certPath)) return createIfNotExists;
+ return (
+ (await certExpiringDate(config.certPath)).getTime() - Date.now() <
+ config.certMarginDays * 24 * 3600 * 1000
+ );
+}
+
+async function generateDummyCert() {
+ if (fs.existsSync(config.dummyCert)) {
+ console.log("Exists, skipping");
+ return;
+ }
+ await shell(
+ `mkdir -p ${config.dummyBaseDir} && openssl req -x509 -newkey rsa:${keyLength} -nodes -out ${config.dummyCert} -keyout ${config.dummyKey} -days 36500 -batch -subj "/CN=default-server.example.com"`
+ );
+}
+
+export {
+ createCSR,
+ generateDomainKey,
+ generateDHParam,
+ generateDummyCert,
+ shouldRenew,
+};
diff --git a/src/config.ts b/src/config.ts
new file mode 100644
index 0000000..625382d
--- /dev/null
+++ b/src/config.ts
@@ -0,0 +1,30 @@
+import path from "path";
+
+const domainsDir = process.env.DOMAINS_DIR || "./domains_dir";
+const certBaseDir =
+ process.env.CERT_BASE_DIR || "/etc/ssl/dappnode/wildcard_certs";
+const nginxDir = process.env.NGINX_PATH || "/etc/nginx";
+const dummyBaseDir = path.join(certBaseDir, "default");
+
+export const config = {
+ db_filepath: path.join(domainsDir, "domains.json"),
+ maximum_domain_length: parseInt(
+ process.env.SERVER_NAMES_HASH_BUCKET_SIZE || "128"
+ ),
+ certMarginDays: parseInt(process.env.MARGIN_DAYS) || 30,
+ nginxDir: nginxDir,
+ nginxConfigPath: path.join(nginxDir, "nginx.conf"),
+ serverConfigDir: path.join(nginxDir, "conf.d"),
+ certBaseDir: certBaseDir,
+ dhparamPath: path.join(certBaseDir, "dhparam.pem"),
+ csrPath: path.join(certBaseDir, "domain.csr"),
+ keyPath: path.join(certBaseDir, "domain.key"),
+ certPath: path.join(certBaseDir, "signed.crt"),
+ dummyBaseDir: dummyBaseDir,
+ dummyCert: path.join(dummyBaseDir, "default.pem"),
+ dummyKey: path.join(dummyBaseDir, "default.key"),
+ certCheckInterval:
+ parseInt(process.env.CERT_CHECK_INTERVAL) || 24 * 3600 * 1000,
+ mappingExternalByDefault:
+ process.env.MAPPING_EXTERNAL_BY_DEFAULT === "true" || false,
+};
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..d31e01d
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,27 @@
+import startAPI from "./api";
+import initCertificateProvider from "./certificates";
+import prepareNGINX from "./nginx";
+import shell from "./utils/shell";
+import fs from "fs";
+import { config } from "./config";
+async function main() {
+ if (!process.env._DAPPNODE_GLOBAL_DOMAIN) {
+ console.error("DAppManager did not inject enviornment. Quitting.");
+ process.exit(1);
+ }
+
+ if (!fs.existsSync(config.serverConfigDir)) {
+ fs.mkdirSync(config.serverConfigDir);
+ }
+
+ if (!fs.existsSync(config.certBaseDir)) {
+ fs.mkdirSync(config.certBaseDir);
+ }
+
+ await prepareNGINX();
+ await initCertificateProvider();
+ await shell("nginx -q");
+ startAPI();
+}
+
+main();
diff --git a/src/nginx/index.ts b/src/nginx/index.ts
new file mode 100644
index 0000000..dd069cd
--- /dev/null
+++ b/src/nginx/index.ts
@@ -0,0 +1,56 @@
+import { DomainMapping } from "../types";
+import fs from "fs";
+import path from "path";
+import { config } from "../config";
+import { ensureValidCert } from "../certificates";
+import generateNginxConf, {
+ generateServerConfig,
+ generateStaticServers,
+} from "./templater";
+
+async function deleteOldConfig(mappings: DomainMapping[]): Promise {
+ const dirContent = await fs.readdirSync(config.serverConfigDir);
+ const allowedFiles = mappings.map((m) => `${m.from}.ssl.conf`);
+ const filesToDelete = dirContent.filter(
+ (x) => x.endsWith(".ssl.conf") && !allowedFiles.includes(x)
+ );
+ await Promise.all(
+ filesToDelete.map((filename) =>
+ fs.rmSync(path.join(config.serverConfigDir, filename))
+ )
+ );
+}
+
+export async function updateServerConfigs(
+ mappings: DomainMapping[],
+ force: boolean
+) {
+ console.log(" *** Updating mappings *** ");
+ await deleteOldConfig(mappings);
+
+ const dirContent = await fs.readdirSync(config.serverConfigDir);
+ let checkedCert = false;
+ for (const mapping of mappings) {
+ const filename = `${mapping.from}.ssl.conf`;
+ if (dirContent.find((x) => x === filename) !== undefined && !force)
+ continue;
+ console.log(` *-> Adding ${mapping.from} mapping <-*`);
+ if (!checkedCert) {
+ await ensureValidCert(true);
+ checkedCert = true;
+ }
+ await fs.writeFileSync(
+ path.join(config.serverConfigDir, filename),
+ await generateServerConfig(mapping)
+ );
+ }
+}
+
+export default async function prepareNGINX() {
+ console.log("Generating NGINX configuration");
+ console.log("- Generating nginx.conf");
+ await generateNginxConf();
+
+ console.log("- Generating static pages");
+ await generateStaticServers();
+}
diff --git a/src/nginx/templater.ts b/src/nginx/templater.ts
new file mode 100644
index 0000000..25db9c3
--- /dev/null
+++ b/src/nginx/templater.ts
@@ -0,0 +1,43 @@
+import ejs from "ejs";
+import fs from "fs";
+import path from "path";
+import { config } from "../config";
+import { DomainMapping } from "../types";
+
+export async function generateServerConfig(
+ mapping: DomainMapping
+): Promise {
+ const data = {
+ certPath: config.certPath,
+ keyPath: config.keyPath,
+ dhparamPath: config.dhparamPath,
+ domain: `${mapping.from}.${process.env._DAPPNODE_GLOBAL_DOMAIN}`,
+ target: mapping.to,
+ external: mapping.external,
+ };
+ return await ejs.renderFile("./templates/default.ssl.conf.ejs", {
+ data: data,
+ });
+}
+
+export default async function generateNginxConf() {
+ const data = {
+ dummyCert: config.dummyCert,
+ dummyKey: config.dummyKey,
+ };
+ await fs.writeFileSync(
+ config.nginxConfigPath,
+ await ejs.renderFile("./templates/nginx.conf.ejs", { data: data })
+ );
+}
+
+export async function generateStaticServers() {
+ const staticDir = "./templates/static";
+ const staticSites = await fs.readdirSync(staticDir);
+ for (const site of staticSites) {
+ await fs.writeFileSync(
+ path.join(config.serverConfigDir, `${site.slice(0, -4)}.conf`),
+ await ejs.renderFile(path.join(staticDir, site), {})
+ );
+ }
+}
diff --git a/api/src/types.ts b/src/types.ts
similarity index 63%
rename from api/src/types.ts
rename to src/types.ts
index 6da8d43..08190d2 100644
--- a/api/src/types.ts
+++ b/src/types.ts
@@ -7,4 +7,8 @@ export interface DomainMapping {
* Target host, may include port
*/
to: string;
+ /**
+ * Indicates whether mapping should made public
+ */
+ external?: boolean;
}
diff --git a/api/src/utils/fileDb.ts b/src/utils/fileDb.ts
similarity index 100%
rename from api/src/utils/fileDb.ts
rename to src/utils/fileDb.ts
diff --git a/api/src/utils/shell.ts b/src/utils/shell.ts
similarity index 100%
rename from api/src/utils/shell.ts
rename to src/utils/shell.ts
diff --git a/templates/default.ssl.conf.ejs b/templates/default.ssl.conf.ejs
new file mode 100644
index 0000000..666de27
--- /dev/null
+++ b/templates/default.ssl.conf.ejs
@@ -0,0 +1,63 @@
+server {
+ listen 80;
+ <% if (process.env.LISTEN_IPV6 && process.envLISTEN_IPV6.toLowerCase() === 'true') { %>
+ listen [::]:80;
+ <% } %>
+ server_name <%- data.domain %>;
+
+ location / {
+ return 301 https://$server_name$request_uri;
+ }
+}
+
+server {
+ listen 443 ssl http2;
+ <% if (process.env.LISTEN_IPV6 && process.env.LISTEN_IPV6.toLowerCase() === 'true') { %>
+ listen [::]:443 ssl http2;
+ <% } %>
+ server_name <%- data.domain %>;
+
+ ssl_certificate <%- data.certPath %>;
+ ssl_certificate_key <%- data.keyPath %>;
+
+ ssl_session_cache shared:SSL:50m;
+ ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA;
+ ssl_prefer_server_ciphers on;
+
+ ssl_dhparam <%- data.dhparamPath %>;
+
+ <% if (process.env.HSTS_MAX_AGE) { %>
+ # Send HSTS header
+ add_header Strict-Transport-Security "max-age=<%- process.env.HSTS_MAX_AGE %>" always;
+ <% } %>
+
+ <% if(!data.external && !process.env.PRIVATE_SUBNET) { %>
+ allow 10.0.0.0/8;
+ allow 172.16.0.0/12;
+ allow 192.168.0.0/16;
+ allow fc00::/7;
+ allow fe80::/10;
+ deny all;
+ <% } %>
+
+ <% if(!data.external && process.env.PRIVATE_SUBNET) { %>
+ allow <%- process.env.PRIVATE_SUBNET %>;
+ deny all;
+ <% } %>
+
+ location / {
+
+ proxy_pass http://<%-data.target %>;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ <% if (process.env.WEBSOCKET && process.env.WEBSOCKET.toLowerCase() === 'true') { %>
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ proxy_read_timeout 2h;
+ <% } %>
+ }
+}
\ No newline at end of file
diff --git a/templates/nginx.conf.ejs b/templates/nginx.conf.ejs
new file mode 100644
index 0000000..848db61
--- /dev/null
+++ b/templates/nginx.conf.ejs
@@ -0,0 +1,81 @@
+# This file will be compiled into /etc/nginx/nginx.conf
+
+user nginx;
+worker_processes <%- process.env.WORKER_PROCESSES || 1 %>;
+
+pid /var/run/nginx.pid;
+
+events {
+ worker_connections <%- process.env.WORKER_CONNECTIONS || 1024 %>;
+}
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+ ssl_protocols TLSv1.2 TLSv1.3;
+
+ <% if (process.env.ACCESS_LOG_INCLUDE_HOST == 'on'){ %>
+ log_format main '$host:$server_port $remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+ <% } else {%>
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+ <% } %>
+ <% if (process.env.WEBSOCKET && process.env.WEBSOCKET.toLowerCase() === 'true') { %>
+ map $http_upgrade $connection_upgrade {
+ default upgrade;
+ '' close;
+ }
+ <% } %>
+
+ sendfile on;
+ keepalive_timeout <%- process.env.KEEPALIVE_TIMEOUT || 65 %>;
+
+ <% if(process.env.GZIP !== 'off') { %>
+ gzip on;
+ gzip_disable "msie6";
+ gzip_vary on;
+ gzip_proxied any;
+ gzip_comp_level 6;
+ gzip_buffers 16 8k;
+ gzip_http_version 1.1;
+ gzip_types application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font application/x-font-opentype application/x-font-otf application/x-font-truetype application/x-font-ttf application/x-javascript application/xhtml+xml application/xml font/opentype font/otf font/ttf image/svg+xml image/x-icon text/css text/javascript text/plain text/xml;
+ <% } %>
+
+ server_tokens <%- process.env.SERVER_TOKENS || 'off' %>;
+ server_names_hash_max_size <%- process.env.SERVER_NAMES_HASH_MAX_SIZE || 4096 %>;
+ server_names_hash_bucket_size <%- process.env.SERVER_NAMES_HASH_BUCKET_SIZE || 128 %>;
+
+ <% if (process.env.CLIENT_MAX_BODY_SIZE) { %>
+ client_max_body_size <%- process.env.CLIENT_MAX_BODY_SIZE %>;
+ <% } %>
+ <% if (process.env.PROXY_BUFFERS){ %>
+ proxy_buffers <%- process.env.PROXY_BUFFERS %>;
+ <%} if (process.env.PROXY_BUFFER_SIZE) {%>
+ proxy_buffer_size <%- process.env.PROXY_BUFFER_SIZE %>;
+ <%} if (process.env.RESOLVER) { %>
+ resolver <%- process.env.RESOLVER %>;
+ <% } if (process.env.PROXY_CONNECT_TIMEOUT) { %>
+ proxy_connect_timeout <%- process.env.PROXY_CONNECT_TIMEOUT %>;
+ <% } if (process.env.PROXY_SEND_TIMEOUT) { %>
+ proxy_send_timeout <%- process.env.PROXY_SEND_TIMEOUT %>;
+ <% } if (process.env.PROXY_READ_TIMEOUT) { %>
+ proxy_read_timeout <%- process.env.PROXY_READ_TIMEOUT %>;
+ <% } %>
+
+ include /etc/nginx/conf.d/*.conf;
+
+ # Prevent Nginx from leaking other server configurations on the same machine
+ server {
+ listen 80 default_server;
+ listen 443 ssl default_server;
+
+ ssl_certificate <%- data.dummyCert %>;
+ ssl_certificate_key <%- data.dummyKey %>;
+ server_name _;
+ return 444;
+ }
+}
+
diff --git a/templates/static/localproxy.ejs b/templates/static/localproxy.ejs
new file mode 100644
index 0000000..7f1df6f
--- /dev/null
+++ b/templates/static/localproxy.ejs
@@ -0,0 +1,23 @@
+<% if(process.env.LOCAL_PROXYING === 'true') { %>
+server {
+ server_name <%-process.env.LOCAL_PROXY_DOMAIN%>.local;
+ listen 80;
+ listen [::]:80;
+ resolver <%- process.env.GLOBAL_RESOLVER %>;
+<% if (process.env.PRIVATE_SUBNET) { %>
+ allow <%- process.env.PRIVATE_SUBNET %>;
+<% } else { %>
+ allow 10.0.0.0/8;
+ allow 172.16.0.0/12;
+ allow 192.168.0.0/16;
+ allow fc00::/7;
+ allow fe80::/10;
+<% } %>
+ deny all;
+ location / {
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_pass http://my.dappnode;
+ }
+}
+<% } %>
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..9657e4b
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ "lib": ["es2022"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ "module": "commonjs", /* Specify what module code is generated. */
+ "rootDir": "src", /* Specify the root folder within your source files. */
+ "outDir": "./build", /* Specify an output folder for all emitted files. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+ /* Type Checking */
+ "strict": false, /* Enable all strict type-checking options. */
+ "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ }
+}
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 0000000..7af1f81
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,1762 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@cspotcode/source-map-support@^0.8.0":
+ version "0.8.1"
+ resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
+ integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
+ dependencies:
+ "@jridgewell/trace-mapping" "0.3.9"
+
+"@eslint/eslintrc@^2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.0.tgz#943309d8697c52fc82c076e90c1c74fbbe69dbff"
+ integrity sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A==
+ dependencies:
+ ajv "^6.12.4"
+ debug "^4.3.2"
+ espree "^9.4.0"
+ globals "^13.19.0"
+ ignore "^5.2.0"
+ import-fresh "^3.2.1"
+ js-yaml "^4.1.0"
+ minimatch "^3.1.2"
+ strip-json-comments "^3.1.1"
+
+"@eslint/js@8.35.0":
+ version "8.35.0"
+ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.35.0.tgz#b7569632b0b788a0ca0e438235154e45d42813a7"
+ integrity sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==
+
+"@humanwhocodes/config-array@^0.11.8":
+ version "0.11.8"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9"
+ integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==
+ dependencies:
+ "@humanwhocodes/object-schema" "^1.2.1"
+ debug "^4.1.1"
+ minimatch "^3.0.5"
+
+"@humanwhocodes/module-importer@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
+ integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
+
+"@humanwhocodes/object-schema@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
+ integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+
+"@jridgewell/resolve-uri@^3.0.3":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+ integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+
+"@jridgewell/sourcemap-codec@^1.4.10":
+ version "1.4.14"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
+ integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
+
+"@jridgewell/trace-mapping@0.3.9":
+ version "0.3.9"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
+ integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.0.3"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
+"@nodelib/fs.scandir@2.1.5":
+ version "2.1.5"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+ integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
+ dependencies:
+ "@nodelib/fs.stat" "2.0.5"
+ run-parallel "^1.1.9"
+
+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+ integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
+
+"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8":
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+ integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
+ dependencies:
+ "@nodelib/fs.scandir" "2.1.5"
+ fastq "^1.6.0"
+
+"@tsconfig/node10@^1.0.7":
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
+ integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==
+
+"@tsconfig/node12@^1.0.7":
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
+ integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
+
+"@tsconfig/node14@^1.0.0":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
+ integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
+
+"@tsconfig/node16@^1.0.2":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
+ integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
+
+"@types/body-parser@*":
+ version "1.19.2"
+ resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0"
+ integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==
+ dependencies:
+ "@types/connect" "*"
+ "@types/node" "*"
+
+"@types/connect@*":
+ version "3.4.35"
+ resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
+ integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==
+ dependencies:
+ "@types/node" "*"
+
+"@types/ejs@^3.1.1":
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.1.1.tgz#29c539826376a65e7f7d672d51301f37ed718f6d"
+ integrity sha512-RQul5wEfY7BjWm0sYY86cmUN/pcXWGyVxWX93DFFJvcrxax5zKlieLwA3T77xJGwNcZW0YW6CYG70p1m8xPFmA==
+
+"@types/express-serve-static-core@^4.17.18":
+ version "4.17.31"
+ resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz#a1139efeab4e7323834bb0226e62ac019f474b2f"
+ integrity sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==
+ dependencies:
+ "@types/node" "*"
+ "@types/qs" "*"
+ "@types/range-parser" "*"
+
+"@types/express@^4.17.14":
+ version "4.17.14"
+ resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.14.tgz#143ea0557249bc1b3b54f15db4c81c3d4eb3569c"
+ integrity sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==
+ dependencies:
+ "@types/body-parser" "*"
+ "@types/express-serve-static-core" "^4.17.18"
+ "@types/qs" "*"
+ "@types/serve-static" "*"
+
+"@types/json-schema@^7.0.9":
+ version "7.0.11"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
+ integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
+
+"@types/mime@*":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10"
+ integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==
+
+"@types/morgan@^1.9.3":
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.9.3.tgz#ae04180dff02c437312bc0cfb1e2960086b2f540"
+ integrity sha512-BiLcfVqGBZCyNCnCH3F4o2GmDLrpy0HeBVnNlyZG4fo88ZiE9SoiBe3C+2ezuwbjlEyT+PDZ17//TAlRxAn75Q==
+ dependencies:
+ "@types/node" "*"
+
+"@types/node@*", "@types/node@^18.11.9":
+ version "18.11.9"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4"
+ integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==
+
+"@types/qs@*":
+ version "6.9.7"
+ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
+ integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
+
+"@types/range-parser@*":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
+ integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
+
+"@types/semver@^7.3.12":
+ version "7.3.13"
+ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91"
+ integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==
+
+"@types/serve-static@*":
+ version "1.15.0"
+ resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155"
+ integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==
+ dependencies:
+ "@types/mime" "*"
+ "@types/node" "*"
+
+"@typescript-eslint/eslint-plugin@^5.54.0":
+ version "5.54.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.0.tgz#2c821ad81b2c786d142279a8292090f77d1881f4"
+ integrity sha512-+hSN9BdSr629RF02d7mMtXhAJvDTyCbprNYJKrXETlul/Aml6YZwd90XioVbjejQeHbb3R8Dg0CkRgoJDxo8aw==
+ dependencies:
+ "@typescript-eslint/scope-manager" "5.54.0"
+ "@typescript-eslint/type-utils" "5.54.0"
+ "@typescript-eslint/utils" "5.54.0"
+ debug "^4.3.4"
+ grapheme-splitter "^1.0.4"
+ ignore "^5.2.0"
+ natural-compare-lite "^1.4.0"
+ regexpp "^3.2.0"
+ semver "^7.3.7"
+ tsutils "^3.21.0"
+
+"@typescript-eslint/parser@^5.54.0":
+ version "5.54.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.54.0.tgz#def186eb1b1dbd0439df0dacc44fb6d8d5c417fe"
+ integrity sha512-aAVL3Mu2qTi+h/r04WI/5PfNWvO6pdhpeMRWk9R7rEV4mwJNzoWf5CCU5vDKBsPIFQFjEq1xg7XBI2rjiMXQbQ==
+ dependencies:
+ "@typescript-eslint/scope-manager" "5.54.0"
+ "@typescript-eslint/types" "5.54.0"
+ "@typescript-eslint/typescript-estree" "5.54.0"
+ debug "^4.3.4"
+
+"@typescript-eslint/scope-manager@5.54.0":
+ version "5.54.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.54.0.tgz#74b28ac9a3fc8166f04e806c957adb8c1fd00536"
+ integrity sha512-VTPYNZ7vaWtYna9M4oD42zENOBrb+ZYyCNdFs949GcN8Miwn37b8b7eMj+EZaq7VK9fx0Jd+JhmkhjFhvnovhg==
+ dependencies:
+ "@typescript-eslint/types" "5.54.0"
+ "@typescript-eslint/visitor-keys" "5.54.0"
+
+"@typescript-eslint/type-utils@5.54.0":
+ version "5.54.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.54.0.tgz#390717216eb61393a0cad2995da154b613ba7b26"
+ integrity sha512-WI+WMJ8+oS+LyflqsD4nlXMsVdzTMYTxl16myXPaCXnSgc7LWwMsjxQFZCK/rVmTZ3FN71Ct78ehO9bRC7erYQ==
+ dependencies:
+ "@typescript-eslint/typescript-estree" "5.54.0"
+ "@typescript-eslint/utils" "5.54.0"
+ debug "^4.3.4"
+ tsutils "^3.21.0"
+
+"@typescript-eslint/types@5.54.0":
+ version "5.54.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.54.0.tgz#7d519df01f50739254d89378e0dcac504cab2740"
+ integrity sha512-nExy+fDCBEgqblasfeE3aQ3NuafBUxZxgxXcYfzYRZFHdVvk5q60KhCSkG0noHgHRo/xQ/BOzURLZAafFpTkmQ==
+
+"@typescript-eslint/typescript-estree@5.54.0":
+ version "5.54.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.0.tgz#f6f3440cabee8a43a0b25fa498213ebb61fdfe99"
+ integrity sha512-X2rJG97Wj/VRo5YxJ8Qx26Zqf0RRKsVHd4sav8NElhbZzhpBI8jU54i6hfo9eheumj4oO4dcRN1B/zIVEqR/MQ==
+ dependencies:
+ "@typescript-eslint/types" "5.54.0"
+ "@typescript-eslint/visitor-keys" "5.54.0"
+ debug "^4.3.4"
+ globby "^11.1.0"
+ is-glob "^4.0.3"
+ semver "^7.3.7"
+ tsutils "^3.21.0"
+
+"@typescript-eslint/utils@5.54.0":
+ version "5.54.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.54.0.tgz#3db758aae078be7b54b8ea8ea4537ff6cd3fbc21"
+ integrity sha512-cuwm8D/Z/7AuyAeJ+T0r4WZmlnlxQ8wt7C7fLpFlKMR+dY6QO79Cq1WpJhvZbMA4ZeZGHiRWnht7ZJ8qkdAunw==
+ dependencies:
+ "@types/json-schema" "^7.0.9"
+ "@types/semver" "^7.3.12"
+ "@typescript-eslint/scope-manager" "5.54.0"
+ "@typescript-eslint/types" "5.54.0"
+ "@typescript-eslint/typescript-estree" "5.54.0"
+ eslint-scope "^5.1.1"
+ eslint-utils "^3.0.0"
+ semver "^7.3.7"
+
+"@typescript-eslint/visitor-keys@5.54.0":
+ version "5.54.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.0.tgz#846878afbf0cd67c19cfa8d75947383d4490db8f"
+ integrity sha512-xu4wT7aRCakGINTLGeyGqDn+78BwFlggwBjnHa1ar/KaGagnmwLYmlrXIrgAaQ3AE1Vd6nLfKASm7LrFHNbKGA==
+ dependencies:
+ "@typescript-eslint/types" "5.54.0"
+ eslint-visitor-keys "^3.3.0"
+
+accepts@~1.3.8:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
+ integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
+ dependencies:
+ mime-types "~2.1.34"
+ negotiator "0.6.3"
+
+acorn-jsx@^5.3.2:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+ integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
+acorn-node@^1.2.0:
+ version "1.8.2"
+ resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8"
+ integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==
+ dependencies:
+ acorn "^7.0.0"
+ acorn-walk "^7.0.0"
+ xtend "^4.0.2"
+
+acorn-walk@^7.0.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
+ integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
+
+acorn-walk@^8.1.1:
+ version "8.2.0"
+ resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
+ integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
+
+acorn@^7.0.0:
+ version "7.4.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
+ integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
+
+acorn@^8.4.1:
+ version "8.8.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73"
+ integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==
+
+acorn@^8.8.0:
+ version "8.8.2"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
+ integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
+
+ajv@^6.10.0, ajv@^6.12.4:
+ version "6.12.6"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
+arg@^4.1.0:
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
+ integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
+
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+array-flatten@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
+ integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
+
+array-union@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
+ integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
+
+async@^3.2.3:
+ version "3.2.4"
+ resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
+ integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
+
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+ integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
+axios@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.0.tgz#1cb65bd75162c70e9f8d118a905126c4a201d383"
+ integrity sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw==
+ dependencies:
+ follow-redirects "^1.15.0"
+ form-data "^4.0.0"
+ proxy-from-env "^1.1.0"
+
+balanced-match@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+basic-auth@~2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
+ integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==
+ dependencies:
+ safe-buffer "5.1.2"
+
+body-parser@1.20.1:
+ version "1.20.1"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
+ integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
+ dependencies:
+ bytes "3.1.2"
+ content-type "~1.0.4"
+ debug "2.6.9"
+ depd "2.0.0"
+ destroy "1.2.0"
+ http-errors "2.0.0"
+ iconv-lite "0.4.24"
+ on-finished "2.4.1"
+ qs "6.11.0"
+ raw-body "2.5.1"
+ type-is "~1.6.18"
+ unpipe "1.0.0"
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+brace-expansion@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+ integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+ dependencies:
+ balanced-match "^1.0.0"
+
+braces@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+ integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+ dependencies:
+ fill-range "^7.0.1"
+
+bytes@3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
+ integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
+
+call-bind@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
+ integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
+ dependencies:
+ function-bind "^1.1.1"
+ get-intrinsic "^1.0.2"
+
+callsites@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+ integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+chalk@^4.0.0, chalk@^4.0.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+chalk@^5.0.0:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.1.2.tgz#d957f370038b75ac572471e83be4c5ca9f8e8c45"
+ integrity sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==
+
+cliui@^8.0.1:
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
+ integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.1"
+ wrap-ansi "^7.0.0"
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+color-name@~1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+combined-stream@^1.0.8:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+ dependencies:
+ delayed-stream "~1.0.0"
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+
+content-disposition@0.5.4:
+ version "0.5.4"
+ resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
+ integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
+ dependencies:
+ safe-buffer "5.2.1"
+
+content-type@~1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
+ integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
+
+cookie-signature@1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
+ integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
+
+cookie@0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
+ integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
+
+create-require@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
+ integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
+
+cross-spawn@^7.0.2:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+ integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+ dependencies:
+ path-key "^3.1.0"
+ shebang-command "^2.0.0"
+ which "^2.0.1"
+
+debug@2.6.9:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+ dependencies:
+ ms "2.0.0"
+
+debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+ integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+ dependencies:
+ ms "2.1.2"
+
+deep-is@^0.1.3:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+ integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+ integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
+depd@2.0.0, depd@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
+ integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
+
+destroy@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
+ integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
+
+diff@^4.0.1:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
+ integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
+
+dir-glob@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
+ integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
+ dependencies:
+ path-type "^4.0.0"
+
+doctrine@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+ integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+ dependencies:
+ esutils "^2.0.2"
+
+ee-first@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+ integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
+
+ejs-include-regex@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/ejs-include-regex/-/ejs-include-regex-1.0.0.tgz#e2f71575cbfd551ac800b2474c1f62a38e70093a"
+ integrity sha512-OTkvS8Dm8XhaE/+EKIjYjInRkNahQwkUUacAZXvA8Gqr5KEZrOsgFk8AkKYSnoTcdvKV0wnoG7YHWb4gkZFu8Q==
+
+ejs-lint@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ejs-lint/-/ejs-lint-2.0.0.tgz#04b403ca4c75c13b6727e7d4f3241078e7ae12cb"
+ integrity sha512-zt3E6MWLBYpWuUEOFRRqzB74gxCOZJAzoKmJR810U2mIrLnP1v6Xyk8tc6tl4pbT63cFoEqELPx9K8URjmpyZg==
+ dependencies:
+ chalk "^5.0.0"
+ ejs "3.1.8"
+ ejs-include-regex "^1.0.0"
+ globby "^13.0.0"
+ read-input "^0.3.1"
+ slash "^5.0.0"
+ syntax-error "^1.1.6"
+ yargs "^17.0.0"
+
+ejs@3.1.8, ejs@^3.1.8:
+ version "3.1.8"
+ resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b"
+ integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==
+ dependencies:
+ jake "^10.8.5"
+
+emoji-regex@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+ integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+encodeurl@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+ integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
+
+escalade@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+ integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+
+escape-html@~1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+ integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
+
+escape-string-regexp@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+ integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-scope@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
+ integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^4.1.1"
+
+eslint-scope@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642"
+ integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^5.2.0"
+
+eslint-utils@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672"
+ integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==
+ dependencies:
+ eslint-visitor-keys "^2.0.0"
+
+eslint-visitor-keys@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
+ integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
+
+eslint-visitor-keys@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
+ integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
+
+eslint@^8.35.0:
+ version "8.35.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.35.0.tgz#fffad7c7e326bae606f0e8f436a6158566d42323"
+ integrity sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==
+ dependencies:
+ "@eslint/eslintrc" "^2.0.0"
+ "@eslint/js" "8.35.0"
+ "@humanwhocodes/config-array" "^0.11.8"
+ "@humanwhocodes/module-importer" "^1.0.1"
+ "@nodelib/fs.walk" "^1.2.8"
+ ajv "^6.10.0"
+ chalk "^4.0.0"
+ cross-spawn "^7.0.2"
+ debug "^4.3.2"
+ doctrine "^3.0.0"
+ escape-string-regexp "^4.0.0"
+ eslint-scope "^7.1.1"
+ eslint-utils "^3.0.0"
+ eslint-visitor-keys "^3.3.0"
+ espree "^9.4.0"
+ esquery "^1.4.2"
+ esutils "^2.0.2"
+ fast-deep-equal "^3.1.3"
+ file-entry-cache "^6.0.1"
+ find-up "^5.0.0"
+ glob-parent "^6.0.2"
+ globals "^13.19.0"
+ grapheme-splitter "^1.0.4"
+ ignore "^5.2.0"
+ import-fresh "^3.0.0"
+ imurmurhash "^0.1.4"
+ is-glob "^4.0.0"
+ is-path-inside "^3.0.3"
+ js-sdsl "^4.1.4"
+ js-yaml "^4.1.0"
+ json-stable-stringify-without-jsonify "^1.0.1"
+ levn "^0.4.1"
+ lodash.merge "^4.6.2"
+ minimatch "^3.1.2"
+ natural-compare "^1.4.0"
+ optionator "^0.9.1"
+ regexpp "^3.2.0"
+ strip-ansi "^6.0.1"
+ strip-json-comments "^3.1.0"
+ text-table "^0.2.0"
+
+espree@^9.4.0:
+ version "9.4.1"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd"
+ integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==
+ dependencies:
+ acorn "^8.8.0"
+ acorn-jsx "^5.3.2"
+ eslint-visitor-keys "^3.3.0"
+
+esquery@^1.4.2:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b"
+ integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==
+ dependencies:
+ estraverse "^5.1.0"
+
+esrecurse@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+ integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+ dependencies:
+ estraverse "^5.2.0"
+
+estraverse@^4.1.1:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+ integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+estraverse@^5.1.0, estraverse@^5.2.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+ integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+esutils@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+etag@~1.8.1:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+ integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
+
+express@^4.18.2:
+ version "4.18.2"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
+ integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
+ dependencies:
+ accepts "~1.3.8"
+ array-flatten "1.1.1"
+ body-parser "1.20.1"
+ content-disposition "0.5.4"
+ content-type "~1.0.4"
+ cookie "0.5.0"
+ cookie-signature "1.0.6"
+ debug "2.6.9"
+ depd "2.0.0"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ etag "~1.8.1"
+ finalhandler "1.2.0"
+ fresh "0.5.2"
+ http-errors "2.0.0"
+ merge-descriptors "1.0.1"
+ methods "~1.1.2"
+ on-finished "2.4.1"
+ parseurl "~1.3.3"
+ path-to-regexp "0.1.7"
+ proxy-addr "~2.0.7"
+ qs "6.11.0"
+ range-parser "~1.2.1"
+ safe-buffer "5.2.1"
+ send "0.18.0"
+ serve-static "1.15.0"
+ setprototypeof "1.2.0"
+ statuses "2.0.1"
+ type-is "~1.6.18"
+ utils-merge "1.0.1"
+ vary "~1.1.2"
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-glob@^3.2.11, fast-glob@^3.2.9:
+ version "3.2.12"
+ resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
+ integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
+ dependencies:
+ "@nodelib/fs.stat" "^2.0.2"
+ "@nodelib/fs.walk" "^1.2.3"
+ glob-parent "^5.1.2"
+ merge2 "^1.3.0"
+ micromatch "^4.0.4"
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
+
+fastq@^1.6.0:
+ version "1.13.0"
+ resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
+ integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==
+ dependencies:
+ reusify "^1.0.4"
+
+file-entry-cache@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+ integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+ dependencies:
+ flat-cache "^3.0.4"
+
+filelist@^1.0.1:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5"
+ integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==
+ dependencies:
+ minimatch "^5.0.1"
+
+fill-range@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+ integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+ dependencies:
+ to-regex-range "^5.0.1"
+
+finalhandler@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
+ integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==
+ dependencies:
+ debug "2.6.9"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ on-finished "2.4.1"
+ parseurl "~1.3.3"
+ statuses "2.0.1"
+ unpipe "~1.0.0"
+
+find-up@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+ integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+ dependencies:
+ locate-path "^6.0.0"
+ path-exists "^4.0.0"
+
+flat-cache@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
+ integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
+ dependencies:
+ flatted "^3.1.0"
+ rimraf "^3.0.2"
+
+flatted@^3.1.0:
+ version "3.2.7"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
+ integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
+
+follow-redirects@^1.15.0:
+ version "1.15.2"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
+ integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
+
+form-data@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
+ integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.8"
+ mime-types "^2.1.12"
+
+forwarded@0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
+ integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
+
+fresh@0.5.2:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+ integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+
+function-bind@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+ integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+get-caller-file@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+ integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+get-intrinsic@^1.0.2:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385"
+ integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==
+ dependencies:
+ function-bind "^1.1.1"
+ has "^1.0.3"
+ has-symbols "^1.0.3"
+
+glob-parent@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+ dependencies:
+ is-glob "^4.0.1"
+
+glob-parent@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+ integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+ dependencies:
+ is-glob "^4.0.3"
+
+glob@^7.1.3:
+ version "7.2.3"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
+ integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.1.1"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+globals@^13.19.0:
+ version "13.20.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82"
+ integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==
+ dependencies:
+ type-fest "^0.20.2"
+
+globby@^11.1.0:
+ version "11.1.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
+ integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
+ dependencies:
+ array-union "^2.1.0"
+ dir-glob "^3.0.1"
+ fast-glob "^3.2.9"
+ ignore "^5.2.0"
+ merge2 "^1.4.1"
+ slash "^3.0.0"
+
+globby@^13.0.0:
+ version "13.1.2"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.2.tgz#29047105582427ab6eca4f905200667b056da515"
+ integrity sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==
+ dependencies:
+ dir-glob "^3.0.1"
+ fast-glob "^3.2.11"
+ ignore "^5.2.0"
+ merge2 "^1.4.1"
+ slash "^4.0.0"
+
+grapheme-splitter@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
+ integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
+
+has-flag@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has-symbols@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
+ integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
+
+has@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+ integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+ dependencies:
+ function-bind "^1.1.1"
+
+http-errors@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
+ integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
+ dependencies:
+ depd "2.0.0"
+ inherits "2.0.4"
+ setprototypeof "1.2.0"
+ statuses "2.0.1"
+ toidentifier "1.0.1"
+
+iconv-lite@0.4.24:
+ version "0.4.24"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+ integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
+
+ignore@^5.2.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.1.tgz#c2b1f76cb999ede1502f3a226a9310fdfe88d46c"
+ integrity sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==
+
+import-fresh@^3.0.0, import-fresh@^3.2.1:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+ integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+ dependencies:
+ parent-module "^1.0.0"
+ resolve-from "^4.0.0"
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+ integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+ipaddr.js@1.9.1:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
+ integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-fullwidth-code-point@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+ integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+is-number@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+ integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+is-path-inside@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
+ integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+jake@^10.8.5:
+ version "10.8.5"
+ resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46"
+ integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==
+ dependencies:
+ async "^3.2.3"
+ chalk "^4.0.2"
+ filelist "^1.0.1"
+ minimatch "^3.0.4"
+
+js-sdsl@^4.1.4:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711"
+ integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==
+
+js-yaml@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ dependencies:
+ argparse "^2.0.1"
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+ integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+
+levn@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+ integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+ dependencies:
+ prelude-ls "^1.2.1"
+ type-check "~0.4.0"
+
+locate-path@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+ integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+ dependencies:
+ p-locate "^5.0.0"
+
+lodash.merge@^4.6.2:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+ integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
+lru-cache@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+ integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+ dependencies:
+ yallist "^4.0.0"
+
+make-error@^1.1.1:
+ version "1.3.6"
+ resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
+ integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
+
+media-typer@0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+ integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
+
+merge-descriptors@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
+ integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
+
+merge2@^1.3.0, merge2@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
+ integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+
+methods@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
+ integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
+
+micromatch@^4.0.4:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+ integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+ dependencies:
+ braces "^3.0.2"
+ picomatch "^2.3.1"
+
+mime-db@1.52.0:
+ version "1.52.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34:
+ version "2.1.35"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
+mime@1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+ integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
+
+minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+ integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimatch@^5.0.1:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7"
+ integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==
+ dependencies:
+ brace-expansion "^2.0.1"
+
+morgan@^1.10.0:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7"
+ integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==
+ dependencies:
+ basic-auth "~2.0.1"
+ debug "2.6.9"
+ depd "~2.0.0"
+ on-finished "~2.3.0"
+ on-headers "~1.0.2"
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+ integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
+
+ms@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+ms@2.1.3:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+natural-compare-lite@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4"
+ integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==
+
+natural-compare@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+ integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+
+negotiator@0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
+ integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
+
+object-inspect@^1.9.0:
+ version "1.12.2"
+ resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
+ integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
+
+on-finished@2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
+ integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
+ dependencies:
+ ee-first "1.1.1"
+
+on-finished@~2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+ integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==
+ dependencies:
+ ee-first "1.1.1"
+
+on-headers@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
+ integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+ dependencies:
+ wrappy "1"
+
+optionator@^0.9.1:
+ version "0.9.1"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
+ integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
+ dependencies:
+ deep-is "^0.1.3"
+ fast-levenshtein "^2.0.6"
+ levn "^0.4.1"
+ prelude-ls "^1.2.1"
+ type-check "^0.4.0"
+ word-wrap "^1.2.3"
+
+p-limit@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+ integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+ dependencies:
+ yocto-queue "^0.1.0"
+
+p-locate@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+ integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+ dependencies:
+ p-limit "^3.0.2"
+
+parent-module@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+ integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+ dependencies:
+ callsites "^3.0.0"
+
+parseurl@~1.3.3:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
+ integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+
+path-exists@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+ integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
+
+path-key@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+ integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+path-to-regexp@0.1.7:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
+ integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
+
+path-type@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+ integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
+picomatch@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+prelude-ls@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+ integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
+prettier@^2.8.0:
+ version "2.8.0"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.0.tgz#c7df58393c9ba77d6fba3921ae01faf994fb9dc9"
+ integrity sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==
+
+proxy-addr@~2.0.7:
+ version "2.0.7"
+ resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
+ integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
+ dependencies:
+ forwarded "0.2.0"
+ ipaddr.js "1.9.1"
+
+proxy-from-env@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
+ integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+
+punycode@^2.1.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
+ integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
+
+qs@6.11.0:
+ version "6.11.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
+ integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
+ dependencies:
+ side-channel "^1.0.4"
+
+queue-microtask@^1.2.2:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
+ integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+
+range-parser@~1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
+ integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
+
+raw-body@2.5.1:
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
+ integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
+ dependencies:
+ bytes "3.1.2"
+ http-errors "2.0.0"
+ iconv-lite "0.4.24"
+ unpipe "1.0.0"
+
+read-input@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/read-input/-/read-input-0.3.1.tgz#5b3169308013464ffda6ec92e58d2d3cea948df1"
+ integrity sha512-J1ZkWCnB4altU7RTe+62PSfa21FrEtfKyO9fuqR3yP8kZku3nIwaw2Krj383JC7egAIl5Zyz2w+EOu9uXH5HZw==
+
+regexpp@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
+ integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
+
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
+
+resolve-from@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+ integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+reusify@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
+ integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
+
+rimraf@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+ integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+ dependencies:
+ glob "^7.1.3"
+
+run-parallel@^1.1.9:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+ integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+ dependencies:
+ queue-microtask "^1.2.2"
+
+safe-buffer@5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-buffer@5.2.1:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+ integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+"safer-buffer@>= 2.1.2 < 3":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+semver@^7.3.7:
+ version "7.3.8"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
+ integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
+ dependencies:
+ lru-cache "^6.0.0"
+
+send@0.18.0:
+ version "0.18.0"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
+ integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
+ dependencies:
+ debug "2.6.9"
+ depd "2.0.0"
+ destroy "1.2.0"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ etag "~1.8.1"
+ fresh "0.5.2"
+ http-errors "2.0.0"
+ mime "1.6.0"
+ ms "2.1.3"
+ on-finished "2.4.1"
+ range-parser "~1.2.1"
+ statuses "2.0.1"
+
+serve-static@1.15.0:
+ version "1.15.0"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
+ integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
+ dependencies:
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ parseurl "~1.3.3"
+ send "0.18.0"
+
+setprototypeof@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
+ integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
+
+shebang-command@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+ integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+ dependencies:
+ shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+ integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+side-channel@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
+ integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
+ dependencies:
+ call-bind "^1.0.0"
+ get-intrinsic "^1.0.2"
+ object-inspect "^1.9.0"
+
+slash@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
+ integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+
+slash@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7"
+ integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
+
+slash@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-5.0.0.tgz#8c18a871096b71ee0e002976a4fe3374991c3074"
+ integrity sha512-n6KkmvKS0623igEVj3FF0OZs1gYYJ0o0Hj939yc1fyxl2xt+xYpLnzJB6xBSqOfV9ZFLEWodBBN/heZJahuIJQ==
+
+statuses@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
+ integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
+
+string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+ integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
+supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
+syntax-error@^1.1.6:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c"
+ integrity sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==
+ dependencies:
+ acorn-node "^1.2.0"
+
+text-table@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+ integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
+
+to-regex-range@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+ integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+ dependencies:
+ is-number "^7.0.0"
+
+toidentifier@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
+ integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
+
+ts-node@^10.9.1:
+ version "10.9.1"
+ resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
+ integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
+ dependencies:
+ "@cspotcode/source-map-support" "^0.8.0"
+ "@tsconfig/node10" "^1.0.7"
+ "@tsconfig/node12" "^1.0.7"
+ "@tsconfig/node14" "^1.0.0"
+ "@tsconfig/node16" "^1.0.2"
+ acorn "^8.4.1"
+ acorn-walk "^8.1.1"
+ arg "^4.1.0"
+ create-require "^1.1.0"
+ diff "^4.0.1"
+ make-error "^1.1.1"
+ v8-compile-cache-lib "^3.0.1"
+ yn "3.1.1"
+
+tslib@^1.8.1:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
+ integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+
+tsutils@^3.21.0:
+ version "3.21.0"
+ resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
+ integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
+ dependencies:
+ tslib "^1.8.1"
+
+type-check@^0.4.0, type-check@~0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+ integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+ dependencies:
+ prelude-ls "^1.2.1"
+
+type-fest@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+ integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
+type-is@~1.6.18:
+ version "1.6.18"
+ resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
+ integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
+ dependencies:
+ media-typer "0.3.0"
+ mime-types "~2.1.24"
+
+typescript@^4.9.3:
+ version "4.9.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db"
+ integrity sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==
+
+unpipe@1.0.0, unpipe@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+ integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
+
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
+utils-merge@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+ integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
+
+v8-compile-cache-lib@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
+ integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
+
+vary@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
+ integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
+
+which@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ dependencies:
+ isexe "^2.0.0"
+
+word-wrap@^1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+ integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+xtend@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
+ integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
+
+y18n@^5.0.5:
+ version "5.0.8"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
+ integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
+
+yallist@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+ integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
+yargs-parser@^21.1.1:
+ version "21.1.1"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
+ integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
+
+yargs@^17.0.0:
+ version "17.6.2"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541"
+ integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==
+ dependencies:
+ cliui "^8.0.1"
+ escalade "^3.1.1"
+ get-caller-file "^2.0.5"
+ require-directory "^2.1.1"
+ string-width "^4.2.3"
+ y18n "^5.0.5"
+ yargs-parser "^21.1.1"
+
+yn@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
+ integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
+
+yocto-queue@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+ integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==