diff --git a/.gitignore b/.gitignore index 380a88047..112d09ee2 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ tmp # docker-compose database directory docker-db-data + +/config/credentials/ diff --git a/Dockerfile.prod b/Dockerfile.prod new file mode 100644 index 000000000..de4202e08 --- /dev/null +++ b/Dockerfile.prod @@ -0,0 +1,72 @@ +FROM debian:bookworm + +ENV DEBIAN_FRONTEND=noninteractive + +# Install system packages then clean up to minimize image size +RUN apt-get update \ + && apt-get install --no-install-recommends -y \ + build-essential \ + curl \ + default-jre-headless \ + file \ + git-core \ + gpg-agent \ + libarchive-dev \ + libffi-dev \ + libgd-dev \ + libpq-dev \ + libsasl2-dev \ + libvips-dev \ + libxml2-dev \ + libxslt1-dev \ + libyaml-dev \ + locales \ + postgresql-client \ + ruby-dev \ + ruby-bundler \ + tzdata \ + unzip \ + nodejs \ + npm \ + osmosis \ + ca-certificates \ + firefox-esr \ + optipng \ + pngcrush \ + advancecomp \ + pngquant \ + jhead \ + jpegoptim \ + libjpeg-turbo-progs \ + gifsicle + +# Install yarn globally +RUN npm install --global yarn + +# Add support for Postgres 16 +RUN apt-get install --no-install-recommends -y postgresql-common \ + && /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y \ + && apt-get install --no-install-recommends -y postgresql-client-16 + +# Setup app location +RUN mkdir -p /app +WORKDIR /app +COPY . . + +# Database template for Docker +COPY config/docker.database.yml config/database.yml +RUN sed -i'.bak' 's/development/production/g' config/database.yml + +COPY config/example.storage.yml config/storage.yml + +# https://help.openstreetmap.org/questions/69887/actionviewtemplateerror-couldnt-find-file-settingslocalyml +RUN touch config/settings.local.yml + +# Install Ruby packages +RUN bundle install + +# Install NodeJS packages using yarn +RUN bundle exec bin/yarn install + +# Build frontend assets +RUN RAILS_ENV=production SECRET_KEY_BASE=dummy rails i18n:js:export assets:precompile diff --git a/Gemfile b/Gemfile index 277346b83..6364b514a 100644 --- a/Gemfile +++ b/Gemfile @@ -144,6 +144,11 @@ gem "unicode-display_width" # Lock some modules to old versions for ruby 3.1 support gem "zeitwerk", "< 2.7" +# TDEI Workspaces: supports multi-tenancy +# The latest stable release doesn't support Rails 7.1, yet. Use dev for now: +gem "ros-apartment", github: 'rails-on-services/apartment', branch: 'development', :require => "apartment" +#gem "ros-apartment", :require => "apartment" + # Gems useful for development group :development do gem "better_errors" diff --git a/Gemfile.lock b/Gemfile.lock index f23429253..6fbc1dd87 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,15 @@ +GIT + remote: https://github.com/rails-on-services/apartment.git + revision: ec5a9bc7aef2ba65d7c3d369ca08aa916e56f880 + branch: development + specs: + ros-apartment (3.1.0) + activerecord (>= 6.1.0, < 8.1) + activesupport (>= 6.1.0, < 8.1) + parallel (< 2.0) + public_suffix (>= 2.0.5, <= 6.0.1) + rack (>= 1.3.6, < 4.0) + GEM remote: https://rubygems.org/ specs: @@ -742,6 +754,7 @@ DEPENDENCIES rails-i18n (~> 7.0.0) rails_param rinku (>= 2.0.6) + ros-apartment! rotp rtlcss rubocop diff --git a/Rakefile b/Rakefile index 9a5ea7383..01dcdcc85 100644 --- a/Rakefile +++ b/Rakefile @@ -3,4 +3,6 @@ require_relative "config/application" +Apartment.db_migrate_tenants = false + Rails.application.load_tasks diff --git a/app/controllers/api/users_controller.rb b/app/controllers/api/users_controller.rb index e9f42e12b..b26694db5 100644 --- a/app/controllers/api/users_controller.rb +++ b/app/controllers/api/users_controller.rb @@ -4,7 +4,8 @@ class UsersController < ApiController before_action :setup_user_auth, :only => [:show, :index] before_action :authorize, :only => [:details, :gpx_files] - authorize_resource + authorize_resource :except => [:provision] + skip_authorization_check :only => [:provision] load_resource :only => :show @@ -52,6 +53,64 @@ def gpx_files render :content_type => "application/xml" end + def provision + user = User.find_by(:auth_provider => "TDEI", :auth_uid => params[:auth_uid]) + + if user + user.email = params[:email] + user.display_name = params[:display_name] + + if user.status != "active" + user.activate + end + + user.save + return head :no_content + end + + # TODO: temporary for TDEI auth transition: + user = User.find_by(:email => params[:email]) + + if user + user.auth_provider = 'TDEI' + user.auth_uid = params[:auth_uid] + user.display_name = params[:display_name] + + if user.status != "active" + user.activate + end + + user.save + return head :no_content + end + + puts 'Create user' + user = User.new() + user.email = params[:email] + user.email_valid = true + user.display_name = params[:display_name] + + user.auth_provider = 'TDEI' + user.auth_uid = params[:auth_uid] + + # Set a random password--TDEI identity services manage the password: + user.pass_crypt = SecureRandom.base64(16) + + user.data_public = true + user.description = '' if user.description.nil? + user.creation_address = request.remote_ip + user.languages = http_accept_language.user_preferred_languages + user.terms_agreed = Time.now.utc + user.tou_agreed = Time.now.utc + user.terms_seen = true + user.auth_provider = nil + user.auth_uid = nil + user.activate + user.save + + head :no_content + end + private def disable_terms_redirect diff --git a/app/controllers/api/workspaces_controller.rb b/app/controllers/api/workspaces_controller.rb new file mode 100644 index 000000000..585816f57 --- /dev/null +++ b/app/controllers/api/workspaces_controller.rb @@ -0,0 +1,72 @@ +# TODO: this controller needs special auth with the workspaces backend +module Api + class WorkspacesController < ApiController + before_action :check_api_readable + before_action :set_request_formats + #before_action :authorize + + #authorize_resource + skip_authorization_check + + around_action :api_call_handle_error, :api_call_timeout + + def create + workspace_schema = 'workspace-' + params[:id] + + # This runs the migrations that scaffold all of the OSM tables into a new + # schema. We don't need every table, but we can't choose. The global non- + # tenant tables match excluded models in config/initializers/apartment.rb + # that Apartment associates with the "public" schema. + # + Apartment::Tenant.create(workspace_schema) + Apartment::Tenant.switch!(workspace_schema) + + # Drop tables that shadow the "public" schema. These tables cause foreign + # key constraint issues for tenant tables. The migrations include all OSM + # tables, and any unqualified references resolve to tenant schema tables. + # Removing these tables from the search path causes PostgreSQL to resolve + # tables references from the "public" schema instead: + # + connection = ActiveRecord::Base.connection + connection.execute("DROP TABLE \"#{workspace_schema}\".users CASCADE") + end + + def switch + cookies[:workspace] = { + :value => params[:id], + :expires => 1.year.from_now, + } + end + + def destroy + Apartment::Tenant.drop('workspace-' + params[:id]) + end + + def bbox + workspace_schema = 'workspace-' + params[:id] + Apartment::Tenant.switch!(workspace_schema) + + out = Node + .select('MAX(latitude) AS max_lat, + MAX(longitude) AS max_lon, + MIN(latitude) AS min_lat, + MIN(longitude) AS min_lon') + .take + + if out.min_lat.nil? # Workspace is empty (no nodes) + return head :no_content + end + + @bbox = BoundingBox.new(out.min_lon, out.min_lat, out.max_lon, out.max_lat) + + respond_to do |format| + format.xml + format.json + end + end + end +end + +# TODO stub +class Workspace +end diff --git a/app/middleware/workspaces_elevator.rb b/app/middleware/workspaces_elevator.rb new file mode 100644 index 000000000..0162b9f43 --- /dev/null +++ b/app/middleware/workspaces_elevator.rb @@ -0,0 +1,20 @@ +require 'apartment/elevators/generic' + +class WorkspacesElevator < Apartment::Elevators::Generic + def parse_tenant_name(request) + return nil if request.path.match? /^\/api\/[0-9.]+\/workspaces\// + + workspace_id = request.env['HTTP_X_WORKSPACE'] # X-Workspace header + + if workspace_id.blank? + workspace_id = request.cookies['workspace'] + end + + return nil if workspace_id.blank? + return nil unless workspace_id.match? /^\d+$/ + + puts 'Selecting workspace ' + workspace_id + + return 'workspace-' + workspace_id + end +end diff --git a/app/models/changeset.rb b/app/models/changeset.rb index e6bde19a5..c3394b493 100644 --- a/app/models/changeset.rb +++ b/app/models/changeset.rb @@ -55,10 +55,10 @@ class Changeset < ApplicationRecord before_save :update_closed_at # maximum number of elements allowed in a changeset - MAX_ELEMENTS = 10000 + MAX_ELEMENTS = 10000000 # maximum time a changeset is allowed to be open for. - MAX_TIME_OPEN = 1.day + MAX_TIME_OPEN = 7.day # idle timeout increment, one hour seems reasonable. IDLE_TIMEOUT = 1.hour diff --git a/app/views/api/workspaces/bbox.json.jbuilder b/app/views/api/workspaces/bbox.json.jbuilder new file mode 100644 index 000000000..087a4c62a --- /dev/null +++ b/app/views/api/workspaces/bbox.json.jbuilder @@ -0,0 +1,4 @@ +json.min_lat GeoRecord::Coord.new(@bbox.to_unscaled.min_lat) +json.min_lon GeoRecord::Coord.new(@bbox.to_unscaled.min_lon) +json.max_lat GeoRecord::Coord.new(@bbox.to_unscaled.max_lat) +json.max_lon GeoRecord::Coord.new(@bbox.to_unscaled.max_lon) diff --git a/config/.gitignore b/config/.gitignore index 95ba2dbdb..28d379bd9 100644 --- a/config/.gitignore +++ b/config/.gitignore @@ -1,2 +1 @@ -database.yml storage.yml diff --git a/config/application.rb b/config/application.rb index 05d7fb41d..0f87b72ee 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,4 +1,5 @@ require_relative "boot" +require_relative "../app/middleware/workspaces_elevator.rb" require "rails/all" @@ -45,5 +46,8 @@ class Application < Rails::Application config.logstasher.logger_path = Settings.logstash_path config.logstasher.log_controller_parameters = true end + + # TDEI Workspaces tenant partitioning: + config.middleware.use WorkspacesElevator end end diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 000000000..5a0bb5b14 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,8 @@ +<%= ENV['RAILS_ENV'] %>: + adapter: postgresql + host: <%= ENV['WS_OSM_DB_HOST'] %> + username: <%= ENV['WS_OSM_DB_USER'] %> + password: <%= ENV['WS_OSM_DB_PASS'] %> + database: <%= ENV['WS_OSM_DB_NAME'] %> + encoding: utf8 + diff --git a/config/docker.database.yml b/config/docker.database.yml index b0e79b0f8..539938602 100644 --- a/config/docker.database.yml +++ b/config/docker.database.yml @@ -1,20 +1,9 @@ # This configuration is tailored for use with docker compose. See DOCKER.md for more information. -development: +<%= ENV['RAILS_ENV'] %>: adapter: postgresql - database: openstreetmap - username: openstreetmap - password: openstreetmap - host: db - encoding: utf8 - -# Warning: The database defined as 'test' will be erased and -# re-generated from your development database when you run 'rake'. -# Do not set this db to the same as development or production. -test: - adapter: postgresql - database: osm_test - username: openstreetmap - password: openstreetmap - host: db + host: <%= ENV['WS_OSM_DB_HOST'] %> + username: <%= ENV['WS_OSM_DB_USER'] %> + password: <%= ENV['WS_OSM_DB_PASS'] %> + database: <%= ENV['WS_OSM_DB_NAME'] %> encoding: utf8 diff --git a/config/initializers/apartment.rb b/config/initializers/apartment.rb new file mode 100644 index 000000000..8f103b3b7 --- /dev/null +++ b/config/initializers/apartment.rb @@ -0,0 +1,138 @@ +# frozen_string_literal: true + +# You can have Apartment route to the appropriate Tenant by adding some Rack middleware. +# Apartment can support many different "Elevators" that can take care of this routing to your data. +# Require whichever Elevator you're using below or none if you have a custom one. +# +# require 'apartment/elevators/generic' +# require 'apartment/elevators/domain' +# require 'apartment/elevators/subdomain' +# require 'apartment/elevators/first_subdomain' +# require 'apartment/elevators/host' +require_relative "../../app/middleware/workspaces_elevator.rb" + +# +# Apartment Configuration +# +Apartment.configure do |config| + # Add any models that you do not want to be multi-tenanted, but remain in the global (public) namespace. + # A typical example would be a Customer or Tenant model that stores each Tenant's information. + # + config.excluded_models = [ + "Acl", + "DiaryComment", + "DiaryEntry", + "DiaryEntrySubscription", + "Doorkeeper::AccessGrant", + "Doorkeeper::AccessToken", + "Doorkeeper::Application", + "Friendship", + "Issue", + "IssueComment", + "Language", + "Message", + "Oauth2Application", + "Report", + "User", + "UserBlock", + "UserMute", + "UserPreference", + "UserRole" + ] + + # In order to migrate all of your Tenants you need to provide a list of Tenant names to Apartment. + # You can make this dynamic by providing a Proc object to be called on migrations. + # This object should yield either: + # - an array of strings representing each Tenant name. + # - a hash which keys are tenant names, and values custom db config + # (must contain all key/values required in database.yml) + # + # config.tenant_names = lambda{ Customer.pluck(:tenant_name) } + # config.tenant_names = ['tenant1', 'tenant2'] + # config.tenant_names = { + # 'tenant1' => { + # adapter: 'postgresql', + # host: 'some_server', + # port: 5555, + # database: 'postgres' # this is not the name of the tenant's db + # # but the name of the database to connect to before creating the tenant's db + # # mandatory in postgresql + # }, + # 'tenant2' => { + # adapter: 'postgresql', + # database: 'postgres' # this is not the name of the tenant's db + # # but the name of the database to connect to before creating the tenant's db + # # mandatory in postgresql + # } + # } + # config.tenant_names = lambda do + # Tenant.all.each_with_object({}) do |tenant, hash| + # hash[tenant.name] = tenant.db_configuration + # end + # end + # + config.tenant_names = -> { ToDo_Tenant_Or_User_Model.pluck :database } + + # PostgreSQL: + # Specifies whether to use PostgreSQL schemas or create a new database per Tenant. + # + # MySQL: + # Specifies whether to switch databases by using `use` statement or re-establish connection. + # + # The default behaviour is true. + # + config.use_schemas = true + + # + # ==> PostgreSQL only options + + # Apartment can be forced to use raw SQL dumps instead of schema.rb for creating new schemas. + # Use this when you are using some extra features in PostgreSQL that can't be represented in + # schema.rb, like materialized views etc. (only applies with use_schemas set to true). + # (Note: this option doesn't use db/structure.sql, it creates SQL dump by executing pg_dump) + # + config.use_sql = true + + # There are cases where you might want some schemas to always be in your search_path + # e.g when using a PostgreSQL extension like hstore. + # Any schemas added here will be available along with your selected Tenant. + # + # config.persistent_schemas = %w{ hstore } + + # <== PostgreSQL only options + # + + # By default, and only when not using PostgreSQL schemas, Apartment will prepend the environment + # to the tenant name to ensure there is no conflict between your environments. + # This is mainly for the benefit of your development and test environments. + # Uncomment the line below if you want to disable this behaviour in production. + # + # config.prepend_environment = !Rails.env.production? + + # When using PostgreSQL schemas, the database dump will be namespaced, and + # apartment will substitute the default namespace (usually public) with the + # name of the new tenant when creating a new tenant. Some items must maintain + # a reference to the default namespace (ie public) - for instance, a default + # uuid generation. Uncomment the line below to create a list of namespaced + # items in the schema dump that should *not* have their namespace replaced by + # the new tenant + # + # config.pg_excluded_names = ["uuid_generate_v4"] + + # Specifies whether the database and schema (when using PostgreSQL schemas) will prepend in ActiveRecord log. + # Uncomment the line below if you want to enable this behavior. + # + # config.active_record_log = true +end + +# Setup a custom Tenant switching middleware. The Proc should return the name of the Tenant that +# you want to switch to. +# Rails.application.config.middleware.use Apartment::Elevators::Generic, lambda { |request| +# request.host.split('.').first +# } + +# Rails.application.config.middleware.use Apartment::Elevators::Domain +# Rails.application.config.middleware.use Apartment::Elevators::Subdomain +# Rails.application.config.middleware.use Apartment::Elevators::FirstSubdomain +# Rails.application.config.middleware.use Apartment::Elevators::Host +Rails.application.config.middleware.use WorkspacesElevator diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb index e7b813b73..4813a7962 100644 --- a/config/initializers/cors.rb +++ b/config/initializers/cors.rb @@ -20,12 +20,12 @@ def call(env) # such as X-Requested-By to requests.) Rails.application.config.middleware.insert_before 0, OpenStreetMap::Cors do allow do - origins "*" + origins { |source, env| true } resource "/oauth/*", :headers => :any, :methods => [:get, :post] resource "/oauth2/token", :headers => :any, :methods => [:post] resource "/oauth2/revoke", :headers => :any, :methods => [:post] resource "/oauth2/introspect", :headers => :any, :methods => [:post] - resource "/api/*", :headers => :any, :methods => [:get, :post, :put, :delete] + resource "/api/*", :headers => :any, :methods => [:get, :post, :put, :delete], :credentials => true resource "/diary/rss", :headers => :any, :methods => [:get] resource "/diary/*/rss", :headers => :any, :methods => [:get] resource "/trace/*/data", :headers => :any, :methods => [:get] diff --git a/config/routes.rb b/config/routes.rb index f65042dd7..d0817512a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -113,6 +113,12 @@ end resources :user_blocks, :only => :show, :id => /\d+/, :controller => "user_blocks" + + # TDEI Workspaces + put "user/:auth_uid" => "users#provision" + put "workspaces/:id" => "workspaces#create", :id => /\d+/ + delete "workspaces/:id" => "workspaces#destroy", :id => /\d+/ + get "workspaces/:id/bbox" => "workspaces#bbox", :id => /\d+/ end # Data browsing diff --git a/config/settings/development.yml b/config/settings/development.yml index e69de29bb..b921bec33 100644 --- a/config/settings/development.yml +++ b/config/settings/development.yml @@ -0,0 +1,14 @@ +server_protocol: "https" +server_url: <%= ENV['WS_OSM_HOST'] %> + +support_email: <%= ENV['WS_MAIL_CONTACT'] %> +email_from: <%= ENV['WS_MAIL_NAME'] %> <<%= ENV['WS_MAIL_FROM'] %>> +email_return_path: <%= ENV['WS_MAIL_RETURN_PATH'] %> + +smtp_address: <%= ENV['WS_SMTP_HOST'] %> +smtp_port: <%= ENV['WS_SMTP_PORT'] %> +smtp_domain: <%= ENV['WS_SMTP_DOMAIN'] %> +smtp_user_name: <%= ENV['WS_SMTP_USER'] %> +smtp_password: <%= ENV['WS_SMTP_PASS'] %> +smtp_authentication: "plain" +smtp_enable_starttls_auto: true diff --git a/config/settings/production.yml b/config/settings/production.yml index e69de29bb..b921bec33 100644 --- a/config/settings/production.yml +++ b/config/settings/production.yml @@ -0,0 +1,14 @@ +server_protocol: "https" +server_url: <%= ENV['WS_OSM_HOST'] %> + +support_email: <%= ENV['WS_MAIL_CONTACT'] %> +email_from: <%= ENV['WS_MAIL_NAME'] %> <<%= ENV['WS_MAIL_FROM'] %>> +email_return_path: <%= ENV['WS_MAIL_RETURN_PATH'] %> + +smtp_address: <%= ENV['WS_SMTP_HOST'] %> +smtp_port: <%= ENV['WS_SMTP_PORT'] %> +smtp_domain: <%= ENV['WS_SMTP_DOMAIN'] %> +smtp_user_name: <%= ENV['WS_SMTP_USER'] %> +smtp_password: <%= ENV['WS_SMTP_PASS'] %> +smtp_authentication: "plain" +smtp_enable_starttls_auto: true diff --git a/script/create-workspace b/script/create-workspace new file mode 100644 index 000000000..9846ee783 --- /dev/null +++ b/script/create-workspace @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby + +require File.join(File.dirname(__FILE__), "..", "config", "environment") + + diff --git a/yarn.lock b/yarn.lock index 4e40de8d5..a6cfc7e82 100644 --- a/yarn.lock +++ b/yarn.lock @@ -148,16 +148,21 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -call-bind@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== dependencies: - es-define-property "^1.0.0" es-errors "^1.3.0" function-bind "^1.1.2" - get-intrinsic "^1.2.4" - set-function-length "^1.2.1" + +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" callsites@^3.0.0: version "3.1.0" @@ -210,32 +215,37 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -define-data-property@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - diacritics@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/diacritics/-/diacritics-1.3.0.tgz#3efa87323ebb863e6696cebb0082d48ff3d6f7a1" integrity sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA== -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== dependencies: - get-intrinsic "^1.2.4" + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== es-errors@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + 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" @@ -385,16 +395,29 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -get-intrinsic@^1.1.3, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== +get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" es-errors "^1.3.0" + es-object-atoms "^1.1.1" function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" glob-parent@^6.0.2: version "6.0.2" @@ -408,39 +431,25 @@ globals@^14.0.0: resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== 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-property-descriptors@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" - integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== - -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-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== -hasown@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.1.tgz#26f48f039de2c0f8d3356c223fb8d50253519faa" - integrity sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA== +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: function-bind "^1.1.2" @@ -548,6 +557,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -565,10 +579,10 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -object-inspect@^1.13.1: - version "1.13.1" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" - integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== optionator@^0.9.3: version "0.9.3" @@ -631,29 +645,17 @@ punycode@^2.1.0: integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== qs@^6.9.4: - version "6.13.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.1.tgz#3ce5fc72bd3a8171b85c99b93c65dd20b7d1b16e" - integrity sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg== + version "6.14.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" + integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== dependencies: - side-channel "^1.0.6" + side-channel "^1.1.0" 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== -set-function-length@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.1.tgz#47cc5945f2c771e2cf261c6737cf9684a2a5e425" - integrity sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g== - dependencies: - define-data-property "^1.1.2" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.3" - gopd "^1.0.1" - has-property-descriptors "^1.0.1" - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -666,15 +668,45 @@ shebang-regex@^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.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" - integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== dependencies: - call-bind "^1.0.7" es-errors "^1.3.0" - get-intrinsic "^1.2.4" - object-inspect "^1.13.1" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" strip-json-comments@^3.1.1: version "3.1.1"