diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..920ff3b6d --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +--- + +tidelift: rubygems/ransack diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 000000000..90408e47e --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,12 @@ +# Security Policy + +## Supported Versions + +At the moment, only the latest major.minor release stream is supported with +security updates. + +## Reporting a Vulnerability + +Please use the Tidelift security contact to [report a security +vulnerability](https://tidelift.com/security). Tidelift will coordinate the fix +and disclosure. diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..7cb2dfbb0 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,72 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ main ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main ] + schedule: + - cron: '43 11 * * 3' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'ruby' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/cronjob.yml b/.github/workflows/cronjob.yml new file mode 100644 index 000000000..32f6c7185 --- /dev/null +++ b/.github/workflows/cronjob.yml @@ -0,0 +1,141 @@ +name: cronjob + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + sqlite3: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + ruby: + - 3.2.2 + env: + DB: sqlite3 + RAILS: main + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + - name: Install dependencies + run: bundle install + - name: Run tests + run: bundle exec rspec + + mysql: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + ruby: + - 3.2.2 + env: + DB: mysql + RAILS: main + MYSQL_USERNAME: root + MYSQL_PASSWORD: root + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + - name: Startup MySQL + run: | + sudo systemctl start mysql.service + - name: Setup databases + run: | + mysql --user=root --password=root --host=127.0.0.1 -e 'create database ransack collate utf8_general_ci;'; + mysql --user=root --password=root --host=127.0.0.1 -e 'use ransack;show variables like "%character%";show variables like "%collation%";'; + - name: Install dependencies + run: bundle install + - name: Run tests + run: bundle exec rspec + + postgis: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + ruby: + - 3.2.2 + env: + DB: postgis + RAILS: main + DATABASE_USERNAME: postgres + DATABASE_PASSWORD: postgres + DATABASE_HOST: 127.0.0.1 + services: + postgres: + image: postgres + ports: + - 5432:5432 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_HOST_AUTH_METHOD: trust + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + - name: Setup databases + run: | + psql -h localhost -p 5432 -W postgres -c 'create database ransack;' -U postgres; + - name: Install dependencies + run: bundle install + - name: Run tests + run: bundle exec rspec + + postgres: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + ruby: + - 3.2.2 + env: + DB: postgres + RAILS: main + DATABASE_USERNAME: postgres + DATABASE_PASSWORD: postgres + DATABASE_HOST: 127.0.0.1 + services: + postgres: + image: postgres + ports: + - 5432:5432 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_HOST_AUTH_METHOD: trust + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + - name: Setup databases + run: | + psql -h localhost -p 5432 -W postgres -c 'create database ransack;' -U postgres; + - name: Install dependencies + run: bundle install + - name: Run tests + run: bundle exec rspec diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..661c39c7b --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,35 @@ +name: Deploy to GitHub Pages + +on: + push: + branches: + - main + +jobs: + deploy: + name: Deploy to GitHub Pages + + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: 16 + cache: yarn + cache-dependency-path: docs/yarn.lock + + - name: Install dependencies + run: yarn install --frozen-lockfile + working-directory: docs + + - name: Build website + run: yarn build + working-directory: docs + + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: docs/build diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml new file mode 100644 index 000000000..7d68b9f60 --- /dev/null +++ b/.github/workflows/rubocop.yml @@ -0,0 +1,20 @@ +name: rubocop + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.2.2 + - name: Install gems + run: bundle install --jobs 4 --retry 3 + - name: Run RuboCop + run: bundle exec rubocop --parallel diff --git a/.github/workflows/test-deploy.yml b/.github/workflows/test-deploy.yml new file mode 100644 index 000000000..8e2c0a1a9 --- /dev/null +++ b/.github/workflows/test-deploy.yml @@ -0,0 +1,29 @@ +name: Test deploy to GitHub Pages + +on: + pull_request: + branches: + - main + +jobs: + test-deploy: + name: Test deploy to GitHub Pages + + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: 16 + cache: yarn + cache-dependency-path: docs/yarn.lock + + - name: Install dependencies + run: yarn install --frozen-lockfile + working-directory: docs + + - name: Test build website + run: yarn build + working-directory: docs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..e45ed287a --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,165 @@ +name: test + +on: + push: + branches: + - main + pull_request: + +jobs: + sqlite3: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rails: + - 8-0-stable + - v7.2.2 + ruby: + - 3.2.2 + - 3.1.4 + exclude: + - rails: 8-0-stable + ruby: 3.1.4 + env: + DB: sqlite3 + RAILS: ${{ matrix.rails }} + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Run tests + run: bundle exec rspec + + mysql: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rails: + - 8-0-stable + - 7-2-stable + ruby: + - 3.2.2 + - 3.1.4 + exclude: + - rails: 8-0-stable + ruby: 3.1.4 + + env: + DB: mysql + RAILS: ${{ matrix.rails }} + MYSQL_USERNAME: root + MYSQL_PASSWORD: root + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Startup MySQL + run: | + sudo systemctl start mysql.service + - name: Setup databases + run: | + mysql --user=root --password=root --host=127.0.0.1 -e 'create database ransack collate utf8_general_ci;'; + mysql --user=root --password=root --host=127.0.0.1 -e 'use ransack;show variables like "%character%";show variables like "%collation%";'; + - name: Run tests + run: bundle exec rspec + + postgis: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + rails: + - 7-2-stable + ruby: + - 3.2.2 + - 3.1.4 + env: + DB: postgis + RAILS: ${{ matrix.rails }} + DATABASE_USERNAME: postgres + DATABASE_PASSWORD: postgres + DATABASE_HOST: 127.0.0.1 + services: + postgres: + image: postgres + ports: + - 5432:5432 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_HOST_AUTH_METHOD: trust + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Setup databases + run: | + psql -h localhost -p 5432 -W postgres -c 'create database ransack;' -U postgres; + - name: Run tests + run: bundle exec rspec + + postgres: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rails: + - 8-0-stable + - 7-2-stable + ruby: + - 3.2.2 + - 3.1.4 + exclude: + - rails: 8-0-stable + ruby: 3.1.4 + env: + DB: postgres + RAILS: ${{ matrix.rails }} + DATABASE_USERNAME: postgres + DATABASE_PASSWORD: postgres + DATABASE_HOST: 127.0.0.1 + services: + postgres: + image: postgres + ports: + - 5432:5432 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_HOST_AUTH_METHOD: trust + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Setup databases + run: | + psql -h localhost -p 5432 -W postgres -c 'create database ransack;' -U postgres; + - name: Run tests + run: bundle exec rspec + diff --git a/.gitignore b/.gitignore index 4040c6c10..10c6c4905 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,10 @@ .bundle Gemfile.lock pkg/* +spec/dummy +coverage/* +.DS_Store +.byebug_history +vendor/bundle +.idea + diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 000000000..c409daaa2 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,59 @@ +plugins: + - rubocop-rspec + +AllCops: + TargetRubyVersion: 3.0 + + DisabledByDefault: true + + Exclude: + - "docs/**/*" + +Layout/EmptyLineAfterMagicComment: + Enabled: true + +Layout/EmptyLineBetweenDefs: + Enabled: true + +Layout/EmptyLines: + Enabled: true + +Layout/EmptyLinesAroundBlockBody: + Enabled: true + +Layout/FirstArrayElementIndentation: + EnforcedStyle: consistent + +Layout/SpaceAfterComma: + Enabled: true + +Layout/SpaceInsideBlockBraces: + Enabled: true + +Layout/SpaceInsideHashLiteralBraces: + Enabled: true + +Layout/SpaceInsideParens: + Enabled: true + +Layout/TrailingEmptyLines: + Enabled: true + +RSpec/EmptyLineAfterFinalLet: + Enabled: true + +Style/HashSyntax: + Enabled: true + +Style/RedundantFileExtensionInRequire: + Enabled: true + +Style/RedundantReturn: + Enabled: true + +Style/SelfAssignment: + Enabled: true + +Style/Semicolon: + Enabled: true + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index cadea22d1..000000000 --- a/.travis.yml +++ /dev/null @@ -1,33 +0,0 @@ -language: ruby - -sudo: false - -rvm: - - 2.1 - - 2.0 - - 1.9 - -env: - - RAILS=master DB=sqlite3 - - RAILS=master DB=mysql - - RAILS=master DB=postgres - - RAILS=4-1-stable DB=sqlite3 - - RAILS=4-1-stable DB=mysql - - RAILS=4-1-stable DB=postgres - - RAILS=4-0-stable DB=sqlite3 - - RAILS=4-0-stable DB=mysql - - RAILS=4-0-stable DB=postgres - - RAILS=3-2-stable DB=sqlite - - RAILS=3-2-stable DB=mysql - - RAILS=3-2-stable DB=postgres - - RAILS=3-1-stable DB=sqlite - - RAILS=3-1-stable DB=mysql - - RAILS=3-1-stable DB=postgres - - RAILS=3-0-stable DB=sqlite - - RAILS=3-0-stable DB=mysql - - RAILS=3-0-stable DB=postgres - -before_script: - - mysql -e 'create database ransack collate utf8_general_ci;' - - mysql -e 'use ransack;show variables like "%character%";show variables like "%collation%";' - - psql -c 'create database ransack;' -U postgres diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d711c669..602a255d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,1062 @@ # Change Log -This change log was started in August 2014. All notable changes to this project -henceforth should be documented here. -## Unreleased +## From v4.4.0, the CHANGELOG is captured in the [Release info](https://github.com/activerecord-hackery/ransack/releases) + +## Historical releases + +## 4.3.0 - 2025-2-7 + +* Fix wildcard escaping with PostGIS adapter. +* Rails 8 compatibility +* Drop Rails 6 and 7.0 compatibility +* Raise Ransack::InvalidSearchError instead of ArgumentError on unknown conditions + +## 4.2.1 - 2024-8-11 + +* Fix Rails 7.1.x compatibility + +## 4.2.0 - 2024-7-10 + +* Add Rails 7.2 support by @robinator and @gregmolnar + +## 4.1.0 - 2023-10-23 + +### 🚀 Features + +* Add Rails 7.1.0 support by @yuki24 in https://github.com/activerecord-hackery/ransack/pull/1439 + +### 🐛 Bug Fixes + +* Fix wrong table aliases in Rails 6.1 by @oneiros in https://github.com/activerecord-hackery/ransack/pull/1447 + +### 💦 Compatibility + +* Drop Ruby 2.7 support by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1449 + +### 📝 Documentation + +* Update blog post link by @meg-gutshall in https://github.com/activerecord-hackery/ransack/pull/1425 +* Add namespaced example of Polymorphic search by @craigmcnamara in https://github.com/activerecord-hackery/ransack/pull/1422 +* Update sorting example to be self-contained by @kinduff in https://github.com/activerecord-hackery/ransack/pull/1442 + +### 💅 Polish + +* Minor updates for Rails 7.1 by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1448 +* Don't mention Ruby compatibility in contribution instructions by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1452 + +### 🏠 Internal + +* Bump @sideway/formula from 3.0.0 to 3.0.1 in /docs by @dependabot in https://github.com/activerecord-hackery/ransack/pull/1417 +* Bump webpack from 5.74.0 to 5.76.1 in /docs by @dependabot in https://github.com/activerecord-hackery/ransack/pull/1412 +* Bump semver from 5.7.1 to 5.7.2 in /docs by @dependabot in https://github.com/activerecord-hackery/ransack/pull/1431 +* Bump @babel/traverse from 7.18.2 to 7.23.2 in /docs by @dependabot in https://github.com/activerecord-hackery/ransack/pull/1446 +* Bump postcss from 8.4.14 to 8.4.31 in /docs by @dependabot in https://github.com/activerecord-hackery/ransack/pull/1443 +* Bump dns-packet from 5.3.1 to 5.6.1 in /docs by @dependabot in https://github.com/activerecord-hackery/ransack/pull/1450 +* Test with Ruby 3.2 by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1451 +* Misc CI bumps by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1453 +* RuboCop tweaks by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1454 + +## 4.0.0 - 2023-02-09 + +### 💥 Breaking Changes + +* **[SECURITY]** Require explicit allowlisting of attributes and associations by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1400 +* Remove Polyamorous entrypoint by @scarroll32 in https://github.com/activerecord-hackery/ransack/pull/1370 +* Remove dead MongoDB code by @scarroll32 in https://github.com/activerecord-hackery/ransack/pull/1345 + +### 🚀 Features + +* Add support for default predicates by @p8 in https://github.com/activerecord-hackery/ransack/pull/1384 + +### 🐛 Bug Fixes + +* Ignore `ActiveModel::RangeError` in Ransack conditions by @JunichiIto in https://github.com/activerecord-hackery/ransack/pull/1340 +* Fix crash when using `q=string` as parameter by @stereobooster in https://github.com/activerecord-hackery/ransack/pull/1374 +* Prevent changing host through params by @AndersGM in https://github.com/activerecord-hackery/ransack/pull/1391 + +### 📝 Documentation + +* Fix broken documentation link by @cpgo in https://github.com/activerecord-hackery/ransack/pull/1332 +* Remove more old wiki references by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1333 +* Improve some wording and correct some typos by @ydah in https://github.com/activerecord-hackery/ransack/pull/1336 +* Add warning about necessary authorization by @AmShaegar13 in https://github.com/activerecord-hackery/ransack/pull/1367 +* Fix required Ruby and Rails version in README by @tagliala in https://github.com/activerecord-hackery/ransack/pull/1389 + +### 💅 Polish + +* Implement CodeQL by @scarroll32 in https://github.com/activerecord-hackery/ransack/pull/1334 +* Code quality improvements by @scarroll32 in https://github.com/activerecord-hackery/ransack/pull/1371 +* Refactor adapters by @scarroll32 in https://github.com/activerecord-hackery/ransack/pull/1348 +* Fix typo: teh -> the by @jdufresne in https://github.com/activerecord-hackery/ransack/pull/1387 +* Fix broken link by @maful in https://github.com/activerecord-hackery/ransack/pull/1394 + +### 🏠 Internal + +* Bump docusaurus by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1338 +* Update dependencies by @scarroll32 in https://github.com/activerecord-hackery/ransack/pull/1342 +* Improve CI by @scarroll32 in https://github.com/activerecord-hackery/ransack/pull/1351 +* Improve CONTRIBUTING.md by @scarroll32 in https://github.com/activerecord-hackery/ransack/pull/1347 +* Add links to GitHub Discussions by @scarroll32 in https://github.com/activerecord-hackery/ransack/pull/1353 +* Bump terser from 5.14.0 to 5.14.2 in /docs by @dependabot in https://github.com/activerecord-hackery/ransack/pull/1355 +* Bump loader-utils from 2.0.2 to 2.0.3 in /docs by @dependabot in https://github.com/activerecord-hackery/ransack/pull/1372 +* Bump loader-utils from 2.0.3 to 2.0.4 in /docs by @dependabot in https://github.com/activerecord-hackery/ransack/pull/1378 +* Upgrade some documentation dependencies by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1379 +* Upgrade local search plugin too by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1380 +* Bump json5 from 2.2.1 to 2.2.3 in /docs by @dependabot in https://github.com/activerecord-hackery/ransack/pull/1390 +* Bump ua-parser-js from 0.7.31 to 0.7.33 in /docs by @dependabot in https://github.com/activerecord-hackery/ransack/pull/1397 +* Bump some doc deps by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1398 +* Bump http-cache-semantics from 4.1.0 to 4.1.1 in /docs by @dependabot in https://github.com/activerecord-hackery/ransack/pull/1401 + +## 3.2.1 - 2022-05-24 + +* Add search functionality to documentation site. + PR [1329](https://github.com/activerecord-hackery/ransack/pull/1329) + +* Fix contributing URLs and syntax highlight in `README.md`. + PR [1326](https://github.com/activerecord-hackery/ransack/pull/1326) + +* Cast PostgreSQL's `timestamptz` columns to time. + PR [1325](https://github.com/activerecord-hackery/ransack/pull/1325) + +* Add Ruby and ERB syntax highlighting support to documentation site. + PR [1324](https://github.com/activerecord-hackery/ransack/pull/1324) + +* Fix a wrong link in `CHANGELOG.md`. + PR [1323](https://github.com/activerecord-hackery/ransack/pull/1323) + +* Update `CONTRIBUTING.md` to encourage failing tests in PRs instead of bug report templates. + PR [1321](https://github.com/activerecord-hackery/ransack/pull/1321) + +## 3.2.0 - 2022-05-08 + +* Drop Rails 6.0 support. + PR [1318](https://github.com/activerecord-hackery/ransack/pull/1318) + +* Exclude "host" from params sent to url generator. + PR [1317](https://github.com/activerecord-hackery/ransack/pull/1317) + +## 3.1.0 - 2022-04-21 + +* Fix predicate name in "Using Predicates" documentation page. + PR [1313](https://github.com/activerecord-hackery/ransack/pull/1313) + +* Drop Ruby 2.6 support. + PR [1311](https://github.com/activerecord-hackery/ransack/pull/1311) + +* Allow Ransack to be used with Rails 7.1.0.alpha. + PR [1309](https://github.com/activerecord-hackery/ransack/pull/1309) + +* Put contributor list last in documentation site. + PR [1308](https://github.com/activerecord-hackery/ransack/pull/1308) + +* Add `acts-as-taggable-on` and polymorphic searches to documentation. + PR [1306](https://github.com/activerecord-hackery/ransack/pull/1306) + PR [1312](https://github.com/activerecord-hackery/ransack/pull/1312) + +* Add full link to issue about merging searches to documentation. + PR [1305](https://github.com/activerecord-hackery/ransack/pull/1305) + +## 3.0.1 - 2022-04-08 + +* Fix `:data` option to `sort_link` being incorrectly appended to the generated + link query parameters. + PR [1301](https://github.com/activerecord-hackery/ransack/pull/1301) + +* Fix "Edit this page" links in documentation site + PR [1303](https://github.com/activerecord-hackery/ransack/pull/1303) + +* Auto deploy documentation site after merging PRs + PR [1302](https://github.com/activerecord-hackery/ransack/pull/1302) + +* Add list of former wiki contributors to documentation site + PR [1300](https://github.com/activerecord-hackery/ransack/pull/1300) + +* Reduce gem package size + PR [1297](https://github.com/activerecord-hackery/ransack/pull/1297) + +## 3.0.0 - 2022-03-30 + +* Move documentation into Docusaurus. + PR [1291](https://github.com/activerecord-hackery/ransack/pull/1291) + +* [BREAKING CHANGE] Remove deprecated `#search` method. + PR [1147](https://github.com/activerecord-hackery/ransack/pull/1147) + +* Allow scopes that define string SQL joins. + PR [1225](https://github.com/activerecord-hackery/ransack/pull/1225) + +* Improve `sort_link` documentation. + PR [1290](https://github.com/activerecord-hackery/ransack/pull/1290) + +* Deprecate passing two trailing hashes to `sort_link`, for example: + + ```ruby + sort_link(@q, :bussiness_name, "bussines_name", {}, class: "foo") + ``` + + Pass a single hash with all options instead. + PR [1289](https://github.com/activerecord-hackery/ransack/pull/1289) + +* Fix `:class` option to `sort_link` not being passed to the generated link + correctly when no additional options are passed. For example: + + ```ruby + sort_link(@q, :bussiness_name, class: "foo") + ``` + + PR [1288](https://github.com/activerecord-hackery/ransack/pull/1288) + +* Evaluate `ransackable_scopes` before attributes when building the query. + PR [759](https://github.com/activerecord-hackery/ransack/pull/759) + +## 2.6.0 - 2022-03-08 + +* Fix regression when joining a table with itself. + PR [1275](https://github.com/activerecord-hackery/ransack/pull/1276) + +* Drop support for ActiveRecord older than 6.0.4. + PR [1276](https://github.com/activerecord-hackery/ransack/pull/1276) + +## 2.5.0 - 2021-12-26 + +* Document release process by @scarroll32 in https://github.com/activerecord-hackery/ransack/pull/1199, https://github.com/activerecord-hackery/ransack/pull/1200. +* Support Rails 7 by @yahonda in https://github.com/activerecord-hackery/ransack/pull/1205, https://github.com/activerecord-hackery/ransack/pull/1209, https://github.com/activerecord-hackery/ransack/pull/1234, https://github.com/activerecord-hackery/ransack/pull/1230, https://github.com/activerecord-hackery/ransack/pull/1266 +* Fix for `ActiveRecord::UnknownAttributeReference` in ransack by @TechnologyHypofriend in https://github.com/activerecord-hackery/ransack/pull/1207 +* Make gem compatible with old polyamorous require by @rtweeks in https://github.com/activerecord-hackery/ransack/pull/1145 +* Adding swedish translations by @johanandre in https://github.com/activerecord-hackery/ransack/pull/1208 +* Document how to do case insensitive searches by @scarroll32 in https://github.com/activerecord-hackery/ransack/pull/1213 +* Add the ability to disable whitespace stripping for string searches by @DCrow in https://github.com/activerecord-hackery/ransack/pull/1214 +* Fix `:default` option in `Translate.attribute` method by @coreyaus in https://github.com/activerecord-hackery/ransack/pull/1218 +* Fix typo in README.md by @d-m-u in https://github.com/activerecord-hackery/ransack/pull/1220 +* Fix another typo in README.md by @plan-do-break-fix in https://github.com/activerecord-hackery/ransack/pull/1221 +* Fix several documentation typos @wonda-tea-coffee in https://github.com/activerecord-hackery/ransack/pull/1233 +* Allow ransack to treat nulls as always first or last by @mollerhoj in https://github.com/activerecord-hackery/ransack/pull/1226 +* Consider ransack aliases when sorting by @faragorn and @waldyr in https://github.com/activerecord-hackery/ransack/pull/1223 +* Fix non-casted array predicates by @danielpclark in https://github.com/activerecord-hackery/ransack/pull/1246 +* Remove Squeel references from README by @Schwad in https://github.com/activerecord-hackery/ransack/pull/1249 +* Remove part of the README that might lead to incorrect results by @RadekMolenda in https://github.com/activerecord-hackery/ransack/pull/1258 +* ActiveRecord 7.0 support + +## 2.4.2 - 2021-01-23 + +* Enable RuboCop and configure GitHub Actions to run RuboCop by @yahonda in https://github.com/activerecord-hackery/ransack/pull/1185 +* Add Ruby 3.0.0 support by @yahonda in https://github.com/activerecord-hackery/ransack/pull/1190 +* Drop Ruby 2.5 or older versions of Ruby by @yahonda in https://github.com/activerecord-hackery/ransack/pull/1189 +* Remove bug report templates and encourage failing tests in PRs instead by @yahonda in https://github.com/activerecord-hackery/ransack/pull/1191 +* Allow Ransack to be tested with Rails main branch by @yahonda in https://github.com/activerecord-hackery/ransack/pull/1192 + +## 2.4.1 - 2020-12-21 + +* Links to Tidelift subscription by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1178 +* Enable GitHub Actions by @yahonda in https://github.com/activerecord-hackery/ransack/pull/1180 +* Move security contact information to SECURITY.md by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1179 +* Add `ActiveRecord::Base.ransack!` which raises error if passed unknown condition by @alipman88 in https://github.com/activerecord-hackery/ransack/pull/1132 +* Add ability to config PostgreSQL ORDER BY ... NULLS FIRST or NULLS LAST by @itsalongstory in https://github.com/activerecord-hackery/ransack/pull/1184 + +## 2.4.0 - 2020-11-27 + +* Specify actual version of polyamorous, so we can release that separately by @gregmolnar in https://github.com/activerecord-hackery/ransack/pull/1101 +* Only include necessary files in gem package by @tvdeyen in https://github.com/activerecord-hackery/ransack/pull/1104 +* Test/Fix for subquery in Rails 5.2.4 by @stevenjonescgm in https://github.com/activerecord-hackery/ransack/pull/1112 +* Polyamorous module by @varyonic in https://github.com/activerecord-hackery/ransack/pull/1113 +* Remove duplicated rows by @sasharevzin in https://github.com/activerecord-hackery/ransack/pull/1116 +* Fix Ruby 2.7 deprecation warnings by @terracatta in https://github.com/activerecord-hackery/ransack/pull/1121 +* Fixes polymorphic joins. by @PhilCoggins in https://github.com/activerecord-hackery/ransack/pull/1122 +* Drop support for activerecord older than 5.2.4 by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1166 +* Adapt to quoting change in Rails by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1165 +* Typo in docs by @brett-anderson in https://github.com/activerecord-hackery/ransack/pull/1155 +* Add Rails 6.1 support by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1172 +* Strip Leading & Trailing Whitespace Before Searching by @itsalongstory in https://github.com/activerecord-hackery/ransack/pull/1126 +* Use unfrozen version of symbol to string by @fauno in https://github.com/activerecord-hackery/ransack/pull/1149 + +## 2.3.2 - 2020-01-11 + +* Breakfix to bump Polyamorous + +## 2.3.1 - 2020-01-11 + +* Drop support for Active Record 5.0, 5.1, and 5.2.0. + PR [#1073](https://github.com/activerecord-hackery/ransack/pull/1073) + +* Drop support for rubies under 2.3. + PR [#1070](https://github.com/activerecord-hackery/ransack/pull/1070) + + ... and others + +## 2.3.0 - 2019-08-18 + +* Arabic translations PR [979](https://github.com/activerecord-hackery/ransack/pull/979) + +* Rails 6 PR [1027](https://github.com/activerecord-hackery/ransack/pull/1027) + *vrodokanakis* + +* Make polyamorous a separate gem PR [1002](https://github.com/activerecord-hackery/ransack/pull/1002) + +* Catalan translations PR[1007](https://github.com/activerecord-hackery/ransack/pull/1007) + *roslavych* + +* Don't escape period characters with wildcard searches in mysql2 PR [1013](https://github.com/activerecord-hackery/ransack/pull/1013) + *daflip* + +* Farsi translations PR [1030](https://github.com/activerecord-hackery/ransack/pull/1030) + +* Finnish translations PR [1049](https://github.com/activerecord-hackery/ransack/pull/1049) + +* Fix wrong table alias when using nested join. for ActiveRecord >= 5.2 + PR [374](https://github.com/activerecord-hackery/ransack/pull/374) + + *hiichan* + +## Version 2.1.1 - 2018-12-05 + +* Add `arabic` translation + https://github.com/activerecord-hackery/ransack/pull/979 + +* Deprecate #search + PR [975](https://github.com/activerecord-hackery/ransack/pull/975) + +## Version 2.1.0 - 2018-10-26 + +* Add support for sorting by scopes + PR [973](https://github.com/activerecord-hackery/ransack/pull/973) + + *Diego Borges* + +* Added a new logo for Ransack + PR [972](https://github.com/activerecord-hackery/ransack/pull/972) + + *Anıl Kılıç*, *Greg Molnar* + +* Greek translations + PR [971](https://github.com/activerecord-hackery/ransack/pull/971) + PR [960](https://github.com/activerecord-hackery/ransack/pull/960) + + *Sean Carroll*, *Greg Molnar* + +* README improvements + PR [963](https://github.com/activerecord-hackery/ransack/pull/963) + + *tommaso1* + +* Bulgarian translations + PR [961](https://github.com/activerecord-hackery/ransack/pull/961) + + *Sean Carroll* + +* README improvements + PR [956](https://github.com/activerecord-hackery/ransack/pull/956) + + *Alex Konoval* + +* Remove lib/ransack/adapters/active_record/compat.rb + PR [954](https://github.com/activerecord-hackery/ransack/pull/954) + + *Ryuta Kamizono* + +* Remove unused aliases + PR [953](https://github.com/activerecord-hackery/ransack/pull/953) + + *Ryuta Kamizono* + +## Version 2.0.1 - 2018-08-18 + +* Don't return association if table is nil + PR [952](https://github.com/activerecord-hackery/ransack/pull/952) + + *Christian Gregg* + +## Version 2.0.0 - 2018-08-09 + +* Add support for Active Record 5.2.1 + PR [#938](https://github.com/activerecord-hackery/ransack/pull/938) + +* Fix sort with joins on existing association + PR [#937](https://github.com/activerecord-hackery/ransack/pull/937) + +* Add the ability to skip arg sanitization on a per scope basis. Using + `ransackable_scopes_skip_sanitize_args`, users can define a list of + scopes which will bypass parameter sanitization. This allows passing 0, + 1, t, f, etc. to a scope as an actual parameter. + PR [#933](https://github.com/activerecord-hackery/ransack/pull/933) + +* Drop support for Active Record < 5.0. + PR [#929](https://github.com/activerecord-hackery/ransack/pull/929) + +* Extract mongoid support to a separate gem. + PR [#928](https://github.com/activerecord-hackery/ransack/pull/928) + +* Absorb polyamorous + PR [#927](https://github.com/activerecord-hackery/ransack/pull/927) + +* Fix broken monkey patch of #form_with + PR [#922](https://github.com/activerecord-hackery/ransack/pull/922) + +## Version 1.8.8 - 2018-03-16 +* Fix multiple database support + PR [#893](https://github.com/activerecord-hackery/ransack/pull/893) + +* Updated Dutch translations + PR [#887](https://github.com/activerecord-hackery/ransack/pull/887) + +* Fixed no method error 'asc' for Rails 4.2 + PR [#885](https://github.com/activerecord-hackery/ransack/pull/885) + + +## Version 1.8.7 - 2018-02-05 + +* Rails 5.2 support + PR [#868](https://github.com/activerecord-hackery/ransack/pull/868) + +* Lock pg gem to 0.21 to support older releases + +* Warnings cleanup + PR [#867](https://github.com/activerecord-hackery/ransack/pull/867) + +* Wildcard escaping + PR [#866] + +## Version 1.8.6 - 2018-01-23 + +### Added + +* Improve memory usage + PR [#820](https://github.com/activerecord-hackery/ransack/pull/820) + +* Bump Polyamorous version to 1.3.2 + PR [#858](https://github.com/activerecord-hackery/ransack/pull/858) + +## Version 1.8.5 + +### Added + +* Added Turkish Translations + PR [#835](https://github.com/activerecord-hackery/ransack/issues/835). + +## Version 1.8.4 - 2017-10-09 + +### Added + +* Added italian translations. + PR [#833](https://github.com/activerecord-hackery/ransack/pull/833). + +* Add an optional default arrow for unsorted fields. + PR [#816](https://github.com/activerecord-hackery/ransack/pull/816/files). + +### Fixed + +* Cast Postgres money type to float. + PR [#823](https://github.com/activerecord-hackery/ransack/pull/823). + +* Fix the bug in sort_link, which causes the multiple fields option to be + ignored when block parameter is specified. + PR [#818](https://github.com/activerecord-hackery/ransack/pull/818). + +* No need pass some arguments to JoinAssociation#join_constraints in Rails 5.1. + PR [#814](https://github.com/activerecord-hackery/ransack/pull/814). + Fixes [#807](https://github.com/activerecord-hackery/ransack/issues/807). + Reference [rails/rails#28267](https://github.com/rails/rails/pull/28267) + and [rails/rails#27851](https://github.com/rails/rails/pull/27851). + +## Version 1.8.3 - 2017-06-15 + +### Added + +* Add a config option to customize the up and down arrows used for direction + indicators in Ransack sort links. + PR [#726](https://github.com/activerecord-hackery/ransack/pull/726). + + *Garett Arrowood* + +* Add ability to turn off sanitization of custom scope arguments. + PR [#742](https://github.com/activerecord-hackery/ransack/pull/742). + + *Garett Arrowood* + +### Fixed + +* Use class attributes properly so that inheritance is respected. + PR [#717](https://github.com/activerecord-hackery/ransack/pull/717). + This fixes two bugs: + + 1. In the Mongoid adapter, subclasses were not properly inheriting their + parents' Ransack aliases because each class defined its own set of + aliases. + + 2. In the Active Record adapter, Ransack aliases were defined in such a way + that the parent's (and grandparent's, etc.) aliases were overwritten by + the child, meaning that all aliases were ultimately kept on + `ActiveRecord::Base`. This had the unfortunate effect of enforcing + uniqueness of Ransack alias names across all models rather than per + model. Depending on the load order of models, earlier definitions of an + alias in other models were clobbered. + + *Steve Richert (laserlemon)* + +* Use `ActiveSupport.on_load` hooks to include Ransack in Active Record, + avoiding autoloading the constant too soon. PR + [#719](https://github.com/activerecord-hackery/ransack/pull/719). Reference: + [This comment in rails#23589] + (https://github.com/rails/rails/issues/23589#issuecomment-229247727). + + *Yuji Yaginuma (y-yagi)* + +## Version 1.8.2 - 2016-08-08 +### Fixed + +* Fix empty attribute_fields regression in advanced search mode introduced by + [235eae3](https://github.com/activerecord-hackery/ransack/commit/235eae3). + Closes + [#701](https://github.com/activerecord-hackery/ransack/issues/701). Commit + [2839acf](https://github.com/activerecord-hackery/ransack/commit/2839acf). + + *Jon Atack, Jay Dorsey, Stefan Haslinger, Igor Kasyanchuk* + +### Added + +* Add `sort_url` view helper that returns only the url of a `sort_link`. PR + [#706](https://github.com/activerecord-hackery/ransack/pull/706). + + *amatotsuji* + +## Version 1.8.1 - 2016-07-27 +### Fixed + +* Fix `rake console` to run a command-line console with ransack + seed data. + Commits + [2cc781e](https://github.com/activerecord-hackery/ransack/commit/2cc781e), + [f2e85ad](https://github.com/activerecord-hackery/ransack/commit/f2e85ad), + [6a059ba](https://github.com/activerecord-hackery/ransack/commit/6a059ba). + + *Jon Atack* + +* Fix returned value of `Ransack::Nodes::Condition#format_predicate`. PR + [#692](https://github.com/activerecord-hackery/ransack/pull/692). + + *Masahiro Saito* + +* Better test coverage on passing arrays to ransackers. Commit + [98df2c5](https://github.com/activerecord-hackery/ransack/commit/98df2c5). + + *Jon Atack* + +* Fix missing Ransack::Constants::ASC constant. Commit + [aece23c](https://github.com/activerecord-hackery/ransack/commit/aece23c). + + *Jon Atack* + +### Changed + +* Replace arrow constants with frozen strings in public methods. Commits + [c0dff33](https://github.com/activerecord-hackery/ransack/commit/c0dff33), + [e489ca7](https://github.com/activerecord-hackery/ransack/commit/e489ca7). + + *Jon Atack* + +## Version 1.8.0 - 2016-07-14 +### Added + +* Support Mongoid 5. PR [#636](https://github.com/activerecord-hackery/ransack/pull/636), commit + [9e5faf4](https://github.com/activerecord-hackery/ransack/commit/9e5faf4). + + *Josef Šimánek* + +* Add optional block argument for the `sort_link` method. PR + [#604](https://github.com/activerecord-hackery/ransack/pull/604). + + *Andrea Dal Ponte* + +* Add `ransack_alias` to allow users to customize the names for long + ransack field names. PR + [#623](https://github.com/activerecord-hackery/ransack/pull/623). + + *Ray Zane* + +* Add support for searching on attributes that have been added to + Active Record models with `alias_attribute` (Rails >= 4 only). PR + [#592](https://github.com/activerecord-hackery/ransack/pull/592), commit + [549342a](https://github.com/activerecord-hackery/ransack/commit/549342a). + + *Marten Schilstra* + +* Add ability to globally hide sort link order indicator arrows with + `Ransack.configure#hide_sort_order_indicators = true`. PR + [#577](https://github.com/activerecord-hackery/ransack/pull/577), commit + [95d4591](https://github.com/activerecord-hackery/ransack/commit/95d4591). + + *Josh Hunter*, *Jon Atack* + +* Add test for `ActionController:Parameter` object params in `sort_link` to + ensure Ransack is handling the Rails 5 changes correctly. Commit + [b1cfed8](https://github.com/activerecord-hackery/ransack/commit/b1cfed8). + + *Ryan Wood* + +* Add failing tests to facilitate work on issue + [#566](https://github.com/activerecord-hackery/ransack/issues/566) + of passing boolean values to search scopes. PR + [#575](https://github.com/activerecord-hackery/ransack/pull/575). + + *Marcel Eeken* + +* Add i18n locale files: + * Taiwanese Hokkien/Mandarin (`zh-TW.yml`). PR + [#674](https://github.com/activerecord-hackery/ransack/pull/674). *Sibevin Wang* + * Danish (`da.yml`). PR + [#663](https://github.com/activerecord-hackery/ransack/pull/663). *Kasper Johansen* + * Brazilian Portuguese (`pt-BR.yml`). PR + [#581](https://github.com/activerecord-hackery/ransack/pull/581). *Diego Henrique Domingues* + * Indonesian (Bahasa) (`id.yml`). PR + [#612](https://github.com/activerecord-hackery/ransack/pull/612). *Adam Pahlevi Baihaqi* + * Japanese (`ja.yml`). PR + [#622](https://github.com/activerecord-hackery/ransack/pull/622). *Masanobu Mizutani* + +### Fixed + +* In `FormHelper::SortLink#parameters_hash`, convert `params#to_unsafe_h` + only if Rails 5, and add tests. Commit + [14e66ca](https://github.com/activerecord-hackery/ransack/commit/14e66ca). + + *Jon Atack* + +* Respect negative conditions for collection associations and fix Mongoid + compat. PR [#645](https://github.com/activerecord-hackery/ransack/pull/645). + + *Andrew Vit* + +* Ensure conditions differing only by ransacker_args aren't filtered out. + PR [#665](https://github.com/activerecord-hackery/ransack/pull/665). + + *Andrew Porterfield* + +* Fix using aliased attributes in association searches, and add a failing + spec. PR [#602](https://github.com/activerecord-hackery/ransack/pull/602). + + *Marten Schilstra* + +* Replace Active Record `table_exists?` API that was deprecated + [here](https://github.com/rails/rails/commit/152b85f) in Rails 5. Commit + [c9d2297](https://github.com/activerecord-hackery/ransack/commit/c9d2297). + + *Jon Atack* + +* Adapt to changes in Rails 5 where AC::Parameters composes a HWIA instead of + inheriting from Hash starting from Rails commit rails/rails@14a3bd5. Commit + [ceafc05](https://github.com/activerecord-hackery/ransack/commit/ceafc05). + + *Jon Atack* + +* Fix test `#sort_link with hide order indicator set to true` to fail properly + ([4f65b09](https://github.com/activerecord-hackery/ransack/commit/4f65b09)). + This spec, added in + [#473](https://github.com/activerecord-hackery/ransack/pull/473), tested + the presence of the attribute name instead of the absence of the order + indicators and did not fail when it should. + + *Josh Hunter*, *Jon Atack* + +* Fix rspec-mocks `stub` deprecation warnings when running the tests. Commit + [600892e](https://github.com/activerecord-hackery/ransack/commit/600892e). + + *Jon Atack* + +* Revert + [f858dd6](https://github.com/activerecord-hackery/ransack/commit/f858dd6). + Fixes [#553](https://github.com/activerecord-hackery/ransack/issues/553) + performance regression with the SQL Server adapter. + + *sschwing3* + +* Fix invalid Chinese I18n locale file name by replacing "zh" with "zh-CN". + PR [#590](https://github.com/activerecord-hackery/ransack/pull/590). + + *Ethan Yang* + +### Changed + +* Memory/speed perf improvement: Freeze strings in array global constants and + partially move from using global string constants to frozen strings + ([381a83c](https://github.com/activerecord-hackery/ransack/commit/381a83c) + and + [ce114ec](https://github.com/activerecord-hackery/ransack/commit/ce114ec)). + + *Jon Atack* + +* Escape underscore `_` wildcard characters with PostgreSQL and MySQL. PR + [#584](https://github.com/activerecord-hackery/ransack/issues/584). + + *Igor Dobryn* + +* Refactor `Ransack::Adapters` from conditionals to classes + ([94a404c](https://github.com/activerecord-hackery/ransack/commit/94a404c)). + + *Jon Atack* + +## Version 1.7.0 - 2015-08-20 +### Added + +* Add Mongoid support for referenced/embedded relations. PR + [#498](https://github.com/activerecord-hackery/ransack/pull/498). + TODO: Missing spec coverage! Add documentation! + + *Penn Su* + +* Add German i18n locale file (`de.yml`). PR + [#537](https://github.com/activerecord-hackery/ransack/pull/537). + + *Philipp Weissensteiner* + +### Fixed + +* Fix + [#499](https://github.com/activerecord-hackery/ransack/issues/499) and + [#549](https://github.com/activerecord-hackery/ransack/issues/549). + Ransack now loads only Active Record if both Active Record and Mongoid are + running to avoid the two adapters overriding each other. This clarifies + that Ransack currently knows how to work with only one database adapter + active at a time. PR + [#541](https://github.com/activerecord-hackery/ransack/pull/541). + + *ASnow (Большов Андрей)* + +* Fix [#299](https://github.com/activerecord-hackery/ransack/issues/299) + `attribute_method?` parsing for attribute names containing `_and_` + and `_or_`. Attributes named like `foo_and_bar` or `foo_or_bar` are + recognized now instead of running failing checks for `foo` and `bar`. + PR [#562](https://github.com/activerecord-hackery/ransack/pull/562). + + *Ryohei Hoshi* + +* Fix a time-dependent test failure. When the database has + `default_timezone = :local` (system time) and the `Time.zone` is set to + elsewhere, then `Date.current` does not match what the query produces for + the stored timestamps. Resolved by setting everything to UTC. PR + [#561](https://github.com/activerecord-hackery/ransack/pull/561). + + *Andrew Vit* + +* Avoid overwriting association conditions with default scope in Rails 3. + When a model with default scope was associated with conditions + (`has_many :x, conditions: ...`), the default scope would overwrite the + association conditions. This patch ensures that both sources of conditions + are applied. Avoid selecting records from joins that would normally be + filtered out if they were selected from the base table. Only applies to + Rails 3, as this issue was fixed since Rails 4. PR + [#560](https://github.com/activerecord-hackery/ransack/pull/560). + + *Andrew Vit* + +* Fix RSpec `its` method deprecation warning: "Use of rspec-core's its + method is deprecated. Use the rspec-its gem instead" + ([c09aa17](https://github.com/activerecord-hackery/ransack/commit/c09aa17)). + +* Fix deprecated RSpec syntax in `grouping_spec.rb` + ([ba92a0b](https://github.com/activerecord-hackery/ransack/commit/ba92a0b)). + + *Jon Atack* + +### Changed + +* Upgrade gemspec dependencies: MySQL2 from '0.3.14' to '0.3.18', and RSpec + from '~> 2.14.0' to '~> 2' which loads 2.99 + ([000cd22](https://github.com/activerecord-hackery/ransack/commit/000cd22)). + +* Upgrade spec suite to RSpec 3 `expect` syntax backward compatible with + RSpec 2.9 + ([87cd36d](https://github.com/activerecord-hackery/ransack/commit/87cd36d) + and + [d296caa](https://github.com/activerecord-hackery/ransack/commit/d296caa)). + +* Various FormHelper refactorings + ([17dd97a](https://github.com/activerecord-hackery/ransack/commit/17dd97a) + and + [29a73b9](https://github.com/activerecord-hackery/ransack/commit/29a73b9)). + +* Various documentation updates. + + *Jon Atack* + + +## Version 1.6.6 - 2015-04-05 +### Added + +* Add the Ruby version to the the header message that shows the database, + Active Record and Arel versions when running tests. + +* Add Code Climate analysis. + + *Jon Atack* + +### Fixed + +* An improved fix for the "undefined method `model_name` for Ransack::Search" + issue [#518](https://github.com/activerecord-hackery/ransack/issues/518) + affecting Rails 4.2.1 and 5.0.0. This fix allows us to remove the + ActionView patch in Ransack version 1.6.4. + + *Gleb Mazovetskiy* + +* Fix an erroneous reference in `ActiveRecord::Associations::JoinDependency` + to a version-dependent Active Record reference, and replace it with a + better, more reliable one defined in Polyamorous. As this class lives + inside an `ActiveRecord` module, the reference needs to be absolute in + order to properly point to the AR class. + + *Nahuel Cuesta Luengo* + +* Fix RubyGems YARD rendering of the README docs. + + *Jon Atack* + +### Changed + +* Upgrade Polyamorous dependency to version 1.2.0, which uses `Module#prepend` + instead of `alias_method` for hooking into Active Record (with Ruby 2.x). + + *Jon Atack* + + +## Version 1.6.5 - 2015-03-28 - Rails 5.0.0 update +### Added + +* [WIP/experimental] Add compatibility with Rails 5/master and Arel 7. + +* Update the [Contributing Guide](CONTRIBUTING.md) with detailed steps for + contributing to Ransack. + +* Broaden the test suite database options in `schema.rb` and add + code documentation. + +* Improve the header message when running tests. + + *Jon Atack* + +* Allow `:wants_array` to be set to `false` in the predicate options + ([#32](https://github.com/activerecord-hackery/ransack/issues/32)). + + *Michael Pavling* + +* Add a failing spec for issue + [#374](https://github.com/activerecord-hackery/ransack/issues/374). + + *Jamie Davidson*, *Jon Atack* + +### Fixed + +* Stop relying on `Active Record::relation#where_values` which are deprecated + in Rails 5. + +* Make the test for passing search arguments to a ransacker + (`ransacker_args`) work correctly with Sqlite3. + + *Jon Atack* + +### Changed + +* Stop CI testing for Rails 3.0 to reduce the size of the Travis test matrix. + + *Jon Atack* + + +## Version 1.6.4 - 2015-03-20 - Rails 4.2.1 update + +* ActionView patch to maintain compatibility with Rails 4.2.1 released today. + + *Jon Atack* + +* Enable scoping I18n by 'ransack.models' + ([#514](https://github.com/activerecord-hackery/ransack/pull/514)). + + *nagyt234* + +* Add ransacker arguments + ([#513](https://github.com/activerecord-hackery/ransack/pull/513)). + + *Denis Tataurov*, *Jon Atack* + + +## Version 1.6.3 - 2015-01-21 + +* Fix a regression + ([#496](https://github.com/activerecord-hackery/ransack/issues/496)) caused + by [ee571fe](https://github.com/activerecord-hackery/ransack/commit/ee571fe) + where passing a multi-parameter attribute (like `date_select`) raised + `RuntimeError: can't add a new key into hash during iteration`, and add a + regression spec for the issue. + + *Nate Berkopec*, *Jon Atack* + +* Update travis-ci to no longer test Rails 3.1 with Ruby 2.2 and speed up the test matrix. + +* Refactor Nodes::Condition. + + *Jon Atack* + + +## Version 1.6.2 - 2015-01-14 + +* Fix a regression + ([#494](https://github.com/activerecord-hackery/ransack/issues/494)) + where passing an array of routes to `search_form_for` no longer worked, + and add a failing/passing test that would have caught the issue. + + *Daniel Rikowski*, *Jon Atack* + + +## Version 1.6.1 - 2015-01-14 + +* Fix a regression with using `in` predicates caused by PR [#488](https://github.com/activerecord-hackery/ransack/pull/488)) and add a test. + +* README improvements to clarify `sort_link` syntax with associations and + Ransack#search vs #ransack. + +* Default the Gemfile to Rails 4-2-stable. + + *Jon Atack* + + +## Version 1.6.0 - 2015-01-12 +### Added + +* Add support for using Ransack with `Mongoid 4.0` without associations + ([PR #407](https://github.com/activerecord-hackery/ransack/pull/407)). + + *Zhomart Mukhamejanov* + +* Add support and tests for passing stringy booleans for ransackable scopes + ([PR #460](https://github.com/activerecord-hackery/ransack/pull/460)). + + *Josh Kovach* + +* Add an sort_link option to not display sort order indicator arrows + ([PR #473](https://github.com/activerecord-hackery/ransack/pull/473)). + + *Fred Bergman* + +* Numerous documentation improvements to the README, Contributing Guide and + wiki. + + *Jon Atack* + +### Fixed + +* Fix passing arrays to ransackers with Rails 4.2 / Arel 6.0 (pull requests + [#486](https://github.com/activerecord-hackery/ransack/pull/486) and + [#488](https://github.com/activerecord-hackery/ransack/pull/488)). + + *Idean Labib* + +* Make `search_form_for`'s default `:as` option respect the custom search key + if it has been set + ([PR #470](https://github.com/activerecord-hackery/ransack/pull/470)). + Prior to this change, if you set a custom `search_key` option in the + Ransack initializer file, you'd have to also pass an `as: :whatever` option + to all of the search forms. Fixes + [#92](https://github.com/activerecord-hackery/ransack/issues/92). + + *Robert Speicher* + +* Fix sorting on polymorphic associations (missing downcase) + ([PR #467](https://github.com/activerecord-hackery/ransack/pull/467)). + + *Eugen Neagoe* + +* Fix Rails 5 / Arel 5 compatibility after the Arel and Active Record API + changed. + +* Fix and add tests for sort_link `default_order` parsing if the option is set + as a string instead of symbol. + +* Fix and add a test to handle `nil` in options passed to sort_link. + +* Fix #search method name conflicts in the README. + + *Jon Atack* + +### Changed + +* Refactor and DRY up FormHelper#SortLink. Encapsulate parsing into a + Plain Old Ruby Object with few public methods and small, private functional + methods. Limit mutations to explicit methods and mutate no ivars. + +* Numerous speed improvements by using more specific Ruby methods like: + - `Hash#each_key` instead of `Hash#keys.each` + - `#none?` instead of `select#empty?` + - `#any?` instead of `#select` followed by `#any?` + - `#flat_map` instead of `#flatten` followed by `#map` + - `!include?` instead of `#none?` + +* Replace `string#freeze` instances with top level constants to reduce string + allocations in Ruby < 2.1. + +* Remove unneeded `Ransack::` namespacing on most of the constants. + +* In enumerable methods, pass a symbol as an argument instead of a block. + +* Update Travis-ci for Rails 5.0.0 and 4-2-stable. + +* Update the Travis-ci tests and the Gemfile for Ruby 2.2. + +* Replace `#search` with `#ransack` class methods in the README and wiki + code examples. Enabling the `#search` alias by default may possibly be + deprecated in the next major release (Ransack v.2.0.0) to address + [#369](https://github.com/activerecord-hackery/ransack/issues/369). + + *Jon Atack* + + +## Version 1.5.1 - 2014-10-30 +### Added + +* Add base specs for search on fields with `_start` and `_end`. + +* Add a failing spec for detecting attribute fields containing `_and_` that + needs to be fixed. Attribute names containing `_and_` and `_or_` are still + not parsed/detected correctly. + + *Jon Atack* + +### Fixed + +* Fix a regression caused by incorrect string constants in `context.rb`. + + *Kazuhiro Nishiyama* + +### Changed + +* Remove duplicate code in `spec/support/schema.rb`. + + *Jon Atack* + + +## Version 1.5.0 - 2014-10-26 ### Added * Add support for multiple sort fields and default orders in Ransack `sort_link` helpers - ([pull request](https://github.com/activerecord-hackery/ransack/pull/438)). - + ([PR #438](https://github.com/activerecord-hackery/ransack/pull/438)). + *Caleb Land*, *James u007* -* Add test specs for the `lteq`, `lt`, `gteq`, and `gt` predicates. These are - also tested in Arel, but testing them in Ransack has proven useful to - detect issues. +* Add tests for `lteq`, `lt`, `gteq` and `gt` predicates. They are also + tested in Arel, but testing them in Ransack has proven useful to detect + issues. + + *Jon Atack* + +* Add tests for unknown attribute names. + + *Joe Yates* + +* Add tests for attribute names containing `_or_` and `_and_`. + + *Joe Yates*, *Jon Atack* + +* Add tests for attribute names ending with `_start` and `_end``. + + *Jon Atack*, *Timo Schilling* + +* Add tests for `start`, `not_start`, `end` and `not_end` predicates, with + emphasis on cases when attribute names end with `_start` and `_end`. *Jon Atack* @@ -31,11 +1074,18 @@ henceforth should be documented here. *Jon Atack* -* Fix checks for `attribute_method?` for method names containing - `_and_` and `_or_`. Now, a `terms_and_conditions` attribute will be - recognized instead of running (failing) checks for `terms` and `conditions`. +* Improve `attribute_method?` parsing for attribute names containing `_and_` + and `_or_`. Attributes named like `foo_and_bar` or `foo_or_bar` are + recognized now instead of running failing checks for `foo` and `bar`. + CORRECTION October 28, 2014: this feature is still not working! + + *Joe Yates* + +* Improve `attribute_method?` parsing for attribute names ending with a + predicate like `_start` and `_end`. For instance, a `foo_start` attribute + is now recognized instead of raising a NoMethodError. - *joeyates* + *Timo Schilling*, *Jon Atack* ### Changed @@ -47,7 +1097,6 @@ henceforth should be documented here. ## Version 1.4.1 - 2014-09-23 -### Fixed * Fix README markdown so RubyGems documentation picks up the formatting correctly. @@ -66,7 +1115,7 @@ henceforth should be documented here. *Pedro Chambino* -* Add `ro.yml` Romanian translation file. +* Add Romanian i18n locale file (`ro.yml`). *Andreas Philippi* @@ -114,9 +1163,10 @@ henceforth should be documented here. * Rewrite much of the Ransack README documentation, including the Associations section code examples and the Authorizations section detailing how to whitelist attributes, associations, sorts and scopes. - + *Jon Atack* + ## Version 1.3.0 - 2014-08-23 ### Added diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4507ea695..390b766ab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,24 +1,57 @@ +# Contributing to Ransack + +Please take a moment to review this document to make contributing easy and +effective for everyone involved! + Ransack is an open source project and we encourage contributions. +Please do not use the issue tracker for personal support requests. Stack +Overflow is a better place for that where a wider community can help you! + ## Filing an issue -When filing an issue on the Ransack project, please provide these details: +Good issue reports are extremely helpful! Please only open an issue if a bug +is caused by Ransack, is new (has not already been reported), and can be +reproduced from the information you provide. -* A comprehensive list of steps to reproduce the issue, or, _far better_, a failing spec! -* The version (and branch) of Ransack *and* the versions of Rails and Ruby. -* Any relevant stack traces ("Full trace" preferred). +Steps: + +1. **Use the GitHub issue search** — check if the issue has already been + reported. + +2. **Check if the issue has been fixed** — try to reproduce it using the + `main` branch in the repository. + +3. **Isolate the real problem** — make sure the issue is really a bug in + Ransack and not in your code or another gem. -In 99% of cases, this information is enough to determine the cause and -solution to the problem that is being described. +4. **Create a failing test** by writing a test that demonstrates the issue. You can: + - Copy and modify an existing test from the test suite + - Create a new test file following the existing patterns + - Submit a pull request with your failing test -Any issue that is open for 14 days without actionable information or activity -will be marked as "stalled" and then closed. Stalled issues can be re-opened -if the information requested is provided. +If you do not provide a failing test and would like your issue to be reviewed, do provide at a minimum: + +* A comprehensive list of steps to reproduce the issue, or even better, a + passing/failing test spec. +* Whether you are using Ransack through another gem like ActiveAdmin, + SimpleForm, etc. +* The versions of Ruby, Rails, Ransack and the database. +* Any relevant stack traces ("Full trace" preferred). + +Issues filed without the above information or that remain open without activity +for 14 days will be closed. They can be re-opened if actionable information to reproduce the issue is provided. ## Pull requests We gladly accept pull requests to fix bugs and, in some circumstances, add new -features to Ransack. +features to Ransack. If you'd like to contribute and wonder what would be +helpful, simply run a search for "FIXME" and "TODO" on the codebase :smiley: + +If you're new to contributing to open source, welcome! It can seem scary, so +here is a [great blog post to help you get started] +(http://robots.thoughtbot.com/8-new-steps-for-fixing-other-peoples-code), +step by step. Before issuing a pull request, please make sure that all specs are passing, that any new features have test coverage, and that anything that breaks @@ -28,46 +61,111 @@ Here's a quick guide: 1. Fork the repo. -2. Run the tests. We only take pull requests with passing tests, and it's great -to know that you have a clean slate: +2. Create a thoughtfully-named branch for your changes (`git checkout -b my-new-feature`). + +3. Install the development dependencies by running `bundle install`. + To install rails other than latest (set in Gemfile): `RAILS='6-1-stable' bundle install` + +4. Begin by running the tests. We only take pull requests with passing tests, + and it's great to know that you have a clean slate: + + ```sh + bundle exec rake spec + ``` + + The test suite runs by default with SQLite3. To run the test suite with PostgreSQL or MySQL, use: + + ```sh + DB=pg bundle exec rake spec + DB=mysql bundle exec rake spec + ``` + + A one-liner to run all three + + ```sh + bundle exec rubocop && bundle exec rake spec && DB=pg bundle exec rake spec && DB=mysql bundle exec rake spec + ``` + + For Postgres and MySQL, databases are expected to exist, called 'ransack'. To create use these commands (assuming OS X and Homebrew): + + ### Postgres + + ```sh + createdb ransack + ``` + + ### MySQL + + ```sh + mysql -u root + mysql> create database ransack; + ``` + + The test suite runs by default + + To run only the tests in a particular file: `bundle exec rspec ` + + ```sh + bundle exec rspec spec/ransack/search_spec.rb + ``` + + To run a single test in that file: `bundle exec rspec -e "test name"` + + ```sh + bundle exec rspec spec/ransack/search_spec.rb -e "accepts a context option" + ``` + +5. Add tests for your changes. Only refactoring and documentation changes + require no new tests. If you are adding functionality or fixing a bug, we + need a test! + +6. Make the tests pass. + +7. Update the Change Log. If you are adding new functionality, document it in + the README. + +8. Make sure git knows your name and email address in your `~/.gitconfig` file: - $ bundle install - $ bundle exec rake spec + ```sh + git config --global user.name "Your Name" + git config --global user.email "contributor@example.com" + ``` -3. Add a test for your change. Only refactoring and documentation changes -require no new tests. If you are adding functionality or fixing a bug, we need -a test! +9. Commit your changes (`git commit -am 'Add feature/fix bug/improve docs'`). + If your pull request only contains documentation changes, please remember + to add `[skip ci]` to the beginning of your commit message so the CI + test suite doesn't :runner: needlessly. -4. Make the test pass. +10. If necessary, rebase your commits into logical chunks, without errors. To + interactively rebase and cherry-pick from, say, the last 10 commits: + `git rebase -i HEAD~10`, then `git push -f`. -5. Push to your fork and submit a pull request. If the changes will apply -cleanly to the latest stable branches and master branch, you will only need -to submit one pull request. If the pull request only contains documentation -changes, please add `[skip ci]` to the commit message so that the Travis test -suite does not needlessly run. +11. Push the branch up to your fork on GitHub + (`git push origin my-new-feature`) and from GitHub submit a pull request to + Ransack's `main` branch. At this point you're waiting on us. We like to at least comment on, if not accept, pull requests within three business days (and, typically, one business day). We may suggest some changes or improvements or alternatives. -Some things that will increase the chance that your pull request is accepted, -taken straight from the Ruby on Rails guide: +Some things that will increase the chance that your pull request is accepted: -* Use Rails idioms and helpers. * Include tests that fail without your code, and pass with it. -* Update the README, the change log, the wiki documentation... anything that is +* Update the README, the change log, the documentation... anything that is affected by your contribution. +* Use idiomatic Ruby and follow the syntax conventions below. Syntax: -* Two spaces, no tabs. +* Indent with two spaces, no tabs. * 80 characters per line. * No trailing whitespace. Blank lines should not have any space. * Prefer `&&`/`||` over `and`/`or`. -* `MyClass.my_method(my_arg)` not `my_method( my_arg )` or my_method my_arg. +* `MyClass.my_method(my_arg)` not `my_method( my_arg )` or `my_method my_arg`. * `a = b` and not `a=b`. * `a_method { |block| ... }` and not `a_method { | block | ... }` or `a_method{|block| ...}`. -* Follow the conventions you see used in the source already. +* Prefer simplicity, readability, and maintainability over terseness. +* Follow the conventions you see used in the code already. -And in case we didn't emphasize it enough: we love tests! +And in case we didn't emphasize it enough: We love tests! diff --git a/Gemfile b/Gemfile index 16b33b007..45e94af7f 100644 --- a/Gemfile +++ b/Gemfile @@ -3,37 +3,57 @@ gemspec gem 'rake' -rails = ENV['RAILS'] || 'master' +rails = ENV['RAILS'] || '7-2-stable' -if rails[0,3] == '4.2' || rails == 'master' - gem 'arel', github: 'rails/arel', branch: 'master' -end - -gem 'polyamorous', '~> 1.1' +rails_version = case rails + when /\// # A path + File.read(File.join(rails, "RAILS_VERSION")) + when /^v/ # A tagged version + rails.gsub(/^v/, '') + else + rails + end -# Provide timezone information on Windows -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw] +gem 'faker' +if ::Gem::Version.new(rails_version) > ::Gem::Version.new('7.3') + gem 'sqlite3', '>= 2.1' +else + gem 'sqlite3', '~> 1.4' +end +gem 'pg' +gem 'activerecord-postgis-adapter' +gem 'pry' +gem 'byebug' case rails when /\// # A path gem 'activesupport', path: "#{rails}/activesupport" - gem 'activerecord', path: "#{rails}/activerecord" + gem 'activemodel', path: "#{rails}/activemodel" + gem 'activerecord', path: "#{rails}/activerecord", require: false gem 'actionpack', path: "#{rails}/actionpack" + gem 'actionview', path: "#{rails}/actionview" when /^v/ # A tagged version - git 'git://github.com/rails/rails.git', :tag => rails do + git 'https://github.com/rails/rails.git', tag: rails do gem 'activesupport' gem 'activemodel' - gem 'activerecord' + gem 'activerecord', require: false gem 'actionpack' end else - git 'git://github.com/rails/rails.git', :branch => rails do + git 'https://github.com/rails/rails.git', branch: rails do gem 'activesupport' gem 'activemodel' - gem 'activerecord' + gem 'activerecord', require: false gem 'actionpack' end - if rails == '3-0-stable' - gem 'mysql2', '< 0.3' - end end +gem 'mysql2' + +group :test do + gem 'factory_bot' + gem 'rspec' + gem 'simplecov', require: false +end + +gem 'rubocop', require: false +gem 'rubocop-rspec', require: false diff --git a/README.md b/README.md index 4c26ec388..de1b56310 100644 --- a/README.md +++ b/README.md @@ -1,611 +1,101 @@ -# Ransack - -[![Build Status](https://travis-ci.org/activerecord-hackery/ransack.svg)] -(https://travis-ci.org/activerecord-hackery/ransack) -[![Gem Version](https://badge.fury.io/rb/ransack.svg)] -(http://badge.fury.io/rb/ransack) - -Ransack is a rewrite of [MetaSearch] -(https://github.com/activerecord-hackery/meta_search) -created by [Ernie Miller](http://twitter.com/erniemiller) -and maintained by [Ryan Bigg](http://twitter.com/ryanbigg), -[Jon Atack](http://twitter.com/jonatack) and a great group of [contributors] -(https://github.com/activerecord-hackery/ransack/graphs/contributors). -While it supports many of the same features as MetaSearch, its underlying -implementation differs greatly from MetaSearch, -and backwards compatibility is not a design goal. - -Ransack enables the creation of both simple and -[advanced](http://ransack-demo.herokuapp.com/users/advanced_search) -search forms against your application's models (demo source code -[here](https://github.com/activerecord-hackery/ransack_demo)). -If you're looking for something that simplifies query generation at the model -or controller layer, you're probably not looking for Ransack (or MetaSearch, -for that matter). Try [Squeel](https://github.com/activerecord-hackery/squeel) -instead. - -## Getting started - -Ransack is currently compatible with Rails 3.x, 4.0, 4.1 and 4.2. - -In your Gemfile, for the last officially released Ransack gem: - -```ruby -gem 'ransack' -``` - -Or, if you would like to use the latest updates: - -```ruby -gem 'ransack', github: 'activerecord-hackery/ransack' -``` - -The other branches (`rails-4`, `rails-4.1`, and `rails-4.2`) were each used for -developing and running Ransack with the latest upcoming version of Rails at the -time. They are smaller and possibly slightly faster because they do not have to -support previous versions of Rails and Active Record. Once support for that -Rails version is merged from the branch into Ransack master, the branch is no -longer actively maintained -- unless the open source community submits pull -requests to maintain them. You are welcome to do so! - -To use one of the branches, for example the `rails-4.1` branch: - -```ruby -gem 'ransack', github: 'activerecord-hackery/ransack', branch: 'rails-4.1' -``` - -## Usage - -Ransack can be used in one of two modes, simple or advanced. - -### Simple Mode - -This mode works much like MetaSearch, for those of you who are familiar with -it, and requires very little setup effort. - -If you're coming from MetaSearch, things to note: - - 1. The default param key for search params is now `:q`, instead of `:search`. - This is primarily to shorten query strings, though advanced queries (below) - will still run afoul of URL length limits in most browsers and require a - switch to HTTP POST requests. This key is [configurable] - (https://github.com/activerecord-hackery/ransack/wiki/Configuration). - - 2. `form_for` is now `search_form_for`, and validates that a Ransack::Search - object is passed to it. - - 3. Common ActiveRecord::Relation methods are no longer delegated by the - search object. Instead, you will get your search results (an - ActiveRecord::Relation in the case of the ActiveRecord adapter) via a call to - `Search#result`. - - 4. If passed `distinct: true`, `result` will generate a `SELECT DISTINCT` to - avoid returning duplicate rows, even if conditions on a join would otherwise - result in some. - - Please note that for many databases, a sort on an associated table's columns - may result in invalid SQL with `distinct: true` -- in those cases, you're on - your own, and will need to modify the result as needed to allow these queries - to work. If `distinct: true` is causing you problems, another way to remove - duplicates is to call `#to_a.uniq` on your collection instead (see the next - section below). - -####In your controller - -```ruby -def index - @q = Person.search(params[:q]) - @people = @q.result(distinct: true) -end -``` -or without `distinct:true`, for sorting on an associated table's columns (in -this example, with preloading each Person's Articles and pagination): - -```ruby -def index - @q = Person.search(params[:q]) - @people = @q.result.includes(:articles).page(params[:page]) - # or use `to_a.uniq` to remove duplicates (can also be done in the view): - @people = @q.result.includes(:articles).page(params[:page]).to_a.uniq -end -``` - -####In your view - -The two primary Ransack view helpers are `search_form_for` and `sort_link`, -which are defined in -[Ransack::Helpers::FormHelper](lib/ransack/helpers/form_helper.rb). - -#####Ransack's `search_form_for` helper replaces `form_for` for creating the view search form: - -```erb -<%= search_form_for @q do |f| %> - - # Search if the name field contains... - <%= f.label :name_cont %> - <%= f.search_field :name_cont %> - - # Search if an associated articles.title starts with... - <%= f.label :articles_title_start %> - <%= f.search_field :articles_title_start %> - - # Attributes may be chained. Search multiple attributes for one value... - <%= f.label :name_or_description_or_email_or_articles_title_cont %> - <%= f.search_field :name_or_description_or_email_or_articles_title_cont %> +# ![Ransack](./docs/static/logo/ransack-h.png "Ransack") - <%= f.submit %> -<% end %> -``` - -`cont` (contains) and `start` (starts with) are just two of the available -search predicates. See [Constants] -(https://github.com/activerecord-hackery/ransack/blob/master/lib/ransack/constants.rb) -for a full list and the [wiki] -(https://github.com/activerecord-hackery/ransack/wiki/Basic-Searching) -for more information. - -The `search_form_for` answer format can be set like this: -```erb -<%= search_form_for(@q, format: :pdf) do |f| %> - -<%= search_form_for(@q, format: :json) do |f| %> -``` - -#####Ransack's `sort_link` helper creates table headers that are sortable links: +[![Build Status](https://github.com/activerecord-hackery/ransack/workflows/test/badge.svg)](https://github.com/activerecord-hackery/ransack/actions) +[![Gem Version](https://badge.fury.io/rb/ransack.svg)](http://badge.fury.io/rb/ransack) +[![Code Climate](https://codeclimate.com/github/activerecord-hackery/ransack/badges/gpa.svg)](https://codeclimate.com/github/activerecord-hackery/ransack) +[![Backers on Open Collective](https://opencollective.com/ransack/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/ransack/sponsors/badge.svg)](#sponsors) -```erb -<%= sort_link(@q, :name) %> -``` -Additional options can be passed after the column attribute, like a different -column title or a default sort order: +# Introduction -```erb -<%= sort_link(@q, :name, 'Last Name', default_order: :desc) %> -``` - -You can also sort on multiple fields by specifying an ordered array: - -```erb -<%= sort_link(@q, :last_name, [:last_name, 'first_name asc'], 'Last Name') %> -``` - -In the example above, clicking the link will sort by `last_name` and then -`first_name`. Specifying the sort direction on a field in the array tells -Ransack to _always_ sort that particular field in the specified direction. - -Multiple `default_order` fields may also be specified with a hash: - -```erb -<%= sort_link(@q, :last_name, %i(last_name first_name), - default_order: { last_name: 'asc', first_name: 'desc' }) %> -``` +Ransack will help you easily add **searching to your Rails application**, without any additional dependencies. -This example toggles the sort directions of both fields, by default -initially sorting the `last_name` field by ascending order, and the -`first_name` field by descending order. +There are advanced searching solutions around, like ElasticSearch or Algolia. **Ransack** will do the job for many Rails websites, without the need to run additional infrastructure or work in a different language. With Ransack you do it all with standard Ruby and ERB. -### Advanced Mode +Ready to move beyond the basics? Use **advanced features** like i18n and extensive configuration options. -"Advanced" searches (ab)use Rails' nested attributes functionality in order to -generate complex queries with nested AND/OR groupings, etc. This takes a bit -more work but can generate some pretty cool search interfaces that put a lot of -power in the hands of your users. A notable drawback with these searches is -that the increased size of the parameter string will typically force you to use -the HTTP POST method instead of GET. :( - -This means you'll need to tweak your routes... - -```ruby -resources :people do - collection do - match 'search' => 'people#search', via: [:get, :post], as: :search - end -end -``` - -... and add another controller action ... - -```ruby -def search - index - render :index -end -``` - -... and update your `search_form_for` line in the view ... - -```erb -<%= search_form_for @q, url: search_people_path, - html: { method: :post } do |f| %> -``` - -Once you've done so, you can make use of the helpers in [Ransack::Helpers::FormBuilder](lib/ransack/helpers/form_builder.rb) to -construct much more complex search forms, such as the one on the -[demo page](http://ransack-demo.heroku.com) (source code [here](https://github.com/activerecord-hackery/ransack_demo)). - -### Ransack #search method - -Ransack will try to to make `#search` available in your models, but in the case -that `#search` has already been defined, you can use `#ransack` instead. For -example the following would be equivalent: - -```ruby -Article.search(params[:q]) -Article.ransack(params[:q]) -``` +Ransack is supported for Rails 8.0, 7.2 on Ruby 3.1 and later. -### Associations +## Installation -You can easily use Ransack to search for objects in `has_many` and `belongs_to` -associations. +To install `ransack` and add it to your Gemfile, run -Given you have these associations ... - -```ruby -class Employee < ActiveRecord::Base - belongs_to :supervisor - - # has attributes first_name:string and last_name:string -end - -class Department < ActiveRecord::Base - has_many :supervisors - - # has attribute title:string -end - -class Supervisor < ActiveRecord::Base - belongs_to :department - has_many :employees - - # has attribute last_name:string -end -``` - -... and a controller ... - -```ruby -class SupervisorsController < ApplicationController - def index - @q = Supervisor.search(params[:q]) - @supervisors = @q.result.includes(:department, :employees) - end -end -``` - -... you might set up your form like this ... - -```erb -<%= search_form_for @q do |f| %> - <%= f.label :last_name_cont %> - <%= f.search_field :last_name_cont %> - - <%= f.label :department_title_cont %> - <%= f.search_field :department_title_cont %> - - <%= f.label :employees_first_name_or_employees_last_name_cont %> - <%= f.search_field :employees_first_name_or_employees_last_name_cont %> - - <%= f.submit "search" %> -<% end %> -... -<%= content_tag :table %> - <%= content_tag :th, sort_link(@q, :last_name) %> - <%= content_tag :th, sort_link(@q, 'departments.title') %> - <%= content_tag :th, sort_link(@q, 'employees.last_name') %> -<% end %> -``` - -### Using Ransackers to add custom search functions via Arel - -The main premise behind Ransack is to provide access to -**Arel predicate methods**. Ransack provides special methods, called -_ransackers_, for creating additional search functions via Arel. More -information about `ransacker` methods can be found [here in the wiki] -(https://github.com/activerecord-hackery/ransack/wiki/Using-Ransackers). -Feel free to contribute working `ransacker` code examples to the wiki! - -### Authorization (whitelisting/blacklisting) - -By default, searching and sorting are authorized on any column of your model -and no class methods/scopes are whitelisted. - -Ransack adds four methods to `ActiveRecord::Base` that you can redefine as -class methods in your models to apply selective authorization: -`ransackable_attributes`, `ransackable_associations`, `ransackable_scopes` and -`ransortable_attributes`. - -Here is how these four methods are implemented in Ransack: - -```ruby - # Ransackable_attributes, by default, returns all column names - # and any defined ransackers as an array of strings. - # For overriding with a whitelist array of strings. - # - def ransackable_attributes(auth_object = nil) - column_names + _ransackers.keys - end - - # Ransackable_associations, by default, returns the names - # of all associations as an array of strings. - # For overriding with a whitelist array of strings. - # - def ransackable_associations(auth_object = nil) - reflect_on_all_associations.map { |a| a.name.to_s } - end - - # Ransortable_attributes, by default, returns the names - # of all attributes available for sorting as an array of strings. - # For overriding with a whitelist array of strings. - # - def ransortable_attributes(auth_object = nil) - ransackable_attributes(auth_object) - end - - # Ransackable_scopes, by default, returns an empty array - # i.e. no class methods/scopes are authorized. - # For overriding with a whitelist array of *symbols*. - # - def ransackable_scopes(auth_object = nil) - [] - end -``` - -Any values not returned from these methods will be ignored by Ransack, i.e. -they are not authorized. - -All four methods can receive a single optional parameter, `auth_object`. When -you call the search or ransack method on your model, you can provide a value -for an `auth_object` key in the options hash which can be used by your own -overridden methods. - -Here is an example that puts all this together, adapted from -[this blog post by Ernie Miller] -(http://erniemiller.org/2012/05/11/why-your-ruby-class-macros-might-suck-mine-did/). -In an `Article` model, add the following `ransackable_attributes` class method -(preferably private): - -```ruby -class Article < ActiveRecord::Base - - private - - def self.ransackable_attributes(auth_object = nil) - if auth_object == :admin - # whitelist all attributes for admin - super - else - # whitelist only the title and body attributes for other users - super & %w(title body) - end - end -end -``` - -Here is example code for the `articles_controller`: - -```ruby -class ArticlesController < ApplicationController - - def index - @q = Article.search(params[:q], auth_object: set_ransack_auth_object) - @articles = @q.result - end - - private - - def set_ransack_auth_object - current_user.admin? ? :admin : nil - end -end -``` - -Trying it out in `rails console`: - -```ruby -> Article -=> Article(id: integer, person_id: integer, title: string, body: text) - -> Article.ransackable_attributes -=> ["title", "body"] - -> Article.ransackable_attributes(:admin) -=> ["id", "person_id", "title", "body"] - -> Article.search(id_eq: 1).result.to_sql -=> SELECT "articles".* FROM "articles" # Note that search param was ignored! - -> Article.search({ id_eq: 1 }, { auth_object: nil }).result.to_sql -=> SELECT "articles".* FROM "articles" # Search param still ignored! - -> Article.search({ id_eq: 1 }, { auth_object: :admin }).result.to_sql -=> SELECT "articles".* FROM "articles" WHERE "articles"."id" = 1 +```ruby title='Gemfile' +gem 'ransack' ``` -That's it! Now you know how to whitelist/blacklist various elements in Ransack. - -### Using Scopes/Class Methods - -Continuing on from the preceding section, searching by scopes requires defining -a whitelist of `ransackable_scopes` on the model class. The whitelist should be -an array of *symbols*. By default, all class methods (e.g. scopes) are ignored. -Scopes will be applied for matching `true` values, or for given values if the -scope accepts a value: - -```ruby -class Employee < ActiveRecord::Base - scope :active, ->(boolean = true) { where(active: boolean) } - scope :salary_gt, ->(amount) { where('salary > ?', amount) } +### Bleeding edge - # Scopes are just syntactical sugar for class methods, which may also be used: +If you would like to use the latest updates not yet published to RubyGems, use the `main` branch: - def self.hired_since(date) - where('start_date >= ?', date) - end - - private - - def self.ransackable_scopes(auth_object = nil) - if auth_object.try(:admin?) - # allow admin users access to all three methods - %i(active hired_since salary_gt) - else - # allow other users to search on active and hired_since only - %i(active hired_since) - end - end -end - -Employee.search({ active: true, hired_since: '2013-01-01' }) - -Employee.search({ salary_gt: 100_000 }, { auth_object: current_user }) -``` - -Scopes are a recent addition to Ransack and currently have a few caveats: -First, a scope involving child associations needs to be defined in the parent -table model, not in the child model. Second, scopes with an array as an -argument are not easily usable yet, because the array currently needs to be -wrapped in an array to function (see -[this issue](https://github.com/activerecord-hackery/ransack/issues/404)), -which is not compatible with Ransack form helpers. For this use case, it may be -better for now to use [ransackers] -(https://github.com/activerecord-hackery/ransack/wiki/Using-Ransackers) instead -where feasible. Finally, there is also -[this issue](https://github.com/activerecord-hackery/ransack/issues/403) -to be aware of. Pull requests with solutions and tests are welcome! - -### Grouping queries by OR instead of AND - -The default `AND` grouping can be changed to `OR` by adding `m: 'or'` to the -query hash. - -You can easily try it in your controller code by changing `params[:q]` in the -`index` action to `params[:q].try(:merge, m: 'or')` as follows: - -```ruby -def index - @q = Artist.search(params[:q].try(:merge, m: 'or')) - @artists = @q.result -end -``` -Normally, if you wanted users to be able to toggle between `AND` and `OR` -query grouping, you would probably set up your search form so that `m` was in -the URL params hash, but here we assigned `m` manually just to try it out -quickly. - -Alternatively, trying it in the Rails console: - -```ruby -artists = Artist.search(name_cont: 'foo', style_cont: 'bar', m: 'or') -=> Ransack::Search, - Condition - ], combinator: or>> - -artists.result.to_sql -=> "SELECT \"artists\".* FROM \"artists\" - WHERE ((\"artists\".\"name\" ILIKE '%foo%' - OR \"artists\".\"style\" ILIKE '%bar%'))" +```ruby title='Gemfile' +gem 'ransack', :github => 'activerecord-hackery/ransack', :branch => 'main' ``` -The combinator becomes `or` instead of the default `and`, and the SQL query -becomes `WHERE...OR` instead of `WHERE...AND`. - -This works with associations as well. Imagine an Artist model that has many -Memberships, and many Musicians through Memberships: - -```ruby -artists = Artist.search(name_cont: 'foo', musicians_email_cont: 'bar', m: 'or') -=> Ransack::Search, - Condition - ], combinator: or>> - -artists.result.to_sql -=> "SELECT \"artists\".* FROM \"artists\" - LEFT OUTER JOIN \"memberships\" - ON \"memberships\".\"artist_id\" = \"artists\".\"id\" - LEFT OUTER JOIN \"musicians\" - ON \"musicians\".\"id\" = \"memberships\".\"musician_id\" - WHERE ((\"artists\".\"name\" ILIKE '%foo%' - OR \"musicians\".\"email\" ILIKE '%bar%'))" -``` +### Documentation -### Using SimpleForm +There is [extensive documentation on Ransack](https://activerecord-hackery.github.io/ransack/), which is a [Docusaurus](https://docusaurus.io/) project and run as a GitHub Pages site. Alternatively there is [AI Generated documentation](https://deepwiki.com/activerecord-hackery/ransack/1-overview) produced by [devin.ai](https://devin.ai/). -If you would like to combine the Ransack and SimpleForm form builders, set the -`RANSACK_FORM_BUILDER` environment variable before Rails boots up, e.g. in -`config/application.rb` before `require 'rails/all'` as shown below (and add -`gem 'simple_form'` in your Gemfile). +This [gist](https://gist.github.com/raghubetina/d5fc3df67ddbadcac271) has a quick-start cheatsheet, created by [@raghubetina](https://gist.github.com/raghubetina) -```ruby -require File.expand_path('../boot', __FILE__) -ENV['RANSACK_FORM_BUILDER'] = '::SimpleForm::FormBuilder' -require 'rails/all' -``` +## Issue tracker -### I18n - -Ransack translation files are available in -[Ransack::Locale](lib/ransack/locale). You may also be interested in one of the -many translations for Ransack available at -http://www.localeapp.com/projects/2999. - -Predicate and attribute translations in forms may be specified as follows (see -the translation files in [Ransack::Locale](lib/ransack/locale) for more examples): - -locales/en.yml: -```yml -en: - ransack: - asc: ascending - desc: descending - predicates: - cont: contains - not_cont: not contains - start: starts with - end: ends with - gt: greater than - lt: less than - attributes: - person: - name: Full Name - article: - title: Article Title - body: Main Content -``` +* Before filing an issue, please read the [Contributing Guide](CONTRIBUTING.md). +* File an issue if a bug is caused by Ransack, is new (has not already been reported), and _can be reproduced from the information you provide_. +* Please consider creating a pull request with a failing test that demonstrates the problem. +* Contributions are welcome. :smiley: +* Please do not use the issue tracker for personal support requests. Stack Overflow or [GitHub Discussions](https://github.com/activerecord-hackery/ransack/discussions) is a better place for that where a wider community can help you! -Attribute names may also be changed globally, or under `activerecord`: - -```yml -en: - attributes: - model_name: - model_field1: field name1 - model_field2: field name2 - activerecord: - attributes: - namespace/article: - title: AR Namespaced Title - namespace_article: - title: Old Ransack Namespaced Title -``` ## Contributions To support the project: +* Consider supporting us via [Open Collective](https://opencollective.com/ransack/backers/badge.svg) * Use Ransack in your apps, and let us know if you encounter anything that's broken or missing. A failing spec to demonstrate the issue is awesome. A pull request with passing tests is even better! * Before filing an issue or pull request, be sure to read and follow the [Contributing Guide](CONTRIBUTING.md). -* Please use Stack Overflow or other sites for questions or discussion not +* Please use Stack Overflow or [GitHub Discussions](https://github.com/activerecord-hackery/ransack/discussions) for questions or discussion not directly related to bug reports, pull requests, or documentation improvements. -* Spread the word on Twitter, Facebook, and elsewhere if Ransack's been useful +* Spread the word on social media if Ransack's been useful to you. The more people who are using the project, the quicker we can find and fix bugs! -## Copyright +## Contributors + +Ransack was created by [Ernie Miller](http://twitter.com/erniemiller) and is developed and maintained by: +* [Sean Carroll](https://github.com/scarroll32) +* [Deivid Rodriguez](https://github.com/deivid-rodriguez) +* [Greg Molnar](https://github.com/gregmolnar) +* [A great group of contributors](https://github.com/activerecord-hackery/ransack/graphs/contributors). +- Ransack's logo is designed by [Anıl Kılıç](https://github.com/anilkilic). + +Alumni Maintainers +- [Jon Atack](http://twitter.com/jonatack) +- [Ryan Bigg](http://twitter.com/ryanbigg) + +This project exists thanks to all the people who contribute. + + +## Backers + +Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/ransack#backer)] + + + + +## Sponsors + +Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/ransack#sponsor)] -Copyright © 2011-2014 [Ernie Miller](http://twitter.com/erniemiller) + + + + + + + + + + diff --git a/Rakefile b/Rakefile index 46350ff7e..77e1a6d0b 100644 --- a/Rakefile +++ b/Rakefile @@ -4,16 +4,136 @@ require 'rspec/core/rake_task' Bundler::GemHelper.install_tasks RSpec::Core::RakeTask.new(:spec) do |rspec| - rspec.rspec_opts = ['--backtrace'] + ENV['SPEC'] = 'spec/ransack/**/*_spec.rb' + # With Rails 3, using `--backtrace` raises 'invalid option' when testing. + # With Rails 4 and 5 it can be uncommented to see the backtrace: + # + # rspec.rspec_opts = ['--backtrace'] end -task :default => :spec +# RuboCop tasks +begin + require 'rubocop/rake_task' + RuboCop::RakeTask.new +rescue LoadError + # RuboCop not available +end + +# Combined test task that runs both specs and RuboCop +desc "Run all tests (specs + RuboCop)" +task :test do + puts "Running RuboCop..." + Rake::Task["rubocop"].invoke + + puts "\nRunning RSpec tests..." + Rake::Task["spec"].invoke +end + +# Test with PostgreSQL +desc "Run all tests with PostgreSQL" +task :test_pg do + puts "Running RuboCop..." + Rake::Task["rubocop"].invoke + + puts "\nRunning RSpec tests with PostgreSQL..." + ENV['DB'] = 'pg' + Rake::Task["spec"].invoke +end + +# Test with MySQL +desc "Run all tests with MySQL" +task :test_mysql do + puts "Running RuboCop..." + Rake::Task["rubocop"].invoke + + puts "\nRunning RSpec tests with MySQL..." + ENV['DB'] = 'mysql' + Rake::Task["spec"].invoke +end + +# Helper method to check database availability +def database_available?(adapter) + case adapter + when 'pg', 'postgres', 'postgresql' + begin + require 'pg' + PG.connect(host: 'localhost', dbname: 'postgres', user: 'postgres', password: '') + true + rescue PG::Error, LoadError + false + end + when 'mysql', 'mysql2' + begin + require 'mysql2' + Mysql2::Client.new(host: 'localhost', username: 'root', password: '') + true + rescue Mysql2::Error, LoadError + false + end + else + true # SQLite is always available + end +rescue + false +end + +# Test with all available databases +desc "Run all tests with available databases (SQLite, PostgreSQL, MySQL)" +task :test_all do + puts "Running RuboCop..." + Rake::Task["rubocop"].invoke + + puts "\nRunning RSpec tests with SQLite..." + ENV.delete('DB') + Rake::Task["spec"].invoke + + if database_available?('pg') + puts "\nPostgreSQL detected. Running RSpec tests with PostgreSQL..." + ENV['DB'] = 'pg' + Rake::Task["spec"].invoke + else + puts "\nPostgreSQL not available. Skipping PostgreSQL tests." + end + + if database_available?('mysql') + puts "\nMySQL detected. Running RSpec tests with MySQL..." + ENV['DB'] = 'mysql' + Rake::Task["spec"].invoke + else + puts "\nMySQL not available. Skipping MySQL tests." + end +end + +# Test with detected databases only +desc "Run tests with all detected databases" +task :test_detected do + puts "Detecting available databases..." + + available_dbs = [] + available_dbs << 'sqlite' if true # SQLite is always available + available_dbs << 'postgresql' if database_available?('pg') + available_dbs << 'mysql' if database_available?('mysql') + + puts "Available databases: #{available_dbs.join(', ')}" + + puts "\nRunning RuboCop..." + Rake::Task["rubocop"].invoke + + available_dbs.each do |db| + puts "\nRunning RSpec tests with #{db.capitalize}..." + ENV['DB'] = db + Rake::Task["spec"].invoke + end +end + +task :default do + Rake::Task["test"].invoke +end desc "Open an irb session with Ransack and the sample data used in specs" task :console do - require 'irb' - require 'irb/completion' - require 'console' + require 'pry' + require File.expand_path('../spec/console.rb', __FILE__) ARGV.clear - IRB.start -end \ No newline at end of file + Pry.start +end diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..d1e4e389c --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,19 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +yarn-debug.log* +yarn-error.log* diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/docs/babel.config.js b/docs/babel.config.js new file mode 100644 index 000000000..e00595dae --- /dev/null +++ b/docs/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: [require.resolve('@docusaurus/core/lib/babel/preset')], +}; diff --git a/docs/blog/2022-03-27-ransack-3.0.0.md b/docs/blog/2022-03-27-ransack-3.0.0.md new file mode 100644 index 000000000..f7bc6f021 --- /dev/null +++ b/docs/blog/2022-03-27-ransack-3.0.0.md @@ -0,0 +1,20 @@ +--- +slug: ransack-3-0-0 +title: Ransack 3.0.0 +authors: + name: Sean Carroll + title: Ransack Core Team +tags: [ransack, release] +--- + +Ransack has been a part of many Rubyists toolboxes for years and 3.0.0 is a major release. We have a number of new features and one breaking change. As part of 3.0.0, we decided to launch this documentation website, merging in the Wiki and the content from the README. + +With 3.0.0 we are hoping to re-energise the community, we need help on closing out old issues, refactoring the codebase and even some design work. + +I also wanted to let you know that Ernie Miller (creator of Ransack) has decided to leave the project completely, he has this message for the community: + +> While my own personal development efforts have been spent elsewhere as of late, I'm keenly aware of how many people still depend on some of the software I originally wrote all those years ago. + +> That's why I'm grateful to be able to step away from the ActiveRecord Hackery organization (and, specifically, maintenance of Ransack) without impacting those users. I'm thankful that Sean, David, Greg, and others will continue to support users, and wish them the best as they move forward without me! + +Please join me in thanking Ernie for bringing Ransack to life, I personally think it is one of the most amazing Rails libraries out there. diff --git a/docs/docs/getting-started/_category_.json b/docs/docs/getting-started/_category_.json new file mode 100644 index 000000000..fd94d1fcf --- /dev/null +++ b/docs/docs/getting-started/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Getting started", + "position": 2 +} diff --git a/docs/docs/getting-started/advanced-mode.md b/docs/docs/getting-started/advanced-mode.md new file mode 100644 index 000000000..4dc44e926 --- /dev/null +++ b/docs/docs/getting-started/advanced-mode.md @@ -0,0 +1,46 @@ +--- +sidebar_position: 2 +title: Advanced Mode +--- + + +"Advanced" searches Rails's nested attributes functionality in order to +generate complex queries with nested AND/OR groupings, etc. This takes a bit +more work but can generate some pretty cool search interfaces that put a lot of +power in the hands of your users. + +A notable drawback with these searches is +that the increased size of the parameter string will typically force you to use +the HTTP POST method instead of GET. + + +## Tweak your routes + +```ruby +resources :people do + collection do + match 'search' => 'people#search', via: [:get, :post], as: :search + end +end +``` + +## Add a controller action + +```ruby +def search + index + render :index +end +``` + +## Update your form + +```erb +<%= search_form_for @q, url: search_people_path, + html: { method: :post } do |f| %> +``` + +Once you've done so, you can make use of the helpers in [Ransack::Helpers::FormBuilder](https://github.com/activerecord-hackery/ransack/blob/main/lib/ransack/helpers/form_builder.rb) to +construct much more complex search forms, such as the one on the +[demo app](http://ransack-demo.herokuapp.com/users/advanced_search) +(source code [here](https://github.com/activerecord-hackery/ransack_demo)). diff --git a/docs/docs/getting-started/configuration.md b/docs/docs/getting-started/configuration.md new file mode 100644 index 000000000..a10a7f662 --- /dev/null +++ b/docs/docs/getting-started/configuration.md @@ -0,0 +1,47 @@ +--- +sidebar_position: 3 +title: Configuration +--- + + + +Ransack may be easily configured. The best place to put configuration is in an initializer file at `config/initializers/ransack.rb`, containing code such as: + +```ruby +Ransack.configure do |config| + + # Change default search parameter key name. + # Default key name is :q + config.search_key = :query + + # Raise errors if a query contains an unknown predicate or attribute. + # Default is true (do not raise error on unknown conditions). + config.ignore_unknown_conditions = false + + # Globally display sort links without the order indicator arrow. + # Default is false (sort order indicators are displayed). + # This can also be configured individually in each sort link (see the README). + config.hide_sort_order_indicators = true + +end +``` + +## Custom search parameter key name + +Sometimes there are situations when the default search parameter name cannot be used, for instance, +if there are two searches on one page. Another name may be set using the `search_key` option in the `ransack` or `search` methods in the controller, and in the `@search_form_for` method in the view. + +### In the controller + +```ruby +@search = Log.ransack(params[:log_search], search_key: :log_search) +# or +@search = Log.search(params[:log_search], search_key: :log_search) +``` + +### In the view + +```erb +<%= f.search_form_for @search, as: :log_search %> +<%= sort_link(@search) %> +``` diff --git a/docs/docs/getting-started/search-matches.md b/docs/docs/getting-started/search-matches.md new file mode 100644 index 000000000..d58d6f8a7 --- /dev/null +++ b/docs/docs/getting-started/search-matches.md @@ -0,0 +1,68 @@ +--- +title: Search Matchers +--- + +### Search Matchers + +List of all possible predicates + + +| Predicate | Description | Notes | +| ------------- | ------------- |-------- | +| `*_eq` | equal | | +| `*_eq_any` | equal to any of the provided values | | +| `*_not_eq` | not equal | | +| `*_matches` | matches with `LIKE` | e.g. `q[email_matches]=%@gmail.com`| +| `*_does_not_match` | does not match with `LIKE` | | +| `*_matches_any` | Matches any | | +| `*_matches_all` | Matches all | | +| `*_does_not_match_any` | Does not match any | | +| `*_does_not_match_all` | Does not match all | | +| `*_lt` | less than | | +| `*_lteq` | less than or equal | | +| `*_gt` | greater than | | +| `*_gteq` | greater than or equal | | +| `*_present` | not null and not empty | Only compatible with string columns. Example: `q[name_present]=1` (SQL: `col is not null AND col != ''`) | +| `*_blank` | is null or empty. | (SQL: `col is null OR col = ''`) | +| `*_null` | is null | | +| `*_not_null` | is not null | | +| `*_in` | match any values in array | e.g. `q[name_in][]=Alice&q[name_in][]=Bob` | +| `*_not_in` | match none of values in array | | +| `*_lt_any` | Less than any | SQL: `col < value1 OR col < value2` | +| `*_lteq_any` | Less than or equal to any | | +| `*_gt_any` | Greater than any | | +| `*_gteq_any` | Greater than or equal to any | | +| `*_lt_all` | Less than all | SQL: `col < value1 AND col < value2` | +| `*_lteq_all` | Less than or equal to all | | +| `*_gt_all` | Greater than all | | +| `*_gteq_all` | Greater than or equal to all | | +| `*_not_eq_all` | none of values in a set | | +| `*_start` | Starts with | SQL: `col LIKE 'value%'` | +| `*_not_start` | Does not start with | | +| `*_start_any` | Starts with any of | | +| `*_start_all` | Starts with all of | | +| `*_not_start_any` | Does not start with any of | | +| `*_not_start_all` | Does not start with all of | | +| `*_end` | Ends with | SQL: `col LIKE '%value'` | +| `*_not_end` | Does not end with | | +| `*_end_any` | Ends with any of | | +| `*_end_all` | Ends with all of | | +| `*_not_end_any` | | | +| `*_not_end_all` | | | +| `*_cont` | Contains value | uses `LIKE` | +| `*_cont_any` | Contains any of | | +| `*_cont_all` | Contains all of | | +| `*_not_cont` | Does not contain | +| `*_not_cont_any` | Does not contain any of | | +| `*_not_cont_all` | Does not contain all of | | +| `*_i_cont` | Contains value with case insensitive | uses `ILIKE` | +| `*_i_cont_any` | Contains any of values with case insensitive | | +| `*_i_cont_all` | Contains all of values with case insensitive | | +| `*_not_i_cont` | Does not contain with case insensitive | +| `*_not_i_cont_any` | Does not contain any of values with case insensitive | | +| `*_not_i_cont_all` | Does not contain all of values with case insensitive | | +| `*_true` | is true | | +| `*_false` | is false | | + + +See full list: https://github.com/activerecord-hackery/ransack/blob/main/lib/ransack/locale/en.yml#L16 diff --git a/docs/docs/getting-started/simple-mode.md b/docs/docs/getting-started/simple-mode.md new file mode 100644 index 000000000..ba920e716 --- /dev/null +++ b/docs/docs/getting-started/simple-mode.md @@ -0,0 +1,322 @@ +--- +sidebar_position: 1 +title: Simple mode +--- + +# Simple Mode + +Ransack can be used in one of two modes, simple or advanced. For +searching/filtering not requiring complex boolean logic, Ransack's simple +mode should meet your needs. + +## In your controller + +```ruby +def index + @q = Person.ransack(params[:q]) + @people = @q.result(distinct: true) +end +``` +or without `distinct: true`, for sorting on an associated table's columns (in +this example, with preloading each Person's Articles and pagination): + +```ruby +def index + @q = Person.ransack(params[:q]) + @people = @q.result.includes(:articles).page(params[:page]) +end +``` + +:::caution +As of v4.0, searching and sorting are not authorized on _any_ column of your model by default. See [Authorization (allowlisting/denylisting)](/going-further/other-notes.md#authorization-allowlistingdenylisting) on how to define searchable attributes. +Prior to v4.0, searching and sorting were authorized on any column of your model by default. +::: + +### Default search options + +#### Search parameter + +Ransack uses a default `:q` param key for search params. This may be changed by +setting the `search_key` option in a Ransack initializer file (typically +`config/initializers/ransack.rb`): + +```ruby +Ransack.configure do |c| + # Change default search parameter key name. + # Default key name is :q + c.search_key = :query +end +``` + +#### String search + +After version 2.4.0 when searching a string query Ransack by default strips all whitespace around the query string. +This may be disabled by setting the `strip_whitespace` option in a Ransack initializer file: + +```ruby +Ransack.configure do |c| + # Change whitespace stripping behavior. + # Default is true + c.strip_whitespace = false +end +``` + +## In your view + +The two primary Ransack view helpers are `search_form_for` and `sort_link`, +which are defined in +[Ransack::Helpers::FormHelper](https://github.com/activerecord-hackery/ransack/blob/main/lib/ransack/helpers/form_helper.rb). + +### Form helper + +Ransack's `search_form_for` helper replaces `form_for` for creating the view search form + +```erb +<%= search_form_for @q do |f| %> + + # Search if the name field contains... + <%= f.label :name_cont %> + <%= f.search_field :name_cont %> + + # Search if an associated articles.title starts with... + <%= f.label :articles_title_start %> + <%= f.search_field :articles_title_start %> + + # Attributes may be chained. Search multiple attributes for one value... + <%= f.label :name_or_description_or_email_or_articles_title_cont %> + <%= f.search_field :name_or_description_or_email_or_articles_title_cont %> + + <%= f.submit %> +<% end %> +``` + +The argument of `f.search_field` has to be in this form: + `attribute_name[_or_attribute_name]..._predicate` + +where `[_or_another_attribute_name]...` means any repetition of `_or_` plus the name of the attribute. + +`cont` (contains) and `start` (starts with) are just two of the available +search predicates. + +The `search_form_for` answer format can be set like this: + +```erb +<%= search_form_for(@q, format: :pdf) do |f| %> + +<%= search_form_for(@q, format: :json) do |f| %> +``` + +### Turbo-enabled search form + +For applications using Turbo (Hotwire), Ransack provides `turbo_search_form_for` helper which submits forms via turbo streams instead of traditional HTML GET requests. This is particularly useful when integrating with paginated results or other turbo-enabled components: + +```erb +<%= turbo_search_form_for @q do |f| %> + <%= f.label :name_cont %> + <%= f.search_field :name_cont %> + <%= f.submit %> +<% end %> +``` + +The turbo form helper supports the same options as `search_form_for`, plus additional turbo-specific options: + +```erb +# Target a specific turbo frame +<%= turbo_search_form_for @q, turbo_frame: 'search_results' do |f| %> + # form fields +<% end %> + +# Use a custom HTTP method (defaults to POST) +<%= turbo_search_form_for @q, method: :patch do |f| %> + # form fields +<% end %> + +# Set turbo action behavior +<%= turbo_search_form_for @q, turbo_action: 'replace' do |f| %> + # form fields +<% end %> +``` + +This eliminates the need for complex controller logic to handle different request formats when combining search with pagination. + +### Search link helper + +Ransack's `sort_link` helper creates table headers that are sortable links + +```erb +<%= sort_link(@q, :name) %> +``` +Additional options can be passed after the column parameter, like a different +column title or a default sort order. + +If the first option after the column parameter is a String, it's considered a +custom label for the link: + +```erb +<%= sort_link(@q, :name, 'Last Name', default_order: :desc) %> +``` + +You can use a block if the link markup is hard to fit into the label parameter: + +```erb +<%= sort_link(@q, :name) do %> + Player Name +<% end %> +``` + +With a polymorphic association, you may need to specify the name of the link +explicitly to avoid an `uninitialized constant Model::Xxxable` error (see issue +[#421](https://github.com/activerecord-hackery/ransack/issues/421)): + +```erb +<%= sort_link(@q, :xxxable_of_Ymodel_type_some_attribute, 'Attribute Name') %> +``` + +If the first option after the column parameter and/or the label parameter is an +Array, it will be used for sorting on multiple fields: + +```erb +<%= sort_link(@q, :last_name, [:last_name, 'first_name asc'], 'Last Name') %> +``` + +In the example above, clicking the link will sort by `last_name` and then +`first_name`. Specifying the sort direction on a field in the array tells +Ransack to _always_ sort that particular field in the specified direction. + +Multiple `default_order` fields may also be specified with a trailing options +Hash: + +```erb +<%= sort_link(@q, :last_name, %i(last_name first_name), + default_order: { last_name: 'asc', first_name: 'desc' }) %> +``` + +This example toggles the sort directions of both fields, by default +initially sorting the `last_name` field by ascending order, and the +`first_name` field by descending order. + +In the case that you wish to sort by some complex value, such as the result +of a SQL function, you may do so using scopes. In your model, define scopes +whose names line up with the name of the virtual field you wish to sort by, +as so: + +```ruby +class Person < ActiveRecord::Base + scope :sort_by_reverse_name_asc, lambda { order("REVERSE(name) ASC") } + scope :sort_by_reverse_name_desc, lambda { order("REVERSE(name) DESC") } +... +``` + +and you can then sort by this virtual field: + +```erb +<%= sort_link(@q, :reverse_name) %> +``` + +The trailing options Hash can also be used for passing additional options to the +generated link, like `class:`. + +The sort link order indicator arrows may be globally customized by setting a +`custom_arrows` option in an initializer file like +`config/initializers/ransack.rb`. + +You can also enable a `default_arrow` which is displayed on all sortable fields +which are not currently used in the sorting. This is disabled by default so +nothing will be displayed: + +```ruby +Ransack.configure do |c| + c.custom_arrows = { + up_arrow: '', + down_arrow: 'U+02193', + default_arrow: '' + } +end +``` + +All sort links may be displayed without the order indicator +arrows by setting `hide_sort_order_indicators` to true in the initializer file. +Note that this hides the arrows even if they were customized: + +```ruby +Ransack.configure do |c| + c.hide_sort_order_indicators = true +end +``` + +Without setting it globally, individual sort links may be displayed without +the order indicator arrow by passing `hide_indicator: true` in the sort link: + +```erb +<%= sort_link(@q, :name, hide_indicator: true) %> +``` + +### sort_url + +Ransack's `sort_url` helper is like a `sort_link` but returns only the url + +`sort_url` has the same API as `sort_link`: + +```erb +<%= sort_url(http://github.com/@q,%20:name,%20default_order:%20:desc) %> +``` + +```erb +<%= sort_url(http://github.com/@q,%20:last_name,%20[:last_name,%20'first_name%20asc']) %> +``` + +```erb +<%= sort_url(http://github.com/@q,%20:last_name,%20%i(last_name%20first_name), + default_order: { last_name: 'asc', first_name: 'desc' }) %> +``` + +### PostgreSQL's sort option + +The `NULLS FIRST` and `NULLS LAST` options can be used to determine whether nulls appear before or after non-null values in the sort ordering. + +You may want to configure it like this: + +```ruby +Ransack.configure do |c| + c.postgres_fields_sort_option = :nulls_first # or :nulls_last +end +``` + +To treat nulls as having the lowest or highest value respectively. To force nulls to always be first or last, use + +```ruby +Ransack.configure do |c| + c.postgres_fields_sort_option = :nulls_always_first # or :nulls_always_last +end +``` + +See this feature: https://www.postgresql.org/docs/13/queries-order.html + +#### Case Insensitive Sorting in PostgreSQL + +In order to request PostgreSQL to do a case insensitive sort for all string columns of a model at once, Ransack can be extended by using this approach: + +```ruby +module RansackObject + + def self.included(base) + base.columns.each do |column| + if column.type == :string + base.ransacker column.name.to_sym, type: :string do + Arel.sql("lower(#{base.table_name}.#{column.name})") + end + end + end + end +end +``` + +```ruby +class UserWithManyAttributes < ActiveRecord::Base + include RansackObject +end +``` + +If this approach is taken, it is advisable to [add a functional index](https://www.postgresql.org/docs/13/citext.html). + +This was originally asked in [a Ransack issue](https://github.com/activerecord-hackery/ransack/issues/1201) and a solution was found on [Stack Overflow](https://stackoverflow.com/a/34677378). diff --git a/docs/docs/getting-started/sorting.md b/docs/docs/getting-started/sorting.md new file mode 100644 index 000000000..1f465119d --- /dev/null +++ b/docs/docs/getting-started/sorting.md @@ -0,0 +1,109 @@ +--- +title: Sorting +--- + + +# Sorting + +## Sorting in the View + +You can add a form to capture sorting and filtering options together. + +```erb +# app/views/posts/index.html.erb + +<%= search_form_for @q do |f| %> + <%= f.label :title_cont %> + <%= f.search_field :title_cont %> + + <%= f.submit "Search" %> +<% end %> + + + + + + + + + + + + <% @posts.each do |post| %> + + + + + + <% end %> + +
<%= sort_link(@q, :title, "Title") %><%= sort_link(@q, :category, "Category") %><%= sort_link(@q, :created_at, "Created at") %>
<%= post.title %><%= post.category %><%= post.created_at.to_s(:long) %>
+``` + +## Sorting in the Controller + +To specify a default search sort field + order in the controller `index`: + +```ruby +# app/controllers/posts_controller.rb +class PostsController < ActionController::Base + def index + @q = Post.ransack(params[:q]) + @q.sorts = 'title asc' if @q.sorts.empty? + + @posts = @q.result(distinct: true) + end +end +``` + +Multiple sorts can be set by: + +```ruby +# app/controllers/posts_controller.rb +class PostsController < ActionController::Base + def index + @q = Post.ransack(params[:q]) + @q.sorts = ['title asc', 'created_at desc'] if @q.sorts.empty? + + @posts = @q.result(distinct: true) + end +end +``` + +## Sorting on Association Attributes + +You can sort on attributes of associated models by using the association name followed by the attribute name: + +```ruby +# Sort by the name of the associated category +@q = Post.ransack(s: 'category_name asc') +@posts = @q.result + +# Sort by attributes of nested associations +@q = Post.ransack(s: 'category_section_title desc') +@posts = @q.result +``` + +### Sorting on Globalized/Translated Attributes + +When working with internationalized models (like those using the Globalize gem), special care is needed when sorting on translated attributes of associations. The simplest approach is to use the `sort_link` helper directly with the translation attribute: + +```erb + +<%= sort_link @q, :translations_name %> +<%= sort_link @q, :category_translations_name %> +``` + +For programmatic sorting, let Ransack handle the joins first: + +```ruby +# Let Ransack establish the necessary joins for sorting +@q = Book.ransack(s: 'category_translations_name asc') +@books = @q.result.joins(:translations) + +# For complex scenarios with multiple translations +@q = Book.ransack(s: 'category_translations_name asc') +@books = @q.result.includes(:translations, category: :translations) +``` + +This ensures that Ransack properly handles the join dependencies between your main model's translations and the associated model's translations. diff --git a/docs/docs/getting-started/using-predicates.md b/docs/docs/getting-started/using-predicates.md new file mode 100644 index 000000000..6faecfbfc --- /dev/null +++ b/docs/docs/getting-started/using-predicates.md @@ -0,0 +1,282 @@ +--- +title: Using Predicates +--- + +The primary method of searching in Ransack is by using what is known as *predicates*. + +Predicates are used within Ransack search queries to determine what information to +match. For instance, the `cont` predicate will check to see if an attribute called +"first_name" contains a value using a wildcard query: + +```ruby +>> User.ransack(first_name_cont: 'Rya').result.to_sql +=> SELECT "users".* FROM "users" WHERE ("users"."first_name" LIKE '%Rya%') +``` + +You can also combine predicates for OR queries: +```ruby +>> User.ransack(first_name_or_last_name_cont: 'Rya').result.to_sql +=> SELECT "users".* FROM "users" WHERE ("users"."first_name" LIKE '%Rya%' + OR "users"."last_name" LIKE '%Rya%') +``` + +The syntax for `OR` queries on an associated model is not immediately obvious, but makes sense. Assuming a `User` `has_one` `Account` and the `Account` has `attributes` `foo` and `bar`: + +```ruby +>> User.ransack(account_foo_or_account_bar_cont: 'val').result.to_sql +=> SELECT "users".* FROM "users" INNER JOIN accounts ON accounts.user_id = users.id WHERE ("accounts.foo LIKE '%val%' OR accounts.bar LIKE '%val%') +``` + +Below is a list of the built-in predicates of Ransack and their opposites. You may already +be familiar with some of the predicates, as they also exist in the ARel library. + +If you want to add your own, please +see the [[Custom-Predicates|Custom Predicates]] page. + +**Please note:** any attempt to use a predicate for an attribute that does not exist will +*silently fail*. For instance, this will not work when there is no `name` attribute: + +```ruby +>> User.ransack(name_cont: 'Rya').result.to_sql +=> "SELECT "users".* FROM "users" +``` + +## eq (equals) + +The `eq` predicate returns all records where a field is *exactly* equal to a given value: + +```ruby +>> User.ransack(first_name_eq: 'Ryan').result.to_sql +=> SELECT "users".* FROM "users" WHERE "users"."first_name" = 'Ryan' +``` + +**Opposite: `not_eq`** + +## matches + +The `matches` predicate returns all records where a field is like a given value: + +```ruby +>> User.ransack(first_name_matches: 'Ryan').result.to_sql +=> SELECT "users".* FROM "users" WHERE ("users"."first_name" LIKE 'Ryan') +``` + +On Postgres, the case-insensitive ILIKE will be used. + +**Opposite: `does_not_match`** + +*Note: If you want to do wildcard matching, you may be looking for the `cont`/`not_cont` +predicates instead.* + +## lt (less than) + +The `lt` predicate returns all records where a field is less than a given value: + +```ruby +>> User.ransack(age_lt: 25).result.to_sql +=> SELECT "users".* FROM "users" WHERE ("users"."age" < 25) +``` + +**Opposite: `gteq` (greater than or equal to)** + +## lteq (less than or equal to) + +The `lteq` predicate returns all records where a field is less than *or equal to* a given value: + +```ruby +>> User.ransack(age_lteq: 25).result.to_sql +=> SELECT "users".* FROM "users" WHERE ("users"."age" <= 25) +``` + +**Opposite: `gt` (greater than)** + +## in + +The `in` predicate returns all records where a field is within a specified list: + +```ruby +>> User.ransack(age_in: 20..25).result.to_sql +=> SELECT "users".* FROM "users" WHERE "users"."age" IN (20, 21, 22, 23, 24, 25) +``` + +It can also take an array: + +```ruby +>> User.ransack(age_in: [20, 21, 22, 23, 24, 25]).result.to_sql +=> SELECT "users".* FROM "users" WHERE "users"."age" IN (20, 21, 22, 23, 24, 25) +``` + +**Opposite: `not_in`** + +## cont + +The `cont` predicate returns all records where a field contains a given value: + +```ruby +>> User.ransack(first_name_cont: 'Rya').result.to_sql +=> SELECT "users".* FROM "users" WHERE ("users"."first_name" LIKE '%Rya%') +``` + +**Opposite: `not_cont`** + +## cont_any (contains any) + +The `cont_any` predicate returns all records where a field contains any of the given values: + +```ruby +>> User.ransack(first_name_cont_any: %w(Rya Lis)).result.to_sql +=> SELECT "users".* FROM "users" WHERE (("users"."first_name" LIKE '%Rya%' OR "users"."first_name" LIKE '%Lis%')) +``` + +**Opposite: `not_cont_any`** + + +## cont_all (contains all) + +The `cont_all` predicate returns all records where a field contains all of the given values: + +```ruby +>> User.ransack(city_cont_all: %w(Grand Rapids)).result.to_sql +=> SELECT "users".* FROM "users" WHERE (("users"."city" LIKE '%Grand%' AND "users"."city" LIKE '%Rapids%')) +``` + +**Opposite: `not_cont_all`** + + +## i_cont + +The `i_cont` case-insensitive predicate returns all records where a field contains a given value and ignores case: + +```ruby +>> User.ransack(first_name_i_cont: 'Rya').result.to_sql +=> SELECT "users".* FROM "users" WHERE (LOWER("users"."first_name") LIKE '%rya%') +``` + +**Opposite: `not_i_cont`** + +## i_cont_any + +The `i_cont_any` case-insensitive predicate returns all records where a field contains any of the given values and ignores case: + +```ruby +>> User.ransack(first_name_i_cont_any: %w(Rya Lis)).result.to_sql +=> SELECT "users".* FROM "users" WHERE ((LOWER("users"."first_name") LIKE '%rya%' OR LOWER("users"."first_name") LIKE '%lis%')) +``` + +**Opposite: `not_i_cont_any`** + + +## i_cont_all + +The `i_cont_all` case-insensitive predicate returns all records where a field contains all of the given values and ignores case: + +```ruby +>> User.ransack(city_i_cont_all: %w(Grand Rapids)).result.to_sql +=> SELECT "users".* FROM "users" WHERE ((LOWER("users"."city") LIKE '%grand%' AND LOWER("users"."city") LIKE '%rapids%')) +``` + +**Opposite: `not_i_cont_all`** + +## start (starts with) + +The `start` predicate returns all records where a field begins with a given value: + +```ruby +>> User.ransack(first_name_start: 'Rya').result.to_sql +=> SELECT "users".* FROM "users" WHERE ("users"."first_name" LIKE 'Rya%') +``` + +**Opposite: `not_start`** + +## end (ends with) + +The `end` predicate returns all records where a field ends with a given value: + +```ruby +>> User.ransack(first_name_end: 'yan').result.to_sql +=> SELECT "users".* FROM "users" WHERE ("users"."first_name" LIKE '%yan') +``` + +**Opposite: `not_end`** + +## true + +The `true` predicate returns all records where a field is true. The '1' indicates that +to Ransack that you indeed want to check the truthiness of this field. The other truthy +values are 'true', 'TRUE', 't' or 'T'. + +```ruby +>> User.ransack(awesome_true: '1').result.to_sql +=> SELECT "users".* FROM "users" WHERE ("users"."awesome" = 't') +``` + +*Note: different database systems use different values to represent truth. In the above +example, we are using SQLite3.* + +**Opposite: `not_true`** + +## false + +The `false` predicate returns all records where a field is false. + +```ruby +>> User.ransack(awesome_false: '1').result.to_sql +=> SELECT "users".* FROM "users" WHERE ("users"."awesome" = 'f') +``` + +**Opposite: `not_false`** + +*Note: the `false` predicate may be considered the opposite of the `true` predicate if the field does not contain `null` values. Otherwise, use `not_false`.* + +## present + +The `present` predicate returns all records where a field is present (not null and not a +blank string). + +```ruby +>> User.ransack(first_name_present: '1').result.to_sql +=> SELECT "users".* FROM "users" WHERE (("users"."first_name" IS NOT NULL AND "users"."first_name" != '')) +``` + +**Opposite: `blank`** + +## null + +The `null` predicate returns all records where a field is null: + +```ruby +>> User.ransack(first_name_null: 1).result.to_sql +=> SELECT "users".* FROM "users" WHERE "users"."first_name" IS NULL +``` + +**Opposite: `not_null`** + +# URL parameter structure + +The search parameters are passed to ransack as a hash. The URL representation of this hash uses the bracket notation: ```hash_name[key]=value```. The hash_name is the parameter which is defined in the controller, for instance ```q```. The key is the attribute and search predicate compound, for instance ```first_name_cont```, the value is the search parameter. When searching without using the search form helpers this URL structure needs to be created manually. + +For example, the URL layout for searching and sorting users could looks like this: + +``` +/users.json?q[first_name_cont]=pete&q[last_name_cont]=jack&q[s]=created_at+desc +``` + +_Note that the sorting parameter ```s``` is nested within the ```q``` hash._ + +When using JavaScript to create such a URL, a matching jQuery request could look like this: + +```javascript +$.ajax({ + url: "/users.json", + data: { + q: { + first_name_cont: "pete", + last_name_cont: "jack", + s: "created_at desc" + } + }, + success: function (data){ + console.log(data); + } +}); +``` diff --git a/docs/docs/going-further/_category_.json b/docs/docs/going-further/_category_.json new file mode 100644 index 000000000..63f74f9df --- /dev/null +++ b/docs/docs/going-further/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Going further", + "position": 2 +} diff --git a/docs/docs/going-further/acts-as-taggable-on.md b/docs/docs/going-further/acts-as-taggable-on.md new file mode 100644 index 000000000..546a999a2 --- /dev/null +++ b/docs/docs/going-further/acts-as-taggable-on.md @@ -0,0 +1,114 @@ +--- +title: Acts-as-taggable-on +sidebar_position: 13 +--- + +## Using Acts As Taggable On + +If you have an `ActiveRecord` model and you're using [acts-as-taggable-on](https://github.com/mbleigh/acts-as-taggable-on), +chances are you might want to search on tagged fields. Follow the instructions to install the gem and then set up your project files. + +### Configure the model + +`app/models/tasks.rb` + +You can call the tagging field anything you like, it just needs to be plural. No migration is needed as this is stored in the internal ActsAsTaggable tables (`tags` and `taggings`). + +```ruby +class Task < ApplicationRecord + acts_as_taggable_on :projects +end +``` + +### Controller + +Add a field to strong params in the controller. Use the singular name with `_list`. + +`app/controllers/tasks_controller.rb` + +```ruby +def strong_params + params + .require(:tasks) + .permit(:task, :example_field, :project_list) +``` + +### Form + +We need to `send` the tag fieldname to our model, also using the singular naming. + +```erb +
+ <%= f.label :project_list %> + <%= f.text_field :project_list, value: @task.send(:project_list).to_s %> +
+``` + +Now we can collect our data via the form, with tags separated by commas. + +## Ransack Search + +Imagine you have the following two instances of `Task`: + +```ruby +{ id: 1, name: 'Clean up my room', projects: [ 'Home', 'Personal' ] } +{ id: 2, name: 'Complete math exercises', projects: [ 'Homework', 'Study' ] } +``` + +When you're writing a `Ransack` search form, you can choose any of the following options: + +```erb +<%= search_form_for @search do |f| %> + <%= f.text_field :projects_name_in %> + <%= f.text_field :projects_name_eq %> + <%= f.text_field :projects_name_cont %> +<% end %> +``` + +### Option A - Match keys exactly + +Option `A` will match keys exactly. This is the solution to choose if you want to distinguish 'Home' from 'Homework': searching for 'Home' will return just the `Task` with id 1. It also allows searching for more than one tag at once (comma separated): +- `Home, Personal` will return task 1 +- `Home, Homework` will return task 1 and 2 + +### Option B - match key combinations + +Option `B` will match all keys exactly. This is the solution if you wanna search for specific combinations of tags: +- `Home` will return nothing, as there is no Task with just the `Home` tag +- `Home, Personal` will return task 1 + +### Option C - match substrings + +Option `C` is used to match substrings. This is useful when you don't care for the exact tag, but only for part of it: +- `Home` will return task 1 and 2 (`/Home/` matches both `"Home"` and `"Homework"`) + +### Option D - select from a list of tags + +In Option `D` we allow the user to select a list of valid tags and then search against them. We use the plural name here. + +```erb +
+ <%= f.label :projects_name, 'Project' %> + <%= f.select :projects_name_in, ActsAsTaggableOn::Tag.distinct.order(:name).pluck(:name) %> +
+``` + +## Multitenancy + +ActsAsTaggableOn allows scoping of tags based on another field on the model. Suppose we have a `language` field on the model, as an effective second level key. We would adjust our model to look like this: + +```ruby +class Task < ApplicationRecord + acts_as_taggable_on :projects + acts_as_taggable_tenant :language +end +``` + +The Ransack search is then filtered using the `for_tenant` method + +```erb +
+ <%= f.label :projects_name, 'Project' %> + <%= f.select :projects_name_in, ActsAsTaggableOn::Tag.for_tenant('fr').distinct.order(:name).pluck(:name) %> +
+ diff --git a/docs/docs/going-further/associations.md b/docs/docs/going-further/associations.md new file mode 100644 index 000000000..1dff7612b --- /dev/null +++ b/docs/docs/going-further/associations.md @@ -0,0 +1,70 @@ +--- +sidebar_position: 1 +title: Associations +--- + +### Associations + +You can easily use Ransack to search for objects in `has_many` and `belongs_to` +associations. + +Given these associations... + +```ruby +class Employee < ActiveRecord::Base + belongs_to :supervisor + + # has attributes first_name:string and last_name:string +end + +class Department < ActiveRecord::Base + has_many :supervisors + + # has attribute title:string +end + +class Supervisor < ActiveRecord::Base + belongs_to :department + has_many :employees + + # has attribute last_name:string +end +``` + +... and a controller... + +```ruby +class SupervisorsController < ApplicationController + def index + @q = Supervisor.ransack(params[:q]) + @supervisors = @q.result.includes(:department, :employees) + end +end +``` + +... you might set up your form like this... + +```erb +<%= search_form_for @q do |f| %> + <%= f.label :last_name_cont %> + <%= f.search_field :last_name_cont %> + + <%= f.label :department_title_cont %> + <%= f.search_field :department_title_cont %> + + <%= f.label :employees_first_name_or_employees_last_name_cont %> + <%= f.search_field :employees_first_name_or_employees_last_name_cont %> + + <%= f.submit "search" %> +<% end %> +... +<%= content_tag :table do %> + <%= content_tag :th, sort_link(@q, :last_name) %> + <%= content_tag :th, sort_link(@q, :department_title) %> + <%= content_tag :th, sort_link(@q, :employees_last_name) %> +<% end %> +``` + +If you have trouble sorting on associations, try using an SQL string with the +pluralized table (`'departments.title'`,`'employees.last_name'`) instead of the +symbolized association (`:department_title)`, `:employees_last_name`). diff --git a/docs/docs/going-further/custom-predicates.md b/docs/docs/going-further/custom-predicates.md new file mode 100644 index 000000000..ce87f8c6f --- /dev/null +++ b/docs/docs/going-further/custom-predicates.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 1 +title: Custom predicates +--- + +If you'd like to add your own custom Ransack predicates: + +```ruby +# config/initializers/ransack.rb + +Ransack.configure do |config| + config.add_predicate 'equals_diddly', # Name your predicate + # What non-compound ARel predicate will it use? (eq, matches, etc) + arel_predicate: 'eq', + # Format incoming values as you see fit. (Default: Don't do formatting) + formatter: proc { |v| "#{v}-diddly" }, + # Validate a value. An "invalid" value won't be used in a search. + # Below is default. + validator: proc { |v| v.present? }, + # Should compounds be created? Will use the compound (any/all) version + # of the arel_predicate to create a corresponding any/all version of + # your predicate. (Default: true) + compounds: true, + # Force a specific column type for type-casting of supplied values. + # (Default: use type from DB column) + type: :string, + # Use LOWER(column on database). + # (Default: false) + case_insensitive: true +end +``` +You can check all Arel predicates [here](https://github.com/rails/rails/blob/main/activerecord/lib/arel/predications.rb). + +If Arel does not have the predicate you are looking for, consider monkey patching it: + +```ruby +# config/initializers/ransack.rb + +module Arel + module Predications + def gteq_or_null(other) + left = gteq(other) + right = eq(nil) + left.or(right) + end + end +end + +Ransack.configure do |config| + config.add_predicate 'gteq_or_null', arel_predicate: 'gteq_or_null' +end +``` diff --git a/docs/docs/going-further/documentation.md b/docs/docs/going-further/documentation.md new file mode 100644 index 000000000..350b3ac2e --- /dev/null +++ b/docs/docs/going-further/documentation.md @@ -0,0 +1,43 @@ +--- +sidebar_position: 11 +title: Documentation +--- + +Ransack uses [Docusaurus](https://docusaurus.io/) for documentation. To contribute to the docs simply use the "Edit this page" link from any page to directly edit, or else pull the repo and edit locally. + +### Local Development + +Switch to docs folder + +``` +cd docs +``` + +Install docusaurus and other dependencies + +``` +yarn install +``` + + +Start a local development server and open up a browser window. Most changes are reflected live without having to restart the server. + +``` +yarn start +``` + +### Build + +``` +yarn build +``` + +This command generates static content into the `build` directory and can be served using any static contents hosting service. + +### Deployment + +Using SSH: + +``` +USE_SSH=true yarn deploy +``` diff --git a/docs/docs/going-further/exporting-to-csv.md b/docs/docs/going-further/exporting-to-csv.md new file mode 100644 index 000000000..971cfed83 --- /dev/null +++ b/docs/docs/going-further/exporting-to-csv.md @@ -0,0 +1,49 @@ +--- +sidebar_position: 2 +title: CSV Export +--- + +Exporting to CSV + +Example downloading a csv file preserving ransack search, based on [this gist](https://gist.github.com/pama/adff25ed1f4b796ce088ea362a08e1c5) + +```ruby title='index.html.erb' +

Users

+ +<%= search_form_for @q, url: dashboard_index_path do |f| %> + <%= f.label :name_cont %> + <%= f.search_field :name_cont %> + + <%= f.submit %> +<% end %> + +
    + <% @users.each do |user| %> +
  • <%= user.name %> [<%= user.devices.map {|device| device.name }.join(', ') %>]
  • + <% end %> +
+ +<% if params[:q] %> + <%= link_to 'Export 1', dashboard_index_path({name: params[:q][:name_cont]}.merge({format: :csv})) %> +<% else %> + <%= link_to 'Export 2', dashboard_index_path(format: 'csv') %> +<% end %> +``` + +```ruby title='user.rb' +require 'csv' + +class User < ApplicationRecord + has_many :devices + + def self.get_csv(users) + CSV.generate do |csv| + csv << ["Name", "Devices"] + + users.each do |user| + csv << [user.name, user.devices.map{|device| device.name}.join(', ')] + end + end + end +end +``` diff --git a/docs/docs/going-further/external-guides.md b/docs/docs/going-further/external-guides.md new file mode 100644 index 000000000..9786cb112 --- /dev/null +++ b/docs/docs/going-further/external-guides.md @@ -0,0 +1,57 @@ +--- +sidebar_position: 9 +title: External resources +--- + +There is a plethora of material on Ransack around the internet. We've collected some here for your convenience. + +Do you want to have a blog post or other content on Ransack highlighted? Please just edit the page, add your content and a Pull Request will be sent to Ransack maintainers for approval. + +# Screencasts + +- [DriftingRuby: Ransack Search and Hotwire](https://www.driftingruby.com/episodes/ransack-search-and-hotwire) +- [GoRails: Forum Series Part 6: Search with Ransack](https://gorails.com/episodes/forum-search-with-ransack) +- [Railscast 370 - Ransack](http://railscasts.com/episodes/370-ransack) +- [Search And Sort Ransack Associations With The Rails Ransack Gem | Ruby On Rails 6 Ransack Tutorial](https://www.youtube.com/watch?v=rtg-5EXwpbg) + + +# Gems + +- [ActiveAdmin](https://activeadmin.info/) The Administration Framework for Rails **_uses Ransack internally_** +- [Ransack Memory](https://github.com/richardrails/ransack_memory) Automatically save and load Ransack's filtered params into the Rail's session +- [Mobility Ransack](https://github.com/shioyama/mobility-ransack) Search attributes translated by Mobility with Ransack. +- [Ransack UI](https://github.com/ndbroadbent/ransack_ui) Framework for building a search UI with Ransack **_seems abandoned_** + +# Blogs + +- [Search And Sort In Ruby On Rails 6 With The Ransack Gem](https://deanin.com/blog/ransack/) +- [Implement Ransack Gem in Ruby on Rails](https://www.botreetechnologies.com/blog/implementing-advanced-search-in-ruby-on-rails-with-ransack/) +- [Searching and Sorting with Ransack](https://jaspercurry.medium.com/searching-and-sorting-on-rails-with-ransack-560e862e650a) +- [How to Build Your Own ActiveAdmin Filters with Ransack](https://www.viget.com/articles/how-to-build-your-own-filters-with-ransack/) +- [Avoid Ransack's N+1 Pitfall!](https://dev.to/husteadrobert/avoid-ransacks-n1-pitfall-33of) +- [Filter and paging with Kaminari](https://gist.github.com/MyklClason/e4dc96fd0e009b7b3a9c84ddbb1e11d2) +- [Pagination for Ransack Forms](https://nicholaide.github.io/ransack/2016/11/26/ransack-pagination.html) +- [AJAX Search, Sort, Paginate with Ransack and Kaminari](https://techbrownbags.wordpress.com/2014/01/17/rails-ajax-search-sort-paginate-with-ransack-kaminari/) +- [Searching with Ransack in Ruby on Rails](http://blog.magmalabs.io/2019/03/12/searching-with-ransack-in-ruby-on-rails.html) +- [Role scopes with gem Ransack](https://blog.corsego.com/rolify-scopes) +- [Searching and Sorting with Ransack](https://www.mintbit.com/blog/searching-and-sorting-with-ransack) +- [Using custom scopes with Ransack gem in Rails](https://profilehunt.net/blog/using-custom-scopes-with-ransack-in-rails) +- [Query Date Range With Ransack](https://lingceng.github.io/blog/2015/12/28/query-date-range-with-ransack/) +- [ransack vs searchkick: Building a search feature in Rails](https://www.cookieshq.co.uk/posts/ransack-vs-searchkick-building-a-search-feature-in-rails) +- [Using ransack and delegate in Rails](https://huangwenwei.com/blogs/using-ransack-and-delegate-in-rails) +- [Using Ransack as a Search Engine](https://medium.com/@jelaniwoods/using-ransack-as-a-search-engine-92e002a68da) +- [Advanced Search with Ransack](https://www.sitepoint.com/advanced-search-ransack/) +- [Sort a table of records in Rails with Ransack](https://alankydd.wordpress.com/2012/03/12/sort-a-table-of-records-in-rails-with-ransack/) +- [Ransack: Search with Multiple Checkboxes (Rails)](https://iamjosh.wordpress.com/2014/03/07/ransack-search-with-multiple-checkboxes/) +- [Rails : Ransack : Sorting data by ratings](https://cbabhusal.wordpress.com/2017/01/03/rails-ransack-sorting-data-by-ratings/) +- [Setting Up Rails 5 API Only App with ActiveAdmin enabled](https://rrott.com/blog/ror/rails-5-api-with-activeadmin-integration/) +- [Ransack, the library formerly known as MetaSearch 2.0](https://ernie.io/2011/04/01/ransack-the-library-formerly-known-as-metasearch-2-0/) **_some Ransack history_** + +## In French + +- [Faciliter les recherches avec Ransack](https://www.synbioz.com/blog/tech/faciliter-les-recherches-avec-ransack) + +## In Vietnamese + +- [Ransack - công cụ tuyệt vời giúp tìm kiếm và sắp xếp dữ liệu đơn giản hơn +](https://nddblog.com/posts/ransack-cong-cu-tuyet-voi-giup-tim-kiem-va-sap-xep-du-lieu-don-gian-hon) diff --git a/docs/docs/going-further/form-customisation.md b/docs/docs/going-further/form-customisation.md new file mode 100644 index 000000000..95b042532 --- /dev/null +++ b/docs/docs/going-further/form-customisation.md @@ -0,0 +1,63 @@ +--- +sidebar_position: 4 +title: Form customisation +--- + +Predicate and attribute labels in forms may be specified with I18n in a translation file (see the locale files in [Ransack::Locale](https://github.com/activerecord-hackery/ransack/tree/main/lib/ransack/locale) for more examples): + +```yml +# locales/en.yml +en: + ransack: + asc: ascending + desc: descending + predicates: + cont: contains + not_cont: not contains + start: starts with + end: ends with + gt: greater than + lt: less than + attributes: + person: + name: Full Name + article: + title: Article Title + body: Main Content +``` +The names of attribute fields may also be changed globally or under activerecord: + +```yml +# locales/en.yml +en: + attributes: + model_name: + model_field1: field name1 + model_field2: field name2 + activerecord: + attributes: + namespace/article: + title: AR Namespaced Title + namespace_article: + title: Old Ransack Namespaced Title +``` + +To limit the predicates in the `predicate_select` form helper in a view template, pass an array of permitted predicates with `only`: + +```erb +<%= f.predicate_select only: %i(cont not_cont eq not_eq blank null) %> +``` + +Compound predicates (`_any` & `_all`) may be removed by passing the option `compounds: false`. + +```erb +<%= f.predicate_select compounds: false %> +``` + +Searchable attributes versus non-searchable ones may be specified as follows: + +```ruby +def self.ransackable_attributes(auth_object = nil) + %w(searchable_attribute_1 searchable_attribute_2 ...) + _ransackers.keys +end +``` diff --git a/docs/docs/going-further/i18n.md b/docs/docs/going-further/i18n.md new file mode 100644 index 000000000..c70fa4b06 --- /dev/null +++ b/docs/docs/going-further/i18n.md @@ -0,0 +1,96 @@ +--- +sidebar_position: 3 +title: i18n +--- + +# i18n and Ransack + +Ransack translation files are available in +[Ransack::Locale](https://github.com/activerecord-hackery/ransack/tree/main/lib/ransack/locale). You may also be interested in one of the +many translations for Ransack available at +http://www.localeapp.com/projects/2999. + +Predicate and attribute translations in forms may be specified as follows (see +the translation files in [Ransack::Locale](https://github.com/activerecord-hackery/ransack/tree/main/lib/ransack/locale) for more examples): + +locales/en.yml: +```yml +en: + ransack: + asc: ascending + desc: descending + predicates: + cont: contains + not_cont: not contains + start: starts with + end: ends with + gt: greater than + lt: less than + models: + person: Passenger + attributes: + person: + name: Full Name + article: + title: Article Title + body: Main Content +``` + +Attribute names may also be changed globally, or under `activerecord`: + +```yml +en: + attributes: + model_name: + model_field1: field name1 + model_field2: field name2 + activerecord: + attributes: + namespace/article: + title: AR Namespaced Title + namespace_article: + title: Old Ransack Namespaced Title +``` + +## Working with Globalized Attributes + +If you're using the [Globalize gem](https://github.com/globalize/globalize) for internationalized model attributes, you may encounter issues when sorting on translated attributes of associations while also joining the main model's translations. + +For example, if you have a `Book` model with translated `title` and a `Category` model with translated `name`, sorting on the category's translated name while joining the book's translations may not work as expected: + +```ruby +# This may not work correctly: +Book.joins(:translations).ransack({ s: ['category_translations_name asc'] }).result +``` + +### Workaround for Globalized Attributes Sorting + +When working with globalized attributes and you need to sort on translated fields of associations, the simplest and most effective approach is to use the `sort_link` helper with the translation attribute directly: + +```erb + +<%= sort_link @search, :translations_name %> +<%= sort_link @search, :category_translations_name %> +``` + +For programmatic sorting, let Ransack handle the joins first: + +```ruby +# Instead of joining translations first, let Ransack handle the joins: +search = Book.ransack({ s: ['category_translations_name asc'] }) +results = search.result.joins(:translations) + +# Or use the includes method to ensure all necessary translations are loaded: +search = Book.ransack({ s: ['category_translations_name asc'] }) +results = search.result.includes(:translations, category: :translations) + +# For more complex scenarios, you can manually specify the joins: +search = Book.ransack({ s: ['category_translations_name asc'] }) +results = search.result + .joins(:translations) + .joins(category: :translations) +``` + +The key is to let Ransack establish the necessary joins for sorting first, then add any additional joins you need for the query. + +This approach ensures that Ransack properly handles the complex join dependencies between your main model's translations and the associated model's translations. diff --git a/docs/docs/going-further/img/create_release.png b/docs/docs/going-further/img/create_release.png new file mode 100644 index 000000000..2139593b9 Binary files /dev/null and b/docs/docs/going-further/img/create_release.png differ diff --git a/docs/docs/going-further/merging-searches.md b/docs/docs/going-further/merging-searches.md new file mode 100644 index 000000000..b20608ca6 --- /dev/null +++ b/docs/docs/going-further/merging-searches.md @@ -0,0 +1,41 @@ +--- +sidebar_position: 5 +title: Merging searches +--- + +To find records that match multiple searches, it's possible to merge all the ransack search conditions into an ActiveRecord relation to perform a single query. In order to avoid conflicts between joined table names it's necessary to set up a shared context to track table aliases used across all the conditions before initializing the searches: + +```ruby +shared_context = Ransack::Context.for(Person) + +search_parents = Person.ransack( + { parent_name_eq: "A" }, context: shared_context +) + +search_children = Person.ransack( + { children_name_eq: "B" }, context: shared_context +) + +shared_conditions = [search_parents, search_children].map { |search| + Ransack::Visitor.new.accept(search.base) +} + +Person.joins(shared_context.join_sources) + .where(shared_conditions.reduce(&:or)) + .to_sql +``` +Produces: +```sql +SELECT "people".* +FROM "people" +LEFT OUTER JOIN "people" "parents_people" + ON "parents_people"."id" = "people"."parent_id" +LEFT OUTER JOIN "people" "children_people" + ON "children_people"."parent_id" = "people"."id" +WHERE ( + ("parents_people"."name" = 'A' OR "children_people"."name" = 'B') + ) +ORDER BY "people"."id" DESC +``` + +Admittedly this is not as simple as it should be, but it's workable for now. (Implementing [issue 417](https://github.com/activerecord-hackery/ransack/issues/417) could make this more straightforward.) diff --git a/docs/docs/going-further/other-notes.md b/docs/docs/going-further/other-notes.md new file mode 100644 index 000000000..b67c6aaf5 --- /dev/null +++ b/docs/docs/going-further/other-notes.md @@ -0,0 +1,458 @@ +--- +sidebar_position: 8 +title: Other notes +--- + +### Ransack Aliases + +You can customize the attribute names for your Ransack searches by using a +`ransack_alias`. This is particularly useful for long attribute names that are +necessary when querying associations or multiple columns. + +```ruby +class Post < ActiveRecord::Base + belongs_to :author + + # Abbreviate :author_first_name_or_author_last_name to :author + ransack_alias :author, :author_first_name_or_author_last_name +end +``` + +Now, rather than using `:author_first_name_or_author_last_name_cont` in your +form, you can simply use `:author_cont`. This serves to produce more expressive +query parameters in your URLs. + +```erb +<%= search_form_for @q do |f| %> + <%= f.label :author_cont %> + <%= f.search_field :author_cont %> +<% end %> +``` + +You can also use `ransack_alias` for sorting. + +```ruby +class Post < ActiveRecord::Base + belongs_to :author + + # Abbreviate :author_first_name to :author + ransack_alias :author, :author_first_name +end +``` + +Now, you can use `:author` instead of `:author_first_name` in a `sort_link`. + +```erb +<%= sort_link(@q, :author) %> +``` + +Note that using `:author_first_name_or_author_last_name_cont` would produce an invalid sql query. In those cases, Ransack ignores the sorting clause. + + + +### Problem with DISTINCT selects + +If passed `distinct: true`, `result` will generate a `SELECT DISTINCT` to +avoid returning duplicate rows, even if conditions on a join would otherwise +result in some. It generates the same SQL as calling `uniq` on the relation. + +Please note that for many databases, a sort on an associated table's columns +may result in invalid SQL with `distinct: true` -- in those cases, you +will need to modify the result as needed to allow these queries to work. + +For example, you could call joins and includes on the result which has the +effect of adding those tables columns to the select statement, overcoming +the issue, like so: + +```ruby +def index + @q = Person.ransack(params[:q]) + @people = @q.result(distinct: true) + .includes(:articles) + .joins(:articles) + .page(params[:page]) +end +``` + +If the above doesn't help, you can also use ActiveRecord's `select` query +to explicitly add the columns you need, which brute force's adding the +columns you need that your SQL engine is complaining about, you need to +make sure you give all of the columns you care about, for example: + +```ruby +def index + @q = Person.ransack(params[:q]) + @people = @q.result(distinct: true) + .select('people.*, articles.name, articles.description') + .page(params[:page]) +end +``` + +Another method to approach this when using Postgresql is to use ActiveRecords's `.includes` in combination with `.group` instead of `distinct: true`. + +For example: +```ruby +def index + @q = Person.ransack(params[:q]) + @people = @q.result + .group('persons.id') + .includes(:articles) + .page(params[:page]) +end + +``` + +A final way of last resort is to call `to_a.uniq` on the collection at the end +with the caveat that the de-duping is taking place in Ruby instead of in SQL, +which is potentially slower and uses more memory, and that it may display +awkwardly with pagination if the number of results is greater than the page size. + +For example: + +```ruby +def index + @q = Person.ransack(params[:q]) + @people = @q.result.includes(:articles).page(params[:page]).to_a.uniq +end +``` + +### Problem with Globalized Attributes and Sorting + +When using internationalization gems like [Globalize](https://github.com/globalize/globalize), you may encounter issues when trying to sort on translated attributes of associations while also having pre-existing joins to translation tables. + +**Problem scenario:** +```ruby +# This may fail to generate proper joins: +Book.joins(:translations).ransack({ s: ['category_translations_name asc'] }).result +``` + +**Solution:** +The simplest and most effective approach is to use the `sort_link` helper directly with the translation attribute: + +```erb + +<%= sort_link @search, :translations_name %> +<%= sort_link @search, :category_translations_name %> +``` + +For programmatic sorting, let Ransack establish the sorting joins first, then add your additional joins: + +```ruby +# Let Ransack handle the sorting joins first +search = Book.ransack({ s: ['category_translations_name asc'] }) +results = search.result.joins(:translations) + +# Or use includes for complex scenarios +search = Book.ransack({ s: ['category_translations_name asc'] }) +results = search.result.includes(:translations, category: :translations) +``` + +This ensures that Ransack properly handles the join dependencies between your main model's translations and the associated model's translations. + +#### `PG::UndefinedFunction: ERROR: could not identify an equality operator for type json` + +If you get the above error while using `distinct: true` that means that +one of the columns that Ransack is selecting is a `json` column. +PostgreSQL does not provide comparison operators for the `json` type. While +it is possible to work around this, in practice it's much better to convert those +to `jsonb`, as [recommended by the PostgreSQL documentation](https://www.postgresql.org/docs/9.6/static/datatype-json.html). + +### Authorization (allowlisting/denylisting) + +By default, searching and sorting are not authorized on any column of your model +and no class methods/scopes are allowlisted. + +Ransack adds four methods to `ActiveRecord::Base` that you can redefine as +class methods in your models to apply selective authorization: + +- `ransackable_attributes` +- `ransackable_associations` +- `ransackable_scopes` +- `ransortable_attributes` + +Here is how these four methods could be implemented in your application: + +```ruby + # `ransackable_attributes` returns searchable column names + # and any defined ransackers as an array of strings. + # + def ransackable_attributes(auth_object = nil) + %w(title body) + _ransackers.keys + end + + # `ransackable_associations` returns the names + # of searchable associations as an array of strings. + # + def ransackable_associations(auth_object = nil) + %w[author] + end + + # `ransortable_attributes` by default returns the names + # of all attributes available for sorting as an array of strings. + # + def ransortable_attributes(auth_object = nil) + ransackable_attributes(auth_object) + end + + # `ransackable_scopes` by default returns an empty array + # i.e. no class methods/scopes are authorized. + # For overriding with an allowlist, return an array of *symbols*. + # + def ransackable_scopes(auth_object = nil) + [] + end +``` + +Any values not returned from these methods will be ignored by Ransack, i.e. +they are not authorized. + +All four methods can receive a single optional parameter, `auth_object`. When +you call the search or ransack method on your model, you can provide a value +for an `auth_object` key in the options hash which can be used by your own +overridden methods. + +Here is an example that puts all this together, adapted from +[this blog post by Ernie Miller](https://ernie.io/2012/05/11/why-your-ruby-class-macros-might-suck-mine-did/). +In an `Article` model, add the following `ransackable_attributes` class method +(preferably private): + +```ruby +class Article < ActiveRecord::Base + def self.ransackable_attributes(auth_object = nil) + if auth_object == :admin + # allow all attributes for admin + column_names + _ransackers.keys + else + # allow only the title and body attributes for other users + %w(title body) + end + end + + private_class_method :ransackable_attributes +end +``` + +Here is example code for the `articles_controller`: + +```ruby +class ArticlesController < ApplicationController + def index + @q = Article.ransack(params[:q], auth_object: set_ransack_auth_object) + @articles = @q.result + end + + private + + def set_ransack_auth_object + current_user.admin? ? :admin : nil + end +end +``` + +Trying it out in `rails console`: + +```ruby +> Article +=> Article(id: integer, person_id: integer, title: string, body: text) + +> Article.ransackable_attributes +=> ["title", "body"] + +> Article.ransackable_attributes(:admin) +=> ["id", "person_id", "title", "body"] + +> Article.ransack(id_eq: 1).result.to_sql +=> SELECT "articles".* FROM "articles" # Note that search param was ignored! + +> Article.ransack({ id_eq: 1 }, { auth_object: nil }).result.to_sql +=> SELECT "articles".* FROM "articles" # Search param still ignored! + +> Article.ransack({ id_eq: 1 }, { auth_object: :admin }).result.to_sql +=> SELECT "articles".* FROM "articles" WHERE "articles"."id" = 1 +``` + +That's it! Now you know how to allow/block various elements in Ransack. + +### Handling unknown predicates or attributes + +By default, Ransack will ignore any unknown predicates or attributes: + +```ruby +Article.ransack(unknown_attr_eq: 'Ernie').result.to_sql +=> SELECT "articles".* FROM "articles" +``` + +Ransack may be configured to raise an error if passed an unknown predicate or +attributes, by setting the `ignore_unknown_conditions` option to `false` in your +Ransack initializer file at `config/initializers/ransack.rb`: + +```ruby +Ransack.configure do |c| + # Raise errors if a query contains an unknown predicate or attribute. + # Default is true (do not raise error on unknown conditions). + c.ignore_unknown_conditions = false +end +``` + +```ruby +Article.ransack(unknown_attr_eq: 'Ernie') +# Ransack::InvalidSearchError (Invalid search term unknown_attr_eq) +``` + +As an alternative to setting a global configuration option, the `.ransack!` +class method also raises an error if passed an unknown condition: + +```ruby +Article.ransack!(unknown_attr_eq: 'Ernie') +# Ransack::InvalidSearchError: Invalid search term unknown_attr_eq +``` + +This is equivalent to the `ignore_unknown_conditions` configuration option, +except it may be applied on a case-by-case basis. + +### Using Scopes/Class Methods + +Continuing on from the preceding section, searching by scopes requires defining +a whitelist of `ransackable_scopes` on the model class. The whitelist should be +an array of *symbols*. By default, all class methods (e.g. scopes) are ignored. +Scopes will be applied for matching `true` values, or for given values if the +scope accepts a value: + +```ruby +class Employee < ActiveRecord::Base + scope :activated, ->(boolean = true) { where(active: boolean) } + scope :salary_gt, ->(amount) { where('salary > ?', amount) } + + # Scopes are just syntactical sugar for class methods, which may also be used: + + def self.hired_since(date) + where('start_date >= ?', date) + end + + def self.ransackable_scopes(auth_object = nil) + if auth_object.try(:admin?) + # allow admin users access to all three methods + %i(activated hired_since salary_gt) + else + # allow other users to search on `activated` and `hired_since` only + %i(activated hired_since) + end + end +end + +Employee.ransack({ activated: true, hired_since: '2013-01-01' }) + +Employee.ransack({ salary_gt: 100_000 }, { auth_object: current_user }) +``` + +In Rails 3 and 4, if the `true` value is being passed via url params or some +other mechanism that will convert it to a string, the true value may not be +passed to the ransackable scope unless you wrap it in an array +(i.e. `activated: ['true']`). Ransack will take care of changing 'true' into a +boolean. This is currently resolved in Rails 5 :smiley: + +However, perhaps you have `user_id: [1]` and you do not want Ransack to convert +1 into a boolean. (Values sanitized to booleans can be found in the +[constants.rb](https://github.com/activerecord-hackery/ransack/blob/master/lib/ransack/constants.rb#L28)). +To turn this off globally, and handle type conversions yourself, set +`sanitize_custom_scope_booleans` to false in an initializer file like +config/initializers/ransack.rb: + +```ruby +Ransack.configure do |c| + c.sanitize_custom_scope_booleans = false +end +``` + +To turn this off on a per-scope basis Ransack adds the following method to +`ActiveRecord::Base` that you can redefine to selectively override sanitization: + +`ransackable_scopes_skip_sanitize_args` + +Add the scope you wish to bypass this behavior to ransackable_scopes_skip_sanitize_args: + +```ruby +def self.ransackable_scopes_skip_sanitize_args + [:scope_to_skip_sanitize_args] +end +``` + +Scopes are a recent addition to Ransack and currently have a few caveats: +First, a scope involving child associations needs to be defined in the parent +table model, not in the child model. Second, scopes with an array as an +argument are not easily usable yet, because the array currently needs to be +wrapped in an array to function (see +[this issue](https://github.com/activerecord-hackery/ransack/issues/404)), +which is not compatible with Ransack form helpers. For this use case, it may be +better for now to use [ransackers](https://activerecord-hackery.github.io/ransack/going-further/ransackers) instead, +where feasible. Pull requests with solutions and tests are welcome! + +### Grouping queries by OR instead of AND + +The default `AND` grouping can be changed to `OR` by adding `m: 'or'` to the +query hash. + +You can easily try it in your controller code by changing `params[:q]` in the +`index` action to `params[:q].try(:merge, m: 'or')` as follows: + +```ruby +def index + @q = Artist.ransack(params[:q].try(:merge, m: 'or')) + @artists = @q.result +end +``` +Normally, if you wanted users to be able to toggle between `AND` and `OR` +query grouping, you would probably set up your search form so that `m` was in +the URL params hash, but here we assigned `m` manually just to try it out +quickly. + +Alternatively, trying it in the Rails console: + +```ruby +artists = Artist.ransack(name_cont: 'foo', style_cont: 'bar', m: 'or') +=> Ransack::Search, + Condition + ], combinator: or>> + +artists.result.to_sql +=> "SELECT \"artists\".* FROM \"artists\" + WHERE ((\"artists\".\"name\" ILIKE '%foo%' + OR \"artists\".\"style\" ILIKE '%bar%'))" +``` + +The combinator becomes `or` instead of the default `and`, and the SQL query +becomes `WHERE...OR` instead of `WHERE...AND`. + +This works with associations as well. Imagine an Artist model that has many +Memberships, and many Musicians through Memberships: + +```ruby +artists = Artist.ransack(name_cont: 'foo', musicians_email_cont: 'bar', m: 'or') +=> Ransack::Search, + Condition + ], combinator: or>> + +artists.result.to_sql +=> "SELECT \"artists\".* FROM \"artists\" + LEFT OUTER JOIN \"memberships\" + ON \"memberships\".\"artist_id\" = \"artists\".\"id\" + LEFT OUTER JOIN \"musicians\" + ON \"musicians\".\"id\" = \"memberships\".\"musician_id\" + WHERE ((\"artists\".\"name\" ILIKE '%foo%' + OR \"musicians\".\"email\" ILIKE '%bar%'))" +``` + +### Using SimpleForm + +If you would like to combine the Ransack and SimpleForm form builders, set the +`RANSACK_FORM_BUILDER` environment variable before Rails boots up, e.g. in +`config/application.rb` before `require 'rails/all'` as shown below (and add +`gem 'simple_form'` in your Gemfile). + +```ruby +require File.expand_path('../boot', __FILE__) +ENV['RANSACK_FORM_BUILDER'] = '::SimpleForm::FormBuilder' +require 'rails/all' +``` diff --git a/docs/docs/going-further/polymorphic-search.md b/docs/docs/going-further/polymorphic-search.md new file mode 100644 index 000000000..724b1532a --- /dev/null +++ b/docs/docs/going-further/polymorphic-search.md @@ -0,0 +1,46 @@ +--- +title: Polymorphic Searches +sidebar_position: 14 +--- + +When making searches from polymorphic models it is necessary to specify the type of model you are searching. + +For example: + +Given two models + +```ruby +class House < ActiveRecord::Base + has_one :location, as: :locatable +end + +class Location < ActiveRecord::Base + belongs_to :locatable, polymorphic: true +end +``` + +Normally (without polymorphic relationship) you would be able to search as per below: + +```ruby +Location.ransack(locatable_number_eq: 100).result +``` + +However when this is searched you will get the following error + +```ruby +ActiveRecord::EagerLoadPolymorphicError: Can not eagerly load the polymorphic association :locatable +``` + +In order to search for locations by house number when the relationship is polymorphic you have to specify the type of records you will be searching and construct your search as below: + +```ruby +Location.ransack(locatable_of_House_type_number_eq: 100).result +``` + +note the `_of_House_type_` added to the search key. This allows Ransack to correctly specify the table names in SQL join queries. + +For namespaced models you should use a quoted string containing the standard Ruby module notation + +```ruby +Location.ransack('locatable_of_Residences::House_type_number_eq' => 100).result +``` diff --git a/docs/docs/going-further/ransackers.md b/docs/docs/going-further/ransackers.md new file mode 100644 index 000000000..e654aa2ea --- /dev/null +++ b/docs/docs/going-further/ransackers.md @@ -0,0 +1,331 @@ +--- +sidebar_position: 6 +title: Ransackers +--- + +## Add custom search functions + +The main premise behind Ransack is to provide access to **Arel predicate methods**. Ransack provides special methods, called _ransackers_, for creating additional search functions via Arel. + +A `ransacker` method can **return any Arel node that allows the usual predicate methods**. Custom `ransacker`s are an expert feature, and require a thorough understanding of Arel. + +## Arel + +Here are some resources for more information about Arel: + +* [Using Arel to Compose SQL Queries](https://robots.thoughtbot.com/using-arel-to-compose-sql-queries) +* [The definitive guide to Arel, the SQL manager for Ruby](http://jpospisil.com/2014/06/16/the-definitive-guide-to-arel-the-sql-manager-for-ruby.html) +* [Creating Advanced Active Record DB Queries with Arel](https://www.cloudbees.com/blog/creating-advanced-active-record-db-queries-arel) + +Ransacker methods enable search customization and are placed in the model. Arguments may be passed to a ransacker method via `ransacker_args` (see Example #6 below). + +Ransackers, like scopes, are not a cure-all. Many use cases can be better solved with a standard Ransack search on a dedicated database search field, which is faster, index-able, and scales better than converting/ransacking data on the fly. + +## Example Ransackers + +### Search on field + +_Search on the `name` field reversed:_ +```ruby +# in the model: +ransacker :reversed_name, formatter: proc { |v| v.reverse } do |parent| + parent.table[:name] +end +``` +### Search using Datetime + +_Convert a user `string` input and a database `datetime` field to the same `date` format to find all records with a `datetime` field (`created_at` in this example) equal to that date :_ + +```ruby +# in the model: +ransacker :created_at do + Arel.sql('date(created_at)') +end +``` +```erb +in the view: +<%= f.search_field( + :created_at_date_equals, placeholder: t(:date_format) + ) %> +... +<%= sort_link(@search, :created_at, default_order: :desc) %> +``` + +```ruby +# config/initializers/ransack.rb +Ransack.configure do |config| + config.add_predicate 'date_equals', + arel_predicate: 'eq', + formatter: proc { |v| v.to_date }, + validator: proc { |v| v.present? }, + type: :string +end +``` + +#### 2.1 +It seems to be enough to change the model only, but don't forget to define the type that will returned as well. + +```ruby +# in the model: +ransacker :created_at, type: :date do + Arel.sql('date(created_at)') +end +``` + +#### 2.2. Postgresql with time zones + +If you're using different time zones for Rails and Postgresql you should expect to have some problems using the above solution. +Example: +- Rails at GMT -03:00 +- Postgresql at GMT -00:00 (UTC) + +A timestamp like `2019-07-18 01:21:29.826484` will be truncated to `2019-07-18`. +But for your Rails application `2019-07-18 01:21:29.826484` is `2019-07-17 22:21:29.826484` at your time zone (GMT -03:00). So it should be truncated to `2019-07-17` instead. + + +So, you should convert the timestamp to your current Rails time zone before extracting the date. + +```ruby +# in the model: +ransacker :created_at, type: :date do + Arel.sql("date(created_at at time zone 'UTC' at time zone '#{Time.zone.name}')") +end +``` + +Note that `Time.zone.name` should return a time zone string suitable for Postgresql. + +### Postgres columns + +_Search on a fixed key in a jsonb / hstore column:_ + +In this example, we are searching a table with a column called `properties` for records containing a key called `link_type`. + +For anything up to and including Rails 4.1, add this to your model +```ruby +ransacker :link_type do |parent| + Arel::Nodes::InfixOperation.new('->>', parent.table[:properties], 'link_type') +end +``` +When using Rails 4.2+ (Arel 6.0+), wrap the value in a `build_quoted` call +```ruby +ransacker :link_type do |parent| + Arel::Nodes::InfixOperation.new('->>', parent.table[:properties], Arel::Nodes.build_quoted('link_type')) +end +``` +In the view, with a search on `link_type_eq` using a collection select (for example with options like 'twitter', 'facebook', etc.), if the user selects 'twitter', Ransack will run a query like: +``` +SELECT * FROM "foos" WHERE "foos"."properties" ->> 'link_type' = 'twitter'; +``` + +To use the JSONB contains operator @> see here: [[PostgreSQL JSONB searches]]. + +### Type conversions + +_Convert an `integer` database field to a `string` in order to be able to use a `cont` predicate (instead of the usual `eq` which works out of the box with integers) to find all records where an integer field (`id` in this example) **contains** an input string:_ + +Simple version, using PostgreSQL: +```ruby +# in the model: +ransacker :id do + Arel.sql("to_char(id, '9999999')") +end +``` +and the same, using MySQL: +```ruby +ransacker :id do + Arel.sql("CONVERT(#{table_name}.id, CHAR(8))") +end +``` +A more complete version (using PostgreSQL) that adds the table name to avoid ambiguity and strips spaces from the input: +```ruby +ransacker :id do + Arel.sql( + "regexp_replace( + to_char(\"#{table_name}\".\"id\", '9999999'), ' ', '', 'g')" + ) +end +``` +In the view, for all 3 versions: +```erb +<%= f.search_field :id_cont, placeholder: 'Id' %> +... +<%= sort_link(@search, :id) %> +``` + +### Concatenated fields + +_Search on a concatenated full name from `first_name` and `last_name` (several examples):_ +```ruby +# in the model: +ransacker :full_name do |parent| + Arel::Nodes::InfixOperation.new('||', + parent.table[:first_name], parent.table[:last_name]) +end + +# or, to insert a space between `first_name` and `last_name`: +ransacker :full_name do |parent| + Arel::Nodes::InfixOperation.new('||', + Arel::Nodes::InfixOperation.new('||', + parent.table[:first_name], ' ' + ), + parent.table[:last_name] + ) +end +# Caveat: with Arel >= 6 the separator ' ' string in the +# preceding example needs to be quoted as follows: +ransacker :full_name do |parent| + Arel::Nodes::InfixOperation.new('||', + Arel::Nodes::InfixOperation.new('||', + parent.table[:first_name], Arel::Nodes.build_quoted(' ') + ), + parent.table[:last_name] + ) +end + +# works also in mariadb +ransacker :full_name do |parent| + Arel::Nodes::NamedFunction.new('concat_ws', + [Arel::Nodes::SqlLiteral.new("' '"), parent.table[:first_name], parent.table[:last_name]]) +end + +# case insensitive lookup +ransacker :full_name, formatter: proc { |v| v.mb_chars.downcase.to_s } do |parent| + Arel::Nodes::NamedFunction.new('LOWER', + [Arel::Nodes::NamedFunction.new('concat_ws', + [Arel::Nodes::SqlLiteral.new("' '"), parent.table[:first_name], parent.table[:last_name]])]) +end +``` + +### Passing arguments + +_Passing arguments to a ransacker:_ +Arguments may be passed to a ransacker method via `ransacker_args`: +```ruby + +class Person + ransacker :author_max_title_of_article_where_body_length_between, + args: [:parent, :ransacker_args] do |parent, args| + min, max = args + query = <<-SQL + (SELECT MAX(articles.title) + FROM articles + WHERE articles.person_id = people.id + AND CHAR_LENGTH(articles.body) BETWEEN #{min.to_i} AND #{max.to_i} + GROUP BY articles.person_id + ) + SQL + Arel.sql(query) + end +end + +# Usage +Person.ransack( + conditions: [{ + attributes: { + '0' => { + name: 'author_max_title_of_article_where_body_length_between', + ransacker_args: [10, 100] + } + }, + predicate_name: 'cont', + values: ['Ransackers can take arguments'] + }] +) + +=> SELECT "people".* FROM "people" WHERE ( + (SELECT MAX(articles.title) + FROM articles + WHERE articles.person_id = people.id + AND CHAR_LENGTH(articles.body) BETWEEN 10 AND 100 + GROUP BY articles.person_id + ) + LIKE '%Ransackers can take arguments%') + ORDER BY "people"."id" DESC +``` + +### Dropdowns + +_Adding the attribute values associated with a column name to a searchable attribute in a dropdown options (instead of a traditional column name coming from a table). This is useful if using an associated table which is acting as a join table between a parent table and domain table. This will cache the data as the selections:_ + +```ruby +# in the model: +Model.pluck(:name).each do |ground| + ransacker ground.to_sym do |parent| + Arel::Nodes::InfixOperation.new('AND', + Arel::Nodes::InfixOperation.new('=', parent.table[:gor_name], ground), + parent.table[:status] + ) + end +end + +# This will not include the column names in the dropdown +def self.ransackable_attributes(auth_object = nil) + %w() + _ransackers.keys +end +``` + +### Testing for existence + +_Testing for the existence of a row in another table via a join:_ + +```ruby +# in the model: +ransacker :price_exists do |parent| + # SQL syntax for PostgreSQL -- others may differ + # This returns boolean true or false + Arel.sql("(select exists (select 1 from prices where prices.book_id = books.id))") +end +``` + +In the view +```haml + %td= f.select :price_exists_true, [["Any", 2], ["No", 0], ["Yes", 1]] +``` + +### Associations + +_Performing a query on an association with a differing class name:_ + +Say we have a model "SalesAccount", which represents a relationship between two users, +one being designated as a "sales_rep". We want to query SalesAccounts by +the name of the sales_rep: + +```ruby +# in the model: +class SalesAccount < ActiveRecord::Base + belongs_to :user + belongs_to :sales_rep, class_name: :User + +# in the controller: + # The line below would lead to errors thrown later if not for the + # "joins(:sales_reps)". + @q = SalesAccount.includes(:user).joins(:sales_rep).ransack(params[:q]) + @sales_accounts = @q.result(distinct: true) +``` + +In the view: +```erb +<%= f.search_field :sales_rep_name_start %> +``` + +### Search on translations + +_Search for a translated value in a jsonb column:_ + +_Note: There is also a gem, [Mobility Ransack](https://github.com/shioyama/mobility-ransack), which allows you to search on translated attributes independent of their storage backend._ + +This will work with any `jsonb` data type. In this case I have a column translated with [Mobility](https://github.com/shioyama/mobility) called `name` with the value `{'en': "Hello", 'es': "Hola"}`. + +```ruby +ransacker :name do |parent| + Arel::Nodes::InfixOperation.new('->>', parent.table[:name], Arel::Nodes.build_quoted(Mobility.locale)) +end +``` + +_If using Rails 4.1 or under, remove the `build_quoted` call._ + +You can then search for `name_eq` or `name_cont` and it will do the proper SQL. + +*** + +Please feel free to contribute further code examples! diff --git a/docs/docs/going-further/release_process.md b/docs/docs/going-further/release_process.md new file mode 100644 index 000000000..1e0d87868 --- /dev/null +++ b/docs/docs/going-further/release_process.md @@ -0,0 +1,48 @@ +--- +title: Versions and Releases +sidebar_position: 11 +--- + + +## Semantic Versioning + +Ransack attempts to follow semantic versioning in the format of `x.y.z`, where: + +`x` stands for a major version (new features that are not backward-compatible). + +`y` stands for a minor version (new features that are backward-compatible). + +`z` stands for a patch (bug fixes). + +In other words: `Major.Minor.Patch`. + + +## Release Process + +*For the maintainers of Ransack.* + +To release a new version of Ransack and publish it to RubyGems, take the following steps: + +### Manual Release Process + +Example for release 4.4.0 + +1. Update the [`version.rb`](https://github.com/activerecord-hackery/ransack/lib/ransack/version.rb) file to the `4.4.0`, commit and push to `main`. +3. Click the [Draft a new Release](https://github.com/activerecord-hackery/ransack/releases/new) button +4. Use these settings: +- Tag: v4.4.0 +- Release Title: 4.4.0 +- Check `Set as the Latest Release` +- Click `Generate release notes` +- Click `Publish Release` + +5. Release to RubyGems + +```bash +gem signin +rake build +rake release +``` + + + diff --git a/docs/docs/going-further/saving-queries.md b/docs/docs/going-further/saving-queries.md new file mode 100644 index 000000000..841ab6c09 --- /dev/null +++ b/docs/docs/going-further/saving-queries.md @@ -0,0 +1,82 @@ +--- +sidebar_position: 7 +title: Saving queries +--- + +## Ransack Memory Gem + +The [Ransack Memory](https://github.com/richardrails/ransack_memory) gem accomplishes this. + +## Custom solution + +If you want a custom solution, you can build it yourself. My ransack AJAX searching doesn’t save your search parameters across transactions. In this post I’ll show you how to easily add this capability in a generic way. + +In this example I added AJAX search ability to index pages. + +```ruby +def index + @search = ComponentDefinition.search(search_params) + # make name the default sort column + @search.sorts = 'name' if @search.sorts.empty? + @component_definitions = @search.result().page(params[:page]) +end +``` + +I added methods(search_params, clear_search_index) in the ApplicationController to add a level of abstraction from the search gem I was using. Turns out this made things super easy, especially considering I won’t have to update my code generation tools for index pages. + +```ruby +class ApplicationController < ActionController::Base + def search_params + params[:q] + end + def clear_search_index + if params[:search_cancel] + params.delete(:search_cancel) + if(!search_params.nil?) + search_params.each do |key, param| + search_params[key] = nil + end + end + end + end +end +``` + +I decided to store the ransack search parameters, params[:q], in the session. To make the session parameter unique I used a key creed from the controllers name and “_search”. + +```ruby +class ApplicationController < ActionController::Base + + # CHECK THE SESSION FOR SEARCH PARAMETERS IS THEY AREN'T IN THE REQUEST + def search_params + if params[:q] == nil + params[:q] = session[search_key] + end + if params[:q] + session[search_key] = params[:q] + end + params[:q] + end + # DELETE SEARCH PARAMETERS FROM THE SESSION + def clear_search_index + if params[:search_cancel] + params.delete(:search_cancel) + if(!search_params.nil?) + search_params.each do |key, param| + search_params[key] = nil + end + end + # REMOVE FROM SESSION + session.delete(search_key) + end + end + +protected + # GENERATE A GENERIC SESSION KEY BASED ON THE CONTROLLER NAME + def search_key + "#{controller_name}_search".to_sym + end +end +``` + +Based on [Saving queries](https://techbrownbags.wordpress.com/2015/02/18/rails-save-ransack-search-queries/) diff --git a/docs/docs/going-further/searching-postgres.md b/docs/docs/going-further/searching-postgres.md new file mode 100644 index 000000000..37db8e3e7 --- /dev/null +++ b/docs/docs/going-further/searching-postgres.md @@ -0,0 +1,57 @@ +--- +sidebar_position: 8 +title: Postgres searches +--- + +Searching on Postgres-specific column types. + +## Postgres Array searches + +See [this issue](https://github.com/activerecord-hackery/ransack/issues/321) for details. + +## PostgreSQL JSONB searches + +### Using a fixed key + +See here for searching on a fixed key in a JSONB column: https://activerecord-hackery.github.io/ransack/going-further/ransackers/#postgres-columns + +### Using the JSONB contains operator + +To fully use the power of the JSONB column you may want to filter on any key though: + +Install the [ActiveRecordExtended](https://github.com/GeorgeKaraszi/ActiveRecordExtended) gem to add the `contains` arel predicate to your project. It let's you use the [Postgres contains operator @>](https://www.postgresql.org/docs/12/functions-json.html#FUNCTIONS-JSONB-OP-TABLE). + +Add a custom predicate in the `config/initializers/ransack.rb` file: +```ruby +Ransack.configure do |config| + config.add_predicate 'jcont', arel_predicate: 'contains', formatter: proc { |v| JSON.parse(v) } +end +``` + +Now you can ransack the JSONB columns using the _jcont predicate. For example the Person model has a `data` JSONB column, find entries where the column contains the {"group": "experts"} key-value pair: + + Person.ransack(data_jcont: '{"group": "experts"}').result.to_sql + + SELECT "persons".* FROM "persons" WHERE "persons"."data" @> '"{\"group\": \"experts\"}"' + +If you have a GIN index on that column, the database will quickly be able to find that result. + +### Treating the column as a string + +Warning: This method converts the column to a string and matches the given string to the result. This will be slow on large data_sets and does not make good use of the JSONB capabilities of Postgres, such as indexes. + +```ruby +class Contact < ApplicationRecord + ransacker :within_json do |parent| + Arel.sql("table.jsonb_data::text") + end +end + +Contact.all.ransack("within_json_cont" => "my") +``` + +Will generate + +`SELECT "contacts".* FROM "contacts" WHERE contacts.json_data ILIKE '%my%'` + +Note that this search treats the entire JSON as string, including parens, etc. i.e. you can search for e.g.: `Contact.all.ransack("within_json_cont" => '{"key": "value"}')` diff --git a/docs/docs/going-further/wiki-contributors.md b/docs/docs/going-further/wiki-contributors.md new file mode 100644 index 000000000..e3b686e89 --- /dev/null +++ b/docs/docs/going-further/wiki-contributors.md @@ -0,0 +1,82 @@ +--- +title: Wiki Contributors +sidebar_position: 20 +--- + +Ransack previously had documentation contained in a GitHub Wiki, and this content has been merged into this documentation website. The following long list of _amazing_ people all made contributions to the Wiki: + +* Abinoam P. Marques Jr +* Alex Stophel +* Andrea Schiavini +* Andrew Vit +* Ben Koshy +* Brainkurv +* Brandan Lennox +* Brendon Muir +* Chris Salzberg +* Colleen McGuckin +* David Aldridge +* Davidson Mohanty +* Denis Tataurov +* Drew Moore +* Eike Send +* Feodor Cherashev +* Glauco Custódio +* Grey Baker +* Harold.Luo +* Herman Singh +* Ian Smith +* Jake Haber +* Jan Klimo +* Jared Beck +* Jon Atack +* Juanito Fatas +* JungaJk +* Leo Chen +* Leon Miller-Out +* Luca F +* Marc Poris +* Matt Oakley +* Michael Kopchick +* Nathan Colgate +* Nguyen Phi Viet(Sun*) +* Nguyễn Đức Long +* NielsKSchjoedt +* Patrick Copeland +* Pedro Chambino +* Rene Hopf +* Richa Arora +* Rob Jones +* Roman Sokhan +* Ryan Bates +* Ryan Bigg +* Sean +* Sean Linsley +* Sergey +* Sunny Ripert +* Tanbir Hasan +* ThuyNguyen97 +* Vanda +* Yana Agun Siswanto +* bonyiii +* charly +* chifung7 +* colorfulberry +* ddonahue99 +* ernie +* gaaady +* gingerlime +* grumpit +* itsalongstory +* jonatack +* kogre +* nguyentrungson97 +* nslocum +* omitter +* radar +* rilian +* terraplane +* tyronewilson +* vansy61 +* willnet +* wzcolon diff --git a/docs/docs/intro.md b/docs/docs/intro.md new file mode 100644 index 000000000..2a1e3fa94 --- /dev/null +++ b/docs/docs/intro.md @@ -0,0 +1,99 @@ +--- +sidebar_position: 1 +slug: '/' +--- + +# Introduction + +Ransack will help you easily add **searching to your Rails application**, without any additional dependencies. + +There are advanced searching solutions around, like ElasticSearch or Algolia. **Ransack** will do the job for many Rails websites, without the need to run additional infrastructure or work in a different language. With Ransack you do it all with standard Ruby and ERB. + +Ready to move beyond the basics? Use **advanced features** like i18n and extensive configuration options. + +Ransack is supported for Rails 8.0, 7.2 on Ruby 3.1 and later. + +## Installation + +To install `ransack` and add it to your Gemfile, run + +```ruby title='Gemfile' +gem 'ransack' +``` + +### Bleeding edge + +If you would like to use the latest updates not yet published to RubyGems, use the `main` branch: + +```ruby title='Gemfile' +gem 'ransack', :github => 'activerecord-hackery/ransack', :branch => 'main' +``` + +### Demo application + +The [Ransack Demo application](https://github.com/activerecord-hackery/ransack_demo) shows how to create [simple](http://ransack-demo.herokuapp.com) and +[advanced](http://ransack-demo.herokuapp.com/users/advanced_search) search forms for your Ruby on Rails application. + + +## Issues tracker + +* Before filing an issue, please read the [Contributing Guide](https://github.com/activerecord-hackery/ransack/CONTRIBUTING.md). +* File an issue if a bug is caused by Ransack, is new (has not already been reported), and _can be reproduced from the information you provide_. +* Please consider creating a pull request with a failing test that demonstrates the problem. +* Contributions are welcome. :smiley: +* Please do not use the issue tracker for personal support requests. Stack Overflow is a better place for that where a wider community can help you! + + +## Contributions + +To support the project: + +* Consider supporting us via [Open Collective](https://opencollective.com/ransack/backers/badge.svg) +* Use Ransack in your apps, and let us know if you encounter anything that's +broken or missing. A failing spec to demonstrate the issue is awesome. A pull +request with passing tests is even better! +* Before filing an issue or pull request, be sure to read and follow the +[Contributing Guide](https://github.com/activerecord-hackery/ransack/CONTRIBUTING.md). +* Please use Stack Overflow or other sites for questions or discussion not +directly related to bug reports, pull requests, or documentation improvements. +* Spread the word on Twitter, Facebook, and elsewhere if Ransack's been useful +to you. The more people who are using the project, the quicker we can find and +fix bugs! + +## Contributors + +Ransack was created by [Ernie Miller](http://twitter.com/erniemiller) and is developed and maintained by: +* [Sean Carroll](https://github.com/scarroll32) +* [Deivid Rodriguez](https://github.com/deivid-rodriguez) +* [Greg Molnar](https://github.com/gregmolnar) +* [A great group of contributors](https://github.com/activerecord-hackery/ransack/graphs/contributors). +- Ransack's logo is designed by [Anıl Kılıç](https://github.com/anilkilic). + +Alumni Maintainers +- [Jon Atack](http://twitter.com/jonatack) +- [Ryan Bigg](http://twitter.com/ryanbigg) + +This project exists thanks to all the people who contribute. + + +## Backers + +Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/ransack#backer)] + + + + +## Sponsors + +Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/ransack#sponsor)] + + + + + + + + + + + diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js new file mode 100644 index 000000000..ee0cb4229 --- /dev/null +++ b/docs/docusaurus.config.js @@ -0,0 +1,120 @@ +// @ts-check +// Note: type annotations allow type checking and IDEs autocompletion + +const lightCodeTheme = require('prism-react-renderer/themes/github'); +const darkCodeTheme = require('prism-react-renderer/themes/dracula'); + +/** @type {import('@docusaurus/types').Config} */ +const config = { + title: 'Ransack documentation', + tagline: 'Object-based searching', + url: 'https://activerecord-hackery.github.io/', + baseUrl: '/ransack/', + //onBrokenLinks: 'throw', + onBrokenMarkdownLinks: 'warn', + favicon: 'img/favicon.ico', + organizationName: 'activerecord-hackery', + projectName: 'ransack', + trailingSlash: true, + + presets: [ + [ + 'classic', + /** @type {import('@docusaurus/preset-classic').Options} */ + ({ + docs: { + routeBasePath: '/', + sidebarPath: require.resolve('./sidebars.js'), + editUrl: 'https://github.com/activerecord-hackery/ransack/edit/main/docs/', + }, + blog: { + showReadingTime: true, + editUrl: 'https://github.com/activerecord-hackery/ransack/edit/main/blog/', + }, + theme: { + customCss: require.resolve('./src/css/custom.css'), + }, + }), + ], + ], + + themeConfig: + /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ + ({ + navbar: { + logo: { + alt: 'Ransack Logo', + src: './logo/ransack-h.png', + }, + items: [ + { + type: 'doc', + docId: 'intro', + position: 'left', + label: 'Documentation', + }, + {to: '/blog', label: 'Blog', position: 'left'}, + { + href: 'https://github.com/activerecord-hackery/ransack', + label: 'GitHub', + position: 'right', + }, + ], + }, + footer: { + style: 'dark', + links: [ + { + title: 'Docs', + items: [ + { + label: 'Documentation', + to: '/', + }, + ], + }, + { + title: 'Community', + items: [ + { + label: 'Stack Overflow', + href: 'https://stackoverflow.com/questions/tagged/ransack', + }, + ], + }, + { + title: 'More', + items: [ + { + label: 'Blog', + to: '/blog', + }, + { + label: 'GitHub', + href: 'https://github.com/activerecord-hackery/ransack', + }, + ], + }, + ], + }, + prism: { + theme: lightCodeTheme, + darkTheme: darkCodeTheme, + additionalLanguages: ['ruby', 'erb'], + }, + }), + + themes: [ + [ + require.resolve("@easyops-cn/docusaurus-search-local"), + { + // `hashed` is recommended as long-term-cache of index file is possible. + hashed: true, + // needs to be the same as routeBasePath in @docusaurus/preset-classic config + docsRouteBasePath: '/' + }, + ] + ] +}; + +module.exports = config; diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 000000000..43cea0386 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,42 @@ +{ + "name": "docs-website", + "version": "0.0.0", + "private": true, + "scripts": { + "docusaurus": "docusaurus", + "start": "docusaurus start", + "build": "docusaurus build", + "swizzle": "docusaurus swizzle", + "deploy": "docusaurus deploy", + "clear": "docusaurus clear", + "serve": "docusaurus serve", + "write-translations": "docusaurus write-translations", + "write-heading-ids": "docusaurus write-heading-ids" + }, + "dependencies": { + "@docusaurus/core": "^2.2.0", + "@docusaurus/preset-classic": "^2.2.0", + "@easyops-cn/docusaurus-search-local": "^0.33.5", + "@mdx-js/react": "^1.6.22", + "clsx": "^1.1.1", + "prism-react-renderer": "^1.3.1", + "react": "^17.0.2", + "react-dom": "^17.0.2" + }, + "resolutions": { + "trim": "^0.0.3", + "got": "^11.8.5" + }, + "browserslist": { + "production": [ + ">0.5%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/docs/sidebars.js b/docs/sidebars.js new file mode 100644 index 000000000..fd342f2cd --- /dev/null +++ b/docs/sidebars.js @@ -0,0 +1,31 @@ +/** + * Creating a sidebar enables you to: + - create an ordered group of docs + - render a sidebar for each doc of that group + - provide next/previous navigation + + The sidebars can be generated from the filesystem, or explicitly defined here. + + Create as many sidebars as you want. + */ + +// @ts-check + +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const sidebars = { + // By default, Docusaurus generates a sidebar from the docs folder structure + tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], + + // But you can create a sidebar manually + /* + tutorialSidebar: [ + { + type: 'category', + label: 'Tutorial', + items: ['hello'], + }, + ], + */ +}; + +module.exports = sidebars; diff --git a/docs/src/components/HomepageFeatures/index.js b/docs/src/components/HomepageFeatures/index.js new file mode 100644 index 000000000..78f410ba6 --- /dev/null +++ b/docs/src/components/HomepageFeatures/index.js @@ -0,0 +1,64 @@ +import React from 'react'; +import clsx from 'clsx'; +import styles from './styles.module.css'; + +const FeatureList = [ + { + title: 'Easy to Use', + Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, + description: ( + <> + Docusaurus was designed from the ground up to be easily installed and + used to get your website up and running quickly. + + ), + }, + { + title: 'Focus on What Matters', + Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, + description: ( + <> + Docusaurus lets you focus on your docs, and we'll do the chores. Go + ahead and move your docs into the docs directory. + + ), + }, + { + title: 'Powered by React', + Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, + description: ( + <> + Extend or customize your website layout by reusing React. Docusaurus can + be extended while reusing the same header and footer. + + ), + }, +]; + +function Feature({Svg, title, description}) { + return ( +
+
+ +
+
+

{title}

+

{description}

+
+
+ ); +} + +export default function HomepageFeatures() { + return ( +
+
+
+ {FeatureList.map((props, idx) => ( + + ))} +
+
+
+ ); +} diff --git a/docs/src/components/HomepageFeatures/styles.module.css b/docs/src/components/HomepageFeatures/styles.module.css new file mode 100644 index 000000000..b248eb2e5 --- /dev/null +++ b/docs/src/components/HomepageFeatures/styles.module.css @@ -0,0 +1,11 @@ +.features { + display: flex; + align-items: center; + padding: 2rem 0; + width: 100%; +} + +.featureSvg { + height: 200px; + width: 200px; +} diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css new file mode 100644 index 000000000..311dc090d --- /dev/null +++ b/docs/src/css/custom.css @@ -0,0 +1,39 @@ +/** + * Any CSS included here will be global. The classic template + * bundles Infima by default. Infima is a CSS framework designed to + * work well for content-centric websites. + */ + +/* You can override the default Infima variables here. */ +:root { + --ifm-color-primary: #2e8555; + --ifm-color-primary-dark: #29784c; + --ifm-color-primary-darker: #277148; + --ifm-color-primary-darkest: #205d3b; + --ifm-color-primary-light: #33925d; + --ifm-color-primary-lighter: #359962; + --ifm-color-primary-lightest: #3cad6e; + --ifm-code-font-size: 95%; +} + +/* For readability concerns, you should choose a lighter palette in dark mode. */ +[data-theme='dark'] { + --ifm-color-primary: #25c2a0; + --ifm-color-primary-dark: #21af90; + --ifm-color-primary-darker: #1fa588; + --ifm-color-primary-darkest: #1a8870; + --ifm-color-primary-light: #29d5b0; + --ifm-color-primary-lighter: #32d8b4; + --ifm-color-primary-lightest: #4fddbf; +} + +.docusaurus-highlight-code-line { + background-color: rgba(0, 0, 0, 0.1); + display: block; + margin: 0 calc(-1 * var(--ifm-pre-padding)); + padding: 0 var(--ifm-pre-padding); +} + +[data-theme='dark'] .docusaurus-highlight-code-line { + background-color: rgba(0, 0, 0, 0.3); +} diff --git a/docs/src/pages/index.module.css b/docs/src/pages/index.module.css new file mode 100644 index 000000000..9f71a5da7 --- /dev/null +++ b/docs/src/pages/index.module.css @@ -0,0 +1,23 @@ +/** + * CSS files with the .module.css suffix will be treated as CSS modules + * and scoped locally. + */ + +.heroBanner { + padding: 4rem 0; + text-align: center; + position: relative; + overflow: hidden; +} + +@media screen and (max-width: 996px) { + .heroBanner { + padding: 2rem; + } +} + +.buttons { + display: flex; + align-items: center; + justify-content: center; +} diff --git a/docs/src/pages/markdown-page.md b/docs/src/pages/markdown-page.md new file mode 100644 index 000000000..9756c5b66 --- /dev/null +++ b/docs/src/pages/markdown-page.md @@ -0,0 +1,7 @@ +--- +title: Markdown page example +--- + +# Markdown page example + +You don't need React to write simple standalone pages. diff --git a/docs/static/.nojekyll b/docs/static/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/docs/static/img/docusaurus.png b/docs/static/img/docusaurus.png new file mode 100644 index 000000000..f458149e3 Binary files /dev/null and b/docs/static/img/docusaurus.png differ diff --git a/docs/static/img/favicon.ico b/docs/static/img/favicon.ico new file mode 100644 index 000000000..c01d54bcd Binary files /dev/null and b/docs/static/img/favicon.ico differ diff --git a/docs/static/img/logo.svg b/docs/static/img/logo.svg new file mode 100644 index 000000000..9db6d0d06 --- /dev/null +++ b/docs/static/img/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/static/img/tutorial/docsVersionDropdown.png b/docs/static/img/tutorial/docsVersionDropdown.png new file mode 100644 index 000000000..ff1cbe688 Binary files /dev/null and b/docs/static/img/tutorial/docsVersionDropdown.png differ diff --git a/docs/static/img/tutorial/localeDropdown.png b/docs/static/img/tutorial/localeDropdown.png new file mode 100644 index 000000000..d7163f967 Binary files /dev/null and b/docs/static/img/tutorial/localeDropdown.png differ diff --git a/docs/static/img/undraw_docusaurus_mountain.svg b/docs/static/img/undraw_docusaurus_mountain.svg new file mode 100644 index 000000000..af961c49a --- /dev/null +++ b/docs/static/img/undraw_docusaurus_mountain.svg @@ -0,0 +1,171 @@ + + Easy to Use + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/static/img/undraw_docusaurus_react.svg b/docs/static/img/undraw_docusaurus_react.svg new file mode 100644 index 000000000..94b5cf08f --- /dev/null +++ b/docs/static/img/undraw_docusaurus_react.svg @@ -0,0 +1,170 @@ + + Powered by React + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/static/img/undraw_docusaurus_tree.svg b/docs/static/img/undraw_docusaurus_tree.svg new file mode 100644 index 000000000..d9161d339 --- /dev/null +++ b/docs/static/img/undraw_docusaurus_tree.svg @@ -0,0 +1,40 @@ + + Focus on What Matters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/static/logo/ransack-h.png b/docs/static/logo/ransack-h.png new file mode 100644 index 000000000..d0866d8cc Binary files /dev/null and b/docs/static/logo/ransack-h.png differ diff --git a/docs/static/logo/ransack-h.svg b/docs/static/logo/ransack-h.svg new file mode 100644 index 000000000..4c49aef2b --- /dev/null +++ b/docs/static/logo/ransack-h.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/static/logo/ransack-v.png b/docs/static/logo/ransack-v.png new file mode 100644 index 000000000..a5b2026e2 Binary files /dev/null and b/docs/static/logo/ransack-v.png differ diff --git a/docs/static/logo/ransack-v.svg b/docs/static/logo/ransack-v.svg new file mode 100644 index 000000000..9a64c43c6 --- /dev/null +++ b/docs/static/logo/ransack-v.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/static/logo/ransack.png b/docs/static/logo/ransack.png new file mode 100644 index 000000000..8318fa4c4 Binary files /dev/null and b/docs/static/logo/ransack.png differ diff --git a/docs/static/logo/ransack.svg b/docs/static/logo/ransack.svg new file mode 100644 index 000000000..0d7393e2c --- /dev/null +++ b/docs/static/logo/ransack.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/yarn.lock b/docs/yarn.lock new file mode 100644 index 000000000..3689ed5e1 --- /dev/null +++ b/docs/yarn.lock @@ -0,0 +1,8906 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@algolia/autocomplete-core@1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.7.2.tgz#8abbed88082f611997538760dffcb43b33b1fd1d" + integrity sha512-eclwUDC6qfApNnEfu1uWcL/rudQsn59tjEoUYZYE2JSXZrHLRjBUGMxiCoknobU2Pva8ejb0eRxpIYDtVVqdsw== + dependencies: + "@algolia/autocomplete-shared" "1.7.2" + +"@algolia/autocomplete-preset-algolia@1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.2.tgz#9cd4f64b3d64399657ee2dc2b7e0a939e0713a26" + integrity sha512-+RYEG6B0QiGGfRb2G3MtPfyrl0dALF3cQNTWBzBX6p5o01vCCGTTinAm2UKG3tfc2CnOMAtnPLkzNZyJUpnVJw== + dependencies: + "@algolia/autocomplete-shared" "1.7.2" + +"@algolia/autocomplete-shared@1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.2.tgz#daa23280e78d3b42ae9564d12470ae034db51a89" + integrity sha512-QCckjiC7xXHIUaIL3ektBtjJ0w7tTA3iqKcAE/Hjn1lZ5omp7i3Y4e09rAr9ZybqirL7AbxCLLq0Ra5DDPKeug== + +"@algolia/cache-browser-local-storage@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.13.1.tgz#ffacb9230119f77de1a6f163b83680be999110e4" + integrity sha512-UAUVG2PEfwd/FfudsZtYnidJ9eSCpS+LW9cQiesePQLz41NAcddKxBak6eP2GErqyFagSlnVXe/w2E9h2m2ttg== + dependencies: + "@algolia/cache-common" "4.13.1" + +"@algolia/cache-common@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.13.1.tgz#c933fdec9f73b4f7c69d5751edc92eee4a63d76b" + integrity sha512-7Vaf6IM4L0Jkl3sYXbwK+2beQOgVJ0mKFbz/4qSxKd1iy2Sp77uTAazcX+Dlexekg1fqGUOSO7HS4Sx47ZJmjA== + +"@algolia/cache-in-memory@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.13.1.tgz#c19baa67b4597e1a93e987350613ab3b88768832" + integrity sha512-pZzybCDGApfA/nutsFK1P0Sbsq6fYJU3DwIvyKg4pURerlJM4qZbB9bfLRef0FkzfQu7W11E4cVLCIOWmyZeuQ== + dependencies: + "@algolia/cache-common" "4.13.1" + +"@algolia/client-account@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.13.1.tgz#fea591943665477a23922ab31863ad0732e26c66" + integrity sha512-TFLiZ1KqMiir3FNHU+h3b0MArmyaHG+eT8Iojio6TdpeFcAQ1Aiy+2gb3SZk3+pgRJa/BxGmDkRUwE5E/lv3QQ== + dependencies: + "@algolia/client-common" "4.13.1" + "@algolia/client-search" "4.13.1" + "@algolia/transporter" "4.13.1" + +"@algolia/client-analytics@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.13.1.tgz#5275956b2d0d16997148f2085f1701b6c39ecc32" + integrity sha512-iOS1JBqh7xaL5x00M5zyluZ9+9Uy9GqtYHv/2SMuzNW1qP7/0doz1lbcsP3S7KBbZANJTFHUOfuqyRLPk91iFA== + dependencies: + "@algolia/client-common" "4.13.1" + "@algolia/client-search" "4.13.1" + "@algolia/requester-common" "4.13.1" + "@algolia/transporter" "4.13.1" + +"@algolia/client-common@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.13.1.tgz#3bf9e3586f20ef85bbb56ccca390f7dbe57c8f4f" + integrity sha512-LcDoUE0Zz3YwfXJL6lJ2OMY2soClbjrrAKB6auYVMNJcoKZZ2cbhQoFR24AYoxnGUYBER/8B+9sTBj5bj/Gqbg== + dependencies: + "@algolia/requester-common" "4.13.1" + "@algolia/transporter" "4.13.1" + +"@algolia/client-personalization@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.13.1.tgz#438a1f58576ef19c4ad4addb8417bdacfe2fce2e" + integrity sha512-1CqrOW1ypVrB4Lssh02hP//YxluoIYXAQCpg03L+/RiXJlCs+uIqlzC0ctpQPmxSlTK6h07kr50JQoYH/TIM9w== + dependencies: + "@algolia/client-common" "4.13.1" + "@algolia/requester-common" "4.13.1" + "@algolia/transporter" "4.13.1" + +"@algolia/client-search@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.13.1.tgz#5501deed01e23c33d4aaa9f9eb96a849f0fce313" + integrity sha512-YQKYA83MNRz3FgTNM+4eRYbSmHi0WWpo019s5SeYcL3HUan/i5R09VO9dk3evELDFJYciiydSjbsmhBzbpPP2A== + dependencies: + "@algolia/client-common" "4.13.1" + "@algolia/requester-common" "4.13.1" + "@algolia/transporter" "4.13.1" + +"@algolia/events@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@algolia/events/-/events-4.0.1.tgz#fd39e7477e7bc703d7f893b556f676c032af3950" + integrity sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ== + +"@algolia/logger-common@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.13.1.tgz#4221378e701e3f1eacaa051bcd4ba1f25ddfaf4d" + integrity sha512-L6slbL/OyZaAXNtS/1A8SAbOJeEXD5JcZeDCPYDqSTYScfHu+2ePRTDMgUTY4gQ7HsYZ39N1LujOd8WBTmM2Aw== + +"@algolia/logger-console@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.13.1.tgz#423d358e4992dd4bceab0d9a4e99d1fd68107043" + integrity sha512-7jQOTftfeeLlnb3YqF8bNgA2GZht7rdKkJ31OCeSH2/61haO0tWPoNRjZq9XLlgMQZH276pPo0NdiArcYPHjCA== + dependencies: + "@algolia/logger-common" "4.13.1" + +"@algolia/requester-browser-xhr@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.13.1.tgz#f8ea79233cf6f0392feaf31e35a6b40d68c5bc9e" + integrity sha512-oa0CKr1iH6Nc7CmU6RE7TnXMjHnlyp7S80pP/LvZVABeJHX3p/BcSCKovNYWWltgTxUg0U1o+2uuy8BpMKljwA== + dependencies: + "@algolia/requester-common" "4.13.1" + +"@algolia/requester-common@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.13.1.tgz#daea143d15ab6ed3909c4c45877f1b6c36a16179" + integrity sha512-eGVf0ID84apfFEuXsaoSgIxbU3oFsIbz4XiotU3VS8qGCJAaLVUC5BUJEkiFENZIhon7hIB4d0RI13HY4RSA+w== + +"@algolia/requester-node-http@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.13.1.tgz#32c63d4c009f22d97e396406de7af9b66fb8e89d" + integrity sha512-7C0skwtLdCz5heKTVe/vjvrqgL/eJxmiEjHqXdtypcE5GCQCYI15cb+wC4ytYioZDMiuDGeVYmCYImPoEgUGPw== + dependencies: + "@algolia/requester-common" "4.13.1" + +"@algolia/transporter@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.13.1.tgz#509e03e9145102843d5be4a031c521f692d4e8d6" + integrity sha512-pICnNQN7TtrcYJqqPEXByV8rJ8ZRU2hCiIKLTLRyNpghtQG3VAFk6fVtdzlNfdUGZcehSKGarPIZEHlQXnKjgw== + dependencies: + "@algolia/cache-common" "4.13.1" + "@algolia/logger-common" "4.13.1" + "@algolia/requester-common" "4.13.1" + +"@ampproject/remapping@^2.1.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== + dependencies: + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.8.3": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + dependencies: + "@babel/highlight" "^7.16.7" + +"@babel/code-frame@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.10": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.10.tgz#711dc726a492dfc8be8220028b1b92482362baab" + integrity sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw== + +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.0", "@babel/compat-data@^7.20.1": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.1.tgz#f2e6ef7790d8c8dbf03d379502dcc246dcce0b30" + integrity sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ== + +"@babel/core@7.12.9": + version "7.12.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" + integrity sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.5" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.5" + "@babel/parser" "^7.12.7" + "@babel/template" "^7.12.7" + "@babel/traverse" "^7.12.9" + "@babel/types" "^7.12.7" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/core@^7.15.5": + version "7.18.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.2.tgz#87b2fcd7cce9becaa7f5acebdc4f09f3dd19d876" + integrity sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.18.2" + "@babel/helper-compilation-targets" "^7.18.2" + "@babel/helper-module-transforms" "^7.18.0" + "@babel/helpers" "^7.18.2" + "@babel/parser" "^7.18.0" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.18.2" + "@babel/types" "^7.18.2" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + +"@babel/core@^7.18.6": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.2.tgz#8dc9b1620a673f92d3624bd926dc49a52cf25b92" + integrity sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.2" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-module-transforms" "^7.20.2" + "@babel/helpers" "^7.20.1" + "@babel/parser" "^7.20.2" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.2" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + +"@babel/generator@^7.12.5", "@babel/generator@^7.18.2": + version "7.18.2" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d" + integrity sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw== + dependencies: + "@babel/types" "^7.18.2" + "@jridgewell/gen-mapping" "^0.3.0" + jsesc "^2.5.1" + +"@babel/generator@^7.18.7", "@babel/generator@^7.20.2": + version "7.20.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.4.tgz#4d9f8f0c30be75fd90a0562099a26e5839602ab8" + integrity sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA== + dependencies: + "@babel/types" "^7.20.2" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + +"@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== + dependencies: + "@babel/types" "^7.23.0" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" + integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" + integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" + integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.18.6" + "@babel/types" "^7.18.9" + +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10", "@babel/helper-compilation-targets@^7.18.2": + version "7.18.2" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz#67a85a10cbd5fc7f1457fec2e7f45441dc6c754b" + integrity sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ== + dependencies: + "@babel/compat-data" "^7.17.10" + "@babel/helper-validator-option" "^7.16.7" + browserslist "^4.20.2" + semver "^6.3.0" + +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" + integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== + dependencies: + "@babel/compat-data" "^7.20.0" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.21.3" + semver "^6.3.0" + +"@babel/helper-create-class-features-plugin@^7.17.12", "@babel/helper-create-class-features-plugin@^7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.0.tgz#fac430912606331cb075ea8d82f9a4c145a4da19" + integrity sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.17.9" + "@babel/helper-member-expression-to-functions" "^7.17.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.2.tgz#3c08a5b5417c7f07b5cf3dfb6dc79cbec682e8c2" + integrity sha512-k22GoYRAHPYr9I+Gvy2ZQlAe5mGy8BqWst2wRt8cwIufWTxrsVshhIBvYNqC80N0GSFWTsqRVexOtfzlgOEDvA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.19.1" + "@babel/helper-split-export-declaration" "^7.18.6" + +"@babel/helper-create-regexp-features-plugin@^7.16.7", "@babel/helper-create-regexp-features-plugin@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.12.tgz#bb37ca467f9694bbe55b884ae7a5cc1e0084e4fd" + integrity sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + regexpu-core "^5.0.1" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" + integrity sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.1.0" + +"@babel/helper-define-polyfill-provider@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" + integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-define-polyfill-provider@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" + integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== + dependencies: + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.2": + version "7.18.2" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz#8a6d2dedb53f6bf248e31b4baf38739ee4a637bd" + integrity sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ== + +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-explode-assignable-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" + integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-explode-assignable-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" + integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" + integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== + dependencies: + "@babel/template" "^7.16.7" + "@babel/types" "^7.17.0" + +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== + dependencies: + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" + +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-member-expression-to-functions@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4" + integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw== + dependencies: + "@babel/types" "^7.17.0" + +"@babel/helper-member-expression-to-functions@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" + integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== + dependencies: + "@babel/types" "^7.18.9" + +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz#baf05dec7a5875fb9235bd34ca18bad4e21221cd" + integrity sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA== + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-simple-access" "^7.17.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.18.0" + "@babel/types" "^7.18.0" + +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.6", "@babel/helper-module-transforms@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" + integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.2" + +"@babel/helper-optimise-call-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" + integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-plugin-utils@7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.17.12", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96" + integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA== + +"@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== + +"@babel/helper-remap-async-to-generator@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" + integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-wrap-function" "^7.16.8" + "@babel/types" "^7.16.8" + +"@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" + integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helper-replace-supers@^7.16.7", "@babel/helper-replace-supers@^7.18.2": + version "7.18.2" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.2.tgz#41fdfcc9abaf900e18ba6e5931816d9062a7b2e0" + integrity sha512-XzAIyxx+vFnrOxiQrToSUOzUOn0e1J2Li40ntddek1Y69AXUTXoDJ40/D5RdjFu7s7qHiaeoTiempZcbuVXh2Q== + dependencies: + "@babel/helper-environment-visitor" "^7.18.2" + "@babel/helper-member-expression-to-functions" "^7.17.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/traverse" "^7.18.2" + "@babel/types" "^7.18.2" + +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz#e1592a9b4b368aa6bdb8784a711e0bcbf0612b78" + integrity sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/traverse" "^7.19.1" + "@babel/types" "^7.19.0" + +"@babel/helper-simple-access@^7.17.7", "@babel/helper-simple-access@^7.18.2": + version "7.18.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz#4dc473c2169ac3a1c9f4a51cfcd091d1c36fcff9" + integrity sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ== + dependencies: + "@babel/types" "^7.18.2" + +"@babel/helper-simple-access@^7.19.4", "@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + +"@babel/helper-skip-transparent-expression-wrappers@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" + integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== + dependencies: + "@babel/types" "^7.16.0" + +"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" + integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== + dependencies: + "@babel/types" "^7.20.0" + +"@babel/helper-split-export-declaration@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/helper-validator-option@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" + integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== + +"@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + +"@babel/helper-wrap-function@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" + integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw== + dependencies: + "@babel/helper-function-name" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.8" + "@babel/types" "^7.16.8" + +"@babel/helper-wrap-function@^7.18.9": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz#89f18335cff1152373222f76a4b37799636ae8b1" + integrity sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg== + dependencies: + "@babel/helper-function-name" "^7.19.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" + +"@babel/helpers@^7.12.5", "@babel/helpers@^7.18.2": + version "7.18.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.2.tgz#970d74f0deadc3f5a938bfa250738eb4ac889384" + integrity sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg== + dependencies: + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.18.2" + "@babel/types" "^7.18.2" + +"@babel/helpers@^7.20.1": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.1.tgz#2ab7a0fcb0a03b5bf76629196ed63c2d7311f4c9" + integrity sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg== + dependencies: + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.0" + +"@babel/highlight@^7.16.7": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.12.tgz#257de56ee5afbd20451ac0a75686b6b404257351" + integrity sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.12.7", "@babel/parser@^7.16.7", "@babel/parser@^7.18.0": + version "7.18.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.4.tgz#6774231779dd700e0af29f6ad8d479582d7ce5ef" + integrity sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow== + +"@babel/parser@^7.18.10", "@babel/parser@^7.18.8", "@babel/parser@^7.20.2": + version "7.20.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2" + integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg== + +"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.17.12.tgz#1dca338caaefca368639c9ffb095afbd4d420b1e" + integrity sha512-xCJQXl4EeQ3J9C4yOmpTrtVGmzpm2iSzyxbkZHw7UCnZBftHpF/hpII80uWVyVrc40ytIClHjgWGTG1g/yB+aw== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" + integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.17.12.tgz#0d498ec8f0374b1e2eb54b9cb2c4c78714c77753" + integrity sha512-/vt0hpIw0x4b6BLKUkwlvEoiGZYYLNZ96CzyHYPbtG2jZGz6LBe7/V+drYrc/d+ovrF9NBi0pmtvmNb/FsWtRQ== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/plugin-proposal-optional-chaining" "^7.17.12" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz#a11af19aa373d68d561f08e0a57242350ed0ec50" + integrity sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" + +"@babel/plugin-proposal-async-generator-functions@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.17.12.tgz#094a417e31ce7e692d84bab06c8e2a607cbeef03" + integrity sha512-RWVvqD1ooLKP6IqWTA5GyFVX2isGEgC5iFxKzfYOIy/QEFdxYyCybBDtIGjipHpb9bDWHzcqGqFakf+mVmBTdQ== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-remap-async-to-generator" "^7.16.8" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-proposal-async-generator-functions@^7.20.1": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.1.tgz#352f02baa5d69f4e7529bdac39aaa02d41146af9" + integrity sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-proposal-class-properties@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz#84f65c0cc247d46f40a6da99aadd6438315d80a4" + integrity sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.17.12" + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-proposal-class-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-class-static-block@^7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.0.tgz#7d02253156e3c3793bdb9f2faac3a1c05f0ba710" + integrity sha512-t+8LsRMMDE74c6sV7KShIw13sqbqd58tlqNrsWoWBTIMw7SVQ0cZ905wLNS/FBCy/3PyooRHLFFlfrUNyyz5lA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.0" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-proposal-class-static-block@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz#8aa81d403ab72d3962fc06c26e222dacfc9b9020" + integrity sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-proposal-dynamic-import@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" + integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-proposal-dynamic-import@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" + integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-proposal-export-namespace-from@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.17.12.tgz#b22864ccd662db9606edb2287ea5fd1709f05378" + integrity sha512-j7Ye5EWdwoXOpRmo5QmRyHPsDIe6+u70ZYZrd7uz+ebPYFKfRcLcNu3Ro0vOlJ5zuv8rU7xa+GttNiRzX56snQ== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-export-namespace-from@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" + integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.17.12.tgz#f4642951792437233216d8c1af370bb0fbff4664" + integrity sha512-rKJ+rKBoXwLnIn7n6o6fulViHMrOThz99ybH+hKHcOZbnN14VuMnH9fo2eHE69C8pO4uX1Q7t2HYYIDmv8VYkg== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" + integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-proposal-logical-assignment-operators@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.17.12.tgz#c64a1bcb2b0a6d0ed2ff674fd120f90ee4b88a23" + integrity sha512-EqFo2s1Z5yy+JeJu7SFfbIUtToJTVlC61/C7WLKDntSw4Sz6JNAIfL7zQ74VvirxpjB5kz/kIx0gCcb+5OEo2Q== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz#8148cbb350483bf6220af06fa6db3690e14b2e23" + integrity sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz#1e93079bbc2cbc756f6db6a1925157c4a92b94be" + integrity sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9" + integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-numeric-separator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" + integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" + +"@babel/plugin-proposal-object-rest-spread@^7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.0.tgz#79f2390c892ba2a68ec112eb0d895cfbd11155e8" + integrity sha512-nbTv371eTrFabDfHLElkn9oyf9VG+VKK6WMzhY2o4eHKaG19BToD9947zzGMO6I/Irstx9d8CwX6njPNIAR/yw== + dependencies: + "@babel/compat-data" "^7.17.10" + "@babel/helper-compilation-targets" "^7.17.10" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.17.12" + +"@babel/plugin-proposal-object-rest-spread@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz#a556f59d555f06961df1e572bb5eca864c84022d" + integrity sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ== + dependencies: + "@babel/compat-data" "^7.20.1" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.20.1" + +"@babel/plugin-proposal-optional-catch-binding@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" + integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-proposal-optional-catch-binding@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-proposal-optional-chaining@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz#f96949e9bacace3a9066323a5cf90cfb9de67174" + integrity sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-optional-chaining@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" + integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-methods@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.17.12.tgz#c2ca3a80beb7539289938da005ad525a038a819c" + integrity sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.17.12" + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-proposal-private-methods@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-private-property-in-object@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.17.12.tgz#b02efb7f106d544667d91ae97405a9fd8c93952d" + integrity sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.17.12" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-proposal-private-property-in-object@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz#a64137b232f0aca3733a67eb1a144c192389c503" + integrity sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-proposal-unicode-property-regex@^7.17.12", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.17.12.tgz#3dbd7a67bd7f94c8238b394da112d86aaf32ad4d" + integrity sha512-Wb9qLjXf3ZazqXA7IvI7ozqRIXIGPtSo+L5coFmEkhTQK18ao4UDDD0zdTGAarmbLj2urpRwrc6893cu5Bfh0A== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.17.12" + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-proposal-unicode-property-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" + integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-import-assertions@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.17.12.tgz#58096a92b11b2e4e54b24c6a0cc0e5e607abcedd" + integrity sha512-n/loy2zkq9ZEM8tEOwON9wTQSTNDTDEz6NujPtJGLU7qObzT1N4c4YZZf8E6ATB2AjNQg/Ib2AIpO03EZaCehw== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-syntax-import-assertions@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" + integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926" + integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-jsx@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.17.12.tgz#834035b45061983a491f60096f61a2e7c5674a47" + integrity sha512-spyY3E3AURfxh/RHtjx5j6hs8am5NbUBGfcZ2vB3uShSpZdQyXSf5rR5Mk76vbtlAZOelyVQ71Fg0x9SG4fsog== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-syntax-jsx@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" + integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@7.8.3", "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.12.tgz#b54fc3be6de734a56b87508f99d6428b5b605a7b" + integrity sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-syntax-typescript@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz#4e9a0cfc769c85689b77a2e642d24e9f697fc8c7" + integrity sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + +"@babel/plugin-transform-arrow-functions@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz#dddd783b473b1b1537ef46423e3944ff24898c45" + integrity sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-arrow-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" + integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-async-to-generator@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.17.12.tgz#dbe5511e6b01eee1496c944e35cdfe3f58050832" + integrity sha512-J8dbrWIOO3orDzir57NRsjg4uxucvhby0L/KZuGsWDj0g7twWK3g7JhJhOrXtuXiw8MeiSdJ3E0OW9H8LYEzLQ== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-remap-async-to-generator" "^7.16.8" + +"@babel/plugin-transform-async-to-generator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz#ccda3d1ab9d5ced5265fdb13f1882d5476c71615" + integrity sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-remap-async-to-generator" "^7.18.6" + +"@babel/plugin-transform-block-scoped-functions@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" + integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-block-scoped-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" + integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-block-scoping@^7.17.12": + version "7.18.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.4.tgz#7988627b3e9186a13e4d7735dc9c34a056613fb9" + integrity sha512-+Hq10ye+jlvLEogSOtq4mKvtk7qwcUQ1f0Mrueai866C82f844Yom2cttfJdMdqRLTxWpsbfbkIkOIfovyUQXw== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-block-scoping@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.2.tgz#f59b1767e6385c663fd0bce655db6ca9c8b236ed" + integrity sha512-y5V15+04ry69OV2wULmwhEA6jwSWXO1TwAtIwiPXcvHcoOQUqpyMVd2bDsQJMW8AurjulIyUV8kDqtjSwHy1uQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-classes@^7.17.12": + version "7.18.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.4.tgz#51310b812a090b846c784e47087fa6457baef814" + integrity sha512-e42NSG2mlKWgxKUAD9EJJSkZxR67+wZqzNxLSpc51T8tRU5SLFHsPmgYR5yr7sdgX4u+iHA1C5VafJ6AyImV3A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.18.2" + "@babel/helper-function-name" "^7.17.9" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-replace-supers" "^7.18.2" + "@babel/helper-split-export-declaration" "^7.16.7" + globals "^11.1.0" + +"@babel/plugin-transform-classes@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz#c0033cf1916ccf78202d04be4281d161f6709bb2" + integrity sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.19.1" + "@babel/helper-split-export-declaration" "^7.18.6" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.17.12.tgz#bca616a83679698f3258e892ed422546e531387f" + integrity sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-computed-properties@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" + integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-destructuring@^7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.0.tgz#dc4f92587e291b4daa78aa20cc2d7a63aa11e858" + integrity sha512-Mo69klS79z6KEfrLg/1WkmVnB8javh75HX4pi2btjvlIoasuxilEyjtsQW6XPrubNd7AQy0MMaNIaQE4e7+PQw== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-destructuring@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz#c23741cfa44ddd35f5e53896e88c75331b8b2792" + integrity sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" + integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-dotall-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" + integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-duplicate-keys@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.17.12.tgz#a09aa709a3310013f8e48e0e23bc7ace0f21477c" + integrity sha512-EA5eYFUG6xeerdabina/xIoB95jJ17mAkR8ivx6ZSu9frKShBjpOGZPn511MTDTkiCO+zXnzNczvUM69YSf3Zw== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-duplicate-keys@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" + integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-exponentiation-operator@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" + integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-exponentiation-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" + integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-for-of@^7.18.1": + version "7.18.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.1.tgz#ed14b657e162b72afbbb2b4cdad277bf2bb32036" + integrity sha512-+TTB5XwvJ5hZbO8xvl2H4XaMDOAK57zF4miuC9qQJgysPNEAZZ9Z69rdF5LJkozGdZrjBIUAIyKUWRMmebI7vg== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-for-of@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" + integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-function-name@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" + integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== + dependencies: + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" + integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== + dependencies: + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-literals@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.17.12.tgz#97131fbc6bbb261487105b4b3edbf9ebf9c830ae" + integrity sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" + integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-member-expression-literals@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" + integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-member-expression-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" + integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-modules-amd@^7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.0.tgz#7ef1002e67e36da3155edc8bf1ac9398064c02ed" + integrity sha512-h8FjOlYmdZwl7Xm2Ug4iX2j7Qy63NANI+NQVWQzv6r25fqgg7k2dZl03p95kvqNclglHs4FZ+isv4p1uXMA+QA== + dependencies: + "@babel/helper-module-transforms" "^7.18.0" + "@babel/helper-plugin-utils" "^7.17.12" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-amd@^7.19.6": + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz#aca391801ae55d19c4d8d2ebfeaa33df5f2a2cbd" + integrity sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg== + dependencies: + "@babel/helper-module-transforms" "^7.19.6" + "@babel/helper-plugin-utils" "^7.19.0" + +"@babel/plugin-transform-modules-commonjs@^7.18.2": + version "7.18.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.2.tgz#1aa8efa2e2a6e818b6a7f2235fceaf09bdb31e9e" + integrity sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ== + dependencies: + "@babel/helper-module-transforms" "^7.18.0" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-simple-access" "^7.18.2" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-commonjs@^7.19.6": + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz#25b32feef24df8038fc1ec56038917eacb0b730c" + integrity sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ== + dependencies: + "@babel/helper-module-transforms" "^7.19.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-simple-access" "^7.19.4" + +"@babel/plugin-transform-modules-systemjs@^7.18.0": + version "7.18.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.4.tgz#3d6fd9868c735cce8f38d6ae3a407fb7e61e6d46" + integrity sha512-lH2UaQaHVOAeYrUUuZ8i38o76J/FnO8vu21OE+tD1MyP9lxdZoSfz+pDbWkq46GogUrdrMz3tiz/FYGB+bVThg== + dependencies: + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-module-transforms" "^7.18.0" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-validator-identifier" "^7.16.7" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-systemjs@^7.19.6": + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz#59e2a84064b5736a4471b1aa7b13d4431d327e0d" + integrity sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ== + dependencies: + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-module-transforms" "^7.19.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-validator-identifier" "^7.19.1" + +"@babel/plugin-transform-modules-umd@^7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.0.tgz#56aac64a2c2a1922341129a4597d1fd5c3ff020f" + integrity sha512-d/zZ8I3BWli1tmROLxXLc9A6YXvGK8egMxHp+E/rRwMh1Kip0AP77VwZae3snEJ33iiWwvNv2+UIIhfalqhzZA== + dependencies: + "@babel/helper-module-transforms" "^7.18.0" + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-modules-umd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" + integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== + dependencies: + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.17.12.tgz#9c4a5a5966e0434d515f2675c227fd8cc8606931" + integrity sha512-vWoWFM5CKaTeHrdUJ/3SIOTRV+MBVGybOC9mhJkaprGNt5demMymDW24yC74avb915/mIRe3TgNb/d8idvnCRA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.17.12" + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz#ec7455bab6cd8fb05c525a94876f435a48128888" + integrity sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.19.0" + "@babel/helper-plugin-utils" "^7.19.0" + +"@babel/plugin-transform-new-target@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.17.12.tgz#10842cd605a620944e81ea6060e9e65c265742e3" + integrity sha512-CaOtzk2fDYisbjAD4Sd1MTKGVIpRtx9bWLyj24Y/k6p4s4gQ3CqDGJauFJxt8M/LEx003d0i3klVqnN73qvK3w== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-new-target@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" + integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-object-super@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" + integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + +"@babel/plugin-transform-object-super@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" + integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" + +"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.17.12.tgz#eb467cd9586ff5ff115a9880d6fdbd4a846b7766" + integrity sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-parameters@^7.20.1": + version "7.20.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.3.tgz#7b3468d70c3c5b62e46be0a47b6045d8590fb748" + integrity sha512-oZg/Fpx0YDrj13KsLyO8I/CX3Zdw7z0O9qOd95SqcoIzuqy/WTGWvePeHAnZCN54SfdyjHcb1S30gc8zlzlHcA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-property-literals@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" + integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-property-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" + integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-constant-elements@^7.14.5": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.17.12.tgz#cc580857696b6dd9e5e3d079e673d060a0657f37" + integrity sha512-maEkX2xs2STuv2Px8QuqxqjhV2LsFobT1elCgyU5704fcyTu9DyD/bJXxD/mrRiVyhpHweOQ00OJ5FKhHq9oEw== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-react-display-name@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" + integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-react-display-name@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" + integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-jsx-development@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz#43a00724a3ed2557ed3f276a01a929e6686ac7b8" + integrity sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.16.7" + +"@babel/plugin-transform-react-jsx-development@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5" + integrity sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.18.6" + +"@babel/plugin-transform-react-jsx@^7.16.7", "@babel/plugin-transform-react-jsx@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.12.tgz#2aa20022709cd6a3f40b45d60603d5f269586dba" + integrity sha512-Lcaw8bxd1DKht3thfD4A12dqo1X16he1Lm8rIv8sTwjAYNInRS1qHa9aJoqvzpscItXvftKDCfaEQzwoVyXpEQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-jsx" "^7.17.12" + "@babel/types" "^7.17.12" + +"@babel/plugin-transform-react-jsx@^7.18.6": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz#b3cbb7c3a00b92ec8ae1027910e331ba5c500eb9" + integrity sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/plugin-syntax-jsx" "^7.18.6" + "@babel/types" "^7.19.0" + +"@babel/plugin-transform-react-pure-annotations@^7.16.7": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.0.tgz#ef82c8e310913f3522462c9ac967d395092f1954" + integrity sha512-6+0IK6ouvqDn9bmEG7mEyF/pwlJXVj5lwydybpyyH3D0A7Hftk+NCTdYjnLNZksn261xaOV5ksmp20pQEmc2RQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-react-pure-annotations@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz#561af267f19f3e5d59291f9950fd7b9663d0d844" + integrity sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-regenerator@^7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.0.tgz#44274d655eb3f1af3f3a574ba819d3f48caf99d5" + integrity sha512-C8YdRw9uzx25HSIzwA7EM7YP0FhCe5wNvJbZzjVNHHPGVcDJ3Aie+qGYYdS1oVQgn+B3eAIJbWFLrJ4Jipv7nw== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + regenerator-transform "^0.15.0" + +"@babel/plugin-transform-regenerator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" + integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + regenerator-transform "^0.15.0" + +"@babel/plugin-transform-reserved-words@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.17.12.tgz#7dbd349f3cdffba751e817cf40ca1386732f652f" + integrity sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-reserved-words@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" + integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-runtime@^7.18.6": + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz#9d2a9dbf4e12644d6f46e5e75bfbf02b5d6e9194" + integrity sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" + semver "^6.3.0" + +"@babel/plugin-transform-shorthand-properties@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" + integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-shorthand-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" + integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-spread@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.17.12.tgz#c112cad3064299f03ea32afed1d659223935d1f5" + integrity sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + +"@babel/plugin-transform-spread@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz#dd60b4620c2fec806d60cfaae364ec2188d593b6" + integrity sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + +"@babel/plugin-transform-sticky-regex@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" + integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-sticky-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" + integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-template-literals@^7.18.2": + version "7.18.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.2.tgz#31ed6915721864847c48b656281d0098ea1add28" + integrity sha512-/cmuBVw9sZBGZVOMkpAEaVLwm4JmK2GZ1dFKOGGpMzEHWFmyZZ59lUU0PdRr8YNYeQdNzTDwuxP2X2gzydTc9g== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-template-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" + integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-typeof-symbol@^7.17.12": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.17.12.tgz#0f12f57ac35e98b35b4ed34829948d42bd0e6889" + integrity sha512-Q8y+Jp7ZdtSPXCThB6zjQ74N3lj0f6TDh1Hnf5B+sYlzQ8i5Pjp8gW0My79iekSpT4WnI06blqP6DT0OmaXXmw== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-transform-typeof-symbol@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" + integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-typescript@^7.17.12": + version "7.18.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.4.tgz#587eaf6a39edb8c06215e550dc939faeadd750bf" + integrity sha512-l4vHuSLUajptpHNEOUDEGsnpl9pfRLsN1XUoDQDD/YBuXTM+v37SHGS+c6n4jdcZy96QtuUuSvZYMLSSsjH8Mw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.0" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-typescript" "^7.17.12" + +"@babel/plugin-transform-typescript@^7.18.6": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.2.tgz#91515527b376fc122ba83b13d70b01af8fe98f3f" + integrity sha512-jvS+ngBfrnTUBfOQq8NfGnSbF9BrqlR6hjJ2yVxMkmO5nL/cdifNbI30EfjRlN4g5wYWNnMPyj5Sa6R1pbLeag== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.20.2" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-typescript" "^7.20.0" + +"@babel/plugin-transform-unicode-escapes@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3" + integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-unicode-escapes@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" + integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-unicode-regex@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" + integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-unicode-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" + integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/preset-env@^7.15.6": + version "7.18.2" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.2.tgz#f47d3000a098617926e674c945d95a28cb90977a" + integrity sha512-PfpdxotV6afmXMU47S08F9ZKIm2bJIQ0YbAAtDfIENX7G1NUAXigLREh69CWDjtgUy7dYn7bsMzkgdtAlmS68Q== + dependencies: + "@babel/compat-data" "^7.17.10" + "@babel/helper-compilation-targets" "^7.18.2" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.17.12" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.17.12" + "@babel/plugin-proposal-async-generator-functions" "^7.17.12" + "@babel/plugin-proposal-class-properties" "^7.17.12" + "@babel/plugin-proposal-class-static-block" "^7.18.0" + "@babel/plugin-proposal-dynamic-import" "^7.16.7" + "@babel/plugin-proposal-export-namespace-from" "^7.17.12" + "@babel/plugin-proposal-json-strings" "^7.17.12" + "@babel/plugin-proposal-logical-assignment-operators" "^7.17.12" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.17.12" + "@babel/plugin-proposal-numeric-separator" "^7.16.7" + "@babel/plugin-proposal-object-rest-spread" "^7.18.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.16.7" + "@babel/plugin-proposal-optional-chaining" "^7.17.12" + "@babel/plugin-proposal-private-methods" "^7.17.12" + "@babel/plugin-proposal-private-property-in-object" "^7.17.12" + "@babel/plugin-proposal-unicode-property-regex" "^7.17.12" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.17.12" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.17.12" + "@babel/plugin-transform-async-to-generator" "^7.17.12" + "@babel/plugin-transform-block-scoped-functions" "^7.16.7" + "@babel/plugin-transform-block-scoping" "^7.17.12" + "@babel/plugin-transform-classes" "^7.17.12" + "@babel/plugin-transform-computed-properties" "^7.17.12" + "@babel/plugin-transform-destructuring" "^7.18.0" + "@babel/plugin-transform-dotall-regex" "^7.16.7" + "@babel/plugin-transform-duplicate-keys" "^7.17.12" + "@babel/plugin-transform-exponentiation-operator" "^7.16.7" + "@babel/plugin-transform-for-of" "^7.18.1" + "@babel/plugin-transform-function-name" "^7.16.7" + "@babel/plugin-transform-literals" "^7.17.12" + "@babel/plugin-transform-member-expression-literals" "^7.16.7" + "@babel/plugin-transform-modules-amd" "^7.18.0" + "@babel/plugin-transform-modules-commonjs" "^7.18.2" + "@babel/plugin-transform-modules-systemjs" "^7.18.0" + "@babel/plugin-transform-modules-umd" "^7.18.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.17.12" + "@babel/plugin-transform-new-target" "^7.17.12" + "@babel/plugin-transform-object-super" "^7.16.7" + "@babel/plugin-transform-parameters" "^7.17.12" + "@babel/plugin-transform-property-literals" "^7.16.7" + "@babel/plugin-transform-regenerator" "^7.18.0" + "@babel/plugin-transform-reserved-words" "^7.17.12" + "@babel/plugin-transform-shorthand-properties" "^7.16.7" + "@babel/plugin-transform-spread" "^7.17.12" + "@babel/plugin-transform-sticky-regex" "^7.16.7" + "@babel/plugin-transform-template-literals" "^7.18.2" + "@babel/plugin-transform-typeof-symbol" "^7.17.12" + "@babel/plugin-transform-unicode-escapes" "^7.16.7" + "@babel/plugin-transform-unicode-regex" "^7.16.7" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.18.2" + babel-plugin-polyfill-corejs2 "^0.3.0" + babel-plugin-polyfill-corejs3 "^0.5.0" + babel-plugin-polyfill-regenerator "^0.3.0" + core-js-compat "^3.22.1" + semver "^6.3.0" + +"@babel/preset-env@^7.18.6": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.20.2.tgz#9b1642aa47bb9f43a86f9630011780dab7f86506" + integrity sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg== + dependencies: + "@babel/compat-data" "^7.20.1" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-async-generator-functions" "^7.20.1" + "@babel/plugin-proposal-class-properties" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.18.6" + "@babel/plugin-proposal-dynamic-import" "^7.18.6" + "@babel/plugin-proposal-export-namespace-from" "^7.18.9" + "@babel/plugin-proposal-json-strings" "^7.18.6" + "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.20.2" + "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.18.6" + "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.20.0" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.18.6" + "@babel/plugin-transform-async-to-generator" "^7.18.6" + "@babel/plugin-transform-block-scoped-functions" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.20.2" + "@babel/plugin-transform-classes" "^7.20.2" + "@babel/plugin-transform-computed-properties" "^7.18.9" + "@babel/plugin-transform-destructuring" "^7.20.2" + "@babel/plugin-transform-dotall-regex" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-for-of" "^7.18.8" + "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-member-expression-literals" "^7.18.6" + "@babel/plugin-transform-modules-amd" "^7.19.6" + "@babel/plugin-transform-modules-commonjs" "^7.19.6" + "@babel/plugin-transform-modules-systemjs" "^7.19.6" + "@babel/plugin-transform-modules-umd" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1" + "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-object-super" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.20.1" + "@babel/plugin-transform-property-literals" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.18.6" + "@babel/plugin-transform-reserved-words" "^7.18.6" + "@babel/plugin-transform-shorthand-properties" "^7.18.6" + "@babel/plugin-transform-spread" "^7.19.0" + "@babel/plugin-transform-sticky-regex" "^7.18.6" + "@babel/plugin-transform-template-literals" "^7.18.9" + "@babel/plugin-transform-typeof-symbol" "^7.18.9" + "@babel/plugin-transform-unicode-escapes" "^7.18.10" + "@babel/plugin-transform-unicode-regex" "^7.18.6" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.20.2" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" + core-js-compat "^3.25.1" + semver "^6.3.0" + +"@babel/preset-modules@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" + integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@^7.14.5": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.17.12.tgz#62adbd2d1870c0de3893095757ed5b00b492ab3d" + integrity sha512-h5U+rwreXtZaRBEQhW1hOJLMq8XNJBQ/9oymXiCXTuT/0uOwpbT0gUt+sXeOqoXBgNuUKI7TaObVwoEyWkpFgA== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-react-display-name" "^7.16.7" + "@babel/plugin-transform-react-jsx" "^7.17.12" + "@babel/plugin-transform-react-jsx-development" "^7.16.7" + "@babel/plugin-transform-react-pure-annotations" "^7.16.7" + +"@babel/preset-react@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d" + integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-react-display-name" "^7.18.6" + "@babel/plugin-transform-react-jsx" "^7.18.6" + "@babel/plugin-transform-react-jsx-development" "^7.18.6" + "@babel/plugin-transform-react-pure-annotations" "^7.18.6" + +"@babel/preset-typescript@^7.15.0": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.17.12.tgz#40269e0a0084d56fc5731b6c40febe1c9a4a3e8c" + integrity sha512-S1ViF8W2QwAKUGJXxP9NAfNaqGDdEBJKpYkxHf5Yy2C4NPPzXGeR3Lhk7G8xJaaLcFTRfNjVbtbVtm8Gb0mqvg== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-typescript" "^7.17.12" + +"@babel/preset-typescript@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399" + integrity sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-typescript" "^7.18.6" + +"@babel/runtime-corejs3@^7.18.6": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.20.1.tgz#d0775a49bb5fba77e42cbb7276c9955c7b05af8d" + integrity sha512-CGulbEDcg/ND1Im7fUNRZdGXmX2MTWVVZacQi/6DiKE5HNwZ3aVTm5PV4lO8HHz0B2h8WQyvKKjbX5XgTtydsg== + dependencies: + core-js-pure "^3.25.1" + regenerator-runtime "^0.13.10" + +"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4": + version "7.18.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4" + integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.18.6": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" + integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg== + dependencies: + regenerator-runtime "^0.13.10" + +"@babel/template@^7.12.7", "@babel/template@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/template@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" + integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.10" + "@babel/types" "^7.18.10" + +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2", "@babel/traverse@^7.18.8", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.1": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.12.7", "@babel/types@^7.15.6", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.17.12", "@babel/types@^7.18.0", "@babel/types@^7.18.2", "@babel/types@^7.4.4": + version "7.18.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.4.tgz#27eae9b9fd18e9dccc3f9d6ad051336f307be354" + integrity sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + +"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.2.tgz#67ac09266606190f496322dbaff360fdaa5e7842" + integrity sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@docsearch/css@3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.3.0.tgz#d698e48302d12240d7c2f7452ccb2d2239a8cd80" + integrity sha512-rODCdDtGyudLj+Va8b6w6Y85KE85bXRsps/R4Yjwt5vueXKXZQKYw0aA9knxLBT6a/bI/GMrAcmCR75KYOM6hg== + +"@docsearch/react@^3.1.1": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.3.0.tgz#b8ac8e7f49b9bf2f96d34c24bc1cfd097ec0eead" + integrity sha512-fhS5adZkae2SSdMYEMVg6pxI5a/cE+tW16ki1V0/ur4Fdok3hBRkmN/H8VvlXnxzggkQIIRIVvYPn00JPjen3A== + dependencies: + "@algolia/autocomplete-core" "1.7.2" + "@algolia/autocomplete-preset-algolia" "1.7.2" + "@docsearch/css" "3.3.0" + algoliasearch "^4.0.0" + +"@docusaurus/core@2.2.0", "@docusaurus/core@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.2.0.tgz#64c9ee31502c23b93c869f8188f73afaf5fd4867" + integrity sha512-Vd6XOluKQqzG12fEs9prJgDtyn6DPok9vmUWDR2E6/nV5Fl9SVkhEQOBxwObjk3kQh7OY7vguFaLh0jqdApWsA== + dependencies: + "@babel/core" "^7.18.6" + "@babel/generator" "^7.18.7" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-transform-runtime" "^7.18.6" + "@babel/preset-env" "^7.18.6" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.18.6" + "@babel/runtime" "^7.18.6" + "@babel/runtime-corejs3" "^7.18.6" + "@babel/traverse" "^7.18.8" + "@docusaurus/cssnano-preset" "2.2.0" + "@docusaurus/logger" "2.2.0" + "@docusaurus/mdx-loader" "2.2.0" + "@docusaurus/react-loadable" "5.5.2" + "@docusaurus/utils" "2.2.0" + "@docusaurus/utils-common" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + "@slorber/static-site-generator-webpack-plugin" "^4.0.7" + "@svgr/webpack" "^6.2.1" + autoprefixer "^10.4.7" + babel-loader "^8.2.5" + babel-plugin-dynamic-import-node "^2.3.3" + boxen "^6.2.1" + chalk "^4.1.2" + chokidar "^3.5.3" + clean-css "^5.3.0" + cli-table3 "^0.6.2" + combine-promises "^1.1.0" + commander "^5.1.0" + copy-webpack-plugin "^11.0.0" + core-js "^3.23.3" + css-loader "^6.7.1" + css-minimizer-webpack-plugin "^4.0.0" + cssnano "^5.1.12" + del "^6.1.1" + detect-port "^1.3.0" + escape-html "^1.0.3" + eta "^1.12.3" + file-loader "^6.2.0" + fs-extra "^10.1.0" + html-minifier-terser "^6.1.0" + html-tags "^3.2.0" + html-webpack-plugin "^5.5.0" + import-fresh "^3.3.0" + leven "^3.1.0" + lodash "^4.17.21" + mini-css-extract-plugin "^2.6.1" + postcss "^8.4.14" + postcss-loader "^7.0.0" + prompts "^2.4.2" + react-dev-utils "^12.0.1" + react-helmet-async "^1.3.0" + react-loadable "npm:@docusaurus/react-loadable@5.5.2" + react-loadable-ssr-addon-v5-slorber "^1.0.1" + react-router "^5.3.3" + react-router-config "^5.1.1" + react-router-dom "^5.3.3" + rtl-detect "^1.0.4" + semver "^7.3.7" + serve-handler "^6.1.3" + shelljs "^0.8.5" + terser-webpack-plugin "^5.3.3" + tslib "^2.4.0" + update-notifier "^5.1.0" + url-loader "^4.1.1" + wait-on "^6.0.1" + webpack "^5.73.0" + webpack-bundle-analyzer "^4.5.0" + webpack-dev-server "^4.9.3" + webpack-merge "^5.8.0" + webpackbar "^5.0.2" + +"@docusaurus/cssnano-preset@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.2.0.tgz#fc05044659051ae74ab4482afcf4a9936e81d523" + integrity sha512-mAAwCo4n66TMWBH1kXnHVZsakW9VAXJzTO4yZukuL3ro4F+JtkMwKfh42EG75K/J/YIFQG5I/Bzy0UH/hFxaTg== + dependencies: + cssnano-preset-advanced "^5.3.8" + postcss "^8.4.14" + postcss-sort-media-queries "^4.2.1" + tslib "^2.4.0" + +"@docusaurus/logger@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-2.2.0.tgz#ea2f7feda7b8675485933b87f06d9c976d17423f" + integrity sha512-DF3j1cA5y2nNsu/vk8AG7xwpZu6f5MKkPPMaaIbgXLnWGfm6+wkOeW7kNrxnM95YOhKUkJUophX69nGUnLsm0A== + dependencies: + chalk "^4.1.2" + tslib "^2.4.0" + +"@docusaurus/mdx-loader@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.2.0.tgz#fd558f429e5d9403d284bd4214e54d9768b041a0" + integrity sha512-X2bzo3T0jW0VhUU+XdQofcEeozXOTmKQMvc8tUnWRdTnCvj4XEcBVdC3g+/jftceluiwSTNRAX4VBOJdNt18jA== + dependencies: + "@babel/parser" "^7.18.8" + "@babel/traverse" "^7.18.8" + "@docusaurus/logger" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@mdx-js/mdx" "^1.6.22" + escape-html "^1.0.3" + file-loader "^6.2.0" + fs-extra "^10.1.0" + image-size "^1.0.1" + mdast-util-to-string "^2.0.0" + remark-emoji "^2.2.0" + stringify-object "^3.3.0" + tslib "^2.4.0" + unified "^9.2.2" + unist-util-visit "^2.0.3" + url-loader "^4.1.1" + webpack "^5.73.0" + +"@docusaurus/module-type-aliases@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-2.2.0.tgz#1e23e54a1bbb6fde1961e4fa395b1b69f4803ba5" + integrity sha512-wDGW4IHKoOr9YuJgy7uYuKWrDrSpsUSDHLZnWQYM9fN7D5EpSmYHjFruUpKWVyxLpD/Wh0rW8hYZwdjJIQUQCQ== + dependencies: + "@docusaurus/react-loadable" "5.5.2" + "@docusaurus/types" "2.2.0" + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router-config" "*" + "@types/react-router-dom" "*" + react-helmet-async "*" + react-loadable "npm:@docusaurus/react-loadable@5.5.2" + +"@docusaurus/plugin-content-blog@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.2.0.tgz#dc55982e76771f4e678ac10e26d10e1da2011dc1" + integrity sha512-0mWBinEh0a5J2+8ZJXJXbrCk1tSTNf7Nm4tYAl5h2/xx+PvH/Bnu0V+7mMljYm/1QlDYALNIIaT/JcoZQFUN3w== + dependencies: + "@docusaurus/core" "2.2.0" + "@docusaurus/logger" "2.2.0" + "@docusaurus/mdx-loader" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@docusaurus/utils-common" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + cheerio "^1.0.0-rc.12" + feed "^4.2.2" + fs-extra "^10.1.0" + lodash "^4.17.21" + reading-time "^1.5.0" + tslib "^2.4.0" + unist-util-visit "^2.0.3" + utility-types "^3.10.0" + webpack "^5.73.0" + +"@docusaurus/plugin-content-docs@2.2.0", "@docusaurus/plugin-content-docs@^2.0.0-rc.1": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.2.0.tgz#0fcb85226fcdb80dc1e2d4a36ef442a650dcc84d" + integrity sha512-BOazBR0XjzsHE+2K1wpNxz5QZmrJgmm3+0Re0EVPYFGW8qndCWGNtXW/0lGKhecVPML8yyFeAmnUCIs7xM2wPw== + dependencies: + "@docusaurus/core" "2.2.0" + "@docusaurus/logger" "2.2.0" + "@docusaurus/mdx-loader" "2.2.0" + "@docusaurus/module-type-aliases" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + "@types/react-router-config" "^5.0.6" + combine-promises "^1.1.0" + fs-extra "^10.1.0" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + lodash "^4.17.21" + tslib "^2.4.0" + utility-types "^3.10.0" + webpack "^5.73.0" + +"@docusaurus/plugin-content-pages@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.2.0.tgz#e3f40408787bbe229545dd50595f87e1393bc3ae" + integrity sha512-+OTK3FQHk5WMvdelz8v19PbEbx+CNT6VSpx7nVOvMNs5yJCKvmqBJBQ2ZSxROxhVDYn+CZOlmyrC56NSXzHf6g== + dependencies: + "@docusaurus/core" "2.2.0" + "@docusaurus/mdx-loader" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + fs-extra "^10.1.0" + tslib "^2.4.0" + webpack "^5.73.0" + +"@docusaurus/plugin-debug@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-2.2.0.tgz#b38741d2c492f405fee01ee0ef2e0029cedb689a" + integrity sha512-p9vOep8+7OVl6r/NREEYxf4HMAjV8JMYJ7Bos5fCFO0Wyi9AZEo0sCTliRd7R8+dlJXZEgcngSdxAUo/Q+CJow== + dependencies: + "@docusaurus/core" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils" "2.2.0" + fs-extra "^10.1.0" + react-json-view "^1.21.3" + tslib "^2.4.0" + +"@docusaurus/plugin-google-analytics@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.2.0.tgz#63c7137eff5a1208d2059fea04b5207c037d7954" + integrity sha512-+eZVVxVeEnV5nVQJdey9ZsfyEVMls6VyWTIj8SmX0k5EbqGvnIfET+J2pYEuKQnDIHxy+syRMoRM6AHXdHYGIg== + dependencies: + "@docusaurus/core" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + tslib "^2.4.0" + +"@docusaurus/plugin-google-gtag@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.2.0.tgz#7b086d169ac5fe9a88aca10ab0fd2bf00c6c6b12" + integrity sha512-6SOgczP/dYdkqUMGTRqgxAS1eTp6MnJDAQMy8VCF1QKbWZmlkx4agHDexihqmYyCujTYHqDAhm1hV26EET54NQ== + dependencies: + "@docusaurus/core" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + tslib "^2.4.0" + +"@docusaurus/plugin-sitemap@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.2.0.tgz#876da60937886032d63143253d420db6a4b34773" + integrity sha512-0jAmyRDN/aI265CbWZNZuQpFqiZuo+5otk2MylU9iVrz/4J7gSc+ZJ9cy4EHrEsW7PV8s1w18hIEsmcA1YgkKg== + dependencies: + "@docusaurus/core" "2.2.0" + "@docusaurus/logger" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@docusaurus/utils-common" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + fs-extra "^10.1.0" + sitemap "^7.1.1" + tslib "^2.4.0" + +"@docusaurus/preset-classic@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.2.0.tgz#bece5a043eeb74430f7c6c7510000b9c43669eb7" + integrity sha512-yKIWPGNx7BT8v2wjFIWvYrS+nvN04W+UameSFf8lEiJk6pss0kL6SG2MRvyULiI3BDxH+tj6qe02ncpSPGwumg== + dependencies: + "@docusaurus/core" "2.2.0" + "@docusaurus/plugin-content-blog" "2.2.0" + "@docusaurus/plugin-content-docs" "2.2.0" + "@docusaurus/plugin-content-pages" "2.2.0" + "@docusaurus/plugin-debug" "2.2.0" + "@docusaurus/plugin-google-analytics" "2.2.0" + "@docusaurus/plugin-google-gtag" "2.2.0" + "@docusaurus/plugin-sitemap" "2.2.0" + "@docusaurus/theme-classic" "2.2.0" + "@docusaurus/theme-common" "2.2.0" + "@docusaurus/theme-search-algolia" "2.2.0" + "@docusaurus/types" "2.2.0" + +"@docusaurus/react-loadable@5.5.2": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" + integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== + dependencies: + "@types/react" "*" + prop-types "^15.6.2" + +"@docusaurus/theme-classic@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.2.0.tgz#a048bb1bc077dee74b28bec25f4b84b481863742" + integrity sha512-kjbg/qJPwZ6H1CU/i9d4l/LcFgnuzeiGgMQlt6yPqKo0SOJIBMPuz7Rnu3r/WWbZFPi//o8acclacOzmXdUUEg== + dependencies: + "@docusaurus/core" "2.2.0" + "@docusaurus/mdx-loader" "2.2.0" + "@docusaurus/module-type-aliases" "2.2.0" + "@docusaurus/plugin-content-blog" "2.2.0" + "@docusaurus/plugin-content-docs" "2.2.0" + "@docusaurus/plugin-content-pages" "2.2.0" + "@docusaurus/theme-common" "2.2.0" + "@docusaurus/theme-translations" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@docusaurus/utils-common" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + "@mdx-js/react" "^1.6.22" + clsx "^1.2.1" + copy-text-to-clipboard "^3.0.1" + infima "0.2.0-alpha.42" + lodash "^4.17.21" + nprogress "^0.2.0" + postcss "^8.4.14" + prism-react-renderer "^1.3.5" + prismjs "^1.28.0" + react-router-dom "^5.3.3" + rtlcss "^3.5.0" + tslib "^2.4.0" + utility-types "^3.10.0" + +"@docusaurus/theme-common@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-2.2.0.tgz#2303498d80448aafdd588b597ce9d6f4cfa930e4" + integrity sha512-R8BnDjYoN90DCL75gP7qYQfSjyitXuP9TdzgsKDmSFPNyrdE3twtPNa2dIN+h+p/pr+PagfxwWbd6dn722A1Dw== + dependencies: + "@docusaurus/mdx-loader" "2.2.0" + "@docusaurus/module-type-aliases" "2.2.0" + "@docusaurus/plugin-content-blog" "2.2.0" + "@docusaurus/plugin-content-docs" "2.2.0" + "@docusaurus/plugin-content-pages" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router-config" "*" + clsx "^1.2.1" + parse-numeric-range "^1.3.0" + prism-react-renderer "^1.3.5" + tslib "^2.4.0" + utility-types "^3.10.0" + +"@docusaurus/theme-search-algolia@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.2.0.tgz#77fd9f7a600917e6024fe3ac7fb6cfdf2ce84737" + integrity sha512-2h38B0tqlxgR2FZ9LpAkGrpDWVdXZ7vltfmTdX+4RsDs3A7khiNsmZB+x/x6sA4+G2V2CvrsPMlsYBy5X+cY1w== + dependencies: + "@docsearch/react" "^3.1.1" + "@docusaurus/core" "2.2.0" + "@docusaurus/logger" "2.2.0" + "@docusaurus/plugin-content-docs" "2.2.0" + "@docusaurus/theme-common" "2.2.0" + "@docusaurus/theme-translations" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + algoliasearch "^4.13.1" + algoliasearch-helper "^3.10.0" + clsx "^1.2.1" + eta "^1.12.3" + fs-extra "^10.1.0" + lodash "^4.17.21" + tslib "^2.4.0" + utility-types "^3.10.0" + +"@docusaurus/theme-translations@2.2.0", "@docusaurus/theme-translations@^2.0.0-rc.1": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-2.2.0.tgz#5fbd4693679806f80c26eeae1381e1f2c23d83e7" + integrity sha512-3T140AG11OjJrtKlY4pMZ5BzbGRDjNs2co5hJ6uYJG1bVWlhcaFGqkaZ5lCgKflaNHD7UHBHU9Ec5f69jTdd6w== + dependencies: + fs-extra "^10.1.0" + tslib "^2.4.0" + +"@docusaurus/types@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-2.2.0.tgz#02c577a4041ab7d058a3c214ccb13647e21a9857" + integrity sha512-b6xxyoexfbRNRI8gjblzVOnLr4peCJhGbYGPpJ3LFqpi5nsFfoK4mmDLvWdeah0B7gmJeXabN7nQkFoqeSdmOw== + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + commander "^5.1.0" + joi "^17.6.0" + react-helmet-async "^1.3.0" + utility-types "^3.10.0" + webpack "^5.73.0" + webpack-merge "^5.8.0" + +"@docusaurus/utils-common@2.2.0", "@docusaurus/utils-common@^2.0.0-rc.1": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-2.2.0.tgz#a401c1b93a8697dd566baf6ac64f0fdff1641a78" + integrity sha512-qebnerHp+cyovdUseDQyYFvMW1n1nv61zGe5JJfoNQUnjKuApch3IVsz+/lZ9a38pId8kqehC1Ao2bW/s0ntDA== + dependencies: + tslib "^2.4.0" + +"@docusaurus/utils-validation@2.2.0", "@docusaurus/utils-validation@^2.0.0-rc.1": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-2.2.0.tgz#04d4d103137ad0145883971d3aa497f4a1315f25" + integrity sha512-I1hcsG3yoCkasOL5qQAYAfnmVoLei7apugT6m4crQjmDGxq+UkiRrq55UqmDDyZlac/6ax/JC0p+usZ6W4nVyg== + dependencies: + "@docusaurus/logger" "2.2.0" + "@docusaurus/utils" "2.2.0" + joi "^17.6.0" + js-yaml "^4.1.0" + tslib "^2.4.0" + +"@docusaurus/utils@2.2.0", "@docusaurus/utils@^2.0.0-rc.1": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.2.0.tgz#3d6f9b7a69168d5c92d371bf21c556a4f50d1da6" + integrity sha512-oNk3cjvx7Tt1Lgh/aeZAmFpGV2pDr5nHKrBVx6hTkzGhrnMuQqLt6UPlQjdYQ3QHXwyF/ZtZMO1D5Pfi0lu7SA== + dependencies: + "@docusaurus/logger" "2.2.0" + "@svgr/webpack" "^6.2.1" + file-loader "^6.2.0" + fs-extra "^10.1.0" + github-slugger "^1.4.0" + globby "^11.1.0" + gray-matter "^4.0.3" + js-yaml "^4.1.0" + lodash "^4.17.21" + micromatch "^4.0.5" + resolve-pathname "^3.0.0" + shelljs "^0.8.5" + tslib "^2.4.0" + url-loader "^4.1.1" + webpack "^5.73.0" + +"@easyops-cn/autocomplete.js@^0.38.1": + version "0.38.1" + resolved "https://registry.yarnpkg.com/@easyops-cn/autocomplete.js/-/autocomplete.js-0.38.1.tgz#46dff5795a9a032fa9b9250fdf63ca6c61c07629" + integrity sha512-drg76jS6syilOUmVNkyo1c7ZEBPcPuK+aJA7AksM5ZIIbV57DMHCywiCr+uHyv8BE5jUTU98j/H7gVrkHrWW3Q== + dependencies: + cssesc "^3.0.0" + immediate "^3.2.3" + +"@easyops-cn/docusaurus-search-local@^0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@easyops-cn/docusaurus-search-local/-/docusaurus-search-local-0.33.5.tgz#8048b75c860b0ab0eb007159aa13b04ac0c72078" + integrity sha512-9juHGVUy6N37Ezg1Msz1paMqT3zrQIlRLNJVw/NTG3aSctaYw1W0zAIRieXgBKvBAPkcCn95GsUrcziH13Grsw== + dependencies: + "@docusaurus/plugin-content-docs" "^2.0.0-rc.1" + "@docusaurus/theme-translations" "^2.0.0-rc.1" + "@docusaurus/utils" "^2.0.0-rc.1" + "@docusaurus/utils-common" "^2.0.0-rc.1" + "@docusaurus/utils-validation" "^2.0.0-rc.1" + "@easyops-cn/autocomplete.js" "^0.38.1" + "@node-rs/jieba" "^1.6.0" + cheerio "^1.0.0-rc.3" + clsx "^1.1.1" + debug "^4.2.0" + fs-extra "^10.0.0" + klaw-sync "^6.0.0" + lunr "^2.3.9" + lunr-languages "^1.4.0" + mark.js "^8.11.1" + tslib "^2.4.0" + +"@hapi/hoek@^9.0.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0", "@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/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" + integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@1.4.14", "@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/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.14": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@jridgewell/trace-mapping@^0.3.17": + version "0.3.20" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" + integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@jridgewell/trace-mapping@^0.3.7": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" + integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.14" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" + integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" + integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== + +"@mdx-js/mdx@^1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" + integrity sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA== + dependencies: + "@babel/core" "7.12.9" + "@babel/plugin-syntax-jsx" "7.12.1" + "@babel/plugin-syntax-object-rest-spread" "7.8.3" + "@mdx-js/util" "1.6.22" + babel-plugin-apply-mdx-type-prop "1.6.22" + babel-plugin-extract-import-names "1.6.22" + camelcase-css "2.0.1" + detab "2.0.4" + hast-util-raw "6.0.1" + lodash.uniq "4.5.0" + mdast-util-to-hast "10.0.1" + remark-footnotes "2.0.0" + remark-mdx "1.6.22" + remark-parse "8.0.3" + remark-squeeze-paragraphs "4.0.0" + style-to-object "0.3.0" + unified "9.2.0" + unist-builder "2.0.3" + unist-util-visit "2.0.3" + +"@mdx-js/react@^1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-1.6.22.tgz#ae09b4744fddc74714ee9f9d6f17a66e77c43573" + integrity sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg== + +"@mdx-js/util@1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" + integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== + +"@node-rs/jieba-android-arm-eabi@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@node-rs/jieba-android-arm-eabi/-/jieba-android-arm-eabi-1.6.1.tgz#196f1052a564d2608e46c87114aa78232453b798" + integrity sha512-R1YQfsPr7sK3Tq1sM0//6lNAGJK9RnMT0ShITT+7EJYr5OufUBb38lf/mRhrLxR0NF1pycEsMjdCAwrWrHd8rA== + +"@node-rs/jieba-android-arm64@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@node-rs/jieba-android-arm64/-/jieba-android-arm64-1.6.1.tgz#cb4db38d9fcb877ac06659281a2dc5f50fe7be52" + integrity sha512-hBRbj2uLmRFYDw2lWppTAPoyjeXkBKUT84h4fHUQj7CMU94Gc1IWkE4ocCqhvUhbaUXlCpocS9mB0/fc2641bw== + +"@node-rs/jieba-darwin-arm64@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@node-rs/jieba-darwin-arm64/-/jieba-darwin-arm64-1.6.1.tgz#83ad4bddd9368556ccd46555a508abbf9007fa4b" + integrity sha512-GeoDe7XVTF6z8JUtD98QvwudsMaHV5EBXs5uO43SobeIkShH3Nujq5gLMD5kWoJXTxDrTgJe4wT42EwUaBEH2Q== + +"@node-rs/jieba-darwin-x64@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@node-rs/jieba-darwin-x64/-/jieba-darwin-x64-1.6.1.tgz#ddd964b7874c208b964897898c5826ef195efcad" + integrity sha512-ENHYIS8b8JdMaUXEm0f8Y3+sHXu2UdukG1D/XGUNx+q5cn07HbwIg6L0tlGhE8dw4AhqoWHsExVaZ241Igh4iA== + +"@node-rs/jieba-freebsd-x64@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@node-rs/jieba-freebsd-x64/-/jieba-freebsd-x64-1.6.1.tgz#8e8be576d471c0837dcf4a6130ab055cca588e80" + integrity sha512-chwB/9edtxqS8Jm3j4RMaJjH9AlXmijUgKv02oMw36e77HKpko+tENUN25Vrn/9GKsKGqIPeXpmCjeXCN1HVQA== + +"@node-rs/jieba-linux-arm-gnueabihf@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@node-rs/jieba-linux-arm-gnueabihf/-/jieba-linux-arm-gnueabihf-1.6.1.tgz#be1c7205f0ecf03f8f9065ad60b58bb4f7615d71" + integrity sha512-tsb5fMGj4p8bHGfkf7bJ+HE2jxaixLTp3YnGg5D+kp8+HQRq8cp3ScG5cn8cq0phnJS/zfAp8rVfWInDagzKKQ== + +"@node-rs/jieba-linux-arm64-gnu@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@node-rs/jieba-linux-arm64-gnu/-/jieba-linux-arm64-gnu-1.6.1.tgz#43608a4506f6b47bec3349312a18c1a51b6f3822" + integrity sha512-bSInORkJFfeZNR+i4rFoSZGbwkQtQlnZ0XfT/noTK9JUBDYErqQZPFjoaYAU45NWTk7p6Zkg30SuV1NTdWLaPw== + +"@node-rs/jieba-linux-arm64-musl@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@node-rs/jieba-linux-arm64-musl/-/jieba-linux-arm64-musl-1.6.1.tgz#aef63ab32d3caa4ab7cc0f717dd7a6e774083b9b" + integrity sha512-qphL6xM7owfU8Hsh7GX73SDr/iApbnc+35mSLxbibAfCQnY89+WcBeWUUOSGM/Ov3VFaq4pyVlDFj0YjR01W2w== + +"@node-rs/jieba-linux-x64-gnu@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@node-rs/jieba-linux-x64-gnu/-/jieba-linux-x64-gnu-1.6.1.tgz#8e09bb2c57fc546acc9cf8e1c879dcdf01e79717" + integrity sha512-f6hhlrbi2wel0xZG7m3Wvksimt9MSu1f3aYO2Kwavf4qjMRZqJzLz9HlCJAal6AXB9Qgg+685P+gftsWve47qw== + +"@node-rs/jieba-linux-x64-musl@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@node-rs/jieba-linux-x64-musl/-/jieba-linux-x64-musl-1.6.1.tgz#f698db4b49cfd367b5c3f6b4352d5092ab3620e2" + integrity sha512-cTVcdR6zWqpnmdEUyWEII9zfE5lTeWN53TbiOPx8TCA+291/31Vqd7GA8YEPndUO8qgCx5uShSDFStBAEIhYNQ== + +"@node-rs/jieba-win32-arm64-msvc@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@node-rs/jieba-win32-arm64-msvc/-/jieba-win32-arm64-msvc-1.6.1.tgz#43a20ca596eac90659a64721f0ad2130b2be199e" + integrity sha512-YuOTrjHazDraXcGXRHgPQ53nyJuH8QtTCngYKjAzxsdt8uN+txb1AY69OLMLBBZqLTOwY9dgcW70vGiLQMCTeg== + +"@node-rs/jieba-win32-ia32-msvc@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@node-rs/jieba-win32-ia32-msvc/-/jieba-win32-ia32-msvc-1.6.1.tgz#eec9062cdc5dd951f7177576993bbc32fa405416" + integrity sha512-4+E843ImGpVlZ+LlT9E/13NHmmUg3UHQx419D6fFMorJUUQuK4cZJfE1z4tCgcrbV8S5Wew5LIFywlJeJLu0LQ== + +"@node-rs/jieba-win32-x64-msvc@1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@node-rs/jieba-win32-x64-msvc/-/jieba-win32-x64-msvc-1.6.1.tgz#3df4853afc175e4dd43794b08a71e8d25d00e807" + integrity sha512-veXNwm2VlseOzl7vaC7A/nZ4okp5/6edN7/Atj6mXnUbze/m/my5Rv5zUcW3U1D9VElnQ3srCHCa5vXljJuk6g== + +"@node-rs/jieba@^1.6.0": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@node-rs/jieba/-/jieba-1.6.1.tgz#221aa586aa671cb029687256a4c86c86bfecbef9" + integrity sha512-pISKu8NIYKRvZp7mhYZYA8VCjJMqTsCe+mQcFFnAi3GNJsijGjef2peMFeDcvP72X8MsnNeYeg3rHkAybtefyQ== + optionalDependencies: + "@node-rs/jieba-android-arm-eabi" "1.6.1" + "@node-rs/jieba-android-arm64" "1.6.1" + "@node-rs/jieba-darwin-arm64" "1.6.1" + "@node-rs/jieba-darwin-x64" "1.6.1" + "@node-rs/jieba-freebsd-x64" "1.6.1" + "@node-rs/jieba-linux-arm-gnueabihf" "1.6.1" + "@node-rs/jieba-linux-arm64-gnu" "1.6.1" + "@node-rs/jieba-linux-arm64-musl" "1.6.1" + "@node-rs/jieba-linux-x64-gnu" "1.6.1" + "@node-rs/jieba-linux-x64-musl" "1.6.1" + "@node-rs/jieba-win32-arm64-msvc" "1.6.1" + "@node-rs/jieba-win32-ia32-msvc" "1.6.1" + "@node-rs/jieba-win32-x64-msvc" "1.6.1" + +"@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": + 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" + +"@polka/url@^1.0.0-next.20": + version "1.0.0-next.21" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" + integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== + +"@sideway/address@^4.1.3": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" + integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + +"@sindresorhus/is@^4.0.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + +"@slorber/static-site-generator-webpack-plugin@^4.0.7": + version "4.0.7" + resolved "https://registry.yarnpkg.com/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz#fc1678bddefab014e2145cbe25b3ce4e1cfc36f3" + integrity sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA== + dependencies: + eval "^0.1.8" + p-map "^4.0.0" + webpack-sources "^3.2.2" + +"@svgr/babel-plugin-add-jsx-attribute@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.0.0.tgz#bd6d1ff32a31b82b601e73672a789cc41e84fe18" + integrity sha512-MdPdhdWLtQsjd29Wa4pABdhWbaRMACdM1h31BY+c6FghTZqNGT7pEYdBoaGeKtdTOBC/XNFQaKVj+r/Ei2ryWA== + +"@svgr/babel-plugin-remove-jsx-attribute@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.0.0.tgz#58654908beebfa069681a83332544b17e5237e89" + integrity sha512-aVdtfx9jlaaxc3unA6l+M9YRnKIZjOhQPthLKqmTXC8UVkBLDRGwPKo+r8n3VZN8B34+yVajzPTZ+ptTSuZZCw== + +"@svgr/babel-plugin-remove-jsx-empty-expression@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.0.0.tgz#d06dd6e8a8f603f92f9979bb9990a1f85a4f57ba" + integrity sha512-Ccj42ApsePD451AZJJf1QzTD1B/BOU392URJTeXFxSK709i0KUsGtbwyiqsKu7vsYxpTM0IA5clAKDyf9RCZyA== + +"@svgr/babel-plugin-replace-jsx-attribute-value@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.0.0.tgz#0b85837577b02c31c09c758a12932820f5245cee" + integrity sha512-88V26WGyt1Sfd1emBYmBJRWMmgarrExpKNVmI9vVozha4kqs6FzQJ/Kp5+EYli1apgX44518/0+t9+NU36lThQ== + +"@svgr/babel-plugin-svg-dynamic-title@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.0.0.tgz#28236ec26f7ab9d486a487d36ae52d58ba15676f" + integrity sha512-F7YXNLfGze+xv0KMQxrl2vkNbI9kzT9oDK55/kUuymh1ACyXkMV+VZWX1zEhSTfEKh7VkHVZGmVtHg8eTZ6PRg== + +"@svgr/babel-plugin-svg-em-dimensions@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.0.0.tgz#40267c5dea1b43c4f83a0eb6169e08b43d8bafce" + integrity sha512-+rghFXxdIqJNLQK08kwPBD3Z22/0b2tEZ9lKiL/yTfuyj1wW8HUXu4bo/XkogATIYuXSghVQOOCwURXzHGKyZA== + +"@svgr/babel-plugin-transform-react-native-svg@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.0.0.tgz#eb688d0a5f539e34d268d8a516e81f5d7fede7c9" + integrity sha512-VaphyHZ+xIKv5v0K0HCzyfAaLhPGJXSk2HkpYfXIOKb7DjLBv0soHDxNv6X0vr2titsxE7klb++u7iOf7TSrFQ== + +"@svgr/babel-plugin-transform-svg-component@^6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.2.0.tgz#7ba61d9fc1fb42b0ba1a04e4630019fa7e993c4f" + integrity sha512-bhYIpsORb++wpsp91fymbFkf09Z/YEKR0DnFjxvN+8JHeCUD2unnh18jIMKnDJTWtvpTaGYPXELVe4OOzFI0xg== + +"@svgr/babel-preset@^6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-6.2.0.tgz#1d3ad8c7664253a4be8e4a0f0e6872f30d8af627" + integrity sha512-4WQNY0J71JIaL03DRn0vLiz87JXx0b9dYm2aA8XHlQJQoixMl4r/soYHm8dsaJZ3jWtkCiOYy48dp9izvXhDkQ== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^6.0.0" + "@svgr/babel-plugin-remove-jsx-attribute" "^6.0.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "^6.0.0" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^6.0.0" + "@svgr/babel-plugin-svg-dynamic-title" "^6.0.0" + "@svgr/babel-plugin-svg-em-dimensions" "^6.0.0" + "@svgr/babel-plugin-transform-react-native-svg" "^6.0.0" + "@svgr/babel-plugin-transform-svg-component" "^6.2.0" + +"@svgr/core@^6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-6.2.1.tgz#195de807a9f27f9e0e0d678e01084b05c54fdf61" + integrity sha512-NWufjGI2WUyrg46mKuySfviEJ6IxHUOm/8a3Ph38VCWSp+83HBraCQrpEM3F3dB6LBs5x8OElS8h3C0oOJaJAA== + dependencies: + "@svgr/plugin-jsx" "^6.2.1" + camelcase "^6.2.0" + cosmiconfig "^7.0.1" + +"@svgr/hast-util-to-babel-ast@^6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.2.1.tgz#ae065567b74cbe745afae617053adf9a764bea25" + integrity sha512-pt7MMkQFDlWJVy9ULJ1h+hZBDGFfSCwlBNW1HkLnVi7jUhyEXUaGYWi1x6bM2IXuAR9l265khBT4Av4lPmaNLQ== + dependencies: + "@babel/types" "^7.15.6" + entities "^3.0.1" + +"@svgr/plugin-jsx@^6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-6.2.1.tgz#5668f1d2aa18c2f1bb7a1fc9f682d3f9aed263bd" + integrity sha512-u+MpjTsLaKo6r3pHeeSVsh9hmGRag2L7VzApWIaS8imNguqoUwDq/u6U/NDmYs/KAsrmtBjOEaAAPbwNGXXp1g== + dependencies: + "@babel/core" "^7.15.5" + "@svgr/babel-preset" "^6.2.0" + "@svgr/hast-util-to-babel-ast" "^6.2.1" + svg-parser "^2.0.2" + +"@svgr/plugin-svgo@^6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-6.2.0.tgz#4cbe6a33ccccdcae4e3b63ded64cc1cbe1faf48c" + integrity sha512-oDdMQONKOJEbuKwuy4Np6VdV6qoaLLvoY86hjvQEgU82Vx1MSWRyYms6Sl0f+NtqxLI/rDVufATbP/ev996k3Q== + dependencies: + cosmiconfig "^7.0.1" + deepmerge "^4.2.2" + svgo "^2.5.0" + +"@svgr/webpack@^6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-6.2.1.tgz#ef5d51c1b6be4e7537fb9f76b3f2b2e22b63c58d" + integrity sha512-h09ngMNd13hnePwgXa+Y5CgOjzlCvfWLHg+MBnydEedAnuLRzUHUJmGS3o2OsrhxTOOqEsPOFt5v/f6C5Qulcw== + dependencies: + "@babel/core" "^7.15.5" + "@babel/plugin-transform-react-constant-elements" "^7.14.5" + "@babel/preset-env" "^7.15.6" + "@babel/preset-react" "^7.14.5" + "@babel/preset-typescript" "^7.15.0" + "@svgr/core" "^6.2.1" + "@svgr/plugin-jsx" "^6.2.1" + "@svgr/plugin-svgo" "^6.2.0" + +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + +"@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/bonjour@^3.5.9": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275" + integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw== + dependencies: + "@types/node" "*" + +"@types/cacheable-request@^6.0.1": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9" + integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" + "@types/node" "*" + "@types/responselike" "*" + +"@types/connect-history-api-fallback@^1.3.5": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" + integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== + dependencies: + "@types/express-serve-static-core" "*" + "@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/eslint-scope@^3.7.3": + version "3.7.3" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224" + integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.4.3" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.3.tgz#5c92815a3838b1985c90034cd85f26f59d9d0ece" + integrity sha512-YP1S7YJRMPs+7KZKDb9G63n8YejIwW9BALq7a5j2+H4yl6iOv9CB29edho+cuFRrvmJbbaH2yiVChKLJVysDGw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^0.0.51": + version "0.0.51" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18": + version "4.17.28" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" + integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*", "@types/express@^4.17.13": + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/hast@^2.0.0": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc" + integrity sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g== + dependencies: + "@types/unist" "*" + +"@types/history@^4.7.11": + version "4.7.11" + resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64" + integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA== + +"@types/html-minifier-terser@^6.0.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" + integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== + +"@types/http-cache-semantics@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" + integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== + +"@types/http-proxy@^1.17.8": + version "1.17.9" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.9.tgz#7f0e7931343761efde1e2bf48c40f02f3f75705a" + integrity sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw== + dependencies: + "@types/node" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@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/keyv@*": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-4.2.0.tgz#65b97868ab757906f2dbb653590d7167ad023fa0" + integrity sha512-xoBtGl5R9jeKUhc8ZqeYaRDx04qqJ10yhhXYGmJ4Jr8qKpvMsDQQrNUvF/wUJ4klOtmJeJM+p2Xo3zp9uaC3tw== + dependencies: + keyv "*" + +"@types/mdast@^3.0.0": + version "3.0.10" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" + integrity sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA== + dependencies: + "@types/unist" "*" + +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + +"@types/node@*", "@types/node@^17.0.5": + version "17.0.41" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.41.tgz#1607b2fd3da014ae5d4d1b31bc792a39348dfb9b" + integrity sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw== + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + +"@types/parse5@^5.0.0": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" + integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== + +"@types/prop-types@*": + version "15.7.5" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" + integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== + +"@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/react-router-config@*", "@types/react-router-config@^5.0.6": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@types/react-router-config/-/react-router-config-5.0.6.tgz#87c5c57e72d241db900d9734512c50ccec062451" + integrity sha512-db1mx37a1EJDf1XeX8jJN7R3PZABmJQXR8r28yUjVMFSjkmnQo6X6pOEEmNl+Tp2gYQOGPdYbFIipBtdElZ3Yg== + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router" "*" + +"@types/react-router-dom@*": + version "5.3.3" + resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83" + integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw== + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router" "*" + +"@types/react-router@*": + version "5.1.18" + resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.18.tgz#c8851884b60bc23733500d86c1266e1cfbbd9ef3" + integrity sha512-YYknwy0D0iOwKQgz9v8nOzt2J6l4gouBmDnWqUUznltOTaon+r8US8ky8HvN0tXvc38U9m6z/t2RsVsnd1zM0g== + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + +"@types/react@*": + version "18.0.12" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.12.tgz#cdaa209d0a542b3fcf69cf31a03976ec4cdd8840" + integrity sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/responselike@*", "@types/responselike@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + +"@types/sax@^1.2.1": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/sax/-/sax-1.2.4.tgz#8221affa7f4f3cb21abd22f244cfabfa63e6a69e" + integrity sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw== + dependencies: + "@types/node" "*" + +"@types/scheduler@*": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" + integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + +"@types/serve-index@^1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" + integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg== + dependencies: + "@types/express" "*" + +"@types/serve-static@*", "@types/serve-static@^1.13.10": + version "1.13.10" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" + integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/sockjs@^0.3.33": + version "0.3.33" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" + integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw== + dependencies: + "@types/node" "*" + +"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" + integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== + +"@types/ws@^8.5.1": + version "8.5.3" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" + integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== + dependencies: + "@types/node" "*" + +"@webassemblyjs/ast@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" + integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + +"@webassemblyjs/floating-point-hex-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" + integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== + +"@webassemblyjs/helper-api-error@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" + integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== + +"@webassemblyjs/helper-buffer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" + integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== + +"@webassemblyjs/helper-numbers@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" + integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" + integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== + +"@webassemblyjs/helper-wasm-section@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" + integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + +"@webassemblyjs/ieee754@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" + integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" + integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" + integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== + +"@webassemblyjs/wasm-edit@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" + integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-wasm-section" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-opt" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/wast-printer" "1.11.1" + +"@webassemblyjs/wasm-gen@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" + integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wasm-opt@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" + integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + +"@webassemblyjs/wasm-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" + integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wast-printer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" + integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@xtuc/long" "4.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +accepts@~1.3.4, accepts@~1.3.5, 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-import-assertions@^1.7.6: + version "1.8.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" + integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== + +acorn-walk@^8.0.0: + 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@^8.0.4, acorn@^8.5.0: + version "8.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" + integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== + +acorn@^8.7.1: + version "8.8.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" + integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== + +address@^1.0.1, address@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/address/-/address-1.2.0.tgz#d352a62c92fee90f89a693eccd2a8b2139ab02d9" + integrity sha512-tNEZYz5G/zYunxFm7sfhAxkXEuLj3K6BKwv6ZURlsF6yiUQ65z0Q2wZW9L5cPUl9ocofGvXOdFYbFHp0+6MOig== + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: + 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" + +ajv@^8.0.0, ajv@^8.8.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" + integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +algoliasearch-helper@^3.10.0: + version "3.11.1" + resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.11.1.tgz#d83ab7f1a2a374440686ef7a144b3c288b01188a" + integrity sha512-mvsPN3eK4E0bZG0/WlWJjeqe/bUD2KOEVOl0GyL/TGXn6wcpZU8NOuztGHCUKXkyg5gq6YzUakVTmnmSSO5Yiw== + dependencies: + "@algolia/events" "^4.0.1" + +algoliasearch@^4.0.0, algoliasearch@^4.13.1: + version "4.13.1" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.13.1.tgz#54195c41c9e4bd13ed64982248cf49d4576974fe" + integrity sha512-dtHUSE0caWTCE7liE1xaL+19AFf6kWEcyn76uhcitWpntqvicFHXKFoZe5JJcv9whQOTRM6+B8qJz6sFj+rDJA== + dependencies: + "@algolia/cache-browser-local-storage" "4.13.1" + "@algolia/cache-common" "4.13.1" + "@algolia/cache-in-memory" "4.13.1" + "@algolia/client-account" "4.13.1" + "@algolia/client-analytics" "4.13.1" + "@algolia/client-common" "4.13.1" + "@algolia/client-personalization" "4.13.1" + "@algolia/client-search" "4.13.1" + "@algolia/logger-common" "4.13.1" + "@algolia/logger-console" "4.13.1" + "@algolia/requester-browser-xhr" "4.13.1" + "@algolia/requester-common" "4.13.1" + "@algolia/requester-node-http" "4.13.1" + "@algolia/transporter" "4.13.1" + +ansi-align@^3.0.0, ansi-align@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + +ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + +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-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +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" + +ansi-styles@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.0.tgz#87313c102b8118abd57371afab34618bf7350ed3" + integrity sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ== + +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +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-flatten@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + +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== + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +autoprefixer@^10.4.12: + version "10.4.13" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.13.tgz#b5136b59930209a321e9fa3dca2e7c4d223e83a8" + integrity sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg== + dependencies: + browserslist "^4.21.4" + caniuse-lite "^1.0.30001426" + fraction.js "^4.2.0" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +autoprefixer@^10.4.7: + version "10.4.7" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.7.tgz#1db8d195f41a52ca5069b7593be167618edbbedf" + integrity sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA== + dependencies: + browserslist "^4.20.3" + caniuse-lite "^1.0.30001335" + fraction.js "^4.2.0" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +axios@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a" + integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g== + dependencies: + follow-redirects "^1.14.7" + +babel-loader@^8.2.5: + version "8.2.5" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.5.tgz#d45f585e654d5a5d90f5350a779d7647c5ed512e" + integrity sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ== + dependencies: + find-cache-dir "^3.3.1" + loader-utils "^2.0.0" + make-dir "^3.1.0" + schema-utils "^2.6.5" + +babel-plugin-apply-mdx-type-prop@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz#d216e8fd0de91de3f1478ef3231e05446bc8705b" + integrity sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + "@mdx-js/util" "1.6.22" + +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-extract-import-names@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz#de5f9a28eb12f3eb2578bf74472204e66d1a13dc" + integrity sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + +babel-plugin-polyfill-corejs2@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" + integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== + dependencies: + "@babel/compat-data" "^7.13.11" + "@babel/helper-define-polyfill-provider" "^0.3.1" + semver "^6.1.1" + +babel-plugin-polyfill-corejs2@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" + integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== + dependencies: + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.3" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" + integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.1" + core-js-compat "^3.21.0" + +babel-plugin-polyfill-corejs3@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" + integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + core-js-compat "^3.25.1" + +babel-plugin-polyfill-regenerator@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" + integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.1" + +babel-plugin-polyfill-regenerator@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" + integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + +bail@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" + integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== + +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== + +base16@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70" + integrity sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ== + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +body-parser@1.20.2: + version "1.20.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour-service@^1.0.11: + version "1.0.13" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.13.tgz#4ac003dc1626023252d58adf2946f57e5da450c1" + integrity sha512-LWKRU/7EqDUC9CTAQtuZl5HzBALoCYwtLhffW3et7vZMwv3bWLpJf8bRYlMD5OCcDpTfnPgNCV4yo9ZIaJGMiA== + dependencies: + array-flatten "^2.1.2" + dns-equal "^1.0.0" + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +boxen@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + +boxen@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-6.2.1.tgz#b098a2278b2cd2845deef2dff2efc38d329b434d" + integrity sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw== + dependencies: + ansi-align "^3.0.1" + camelcase "^6.2.0" + chalk "^4.1.2" + cli-boxes "^3.0.0" + string-width "^5.0.1" + type-fest "^2.5.0" + widest-line "^4.0.1" + wrap-ansi "^8.0.1" + +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" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +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" + +browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.18.1, browserslist@^4.20.2, browserslist@^4.20.3: + version "4.20.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.4.tgz#98096c9042af689ee1e0271333dbc564b8ce4477" + integrity sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw== + dependencies: + caniuse-lite "^1.0.30001349" + electron-to-chromium "^1.4.147" + escalade "^3.1.1" + node-releases "^2.0.5" + picocolors "^1.0.0" + +browserslist@^4.21.3, browserslist@^4.21.4: + version "4.21.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" + integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== + dependencies: + caniuse-lite "^1.0.30001400" + electron-to-chromium "^1.4.251" + node-releases "^2.0.6" + update-browserslist-db "^1.0.9" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-request@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" + integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" + +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== + +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +camelcase-css@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001335, caniuse-lite@^1.0.30001349: + version "1.0.30001352" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001352.tgz#cc6f5da3f983979ad1e2cdbae0505dccaa7c6a12" + integrity sha512-GUgH8w6YergqPQDGWhJGt8GDRnY0L/iJVQcU3eJ46GYf52R8tk0Wxp0PymuFVZboJYXGiCqwozAYZNRjVj6IcA== + +caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001426: + version "1.0.30001431" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz#e7c59bd1bc518fae03a4656be442ce6c4887a795" + integrity sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ== + +ccount@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" + integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== + +chalk@^2.0.0, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.1.0, chalk@^4.1.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" + +character-entities-legacy@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" + integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + +cheerio-select@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4" + integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g== + dependencies: + boolbase "^1.0.0" + css-select "^5.1.0" + css-what "^6.1.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.0.1" + +cheerio@^1.0.0-rc.12: + version "1.0.0-rc.12" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683" + integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q== + dependencies: + cheerio-select "^2.1.0" + dom-serializer "^2.0.0" + domhandler "^5.0.3" + domutils "^3.0.1" + htmlparser2 "^8.0.1" + parse5 "^7.0.0" + parse5-htmlparser2-tree-adapter "^7.0.0" + +cheerio@^1.0.0-rc.3: + version "1.0.0-rc.11" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.11.tgz#1be84be1a126958366bcc57a11648cd9b30a60c2" + integrity sha512-bQwNaDIBKID5ts/DsdhxrjqFXYfLw4ste+wMKqWA8DyKcS4qwsPP4Bk8ZNaTJjvpiX/qW3BT4sU7d6Bh5i+dag== + dependencies: + cheerio-select "^2.1.0" + dom-serializer "^2.0.0" + domhandler "^5.0.3" + domutils "^3.0.1" + htmlparser2 "^8.0.1" + parse5 "^7.0.0" + parse5-htmlparser2-tree-adapter "^7.0.0" + tslib "^2.4.0" + +chokidar@^3.4.2, chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +clean-css@^5.2.2, clean-css@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.0.tgz#ad3d8238d5f3549e83d5f87205189494bc7cbb59" + integrity sha512-YYuuxv4H/iNb1Z/5IbMRoxgrzjWGhOEFfd+groZ5dMCVkpENiMZmwspdrzBo9286JjM1gZJPAyL7ZIdzuvu2AQ== + dependencies: + source-map "~0.6.0" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + +cli-boxes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145" + integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g== + +cli-table3@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.2.tgz#aaf5df9d8b5bf12634dc8b3040806a0c07120d2a" + integrity sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q== + dependencies: + mimic-response "^1.0.0" + +clsx@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" + integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== + +clsx@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + +collapse-white-space@^1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" + integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +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.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +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== + +colord@^2.9.1: + version "2.9.2" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.2.tgz#25e2bacbbaa65991422c07ea209e2089428effb1" + integrity sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ== + +colorette@^2.0.10: + version "2.0.17" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.17.tgz#5dd4c0d15e2984b7433cb4a9f2ead45063b80c47" + integrity sha512-hJo+3Bkn0NCHybn9Tu35fIeoOKGOk5OCC32y4Hz2It+qlCO2Q3DeQ1hRn/tDDMQKRYUEzqsl7jbF6dYKjlE60g== + +combine-promises@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/combine-promises/-/combine-promises-1.1.0.tgz#72db90743c0ca7aab7d0d8d2052fd7b0f674de71" + integrity sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg== + +comma-separated-tokens@^1.0.0: + version "1.0.8" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" + integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +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== + +configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== + dependencies: + dot-prop "^5.2.0" + graceful-fs "^4.1.2" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +consola@^2.15.3: + version "2.15.3" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" + integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA== + +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== + +content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +convert-source-map@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" + integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== + dependencies: + safe-buffer "~5.1.1" + +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.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + +copy-text-to-clipboard@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz#8cbf8f90e0a47f12e4a24743736265d157bce69c" + integrity sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q== + +copy-webpack-plugin@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a" + integrity sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ== + dependencies: + fast-glob "^3.2.11" + glob-parent "^6.0.1" + globby "^13.1.1" + normalize-path "^3.0.0" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + +core-js-compat@^3.21.0, core-js-compat@^3.22.1: + version "3.22.8" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.22.8.tgz#46fa34ce1ddf742acd7f95f575f66bbb21e05d62" + integrity sha512-pQnwg4xtuvc2Bs/5zYQPaEYYSuTxsF7LBWF0SvnVhthZo/Qe+rJpcEekrdNK5DWwDJ0gv0oI9NNX5Mppdy0ctg== + dependencies: + browserslist "^4.20.3" + semver "7.0.0" + +core-js-compat@^3.25.1: + version "3.26.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.26.0.tgz#94e2cf8ba3e63800c4956ea298a6473bc9d62b44" + integrity sha512-piOX9Go+Z4f9ZiBFLnZ5VrOpBl0h7IGCkiFUN11QTe6LjAvOT3ifL/5TdoizMh99hcGy5SoLyWbapIY/PIb/3A== + dependencies: + browserslist "^4.21.4" + +core-js-pure@^3.25.1: + version "3.26.0" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.26.0.tgz#7ad8a5dd7d910756f3124374b50026e23265ca9a" + integrity sha512-LiN6fylpVBVwT8twhhluD9TzXmZQQsr2I2eIKtWNbZI1XMfBT7CV18itaN6RA7EtQd/SDdRx/wzvAShX2HvhQA== + +core-js@^3.23.3: + version "3.26.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.26.0.tgz#a516db0ed0811be10eac5d94f3b8463d03faccfe" + integrity sha512-+DkDrhoR4Y0PxDz6rurahuB+I45OsEUv8E1maPTB6OuHRohMMcznBq9TMpdpDMm/hUPob/mJJS3PqgbHpMTQgw== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cosmiconfig@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" + integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.7.2" + +cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cross-fetch@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" + integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== + dependencies: + node-fetch "2.6.7" + +cross-spawn@^7.0.3: + 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" + +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + +css-declaration-sorter@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz#bfd2f6f50002d6a3ae779a87d3a0c5d5b10e0f02" + integrity sha512-Ufadglr88ZLsrvS11gjeu/40Lw74D9Am/Jpr3LlYm5Q4ZP5KdlUhG+6u2EjyXeZcxmZ2h1ebCKngDjolpeLHpg== + +css-declaration-sorter@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz#be5e1d71b7a992433fb1c542c7a1b835e45682ec" + integrity sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w== + +css-loader@^6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.1.tgz#e98106f154f6e1baf3fc3bc455cb9981c1d5fd2e" + integrity sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw== + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.7" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.3.5" + +css-minimizer-webpack-plugin@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.0.0.tgz#e11800388c19c2b7442c39cc78ac8ae3675c9605" + integrity sha512-7ZXXRzRHvofv3Uac5Y+RkWRNo0ZMlcg8e9/OtrqUYmwDWJo+qs67GvdeFrXLsFb7czKNwjQhPkM0avlIYl+1nA== + dependencies: + cssnano "^5.1.8" + jest-worker "^27.5.1" + postcss "^8.4.13" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + source-map "^0.6.1" + +css-select@^4.1.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + +css-tree@^1.1.2, css-tree@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + +css-what@^6.0.1, css-what@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-advanced@^5.3.8: + version "5.3.9" + resolved "https://registry.yarnpkg.com/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.9.tgz#99e1cdf81a467a5e6c366cfc6d874a166c4d9a67" + integrity sha512-njnh4pp1xCsibJcEHnWZb4EEzni0ePMqPuPNyuWT4Z+YeXmsgqNuTPIljXFEXhxGsWs9183JkXgHxc1TcsahIg== + dependencies: + autoprefixer "^10.4.12" + cssnano-preset-default "^5.2.13" + postcss-discard-unused "^5.1.0" + postcss-merge-idents "^5.1.1" + postcss-reduce-idents "^5.2.0" + postcss-zindex "^5.1.0" + +cssnano-preset-default@^5.2.11: + version "5.2.11" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.11.tgz#28350471bc1af9df14052472b61340347f453a53" + integrity sha512-4PadR1NtuaIK8MvLNuY7MznK4WJteldGlzCiMaaTiOUP+apeiIvUDIXykzUOoqgOOUAHrU64ncdD90NfZR3LSQ== + dependencies: + css-declaration-sorter "^6.2.2" + cssnano-utils "^3.1.0" + postcss-calc "^8.2.3" + postcss-colormin "^5.3.0" + postcss-convert-values "^5.1.2" + postcss-discard-comments "^5.1.2" + postcss-discard-duplicates "^5.1.0" + postcss-discard-empty "^5.1.1" + postcss-discard-overridden "^5.1.0" + postcss-merge-longhand "^5.1.5" + postcss-merge-rules "^5.1.2" + postcss-minify-font-values "^5.1.0" + postcss-minify-gradients "^5.1.1" + postcss-minify-params "^5.1.3" + postcss-minify-selectors "^5.2.1" + postcss-normalize-charset "^5.1.0" + postcss-normalize-display-values "^5.1.0" + postcss-normalize-positions "^5.1.0" + postcss-normalize-repeat-style "^5.1.0" + postcss-normalize-string "^5.1.0" + postcss-normalize-timing-functions "^5.1.0" + postcss-normalize-unicode "^5.1.0" + postcss-normalize-url "^5.1.0" + postcss-normalize-whitespace "^5.1.1" + postcss-ordered-values "^5.1.2" + postcss-reduce-initial "^5.1.0" + postcss-reduce-transforms "^5.1.0" + postcss-svgo "^5.1.0" + postcss-unique-selectors "^5.1.1" + +cssnano-preset-default@^5.2.13: + version "5.2.13" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.13.tgz#e7353b0c57975d1bdd97ac96e68e5c1b8c68e990" + integrity sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ== + dependencies: + css-declaration-sorter "^6.3.1" + cssnano-utils "^3.1.0" + postcss-calc "^8.2.3" + postcss-colormin "^5.3.0" + postcss-convert-values "^5.1.3" + postcss-discard-comments "^5.1.2" + postcss-discard-duplicates "^5.1.0" + postcss-discard-empty "^5.1.1" + postcss-discard-overridden "^5.1.0" + postcss-merge-longhand "^5.1.7" + postcss-merge-rules "^5.1.3" + postcss-minify-font-values "^5.1.0" + postcss-minify-gradients "^5.1.1" + postcss-minify-params "^5.1.4" + postcss-minify-selectors "^5.2.1" + postcss-normalize-charset "^5.1.0" + postcss-normalize-display-values "^5.1.0" + postcss-normalize-positions "^5.1.1" + postcss-normalize-repeat-style "^5.1.1" + postcss-normalize-string "^5.1.0" + postcss-normalize-timing-functions "^5.1.0" + postcss-normalize-unicode "^5.1.1" + postcss-normalize-url "^5.1.0" + postcss-normalize-whitespace "^5.1.1" + postcss-ordered-values "^5.1.3" + postcss-reduce-initial "^5.1.1" + postcss-reduce-transforms "^5.1.0" + postcss-svgo "^5.1.0" + postcss-unique-selectors "^5.1.1" + +cssnano-utils@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz#95684d08c91511edfc70d2636338ca37ef3a6861" + integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA== + +cssnano@^5.1.12: + version "5.1.14" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.14.tgz#07b0af6da73641276fe5a6d45757702ebae2eb05" + integrity sha512-Oou7ihiTocbKqi0J1bB+TRJIQX5RMR3JghA8hcWSw9mjBLQ5Y3RWqEDoYG3sRNlAbCIXpqMoZGbq5KDR3vdzgw== + dependencies: + cssnano-preset-default "^5.2.13" + lilconfig "^2.0.3" + yaml "^1.10.2" + +cssnano@^5.1.8: + version "5.1.11" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.11.tgz#3bb003380718c7948ce3813493370e8946caf04b" + integrity sha512-2nx+O6LvewPo5EBtYrKc8762mMkZRk9cMGIOP4UlkmxHm7ObxH+zvsJJ+qLwPkUc4/yumL/qJkavYi9NlodWIQ== + dependencies: + cssnano-preset-default "^5.2.11" + lilconfig "^2.0.3" + yaml "^1.10.2" + +csso@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" + integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== + dependencies: + css-tree "^1.1.2" + +csstype@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" + integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== + +debug@2.6.9, debug@^2.6.0: + 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.0, debug@^4.1.1, debug@^4.2.0: + 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" + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-properties@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +del@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" + integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== + dependencies: + globby "^11.0.1" + graceful-fs "^4.2.4" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.2" + p-map "^4.0.0" + rimraf "^3.0.2" + slash "^3.0.0" + +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== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detab@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detab/-/detab-2.0.4.tgz#b927892069aff405fbb9a186fe97a44a92a94b43" + integrity sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g== + dependencies: + repeat-string "^1.5.4" + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +detect-port-alt@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" + integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== + dependencies: + address "^1.0.1" + debug "^2.6.0" + +detect-port@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.3.0.tgz#d9c40e9accadd4df5cac6a782aefd014d573d1f1" + integrity sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ== + dependencies: + address "^1.0.1" + debug "^2.6.0" + +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" + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== + +dns-packet@^5.2.2: + version "5.6.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" + integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +dom-converter@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-serializer@^1.0.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + +domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^2.5.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +domutils@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c" + integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.1" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +dot-prop@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + +duplexer@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +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== + +electron-to-chromium@^1.4.147: + version "1.4.150" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.150.tgz#89f0e12505462d5df7e56c5b91aff7e1dfdd33ec" + integrity sha512-MP3oBer0X7ZeS9GJ0H6lmkn561UxiwOIY9TTkdxVY7lI9G6GVCKfgJaHaDcakwdKxBXA4T3ybeswH/WBIN/KTA== + +electron-to-chromium@^1.4.251: + version "1.4.284" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" + integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== + +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== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +emoticon@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/emoticon/-/emoticon-3.2.0.tgz#c008ca7d7620fac742fe1bf4af8ff8fed154ae7f" + integrity sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg== + +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== + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^5.10.0: + version "5.10.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6" + integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +entities@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" + integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== + +entities@^4.2.0, entities@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.0.tgz#62915f08d67353bb4eb67e3d62641a4059aec656" + integrity sha512-/iP1rZrSEJ0DTlPiX+jbzlA3eVkY/e8L8SozroF395fIqE3TYF/Nz7YOMAawta+vLmyJ/hkGNNPcSbMADCCXbg== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-module-lexer@^0.9.0: + version "0.9.3" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" + integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== + +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-goat@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== + +escape-html@^1.0.3, 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@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +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" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +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.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== + +eta@^1.12.3: + version "1.12.3" + resolved "https://registry.yarnpkg.com/eta/-/eta-1.12.3.tgz#2982d08adfbef39f9fa50e2fbd42d7337e7338b1" + integrity sha512-qHixwbDLtekO/d51Yr4glcaUJCIjGVJyTzuqV4GPlgZo1YpgOKG+avQynErZIYrfM6JIJdtiG2Kox8tbb+DoGg== + +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== + +eval@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/eval/-/eval-0.1.8.tgz#2b903473b8cc1d1989b83a1e7923f883eb357f85" + integrity sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw== + dependencies: + "@types/node" "*" + require-like ">= 0.1.1" + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +express@^4.17.3: + version "4.19.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" + integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.2" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.6.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" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +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.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +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-url-parser@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" + integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== + dependencies: + punycode "^1.3.2" + +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" + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +fbemitter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-3.0.0.tgz#00b2a1af5411254aab416cd75f9e6289bee4bff3" + integrity sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw== + dependencies: + fbjs "^3.0.0" + +fbjs-css-vars@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" + integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== + +fbjs@^3.0.0, fbjs@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.4.tgz#e1871c6bd3083bac71ff2da868ad5067d37716c6" + integrity sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ== + dependencies: + cross-fetch "^3.1.5" + fbjs-css-vars "^1.0.0" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.30" + +feed@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/feed/-/feed-4.2.2.tgz#865783ef6ed12579e2c44bbef3c9113bc4956a7e" + integrity sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ== + dependencies: + xml-js "^1.6.11" + +file-loader@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" + integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +filesize@^8.0.6: + version "8.0.7" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8" + integrity sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ== + +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" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + 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-cache-dir@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.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" + +flux@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/flux/-/flux-4.0.3.tgz#573b504a24982c4768fdfb59d8d2ea5637d72ee7" + integrity sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw== + dependencies: + fbemitter "^3.0.0" + fbjs "^3.0.1" + +follow-redirects@^1.0.0, follow-redirects@^1.14.7: + version "1.15.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" + integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== + +fork-ts-checker-webpack-plugin@^6.5.0: + version "6.5.2" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz#4f67183f2f9eb8ba7df7177ce3cf3e75cdafb340" + integrity sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA== + dependencies: + "@babel/code-frame" "^7.8.3" + "@types/json-schema" "^7.0.5" + chalk "^4.1.0" + chokidar "^3.4.2" + cosmiconfig "^6.0.0" + deepmerge "^4.2.2" + fs-extra "^9.0.0" + glob "^7.1.6" + memfs "^3.1.2" + minimatch "^3.0.4" + schema-utils "2.7.0" + semver "^7.3.2" + tapable "^1.0.0" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fraction.js@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" + integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== + +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-extra@^10.0.0, fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-monkey@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" + integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== + +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== + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +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== + +gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" + integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +github-slugger@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e" + integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ== + +glob-parent@^5.1.2, 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.1: + 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-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.0.0, glob@^7.1.3, glob@^7.1.6: + 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" + +global-dirs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" + integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== + dependencies: + ini "2.0.0" + +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globby@^11.0.1, globby@^11.0.4, 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.1.1: + version "13.1.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.1.tgz#7c44a93869b0b7612e38f22ed532bfe37b25ea6f" + integrity sha512-XMzoDZbGZ37tufiv7g0N4F/zp3zkwdFtVbV3EHsVl1KQr4RPLfNoT068/97RPshz2J5xYNEjLKKBKaGHifBd3Q== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.2.11" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^4.0.0" + +got@^11.8.5, got@^9.6.0: + version "11.8.5" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046" + integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +gray-matter@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" + integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== + dependencies: + js-yaml "^3.13.1" + kind-of "^6.0.2" + section-matter "^1.0.0" + strip-bom-string "^1.0.0" + +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== + dependencies: + duplexer "^0.1.2" + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +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.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-symbols@^1.0.1, 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-yarn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== + +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" + +hast-to-hyperscript@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz#9b67fd188e4c81e8ad66f803855334173920218d" + integrity sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA== + dependencies: + "@types/unist" "^2.0.3" + comma-separated-tokens "^1.0.0" + property-information "^5.3.0" + space-separated-tokens "^1.0.0" + style-to-object "^0.3.0" + unist-util-is "^4.0.0" + web-namespaces "^1.0.0" + +hast-util-from-parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz#554e34abdeea25ac76f5bd950a1f0180e0b3bc2a" + integrity sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA== + dependencies: + "@types/parse5" "^5.0.0" + hastscript "^6.0.0" + property-information "^5.0.0" + vfile "^4.0.0" + vfile-location "^3.2.0" + web-namespaces "^1.0.0" + +hast-util-parse-selector@^2.0.0: + version "2.2.5" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" + integrity sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ== + +hast-util-raw@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-6.0.1.tgz#973b15930b7529a7b66984c98148b46526885977" + integrity sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig== + dependencies: + "@types/hast" "^2.0.0" + hast-util-from-parse5 "^6.0.0" + hast-util-to-parse5 "^6.0.0" + html-void-elements "^1.0.0" + parse5 "^6.0.0" + unist-util-position "^3.0.0" + vfile "^4.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hast-util-to-parse5@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" + integrity sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ== + dependencies: + hast-to-hyperscript "^9.0.0" + property-information "^5.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hastscript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" + integrity sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +history@^4.9.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" + integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== + dependencies: + "@babel/runtime" "^7.1.2" + loose-envify "^1.2.0" + resolve-pathname "^3.0.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + value-equal "^1.0.1" + +hoist-non-react-statics@^3.1.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-entities@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46" + integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== + +html-minifier-terser@^6.0.2, html-minifier-terser@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" + integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== + dependencies: + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" + +html-tags@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.2.0.tgz#dbb3518d20b726524e4dd43de397eb0a95726961" + integrity sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg== + +html-void-elements@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" + integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== + +html-webpack-plugin@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz#c3911936f57681c1f9f4d8b68c158cd9dfe52f50" + integrity sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw== + dependencies: + "@types/html-minifier-terser" "^6.0.0" + html-minifier-terser "^6.0.2" + lodash "^4.17.21" + pretty-error "^4.0.0" + tapable "^2.0.0" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +htmlparser2@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010" + integrity sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + domutils "^3.0.1" + entities "^4.3.0" + +http-cache-semantics@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + +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" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.6.tgz#2e02406ab2df8af8a7abfba62e0da01c62b95afd" + integrity sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA== + +http-proxy-middleware@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +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" + +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + +image-size@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.1.tgz#86d6cfc2b1d19eab5d2b368d4b9194d9e48541c5" + integrity sha512-VAwkvNSNGClRw9mDHhc5Efax8PLlsOGcUTh0T/LIriC8vPA3U5PdqXWqkz406MoYHMKW8Uf9gWr05T/rYB44kQ== + dependencies: + queue "6.0.2" + +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + +immer@^9.0.7: + version "9.0.14" + resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.14.tgz#e05b83b63999d26382bb71676c9d827831248a48" + integrity sha512-ubBeqQutOSLIFCUBN03jGeOS6a3DoYlSYwYJTa+gSKEZKU5redJIqkIdZ3JVv/4RZpfcXdAWH5zCNLWPRv2WDw== + +import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0: + 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" + +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + integrity sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A== + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +infima@0.2.0-alpha.42: + version "0.2.0-alpha.42" + resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.42.tgz#f6e86a655ad40877c6b4d11b2ede681eb5470aa5" + integrity sha512-ift8OXNbQQwtbIt6z16KnSWP7uJ/SysSMFI4F87MNRTicypfl4Pv3E2OGVv6N3nSZFJvA8imYulCBS64iyHYww== + +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, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + +ini@^1.3.5, ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +inline-style-parser@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" + integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== + +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +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== + +ipaddr.js@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" + integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== + +is-alphabetical@1.0.4, is-alphabetical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-core-module@^2.8.1: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== + dependencies: + has "^1.0.3" + +is-decimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extendable@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +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.1, is-glob@^4.0.3, is-glob@~4.0.1: + 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-hexadecimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + +is-installed-globally@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + +is-npm@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" + integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== + +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-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg== + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-path-cwd@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-inside@^3.0.2: + 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== + +is-plain-obj@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA== + +is-root@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" + integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-whitespace-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" + integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== + +is-word-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" + integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +is-yarn-global@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-worker@^27.4.5, jest-worker@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +joi@^17.6.0: + version "17.6.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2" + integrity sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.3" + "@sideway/formula" "^3.0.0" + "@sideway/pinpoint" "^2.0.0" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +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" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +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-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json5@^2.1.2, json5@^2.2.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +keyv@*, keyv@^4.0.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56" + integrity sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g== + dependencies: + json-buffer "3.0.1" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c" + integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== + dependencies: + graceful-fs "^4.1.11" + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +klona@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" + integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== + +latest-version@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== + dependencies: + package-json "^6.3.0" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +lilconfig@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.5.tgz#19e57fd06ccc3848fd1891655b5a447092225b25" + integrity sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +loader-utils@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +loader-utils@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.0.tgz#bcecc51a7898bee7473d4bc6b845b23af8304d4f" + integrity sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ== + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.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.curry@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170" + integrity sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA== + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lodash.flow@^3.3.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a" + integrity sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw== + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.uniq@4.5.0, lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +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" + +lunr-languages@^1.4.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/lunr-languages/-/lunr-languages-1.9.0.tgz#7105230807788a112a69910561b7bbd055a0e588" + integrity sha512-Be5vFuc8NAheOIjviCRms3ZqFFBlzns3u9DXpPSZvALetgnydAN0poV71pVLFn0keYy/s4VblMMkqewTLe+KPg== + +lunr@^2.3.9: + version "2.3.9" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" + integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== + +make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +mark.js@^8.11.1: + version "8.11.1" + resolved "https://registry.yarnpkg.com/mark.js/-/mark.js-8.11.1.tgz#180f1f9ebef8b0e638e4166ad52db879beb2ffc5" + integrity sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ== + +markdown-escapes@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" + integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== + +mdast-squeeze-paragraphs@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97" + integrity sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ== + dependencies: + unist-util-remove "^2.0.0" + +mdast-util-definitions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" + integrity sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ== + dependencies: + unist-util-visit "^2.0.0" + +mdast-util-to-hast@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz#0cfc82089494c52d46eb0e3edb7a4eb2aea021eb" + integrity sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + mdast-util-definitions "^4.0.0" + mdurl "^1.0.0" + unist-builder "^2.0.0" + unist-util-generated "^1.0.0" + unist-util-position "^3.0.0" + unist-util-visit "^2.0.0" + +mdast-util-to-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" + integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w== + +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + +mdurl@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== + +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== + +memfs@^3.1.2, memfs@^3.4.3: + version "3.4.4" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.4.tgz#e8973cd8060548916adcca58a248e7805c715e89" + integrity sha512-W4gHNUE++1oSJVn8Y68jPXi+mkx3fXR5ITE/Ubz6EQ3xRpCN5k2CQ4AUR8094Z7211F876TyoBACGsIveqgiGA== + dependencies: + fs-monkey "1.0.3" + +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== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +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.2, micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== + +mime-types@2.1.18: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== + dependencies: + mime-db "~1.33.0" + +mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, 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== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +mini-create-react-context@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e" + integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ== + dependencies: + "@babel/runtime" "^7.12.1" + tiny-warning "^1.0.3" + +mini-css-extract-plugin@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz#9a1251d15f2035c342d99a468ab9da7a0451b71e" + integrity sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg== + dependencies: + schema-utils "^4.0.0" + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: + 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" + +minimist@^1.2.0, minimist@^1.2.5: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + +mrmime@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27" + integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw== + +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== + +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + +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== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-emoji@^1.10.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" + integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A== + dependencies: + lodash "^4.17.21" + +node-fetch@2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + +node-forge@^1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-releases@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666" + integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== + +node-releases@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" + integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +nprogress@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" + integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA== + +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +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== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +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-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, once@^1.3.1, once@^1.4.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" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +open@^8.0.9, open@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" + integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +opener@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== + +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +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@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.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" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-retry@^4.5.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +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" + +parse-entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" + integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse-numeric-range@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz#7c63b61190d61e4d53a1197f0c83c47bb670ffa3" + integrity sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ== + +parse5-htmlparser2-tree-adapter@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz#23c2cc233bcf09bb7beba8b8a69d46b08c62c2f1" + integrity sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g== + dependencies: + domhandler "^5.0.2" + parse5 "^7.0.0" + +parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parse5@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.0.0.tgz#51f74a5257f5fcc536389e8c2d0b3802e1bfa91a" + integrity sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g== + dependencies: + entities "^4.3.0" + +parseurl@~1.3.2, 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== + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + +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-is-inside@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== + +path-key@^3.0.0, 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-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +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-to-regexp@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" + integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== + +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + +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== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-up@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.0" + +postcss-calc@^8.2.3: + version "8.2.4" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5" + integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q== + dependencies: + postcss-selector-parser "^6.0.9" + postcss-value-parser "^4.2.0" + +postcss-colormin@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.0.tgz#3cee9e5ca62b2c27e84fce63affc0cfb5901956a" + integrity sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg== + dependencies: + browserslist "^4.16.6" + caniuse-api "^3.0.0" + colord "^2.9.1" + postcss-value-parser "^4.2.0" + +postcss-convert-values@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz#31586df4e184c2e8890e8b34a0b9355313f503ab" + integrity sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g== + dependencies: + browserslist "^4.20.3" + postcss-value-parser "^4.2.0" + +postcss-convert-values@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz#04998bb9ba6b65aa31035d669a6af342c5f9d393" + integrity sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA== + dependencies: + browserslist "^4.21.4" + postcss-value-parser "^4.2.0" + +postcss-discard-comments@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz#8df5e81d2925af2780075840c1526f0660e53696" + integrity sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ== + +postcss-discard-duplicates@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz#9eb4fe8456706a4eebd6d3b7b777d07bad03e848" + integrity sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw== + +postcss-discard-empty@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz#e57762343ff7f503fe53fca553d18d7f0c369c6c" + integrity sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A== + +postcss-discard-overridden@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz#7e8c5b53325747e9d90131bb88635282fb4a276e" + integrity sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw== + +postcss-discard-unused@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz#8974e9b143d887677304e558c1166d3762501142" + integrity sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw== + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-loader@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.0.0.tgz#367d10eb1c5f1d93700e6b399683a6dc7c3af396" + integrity sha512-IDyttebFzTSY6DI24KuHUcBjbAev1i+RyICoPEWcAstZsj03r533uMXtDn506l6/wlsRYiS5XBdx7TpccCsyUg== + dependencies: + cosmiconfig "^7.0.0" + klona "^2.0.5" + semver "^7.3.7" + +postcss-merge-idents@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz#7753817c2e0b75d0853b56f78a89771e15ca04a1" + integrity sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw== + dependencies: + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-merge-longhand@^5.1.5: + version "5.1.5" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.5.tgz#b0e03bee3b964336f5f33c4fc8eacae608e91c05" + integrity sha512-NOG1grw9wIO+60arKa2YYsrbgvP6tp+jqc7+ZD5/MalIw234ooH2C6KlR6FEn4yle7GqZoBxSK1mLBE9KPur6w== + dependencies: + postcss-value-parser "^4.2.0" + stylehacks "^5.1.0" + +postcss-merge-longhand@^5.1.7: + version "5.1.7" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz#24a1bdf402d9ef0e70f568f39bdc0344d568fb16" + integrity sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ== + dependencies: + postcss-value-parser "^4.2.0" + stylehacks "^5.1.1" + +postcss-merge-rules@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz#7049a14d4211045412116d79b751def4484473a5" + integrity sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ== + dependencies: + browserslist "^4.16.6" + caniuse-api "^3.0.0" + cssnano-utils "^3.1.0" + postcss-selector-parser "^6.0.5" + +postcss-merge-rules@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.3.tgz#8f97679e67cc8d08677a6519afca41edf2220894" + integrity sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA== + dependencies: + browserslist "^4.21.4" + caniuse-api "^3.0.0" + cssnano-utils "^3.1.0" + postcss-selector-parser "^6.0.5" + +postcss-minify-font-values@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz#f1df0014a726083d260d3bd85d7385fb89d1f01b" + integrity sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-minify-gradients@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz#f1fe1b4f498134a5068240c2f25d46fcd236ba2c" + integrity sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw== + dependencies: + colord "^2.9.1" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz#ac41a6465be2db735099bbd1798d85079a6dc1f9" + integrity sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg== + dependencies: + browserslist "^4.16.6" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz#c06a6c787128b3208b38c9364cfc40c8aa5d7352" + integrity sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw== + dependencies: + browserslist "^4.21.4" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-minify-selectors@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz#d4e7e6b46147b8117ea9325a915a801d5fe656c6" + integrity sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg== + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== + +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-normalize-charset@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz#9302de0b29094b52c259e9b2cf8dc0879879f0ed" + integrity sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg== + +postcss-normalize-display-values@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz#72abbae58081960e9edd7200fcf21ab8325c3da8" + integrity sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-positions@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.0.tgz#902a7cb97cf0b9e8b1b654d4a43d451e48966458" + integrity sha512-8gmItgA4H5xiUxgN/3TVvXRoJxkAWLW6f/KKhdsH03atg0cB8ilXnrB5PpSshwVu/dD2ZsRFQcR1OEmSBDAgcQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-positions@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz#ef97279d894087b59325b45c47f1e863daefbb92" + integrity sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-repeat-style@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.0.tgz#f6d6fd5a54f51a741cc84a37f7459e60ef7a6398" + integrity sha512-IR3uBjc+7mcWGL6CtniKNQ4Rr5fTxwkaDHwMBDGGs1x9IVRkYIT/M4NelZWkAOBdV6v3Z9S46zqaKGlyzHSchw== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-repeat-style@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz#e9eb96805204f4766df66fd09ed2e13545420fb2" + integrity sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-string@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz#411961169e07308c82c1f8c55f3e8a337757e228" + integrity sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-timing-functions@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz#d5614410f8f0b2388e9f240aa6011ba6f52dafbb" + integrity sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz#3d23aede35e160089a285e27bf715de11dc9db75" + integrity sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ== + dependencies: + browserslist "^4.16.6" + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz#f67297fca3fea7f17e0d2caa40769afc487aa030" + integrity sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA== + dependencies: + browserslist "^4.21.4" + postcss-value-parser "^4.2.0" + +postcss-normalize-url@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz#ed9d88ca82e21abef99f743457d3729a042adcdc" + integrity sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew== + dependencies: + normalize-url "^6.0.1" + postcss-value-parser "^4.2.0" + +postcss-normalize-whitespace@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz#08a1a0d1ffa17a7cc6efe1e6c9da969cc4493cfa" + integrity sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-ordered-values@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.1.2.tgz#daffacd4abf327d52d5ac570b59dfbcf4b836614" + integrity sha512-wr2avRbW4HS2XE2ZCqpfp4N/tDC6GZKZ+SVP8UBTOVS8QWrc4TD8MYrebJrvVVlGPKszmiSCzue43NDiVtgDmg== + dependencies: + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-ordered-values@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz#b6fd2bd10f937b23d86bc829c69e7732ce76ea38" + integrity sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ== + dependencies: + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-reduce-idents@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz#c89c11336c432ac4b28792f24778859a67dfba95" + integrity sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-reduce-initial@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz#fc31659ea6e85c492fb2a7b545370c215822c5d6" + integrity sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw== + dependencies: + browserslist "^4.16.6" + caniuse-api "^3.0.0" + +postcss-reduce-initial@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.1.tgz#c18b7dfb88aee24b1f8e4936541c29adbd35224e" + integrity sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w== + dependencies: + browserslist "^4.21.4" + caniuse-api "^3.0.0" + +postcss-reduce-transforms@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz#333b70e7758b802f3dd0ddfe98bb1ccfef96b6e9" + integrity sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: + version "6.0.10" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" + integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-sort-media-queries@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/postcss-sort-media-queries/-/postcss-sort-media-queries-4.2.1.tgz#a99bae69ef1098ee3b64a5fa94d258ec240d0355" + integrity sha512-9VYekQalFZ3sdgcTjXMa0dDjsfBVHXlraYJEMiOJ/2iMmI2JGCMavP16z3kWOaRu8NSaJCTgVpB/IVpH5yT9YQ== + dependencies: + sort-css-media-queries "2.0.4" + +postcss-svgo@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d" + integrity sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA== + dependencies: + postcss-value-parser "^4.2.0" + svgo "^2.7.0" + +postcss-unique-selectors@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz#a9f273d1eacd09e9aa6088f4b0507b18b1b541b6" + integrity sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA== + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss-zindex@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-5.1.0.tgz#4a5c7e5ff1050bd4c01d95b1847dfdcc58a496ff" + integrity sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A== + +postcss@^8.3.11, postcss@^8.4.13, postcss@^8.4.14, postcss@^8.4.7: + version "8.4.31" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" + integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +pretty-error@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" + integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== + dependencies: + lodash "^4.17.20" + renderkid "^3.0.0" + +pretty-time@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" + integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== + +prism-react-renderer@^1.3.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.3.3.tgz#9b5a4211a6756eee3c96fee9a05733abc0b0805c" + integrity sha512-Viur/7tBTCH2HmYzwCHmt2rEFn+rdIWNIINXyg0StiISbDiIhHKhrFuEK8eMkKgvsIYSjgGqy/hNyucHp6FpoQ== + +prism-react-renderer@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz#786bb69aa6f73c32ba1ee813fbe17a0115435085" + integrity sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg== + +prismjs@^1.28.0: + version "1.28.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.28.0.tgz#0d8f561fa0f7cf6ebca901747828b149147044b6" + integrity sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + +prompts@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +prop-types@^15.6.2, prop-types@^15.7.2: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +property-information@^5.0.0, property-information@^5.3.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" + integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA== + dependencies: + xtend "^4.0.0" + +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" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +pupa@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== + dependencies: + escape-goat "^2.0.0" + +pure-color@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e" + integrity sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA== + +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== + +queue@6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" + integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== + dependencies: + inherits "~2.0.3" + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== + +range-parser@^1.2.1, 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.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-base16-styling@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.6.0.tgz#ef2156d66cf4139695c8a167886cb69ea660792c" + integrity sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ== + dependencies: + base16 "^1.0.0" + lodash.curry "^4.0.1" + lodash.flow "^3.3.0" + pure-color "^1.2.0" + +react-dev-utils@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.1.tgz#ba92edb4a1f379bd46ccd6bcd4e7bc398df33e73" + integrity sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ== + dependencies: + "@babel/code-frame" "^7.16.0" + address "^1.1.2" + browserslist "^4.18.1" + chalk "^4.1.2" + cross-spawn "^7.0.3" + detect-port-alt "^1.1.6" + escape-string-regexp "^4.0.0" + filesize "^8.0.6" + find-up "^5.0.0" + fork-ts-checker-webpack-plugin "^6.5.0" + global-modules "^2.0.0" + globby "^11.0.4" + gzip-size "^6.0.0" + immer "^9.0.7" + is-root "^2.1.0" + loader-utils "^3.2.0" + open "^8.4.0" + pkg-up "^3.1.0" + prompts "^2.4.2" + react-error-overlay "^6.0.11" + recursive-readdir "^2.2.2" + shell-quote "^1.7.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +react-dom@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" + integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler "^0.20.2" + +react-error-overlay@^6.0.11: + version "6.0.11" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" + integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== + +react-fast-compare@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" + integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== + +react-helmet-async@*, react-helmet-async@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.3.0.tgz#7bd5bf8c5c69ea9f02f6083f14ce33ef545c222e" + integrity sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg== + dependencies: + "@babel/runtime" "^7.12.5" + invariant "^2.2.4" + prop-types "^15.7.2" + react-fast-compare "^3.2.0" + shallowequal "^1.1.0" + +react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-json-view@^1.21.3: + version "1.21.3" + resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.21.3.tgz#f184209ee8f1bf374fb0c41b0813cff54549c475" + integrity sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw== + dependencies: + flux "^4.0.1" + react-base16-styling "^0.6.0" + react-lifecycles-compat "^3.0.4" + react-textarea-autosize "^8.3.2" + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-loadable-ssr-addon-v5-slorber@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz#2cdc91e8a744ffdf9e3556caabeb6e4278689883" + integrity sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A== + dependencies: + "@babel/runtime" "^7.10.3" + +"react-loadable@npm:@docusaurus/react-loadable@5.5.2": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" + integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== + dependencies: + "@types/react" "*" + prop-types "^15.6.2" + +react-router-config@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/react-router-config/-/react-router-config-5.1.1.tgz#0f4263d1a80c6b2dc7b9c1902c9526478194a988" + integrity sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg== + dependencies: + "@babel/runtime" "^7.1.2" + +react-router-dom@^5.3.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.3.tgz#8779fc28e6691d07afcaf98406d3812fe6f11199" + integrity sha512-Ov0tGPMBgqmbu5CDmN++tv2HQ9HlWDuWIIqn4b88gjlAN5IHI+4ZUZRcpz9Hl0azFIwihbLDYw1OiHGRo7ZIng== + dependencies: + "@babel/runtime" "^7.12.13" + history "^4.9.0" + loose-envify "^1.3.1" + prop-types "^15.6.2" + react-router "5.3.3" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-router@5.3.3, react-router@^5.3.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.3.3.tgz#8e3841f4089e728cf82a429d92cdcaa5e4a3a288" + integrity sha512-mzQGUvS3bM84TnbtMYR8ZjKnuPJ71IjSzR+DE6UkUqvN4czWIqEs17yLL8xkAycv4ev0AiN+IGrWu88vJs/p2w== + dependencies: + "@babel/runtime" "^7.12.13" + history "^4.9.0" + hoist-non-react-statics "^3.1.0" + loose-envify "^1.3.1" + mini-create-react-context "^0.4.0" + path-to-regexp "^1.7.0" + prop-types "^15.6.2" + react-is "^16.6.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-textarea-autosize@^8.3.2: + version "8.3.4" + resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.3.4.tgz#270a343de7ad350534141b02c9cb78903e553524" + integrity sha512-CdtmP8Dc19xL8/R6sWvtknD/eCXkQr30dtvC4VmGInhRsfF8X/ihXCq6+9l9qbxmKRiq407/7z5fxE7cVWQNgQ== + dependencies: + "@babel/runtime" "^7.10.2" + use-composed-ref "^1.3.0" + use-latest "^1.2.1" + +react@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" + integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +readable-stream@^2.0.1: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +reading-time@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/reading-time/-/reading-time-1.5.0.tgz#d2a7f1b6057cb2e169beaf87113cc3411b5bc5bb" + integrity sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg== + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== + dependencies: + resolve "^1.1.6" + +recursive-readdir@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372" + integrity sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA== + dependencies: + minimatch "^3.0.5" + +regenerate-unicode-properties@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" + integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== + dependencies: + regenerate "^1.4.2" + +regenerate-unicode-properties@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" + integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.13.10: + version "0.13.10" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee" + integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw== + +regenerator-runtime@^0.13.4: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + +regenerator-transform@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" + integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== + dependencies: + "@babel/runtime" "^7.8.4" + +regexpu-core@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3" + integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties "^10.0.1" + regjsgen "^0.6.0" + regjsparser "^0.8.2" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.0.0" + +regexpu-core@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.1.tgz#a69c26f324c1e962e9ffd0b88b055caba8089139" + integrity sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsgen "^0.7.1" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.0.0" + +registry-auth-token@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" + integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== + dependencies: + rc "^1.2.8" + +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== + dependencies: + rc "^1.2.8" + +regjsgen@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" + integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== + +regjsgen@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.7.1.tgz#ee5ef30e18d3f09b7c369b76e7c2373ed25546f6" + integrity sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA== + +regjsparser@^0.8.2: + version "0.8.4" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" + integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== + dependencies: + jsesc "~0.5.0" + +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== + dependencies: + jsesc "~0.5.0" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + +remark-emoji@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/remark-emoji/-/remark-emoji-2.2.0.tgz#1c702090a1525da5b80e15a8f963ef2c8236cac7" + integrity sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w== + dependencies: + emoticon "^3.2.0" + node-emoji "^1.10.0" + unist-util-visit "^2.0.3" + +remark-footnotes@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/remark-footnotes/-/remark-footnotes-2.0.0.tgz#9001c4c2ffebba55695d2dd80ffb8b82f7e6303f" + integrity sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ== + +remark-mdx@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-1.6.22.tgz#06a8dab07dcfdd57f3373af7f86bd0e992108bbd" + integrity sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ== + dependencies: + "@babel/core" "7.12.9" + "@babel/helper-plugin-utils" "7.10.4" + "@babel/plugin-proposal-object-rest-spread" "7.12.1" + "@babel/plugin-syntax-jsx" "7.12.1" + "@mdx-js/util" "1.6.22" + is-alphabetical "1.0.4" + remark-parse "8.0.3" + unified "9.2.0" + +remark-parse@8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-8.0.3.tgz#9c62aa3b35b79a486454c690472906075f40c7e1" + integrity sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q== + dependencies: + ccount "^1.0.0" + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^2.0.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^2.0.0" + vfile-location "^3.0.0" + xtend "^4.0.1" + +remark-squeeze-paragraphs@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz#76eb0e085295131c84748c8e43810159c5653ead" + integrity sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw== + dependencies: + mdast-squeeze-paragraphs "^4.0.0" + +renderkid@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" + integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^6.0.1" + +repeat-string@^1.5.4: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +"require-like@>= 0.1.1": + version "0.1.2" + resolved "https://registry.yarnpkg.com/require-like/-/require-like-0.1.2.tgz#ad6f30c13becd797010c468afa775c0c0a6b47fa" + integrity sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-alpn@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + +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== + +resolve-pathname@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" + integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== + +resolve@^1.1.6, resolve@^1.14.2, resolve@^1.3.2: + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== + dependencies: + is-core-module "^2.8.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== + dependencies: + lowercase-keys "^2.0.0" + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +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" + +rtl-detect@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.0.4.tgz#40ae0ea7302a150b96bc75af7d749607392ecac6" + integrity sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ== + +rtlcss@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/rtlcss/-/rtlcss-3.5.0.tgz#c9eb91269827a102bac7ae3115dd5d049de636c3" + integrity sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A== + dependencies: + find-up "^5.0.0" + picocolors "^1.0.0" + postcss "^8.3.11" + strip-json-comments "^3.1.1" + +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" + +rxjs@^7.5.4: + version "7.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" + integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== + dependencies: + tslib "^2.1.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + 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, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + 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== + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +scheduler@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" + integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + +schema-utils@^2.6.5: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" + +schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" + integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" + integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.8.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.0.0" + +section-matter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" + integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== + dependencies: + extend-shallow "^2.0.1" + kind-of "^6.0.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +selfsigned@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.1.1.tgz#18a7613d714c0cd3385c48af0075abf3f266af61" + integrity sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ== + dependencies: + node-forge "^1" + +semver-diff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== + dependencies: + semver "^6.3.0" + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@^5.4.1: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + 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" + +serialize-javascript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +serve-handler@^6.1.3: + version "6.1.5" + resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.5.tgz#a4a0964f5c55c7e37a02a633232b6f0d6f068375" + integrity sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg== + dependencies: + bytes "3.0.0" + content-disposition "0.5.2" + fast-url-parser "1.1.3" + mime-types "2.1.18" + minimatch "3.1.2" + path-is-inside "1.0.2" + path-to-regexp "2.2.1" + range-parser "1.2.0" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +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" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shallowequal@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" + integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== + +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== + +shell-quote@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" + integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== + +shelljs@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +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" + +signal-exit@^3.0.2, signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +sirv@^1.0.7: + version "1.0.19" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" + integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== + dependencies: + "@polka/url" "^1.0.0-next.20" + mrmime "^1.0.0" + totalist "^1.0.0" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +sitemap@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/sitemap/-/sitemap-7.1.1.tgz#eeed9ad6d95499161a3eadc60f8c6dce4bea2bef" + integrity sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg== + dependencies: + "@types/node" "^17.0.5" + "@types/sax" "^1.2.1" + arg "^5.0.0" + sax "^1.2.4" + +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== + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +sort-css-media-queries@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.0.4.tgz#b2badfa519cb4a938acbc6d3aaa913d4949dc908" + integrity sha512-PAIsEK/XupCQwitjv7XxoMvYhT7EAfyzI3hsy/MyDgTvc+Ft55ctdkctJLOy6cQejaIC+zjpUL4djFVm2ivOOw== + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.5.0: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +space-separated-tokens@^1.0.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" + integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +state-toggle@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" + integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== + +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== + +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +std-env@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.1.1.tgz#1f19c4d3f6278c52efd08a94574a2a8d32b7d092" + integrity sha512-/c645XdExBypL01TpFKiG/3RAa/Qmu+zRi0MwAmrdEkwHNuN0ebo8ccAXBBDa5Z0QOJgBskUIbuCK91x0sCVEw== + +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: + 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" + +string-width@^5.0.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +stringify-object@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +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-ansi@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" + integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== + dependencies: + ansi-regex "^6.0.1" + +strip-bom-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" + integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +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== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +style-to-object@0.3.0, style-to-object@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" + integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA== + dependencies: + inline-style-parser "0.1.1" + +stylehacks@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.0.tgz#a40066490ca0caca04e96c6b02153ddc39913520" + integrity sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q== + dependencies: + browserslist "^4.16.6" + postcss-selector-parser "^6.0.4" + +stylehacks@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.1.tgz#7934a34eb59d7152149fa69d6e9e56f2fc34bcc9" + integrity sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw== + dependencies: + browserslist "^4.21.4" + postcss-selector-parser "^6.0.4" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +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" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svg-parser@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" + integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== + +svgo@^2.5.0, svgo@^2.7.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" + integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^4.1.3" + css-tree "^1.1.3" + csso "^4.2.0" + picocolors "^1.0.0" + stable "^0.1.8" + +tapable@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.1.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz#8033db876dd5875487213e87c627bca323e5ed90" + integrity sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ== + dependencies: + "@jridgewell/trace-mapping" "^0.3.7" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.0" + terser "^5.7.2" + +terser-webpack-plugin@^5.3.3: + version "5.3.6" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz#5590aec31aa3c6f771ce1b1acca60639eab3195c" + integrity sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ== + dependencies: + "@jridgewell/trace-mapping" "^0.3.14" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.0" + terser "^5.14.1" + +terser@^5.10.0, terser@^5.7.2: + version "5.14.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10" + integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA== + dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + commander "^2.20.0" + source-map-support "~0.5.20" + +terser@^5.14.1: + version "5.15.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.15.1.tgz#8561af6e0fd6d839669c73b92bdd5777d870ed6c" + integrity sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw== + dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + commander "^2.20.0" + source-map-support "~0.5.20" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +tiny-invariant@^1.0.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" + integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== + +tiny-warning@^1.0.0, tiny-warning@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +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== + +totalist@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" + integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + +trim-trailing-lines@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" + integrity sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ== + +trim@0.0.1, trim@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.3.tgz#05243a47a3a4113e6b49367880a9cca59697a20b" + integrity sha512-h82ywcYhHK7veeelXrCScdH7HkWfbIT1D/CgYO+nmDarz3SGNssVBMws6jU16Ga60AJCRAvPV6w6RLuNerQqjg== + +trough@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" + integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== + +tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + +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-fest@^2.5.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.13.0.tgz#d1ecee38af29eb2e863b22299a3d68ef30d2abfb" + integrity sha512-lPfAm42MxE4/456+QyIaaVBAwgpJb6xZ8PRu09utnhPdWwcyj9vgy6Sq0Z5yNbJ21EdxB5dRU/Qg8bsyAMtlcw== + +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" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +ua-parser-js@^0.7.30: + version "0.7.33" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532" + integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw== + +unherit@^1.0.4: + version "1.1.3" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" + integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== + dependencies: + inherits "^2.0.0" + xtend "^4.0.0" + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" + integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" + integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== + +unified@9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.0.tgz#67a62c627c40589edebbf60f53edfd4d822027f8" + integrity sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg== + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-buffer "^2.0.0" + is-plain-obj "^2.0.0" + trough "^1.0.0" + vfile "^4.0.0" + +unified@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.2.tgz#67649a1abfc3ab85d2969502902775eb03146975" + integrity sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ== + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-buffer "^2.0.0" + is-plain-obj "^2.0.0" + trough "^1.0.0" + vfile "^4.0.0" + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + +unist-builder@2.0.3, unist-builder@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" + integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== + +unist-util-generated@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" + integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== + +unist-util-is@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" + integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== + +unist-util-position@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" + integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== + +unist-util-remove-position@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" + integrity sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA== + dependencies: + unist-util-visit "^2.0.0" + +unist-util-remove@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-2.1.0.tgz#b0b4738aa7ee445c402fda9328d604a02d010588" + integrity sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q== + dependencies: + unist-util-is "^4.0.0" + +unist-util-stringify-position@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" + integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== + dependencies: + "@types/unist" "^2.0.2" + +unist-util-visit-parents@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" + integrity sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + +unist-util-visit@2.0.3, unist-util-visit@^2.0.0, unist-util-visit@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" + integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + unist-util-visit-parents "^3.0.0" + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +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 sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +update-browserslist-db@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +update-notifier@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" + integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== + dependencies: + boxen "^5.0.0" + chalk "^4.1.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.4.0" + is-npm "^5.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.1.0" + pupa "^2.1.1" + semver "^7.3.4" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + +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" + +url-loader@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" + integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== + dependencies: + loader-utils "^2.0.0" + mime-types "^2.1.27" + schema-utils "^3.0.0" + +use-composed-ref@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.3.0.tgz#3d8104db34b7b264030a9d916c5e94fbe280dbda" + integrity sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ== + +use-isomorphic-layout-effect@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb" + integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA== + +use-latest@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.2.1.tgz#d13dfb4b08c28e3e33991546a2cee53e14038cf2" + integrity sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw== + dependencies: + use-isomorphic-layout-effect "^1.1.1" + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= + +utility-types@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" + integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +value-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" + integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +vfile-location@^3.0.0, vfile-location@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.2.0.tgz#d8e41fbcbd406063669ebf6c33d56ae8721d0f3c" + integrity sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA== + +vfile-message@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" + integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position "^2.0.0" + +vfile@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" + integrity sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA== + dependencies: + "@types/unist" "^2.0.0" + is-buffer "^2.0.0" + unist-util-stringify-position "^2.0.0" + vfile-message "^2.0.0" + +wait-on@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.1.tgz#16bbc4d1e4ebdd41c5b4e63a2e16dbd1f4e5601e" + integrity sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw== + dependencies: + axios "^0.25.0" + joi "^17.6.0" + lodash "^4.17.21" + minimist "^1.2.5" + rxjs "^7.5.4" + +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +web-namespaces@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" + integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + +webpack-bundle-analyzer@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5" + integrity sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ== + dependencies: + acorn "^8.0.4" + acorn-walk "^8.0.0" + chalk "^4.1.0" + commander "^7.2.0" + gzip-size "^6.0.0" + lodash "^4.17.20" + opener "^1.5.2" + sirv "^1.0.7" + ws "^7.3.1" + +webpack-dev-middleware@^5.3.1: + version "5.3.3" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" + integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== + dependencies: + colorette "^2.0.10" + memfs "^3.4.3" + mime-types "^2.1.31" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@^4.9.3: + version "4.11.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz#ae07f0d71ca0438cf88446f09029b92ce81380b5" + integrity sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw== + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" + "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.5.1" + ansi-html-community "^0.0.8" + bonjour-service "^1.0.11" + chokidar "^3.5.3" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.3.2" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.0.1" + open "^8.0.9" + p-retry "^4.5.0" + rimraf "^3.0.2" + schema-utils "^4.0.0" + selfsigned "^2.1.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^5.3.1" + ws "^8.4.2" + +webpack-merge@^5.8.0: + version "5.8.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" + integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +webpack-sources@^3.2.2, webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.73.0: + version "5.76.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.1.tgz#7773de017e988bccb0f13c7d75ec245f377d295c" + integrity sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^0.0.51" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + acorn "^8.7.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.10.0" + es-module-lexer "^0.9.0" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.3" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +webpackbar@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-5.0.2.tgz#d3dd466211c73852741dfc842b7556dcbc2b0570" + integrity sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ== + dependencies: + chalk "^4.1.0" + consola "^2.15.3" + pretty-time "^1.1.0" + std-env "^3.0.1" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +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" + +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + +widest-line@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-4.0.1.tgz#a0fc673aaba1ea6f0a0d35b3c2795c9a9cc2ebf2" + integrity sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig== + dependencies: + string-width "^5.0.1" + +wildcard@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" + integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + +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" + +wrap-ansi@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.0.1.tgz#2101e861777fec527d0ea90c57c6b03aac56a5b3" + integrity sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +ws@^7.3.1: + version "7.5.8" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.8.tgz#ac2729881ab9e7cbaf8787fe3469a48c5c7f636a" + integrity sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw== + +ws@^8.4.2: + version "8.7.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.7.0.tgz#eaf9d874b433aa00c0e0d8752532444875db3957" + integrity sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg== + +xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== + +xml-js@^1.6.11: + version "1.6.11" + resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" + integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== + dependencies: + sax "^1.2.4" + +xtend@^4.0.0, xtend@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +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== + +yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +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== + +zwitch@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" + integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== diff --git a/lib/polyamorous/activerecord/join_association.rb b/lib/polyamorous/activerecord/join_association.rb new file mode 100644 index 000000000..42eaf47a8 --- /dev/null +++ b/lib/polyamorous/activerecord/join_association.rb @@ -0,0 +1,70 @@ +module Polyamorous + module JoinAssociationExtensions + include SwappingReflectionClass + def self.prepended(base) + base.class_eval { attr_reader :join_type } + end + + def initialize(reflection, children, polymorphic_class = nil, join_type = Arel::Nodes::InnerJoin) + @join_type = join_type + if polymorphic_class && ::ActiveRecord::Base > polymorphic_class + swapping_reflection_klass(reflection, polymorphic_class) do |reflection| + super(reflection, children) + self.reflection.options[:polymorphic] = true + end + else + super(reflection, children) + end + end + + # Same as #join_constraints, but instead of constructing tables from the + # given block, uses the ones passed + def join_constraints_with_tables(foreign_table, foreign_klass, join_type, alias_tracker, tables) + joins = [] + chain = [] + + reflection.chain.each.with_index do |reflection, i| + table = tables[i] + + @table ||= table + chain << [reflection, table] + end + + # The chain starts with the target table, but we want to end with it here (makes + # more sense in this context), so we reverse + chain.reverse_each do |reflection, table| + klass = reflection.klass + + join_scope = reflection.join_scope(table, foreign_table, foreign_klass) + + unless join_scope.references_values.empty? + join_dependency = join_scope.construct_join_dependency( + join_scope.eager_load_values | join_scope.includes_values, Arel::Nodes::OuterJoin + ) + join_scope.joins!(join_dependency) + end + + arel = join_scope.arel(alias_tracker.aliases) + nodes = arel.constraints.first + + if nodes.is_a?(Arel::Nodes::And) + others = nodes.children.extract! do |node| + !Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name } + end + end + + joins << table.create_join(table, table.create_on(nodes), join_type) + + if others && !others.empty? + joins.concat arel.join_sources + append_constraints(joins.last, others) + end + + # The current table in this iteration becomes the foreign table in the next + foreign_table, foreign_klass = table, klass + end + + joins + end + end +end diff --git a/lib/polyamorous/activerecord/join_association_7_2.rb b/lib/polyamorous/activerecord/join_association_7_2.rb new file mode 100644 index 000000000..7cfa82fd1 --- /dev/null +++ b/lib/polyamorous/activerecord/join_association_7_2.rb @@ -0,0 +1,55 @@ +module Polyamorous + module JoinAssociationExtensions + # Same as #join_constraints, but instead of constructing tables from the + # given block, uses the ones passed + def join_constraints_with_tables(foreign_table, foreign_klass, join_type, alias_tracker, tables) + joins = [] + chain = [] + + reflection.chain.each.with_index do |reflection, i| + table = tables[i] + + @table ||= table + chain << [reflection, table] + end + + base_klass.with_connection do |connection| + # The chain starts with the target table, but we want to end with it here (makes + # more sense in this context), so we reverse + chain.reverse_each do |reflection, table| + klass = reflection.klass + + join_scope = reflection.join_scope(table, foreign_table, foreign_klass) + + unless join_scope.references_values.empty? + join_dependency = join_scope.construct_join_dependency( + join_scope.eager_load_values | join_scope.includes_values, Arel::Nodes::OuterJoin + ) + join_scope.joins!(join_dependency) + end + + arel = join_scope.arel(alias_tracker.aliases) + nodes = arel.constraints.first + + if nodes.is_a?(Arel::Nodes::And) + others = nodes.children.extract! do |node| + !Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name } + end + end + + joins << table.create_join(table, table.create_on(nodes), join_type) + + if others && !others.empty? + joins.concat arel.join_sources + append_constraints(connection, joins.last, others) + end + + # The current table in this iteration becomes the foreign table in the next + foreign_table, foreign_klass = table, klass + end + + joins + end + end + end +end diff --git a/lib/polyamorous/activerecord/join_dependency.rb b/lib/polyamorous/activerecord/join_dependency.rb new file mode 100644 index 000000000..0bcee6f74 --- /dev/null +++ b/lib/polyamorous/activerecord/join_dependency.rb @@ -0,0 +1,102 @@ +module Polyamorous + module JoinDependencyExtensions + # Replaces ActiveRecord::Associations::JoinDependency#build + def build(associations, base_klass) + associations.map do |name, right| + if name.is_a? Join + reflection = find_reflection base_klass, name.name + reflection.check_validity! + reflection.check_eager_loadable! + + klass = if reflection.polymorphic? + name.klass || base_klass + else + reflection.klass + end + JoinAssociation.new(reflection, build(right, klass), name.klass, name.type) + else + reflection = find_reflection base_klass, name + reflection.check_validity! + reflection.check_eager_loadable! + + if reflection.polymorphic? + raise ActiveRecord::EagerLoadPolymorphicError.new(reflection) + end + JoinAssociation.new(reflection, build(right, reflection.klass)) + end + end + end + + def join_constraints(joins_to_add, alias_tracker, references) + @alias_tracker = alias_tracker + @joined_tables = {} + @references = {} + + references.each do |table_name| + @references[table_name.to_sym] = table_name if table_name.is_a?(String) + end + + joins = make_join_constraints(join_root, join_type) + + joins.concat joins_to_add.flat_map { |oj| + if join_root.match?(oj.join_root) && join_root.table.name == oj.join_root.table.name + walk join_root, oj.join_root, oj.join_type + else + make_join_constraints(oj.join_root, oj.join_type) + end + } + end + + def construct_tables_for_association!(join_root, association) + tables = table_aliases_for(join_root, association) + association.table = tables.first + tables + end + + private + + def table_aliases_for(parent, node) + @joined_tables ||= {} + node.reflection.chain.map { |reflection| + table, terminated = @joined_tables[reflection] + root = reflection == node.reflection + + if table && (!root || !terminated) + @joined_tables[reflection] = [table, true] if root + table + else + table = alias_tracker.aliased_table_for(reflection.klass.arel_table) do + name = reflection.alias_candidate(parent.table_name) + root ? name : "#{name}_join" + end + @joined_tables[reflection] ||= [table, root] if join_type == Arel::Nodes::OuterJoin + table + end + } + end + + module ClassMethods + # Prepended before ActiveRecord::Associations::JoinDependency#walk_tree + # + def walk_tree(associations, hash) + case associations + when TreeNode + associations.add_to_tree(hash) + when Hash + associations.each do |k, v| + cache = + if TreeNode === k + k.add_to_tree(hash) + else + hash[k] ||= {} + end + walk_tree(v, cache) + end + else + super(associations, hash) + end + end + end + + end +end diff --git a/lib/polyamorous/activerecord/reflection.rb b/lib/polyamorous/activerecord/reflection.rb new file mode 100644 index 000000000..9f5b554ef --- /dev/null +++ b/lib/polyamorous/activerecord/reflection.rb @@ -0,0 +1,11 @@ +module Polyamorous + module ReflectionExtensions + def join_scope(table, foreign_table, foreign_klass) + if respond_to?(:polymorphic?) && polymorphic? + super.where!(foreign_table[foreign_type].eq(klass.name)) + else + super + end + end + end +end diff --git a/lib/polyamorous/join.rb b/lib/polyamorous/join.rb new file mode 100644 index 000000000..45ce4dac3 --- /dev/null +++ b/lib/polyamorous/join.rb @@ -0,0 +1,70 @@ +module Polyamorous + class Join + include TreeNode + + attr_accessor :name + attr_reader :type, :klass + + def initialize(name, type = InnerJoin, klass = nil) + @name = name + @type = convert_to_arel_join_type(type) + @klass = convert_to_class(klass) if klass + end + + def klass=(klass) + @klass = convert_to_class(klass) if klass + end + + def type=(type) + @type = convert_to_arel_join_type(type) if type + end + + def hash + [@name, @type, @klass].hash + end + + def eql?(other) + self.class == other.class && + self.name == other.name && + self.type == other.type && + self.klass == other.klass + end + + alias :== :eql? + + def add_to_tree(hash) + hash[self] ||= {} + end + + private + + def convert_to_arel_join_type(type) + case type + when 'inner', :inner + InnerJoin + when 'outer', :outer + OuterJoin + when Class + if [InnerJoin, OuterJoin].include? type + type + else + raise ArgumentError, "#{type} cannot be converted to an ARel join type" + end + else + raise ArgumentError, "#{type} cannot be converted to an ARel join type" + end + end + + def convert_to_class(value) + case value + when String, Symbol + Kernel.const_get(value) + when Class + value + else + raise ArgumentError, "#{value} cannot be converted to a Class" + end + end + + end +end diff --git a/lib/polyamorous/polyamorous.rb b/lib/polyamorous/polyamorous.rb new file mode 100644 index 000000000..ca8311ddf --- /dev/null +++ b/lib/polyamorous/polyamorous.rb @@ -0,0 +1,27 @@ +ActiveSupport.on_load(:active_record) do + module Polyamorous + InnerJoin = Arel::Nodes::InnerJoin + OuterJoin = Arel::Nodes::OuterJoin + + JoinDependency = ::ActiveRecord::Associations::JoinDependency + JoinAssociation = ::ActiveRecord::Associations::JoinDependency::JoinAssociation + end + + require 'polyamorous/tree_node' + require 'polyamorous/join' + require 'polyamorous/swapping_reflection_class' + + require 'polyamorous/activerecord/join_association' + require 'polyamorous/activerecord/join_dependency' + require 'polyamorous/activerecord/reflection' + + if ::ActiveRecord.version >= ::Gem::Version.new("7.2") && ::ActiveRecord.version < ::Gem::Version.new("7.2.2.1") + require "polyamorous/activerecord/join_association_7_2" + end + + ActiveRecord::Reflection::AbstractReflection.send(:prepend, Polyamorous::ReflectionExtensions) + + Polyamorous::JoinDependency.send(:prepend, Polyamorous::JoinDependencyExtensions) + Polyamorous::JoinDependency.singleton_class.send(:prepend, Polyamorous::JoinDependencyExtensions::ClassMethods) + Polyamorous::JoinAssociation.send(:prepend, Polyamorous::JoinAssociationExtensions) +end diff --git a/lib/polyamorous/swapping_reflection_class.rb b/lib/polyamorous/swapping_reflection_class.rb new file mode 100644 index 000000000..31cd3fe8a --- /dev/null +++ b/lib/polyamorous/swapping_reflection_class.rb @@ -0,0 +1,11 @@ +module Polyamorous + module SwappingReflectionClass + def swapping_reflection_klass(reflection, klass) + new_reflection = reflection.clone + new_reflection.instance_variable_set(:@options, reflection.options.clone) + new_reflection.options.delete(:polymorphic) + new_reflection.instance_variable_set(:@klass, klass) + yield new_reflection + end + end +end diff --git a/lib/polyamorous/tree_node.rb b/lib/polyamorous/tree_node.rb new file mode 100644 index 000000000..f77cefa44 --- /dev/null +++ b/lib/polyamorous/tree_node.rb @@ -0,0 +1,7 @@ +module Polyamorous + module TreeNode + def add_to_tree(hash) + raise NotImplementedError + end + end +end diff --git a/lib/ransack.rb b/lib/ransack.rb index 70d4a6298..d5891fdb3 100644 --- a/lib/ransack.rb +++ b/lib/ransack.rb @@ -1,28 +1,33 @@ -require 'active_support/core_ext' +require 'active_support/dependencies/autoload' +require 'active_support/deprecation' +require 'active_support/deprecator' +require 'active_support/core_ext' require 'ransack/configuration' +require 'polyamorous/polyamorous' module Ransack extend Configuration - - class UntraversableAssociationError < StandardError; end; + class UntraversableAssociationError < StandardError; end end Ransack.configure do |config| Ransack::Constants::AREL_PREDICATES.each do |name| - config.add_predicate name, :arel_predicate => name + config.add_predicate name, arel_predicate: name end - Ransack::Constants::DERIVED_PREDICATES.each do |args| - config.add_predicate *args + config.add_predicate(*args) end end -require 'ransack/translate' require 'ransack/search' require 'ransack/ransacker' -require 'ransack/adapters/active_record' if defined?(::ActiveRecord::Base) -require 'ransack/helpers' -require 'action_controller' +require 'ransack/translate' +require 'ransack/active_record' +require 'ransack/context' +require 'ransack/version' -ActionController::Base.helper Ransack::Helpers::FormHelper +ActiveSupport.on_load(:action_controller) do + require 'ransack/helpers' + ActionController::Base.helper Ransack::Helpers::FormHelper +end diff --git a/lib/ransack/active_record.rb b/lib/ransack/active_record.rb new file mode 100644 index 000000000..338c0fd20 --- /dev/null +++ b/lib/ransack/active_record.rb @@ -0,0 +1,14 @@ +require 'ransack/adapters/active_record/base' + +ActiveSupport.on_load(:active_record) do + extend Ransack::Adapters::ActiveRecord::Base + + Ransack::SUPPORTS_ATTRIBUTE_ALIAS = + begin + ActiveRecord::Base.respond_to?(:attribute_aliases) + rescue NameError + false + end +end + +require 'ransack/adapters/active_record/context' diff --git a/lib/ransack/adapters/active_record.rb b/lib/ransack/adapters/active_record.rb deleted file mode 100644 index 1a4369d59..000000000 --- a/lib/ransack/adapters/active_record.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'ransack/adapters/active_record/base' -ActiveRecord::Base.extend Ransack::Adapters::ActiveRecord::Base - -case ActiveRecord::VERSION::STRING -when /^3\.0\./ - require 'ransack/adapters/active_record/3.0/context' -when /^3\.1\./ - require 'ransack/adapters/active_record/3.1/context' -when /^3\.2\./ - require 'ransack/adapters/active_record/3.2/context' -else - require 'ransack/adapters/active_record/context' -end diff --git a/lib/ransack/adapters/active_record/3.0/compat.rb b/lib/ransack/adapters/active_record/3.0/compat.rb deleted file mode 100644 index 486789fbb..000000000 --- a/lib/ransack/adapters/active_record/3.0/compat.rb +++ /dev/null @@ -1,176 +0,0 @@ -# UGLY, UGLY MONKEY PATCHES FOR BACKWARDS COMPAT!!! AVERT YOUR EYES!! -if Arel::Nodes::And < Arel::Nodes::Binary - class Ransack::Visitor - def visit_Ransack_Nodes_And(object) - nodes = object.values.map { |o| accept(o) }.compact - return nil unless nodes.size > 0 - - if nodes.size > 1 - nodes.inject(&:and) - else - nodes.first - end - end - end -end - -class ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase - def table - Arel::Table.new(table_name, :as => aliased_table_name, - :engine => active_record.arel_engine, - :columns => active_record.columns) - end -end - -module Arel - - class Table - alias :table_name :name - - def [] name - ::Arel::Attribute.new self, name.to_sym - end - end - - module Nodes - class Node - def not - Nodes::Not.new self - end - end - - remove_const :And - class And < Arel::Nodes::Node - attr_reader :children - - def initialize children, right = nil - unless Array === children - children = [children, right] - end - @children = children - end - - def left - children.first - end - - def right - children[1] - end - end - - class NamedFunction < Arel::Nodes::Function - attr_accessor :name, :distinct - - include Arel::Predications - - def initialize name, expr, aliaz = nil - super(expr, aliaz) - @name = name - @distinct = false - end - end - - class InfixOperation < Binary - include Arel::Expressions - include Arel::Predications - - attr_reader :operator - - def initialize operator, left, right - super(left, right) - @operator = operator - end - end - - class Multiplication < InfixOperation - def initialize left, right - super(:*, left, right) - end - end - - class Division < InfixOperation - def initialize left, right - super(:/, left, right) - end - end - - class Addition < InfixOperation - def initialize left, right - super(:+, left, right) - end - end - - class Subtraction < InfixOperation - def initialize left, right - super(:-, left, right) - end - end - end - - module Visitors - class ToSql - def column_for attr - name = attr.name.to_s - table = attr.relation.table_name - - column_cache[table][name] - end - - def column_cache - @column_cache ||= Hash.new do |hash, key| - hash[key] = Hash[ - @engine.connection.columns(key, "#{key} Columns").map do |c| - [c.name, c] - end - ] - end - end - - def visit_Arel_Nodes_InfixOperation o - "#{visit o.left} #{o.operator} #{visit o.right}" - end - - def visit_Arel_Nodes_NamedFunction o - "#{ - o.name - }(#{ - o.distinct ? Ransack::Constants::DISTINCT : Ransack::Constants::EMPTY - }#{ - o.expressions.map { |x| visit x }.join(Ransack::Constants::COMMA_SPACE) - })#{ - o.alias ? " AS #{visit o.alias}" : Ransack::Constants::EMPTY - }" - end - - def visit_Arel_Nodes_And o - o.children.map { |x| visit x }.join(' AND '.freeze) - end - - def visit_Arel_Nodes_Not o - "NOT (#{visit o.expr})" - end - - def visit_Arel_Nodes_Values o - "VALUES (#{ - o.expressions.zip(o.columns) - .map { |value, attr| - if Nodes::SqlLiteral === value - visit_Arel_Nodes_SqlLiteral value - else - quote(value, attr && column_for(attr)) - end - } - .join(Ransack::Constants::COMMA_SPACE) - })" - end - end - end - - module Predications - def as other - Nodes::As.new self, Nodes::SqlLiteral.new(other) - end - end - -end \ No newline at end of file diff --git a/lib/ransack/adapters/active_record/3.0/context.rb b/lib/ransack/adapters/active_record/3.0/context.rb deleted file mode 100644 index 1bdde28ba..000000000 --- a/lib/ransack/adapters/active_record/3.0/context.rb +++ /dev/null @@ -1,213 +0,0 @@ -require 'ransack/context' -require 'polyamorous' -require 'ransack/adapters/active_record/3.0/compat' - -module Ransack - - module Adapters - module ActiveRecord - class Context < ::Ransack::Context - # Because the AR::Associations namespace is insane - JoinDependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency - JoinBase = JoinDependency::JoinBase - - # Redefine a few things for ActiveRecord 3.0. - - def initialize(object, options = {}) - super - @arel_visitor = Arel::Visitors.visitor_for @engine - end - - def relation_for(object) - object.scoped - end - - def evaluate(search, opts = {}) - viz = Visitor.new - relation = @object.where(viz.accept(search.base)) - if search.sorts.any? - relation = relation.except(:order) - .reorder(viz.accept(search.sorts)) - end - if opts[:distinct] - relation.select( - Ransack::Constants::DISTINCT + @klass.quoted_table_name + - '.*'.freeze - ) - else - relation - end - end - - def attribute_method?(str, klass = @klass) - exists = false - - if ransackable_attribute?(str, klass) - exists = true - elsif (segments = str.split(/_/)).size > 1 - remainder = [] - found_assoc = nil - while !found_assoc && remainder.unshift(segments.pop) && - segments.size > 0 do - assoc, poly_class = unpolymorphize_association( - segments.join(Ransack::Constants::UNDERSCORE) - ) - if found_assoc = get_association(assoc, klass) - exists = attribute_method?( - remainder.join(Ransack::Constants::UNDERSCORE), - poly_class || found_assoc.klass - ) - end - end - end - - exists - end - - def table_for(parent) - parent.table - end - - def type_for(attr) - return nil unless attr && attr.valid? - klassify(attr.parent) - .columns_hash[attr.arel_attribute.name.to_s] - .type - end - - # All dependent JoinAssociation items used in the search query - # - def join_associations - @join_dependency.join_associations - end - - def join_sources - raise NotImplementedError, - "ActiveRecord 3.0 does not use join_sources or support joining relations with Arel::Join nodes. Use join_associations." - end - - def alias_tracker - raise NotImplementedError, - "ActiveRecord 3.0 does not have an alias tracker" - end - - private - - def get_parent_and_attribute_name(str, parent = @base) - attr_name = nil - - if ransackable_attribute?(str, klassify(parent)) - attr_name = str - elsif (segments = str.split(/_/)).size > 1 - remainder = [] - found_assoc = nil - while remainder.unshift(segments.pop) && segments.size > 0 && - !found_assoc do - assoc, klass = unpolymorphize_association( - segments.join(Ransack::Constants::UNDERSCORE) - ) - if found_assoc = get_association(assoc, parent) - join = build_or_find_association( - found_assoc.name, parent, klass - ) - parent, attr_name = get_parent_and_attribute_name( - remainder.join(Ransack::Constants::UNDERSCORE), join - ) - end - end - end - [parent, attr_name] - end - - def get_association(str, parent = @base) - klass = klassify parent - ransackable_association?(str, klass) && - klass.reflect_on_all_associations.detect { |a| a.name.to_s == str } - end - - def join_dependency(relation) - if relation.respond_to?(:join_dependency) # Squeel will enable this - relation.join_dependency - else - build_join_dependency(relation) - end - end - - def build_join_dependency(relation) - buckets = relation.joins_values.group_by do |join| - case join - when String - Ransack::Constants::STRING_JOIN - when Hash, Symbol, Array - Ransack::Constants::ASSOCIATION_JOIN - when ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation - Ransack::Constants::STASHED_JOIN - when Arel::Nodes::Join - Ransack::Constants::JOIN_NODE - else - raise 'unknown class: %s' % join.class.name - end - end - - association_joins = - buckets[Ransack::Constants::ASSOCIATION_JOIN] || [] - - stashed_association_joins = - buckets[Ransack::Constants::STASHED_JOIN] || [] - - join_nodes = - buckets[Ransack::Constants::JOIN_NODE] || [] - - string_joins = - (buckets[Ransack::Constants::STRING_JOIN] || []) - .map { |x| x.strip } - .uniq - - join_list = relation.send :custom_join_sql, (string_joins + join_nodes) - - join_dependency = JoinDependency.new( - relation.klass, - association_joins, - join_list - ) - - join_nodes.each do |join| - join_dependency.table_aliases[join.left.name.downcase] = 1 - end - - join_dependency.graft(*stashed_association_joins) - end - - def build_or_find_association(name, parent = @base, klass = nil) - found_association = @join_dependency.join_associations - .detect do |assoc| - assoc.reflection.name == name && - assoc.parent == parent && - (!klass || assoc.reflection.klass == klass) - end - unless found_association - @join_dependency.send( - :build, Polyamorous::Join.new(name, @join_type, klass), parent - ) - found_association = @join_dependency.join_associations.last - apply_default_conditions(found_association) - # Leverage the stashed association functionality in AR - @object = @object.joins(found_association) - end - - found_association - end - - def apply_default_conditions(join_association) - reflection = join_association.reflection - default_scope = join_association.active_record.scoped - default_conditions = default_scope.arel.where_clauses - if default_conditions.any? - reflection.options[:conditions] = default_conditions - end - end - - end - end - end -end diff --git a/lib/ransack/adapters/active_record/3.1/context.rb b/lib/ransack/adapters/active_record/3.1/context.rb deleted file mode 100644 index 3136a5a0b..000000000 --- a/lib/ransack/adapters/active_record/3.1/context.rb +++ /dev/null @@ -1,227 +0,0 @@ -require 'ransack/context' -require 'ransack/adapters/active_record/compat' -require 'polyamorous' - -module Ransack - module Adapters - module ActiveRecord - class Context < ::Ransack::Context - # Because the AR::Associations namespace is insane - JoinDependency = ::ActiveRecord::Associations::JoinDependency - JoinPart = JoinDependency::JoinPart - - # Redefine a few things for ActiveRecord 3.1. - - def initialize(object, options = {}) - super - @arel_visitor = Arel::Visitors.visitor_for @engine - end - - def relation_for(object) - object.scoped - end - - def evaluate(search, opts = {}) - viz = Visitor.new - relation = @object.where(viz.accept(search.base)) - if search.sorts.any? - relation = relation.except(:order) - .reorder(viz.accept(search.sorts)) - end - if opts[:distinct] - relation.select( - Ransack::Constants::DISTINCT + @klass.quoted_table_name + - '.*'.freeze - ) - else - relation - end - end - - def attribute_method?(str, klass = @klass) - exists = false - - if ransackable_attribute?(str, klass) - exists = true - elsif (segments = str.split(/_/)).size > 1 - remainder = [] - found_assoc = nil - while !found_assoc && remainder.unshift(segments.pop) && - segments.size > 0 do - assoc, poly_class = unpolymorphize_association( - segments.join(Ransack::Constants::UNDERSCORE) - ) - if found_assoc = get_association(assoc, klass) - exists = attribute_method?( - remainder.join(Ransack::Constants::UNDERSCORE), - poly_class || found_assoc.klass - ) - end - end - end - - exists - end - - def table_for(parent) - parent.table - end - - def type_for(attr) - return nil unless attr && attr.valid? - name = attr.arel_attribute.name.to_s - table = attr.arel_attribute.relation.table_name - - unless @engine.connection_pool.table_exists?(table) - raise "No table named #{table} exists" - end - - @engine.connection_pool.columns_hash[table][name].type - end - - def join_associations - @join_dependency.join_associations - end - - # All dependent Arel::Join nodes used in the search query - # - # This could otherwise be done as `@object.arel.join_sources`, except - # that ActiveRecord's build_joins sets up its own JoinDependency. - # This extracts what we need to access the joins using our existing - # JoinDependency to track table aliases. - # - def join_sources - base = Arel::SelectManager.new(@object.engine, @object.table) - joins = @object.joins_values - joins.each do |assoc| - assoc.join_to(base) - end - base.join_sources - end - - def alias_tracker - @join_dependency.alias_tracker - end - - private - - def get_parent_and_attribute_name(str, parent = @base) - attr_name = nil - - if ransackable_attribute?(str, klassify(parent)) - attr_name = str - elsif (segments = str.split(/_/)).size > 1 - remainder = [] - found_assoc = nil - while remainder.unshift(segments.pop) && segments.size > 0 && - !found_assoc do - assoc, klass = unpolymorphize_association( - segments.join(Ransack::Constants::UNDERSCORE) - ) - if found_assoc = get_association(assoc, parent) - join = build_or_find_association( - found_assoc.name, parent, klass - ) - parent, attr_name = get_parent_and_attribute_name( - remainder.join(Ransack::Constants::UNDERSCORE), join - ) - end - end - end - - [parent, attr_name] - end - - def get_association(str, parent = @base) - klass = klassify parent - ransackable_association?(str, klass) && - klass.reflect_on_all_associations.detect { |a| a.name.to_s == str } - end - - def join_dependency(relation) - if relation.respond_to?(:join_dependency) # Squeel will enable this - relation.join_dependency - else - build_join_dependency(relation) - end - end - - def build_join_dependency(relation) - buckets = relation.joins_values.group_by do |join| - case join - when String - Ransack::Constants::STRING_JOIN - when Hash, Symbol, Array - Ransack::Constants::ASSOCIATION_JOIN - when ::ActiveRecord::Associations::JoinDependency::JoinAssociation - Ransack::Constants::STASHED_JOIN - when Arel::Nodes::Join - Ransack::Constants::JOIN_NODE - else - raise 'unknown class: %s' % join.class.name - end - end - - association_joins = - buckets[Ransack::Constants::ASSOCIATION_JOIN] || [] - - stashed_association_joins = - buckets[Ransack::Constants::STASHED_JOIN] || [] - - join_nodes = - buckets[Ransack::Constants::JOIN_NODE] || [] - - string_joins = - (buckets[Ransack::Constants::STRING_JOIN] || []) - .map { |x| x.strip } - .uniq - - join_list = relation.send :custom_join_ast, - relation.table.from(relation.table), string_joins - - join_dependency = JoinDependency.new( - relation.klass, - association_joins, - join_list - ) - - join_nodes.each do |join| - join_dependency.alias_tracker.aliases[join.left.name.downcase] = 1 - end - - join_dependency.graft(*stashed_association_joins) - end - - def build_or_find_association(name, parent = @base, klass = nil) - found_association = @join_dependency.join_associations - .detect do |assoc| - assoc.reflection.name == name && - assoc.parent == parent && - (!klass || assoc.reflection.klass == klass) - end - unless found_association - @join_dependency.send( - :build, Polyamorous::Join.new(name, @join_type, klass), parent - ) - found_association = @join_dependency.join_associations.last - apply_default_conditions(found_association) - # Leverage the stashed association functionality in AR - @object = @object.joins(found_association) - end - - found_association - end - - def apply_default_conditions(join_association) - reflection = join_association.reflection - default_scope = join_association.active_record.scoped - default_conditions = default_scope.arel.where_clauses - if default_conditions.any? - reflection.options[:conditions] = default_conditions - end - end - - end - end - end -end diff --git a/lib/ransack/adapters/active_record/3.2/context.rb b/lib/ransack/adapters/active_record/3.2/context.rb deleted file mode 100644 index c87de34d6..000000000 --- a/lib/ransack/adapters/active_record/3.2/context.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'ransack/context' -require 'ransack/adapters/active_record/3.1/context' -require 'ransack/adapters/active_record/compat' -require 'polyamorous' - -module Ransack - module Adapters - module ActiveRecord - class Context < ::Ransack::Context - - # Redefine a few things for ActiveRecord 3.2. - - def initialize(object, options = {}) - super - @arel_visitor = @engine.connection.visitor - end - - def relation_for(object) - object.scoped - end - - def type_for(attr) - return nil unless attr && attr.valid? - name = attr.arel_attribute.name.to_s - table = attr.arel_attribute.relation.table_name - - schema_cache = @engine.connection.schema_cache - raise "No table named #{table} exists" unless schema_cache.table_exists?(table) - schema_cache.columns_hash[table][name].type - end - - def evaluate(search, opts = {}) - viz = Visitor.new - relation = @object.where(viz.accept(search.base)) - if search.sorts.any? - relation = relation.except(:order).reorder(viz.accept(search.sorts)) - end - opts[:distinct] ? relation.uniq : relation - end - - end - end - end -end diff --git a/lib/ransack/adapters/active_record/base.rb b/lib/ransack/adapters/active_record/base.rb index 66b2b8e55..15a4cd666 100644 --- a/lib/ransack/adapters/active_record/base.rb +++ b/lib/ransack/adapters/active_record/base.rb @@ -4,10 +4,11 @@ module ActiveRecord module Base def self.extended(base) - alias :search :ransack unless base.respond_to? :search base.class_eval do class_attribute :_ransackers + class_attribute :_ransack_aliases self._ransackers ||= {} + self._ransack_aliases ||= {} end end @@ -15,17 +16,26 @@ def ransack(params = {}, options = {}) Search.new(self, params, options) end + def ransack!(params = {}, options = {}) + ransack(params, options.merge(ignore_unknown_conditions: false)) + end + def ransacker(name, opts = {}, &block) self._ransackers = _ransackers.merge name.to_s => Ransacker .new(self, name, opts, &block) end + def ransack_alias(new_name, old_name) + self._ransack_aliases = _ransack_aliases.merge new_name.to_s => + old_name.to_s + end + # Ransackable_attributes, by default, returns all column names # and any defined ransackers as an array of strings. # For overriding with a whitelist array of strings. # def ransackable_attributes(auth_object = nil) - column_names + _ransackers.keys + @ransackable_attributes ||= deprecated_ransackable_list(:ransackable_attributes) end # Ransackable_associations, by default, returns the names @@ -33,7 +43,7 @@ def ransackable_attributes(auth_object = nil) # For overriding with a whitelist array of strings. # def ransackable_associations(auth_object = nil) - reflect_on_all_associations.map { |a| a.name.to_s } + @ransackable_associations ||= deprecated_ransackable_list(:ransackable_associations) end # Ransortable_attributes, by default, returns the names @@ -52,6 +62,90 @@ def ransackable_scopes(auth_object = nil) [] end + # ransack_scope_skip_sanitize_args, by default, returns an empty array. + # i.e. use the sanitize_scope_args setting to determine if args should be converted. + # For overriding with a list of scopes which should be passed the args as-is. + # + def ransackable_scopes_skip_sanitize_args + [] + end + + # Bare list of all potentially searchable attributes. Searchable attributes + # need to be explicitly allowlisted through the `ransackable_attributes` + # method in each model, but if you're allowing almost everything to be + # searched, this list can be used as a base for exclusions. + # + def authorizable_ransackable_attributes + if Ransack::SUPPORTS_ATTRIBUTE_ALIAS + column_names + _ransackers.keys + _ransack_aliases.keys + + attribute_aliases.keys + else + column_names + _ransackers.keys + _ransack_aliases.keys + end.uniq + end + + # Bare list of all potentially searchable associations. Searchable + # associations need to be explicitly allowlisted through the + # `ransackable_associations` method in each model, but if you're + # allowing almost everything to be searched, this list can be used as a + # base for exclusions. + # + def authorizable_ransackable_associations + reflect_on_all_associations.map { |a| a.name.to_s } + end + + private + + def deprecated_ransackable_list(method) + list_type = method.to_s.delete_prefix("ransackable_") + + if explicitly_defined?(method) + warn_deprecated <<~ERROR + Ransack's builtin `#{method}` method is deprecated and will result + in an error in the future. If you want to authorize the full list + of searchable #{list_type} for this model, use + `authorizable_#{method}` instead of delegating to `super`. + ERROR + + public_send("authorizable_#{method}") + else + raise <<~MESSAGE + Ransack needs #{name} #{list_type} explicitly allowlisted as + searchable. Define a `#{method}` class method in your `#{name}` + model, watching out for items you DON'T want searchable (for + example, `encrypted_password`, `password_reset_token`, `owner` or + other sensitive information). You can use the following as a base: + + ```ruby + class #{name} < ApplicationRecord + + # ... + + def self.#{method}(auth_object = nil) + #{public_send("authorizable_#{method}").sort.inspect} + end + + # ... + + end + ``` + MESSAGE + end + end + + def explicitly_defined?(method) + definer_ancestor = singleton_class.ancestors.find do |ancestor| + ancestor.instance_methods(false).include?(method) + end + + definer_ancestor != Ransack::Adapters::ActiveRecord::Base + end + + def warn_deprecated(message) + caller_location = caller_locations.find { |location| !location.path.start_with?(File.expand_path("../..", __dir__)) } + + warn "DEPRECATION WARNING: #{message.squish} (called at #{caller_location.path}:#{caller_location.lineno})" + end end end end diff --git a/lib/ransack/adapters/active_record/compat.rb b/lib/ransack/adapters/active_record/compat.rb deleted file mode 100644 index 06f4784bf..000000000 --- a/lib/ransack/adapters/active_record/compat.rb +++ /dev/null @@ -1,14 +0,0 @@ -module Arel - - module Visitors - - class DepthFirst < Visitor - - unless method_defined?(:visit_Arel_Nodes_InfixOperation) - alias :visit_Arel_Nodes_InfixOperation :binary - end - - end - - end -end diff --git a/lib/ransack/adapters/active_record/context.rb b/lib/ransack/adapters/active_record/context.rb index a04af9623..802f025eb 100644 --- a/lib/ransack/adapters/active_record/context.rb +++ b/lib/ransack/adapters/active_record/context.rb @@ -1,41 +1,65 @@ require 'ransack/context' -require 'ransack/adapters/active_record/compat' -require 'polyamorous' +require 'polyamorous/polyamorous' module Ransack module Adapters module ActiveRecord class Context < ::Ransack::Context - # Because the AR::Associations namespace is insane - JoinDependency = ::ActiveRecord::Associations::JoinDependency - JoinPart = JoinDependency::JoinPart - - def initialize(object, options = {}) - super - @arel_visitor = @engine.connection.visitor - end - def relation_for(object) object.all end def type_for(attr) return nil unless attr && attr.valid? - name = attr.arel_attribute.name.to_s - table = attr.arel_attribute.relation.table_name - - schema_cache = @engine.connection.schema_cache - raise "No table named #{table} exists" unless schema_cache.table_exists?(table) - schema_cache.columns_hash(table)[name].type + relation = attr.arel_attribute.relation + name = attr.arel_attribute.name.to_s + table = relation.respond_to?(:table_name) ? relation.table_name : relation.name + schema_cache = self.klass.connection.schema_cache + unless schema_cache.send(:data_source_exists?, table) + raise "No table named #{table} exists." + end + column = attr.klass.columns.find { |column| column.name == name } + column&.type end def evaluate(search, opts = {}) viz = Visitor.new relation = @object.where(viz.accept(search.base)) + if search.sorts.any? - relation = relation.except(:order).reorder(viz.accept(search.sorts)) + relation = relation.except(:order) + # Rather than applying all of the search's sorts in one fell swoop, + # as the original implementation does, we apply one at a time. + # + # If the sort (returned by the Visitor above) is a symbol, we know + # that it represents a scope on the model and we can apply that + # scope. + # + # Otherwise, we fall back to the applying the sort with the "order" + # method as the original implementation did. Actually the original + # implementation used "reorder," which was overkill since we already + # have a clean slate after "relation.except(:order)" above. + viz.accept(search.sorts).each do |scope_or_sort| + if scope_or_sort.is_a?(Symbol) + relation = relation.send(scope_or_sort) + else + case Ransack.options[:postgres_fields_sort_option] + when :nulls_first + scope_or_sort = scope_or_sort.direction == :asc ? Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST") : Arel.sql("#{scope_or_sort.to_sql} NULLS LAST") + when :nulls_last + scope_or_sort = scope_or_sort.direction == :asc ? Arel.sql("#{scope_or_sort.to_sql} NULLS LAST") : Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST") + when :nulls_always_first + scope_or_sort = Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST") + when :nulls_always_last + scope_or_sort = Arel.sql("#{scope_or_sort.to_sql} NULLS LAST") + end + + relation = relation.order(scope_or_sort) + end + end end + opts[:distinct] ? relation.distinct : relation end @@ -43,17 +67,17 @@ def attribute_method?(str, klass = @klass) exists = false if ransackable_attribute?(str, klass) exists = true - elsif (segments = str.split(/_/)).size > 1 + elsif (segments = str.split(Constants::UNDERSCORE)).size > 1 remainder = [] found_assoc = nil while !found_assoc && remainder.unshift(segments.pop) && segments.size > 0 do assoc, poly_class = unpolymorphize_association( - segments.join(Ransack::Constants::UNDERSCORE) + segments.join(Constants::UNDERSCORE) ) if found_assoc = get_association(assoc, klass) exists = attribute_method?( - remainder.join(Ransack::Constants::UNDERSCORE), + remainder.join(Constants::UNDERSCORE), poly_class || found_assoc.klass ) end @@ -78,75 +102,159 @@ def klassify(obj) end end - if ::ActiveRecord::VERSION::STRING >= '4.1'.freeze - - def join_associations - raise NotImplementedError, - "ActiveRecord 4.1 and later does not use join_associations. Use join_sources." + # All dependent Arel::Join nodes used in the search query. + # + # This could otherwise be done as `@object.arel.join_sources`, except + # that ActiveRecord's build_joins sets up its own JoinDependency. + # This extracts what we need to access the joins using our existing + # JoinDependency to track table aliases. + # + def join_sources + base, joins = begin + alias_tracker = @object.alias_tracker + constraints = @join_dependency.join_constraints(@object.joins_values, alias_tracker, @object.references_values) + + [ + Arel::SelectManager.new(@object.table), + constraints + ] end - - # All dependent Arel::Join nodes used in the search query - # - # This could otherwise be done as `@object.arel.join_sources`, except - # that ActiveRecord's build_joins sets up its own JoinDependency. - # This extracts what we need to access the joins using our existing - # JoinDependency to track table aliases. - # - def join_sources - base = Arel::SelectManager.new(@object.engine, @object.table) - joins = @join_dependency.join_constraints(@object.joins_values) - joins.each do |aliased_join| - base.from(aliased_join) - end - base.join_sources + joins.each do |aliased_join| + base.from(aliased_join) end + base.join_sources + end + + def alias_tracker + @join_dependency.send(:alias_tracker) + end + + def lock_association(association) + @lock_associations << association + end - else + def remove_association(association) + return if @lock_associations.include?(association) + @join_dependency.instance_variable_get(:@join_root).children.delete_if { |stashed| + stashed.eql?(association) + } + @object.joins_values.delete_if { |jd| + jd.instance_variables.include?(:@join_root) && + jd.instance_variable_get(:@join_root).children.map(&:object_id) == [association.object_id] + } + end - # All dependent JoinAssociation items used in the search query - # - # Deprecated: this goes away in ActiveRecord 4.1. Use join_sources. - # - def join_associations - @join_dependency.join_associations + # Build an Arel subquery that selects keys for the top query, + # drawn from the first join association's foreign_key. + # + # Example: for an Article that has_and_belongs_to_many Tags + # + # context = Article.search.context + # attribute = Attribute.new(context, "tags_name").tap do |a| + # context.bind(a, a.name) + # end + # context.build_correlated_subquery(attribute.parent).to_sql + # + # # SELECT "articles_tags"."article_id" FROM "articles_tags" + # # INNER JOIN "tags" ON "tags"."id" = "articles_tags"."tag_id" + # # WHERE "articles_tags"."article_id" = "articles"."id" + # + # The WHERE condition on this query makes it invalid by itself, + # because it is correlated to the primary key on the outer query. + # + def build_correlated_subquery(association) + join_constraints = extract_joins(association) + join_root = join_constraints.shift + correlated_key = extract_correlated_key(join_root) + subquery = Arel::SelectManager.new(association.base_klass) + subquery.from(join_root.left) + subquery.project(correlated_key) + join_constraints.each do |j| + subquery.join_sources << Arel::Nodes::InnerJoin.new(j.left, j.right) end - def join_sources - base = Arel::SelectManager.new(@object.engine, @object.table) - joins = @object.joins_values - joins.each do |assoc| - assoc.join_to(base) + # Handle polymorphic associations where correlated_key is an array + if correlated_key.is_a?(Array) + # For polymorphic associations, we need to add conditions for both the foreign key and type + correlated_key.each_with_index do |key, index| + if index == 0 + # This is the foreign key + subquery = subquery.where(key.eq(primary_key)) + else + # This is the type key, which should be equal to the model name + subquery = subquery.where(key.eq(@klass.name)) + end end - base.join_sources + else + # Original behavior for non-polymorphic associations + subquery = subquery.where(correlated_key.eq(primary_key)) end + subquery end - def alias_tracker - @join_dependency.alias_tracker + def primary_key + @object.table[@object.primary_key] end private + def extract_correlated_key(join_root) + case join_root + when Arel::Nodes::OuterJoin + # one of join_root.right/join_root.left is expected to be Arel::Nodes::On + if join_root.right.is_a?(Arel::Nodes::On) + extract_correlated_key(join_root.right.expr) + elsif join_root.left.is_a?(Arel::Nodes::On) + extract_correlated_key(join_root.left.expr) + else + raise 'Ransack encountered an unexpected arel structure' + end + when Arel::Nodes::Equality + pk = primary_key + if join_root.left == pk + join_root.right + elsif join_root.right == pk + join_root.left + else + nil + end + when Arel::Nodes::And + # And may have multiple children, so we need to check all, not via left/right + if join_root.children.any? + join_root.children.each do |child| + key = extract_correlated_key(child) + return key if key + end + else + extract_correlated_key(join_root.left) || extract_correlated_key(join_root.right) + end + else + # eg parent was Arel::Nodes::And and the evaluated side was one of + # Arel::Nodes::Grouping or MultiTenant::TenantEnforcementClause + nil + end + end + def get_parent_and_attribute_name(str, parent = @base) attr_name = nil if ransackable_attribute?(str, klassify(parent)) attr_name = str - elsif (segments = str.split(Ransack::Constants::UNDERSCORE)).size > 1 + elsif (segments = str.split(Constants::UNDERSCORE)).size > 1 remainder = [] found_assoc = nil while remainder.unshift(segments.pop) && segments.size > 0 && !found_assoc do assoc, klass = unpolymorphize_association( - segments.join(Ransack::Constants::UNDERSCORE) + segments.join(Constants::UNDERSCORE) ) if found_assoc = get_association(assoc, parent) join = build_or_find_association( found_assoc.name, parent, klass ) parent, attr_name = get_parent_and_attribute_name( - remainder.join(Ransack::Constants::UNDERSCORE), join + remainder.join(Constants::UNDERSCORE), join ) end end @@ -158,130 +266,107 @@ def get_parent_and_attribute_name(str, parent = @base) def get_association(str, parent = @base) klass = klassify parent ransackable_association?(str, klass) && - klass.reflect_on_all_associations.detect { |a| a.name.to_s == str } + klass.reflect_on_all_associations.detect { |a| a.name.to_s == str } end def join_dependency(relation) - if relation.respond_to?(:join_dependency) # Squeel will enable this + if relation.respond_to?(:join_dependency) # Polyamorous enables this relation.join_dependency else - build_join_dependency(relation) + build_joins(relation) end end # Checkout active_record/relation/query_methods.rb +build_joins+ for # reference. Lots of duplicated code maybe we can avoid it - def build_join_dependency(relation) - buckets = relation.joins_values.group_by do |join| + def build_joins(relation) + buckets = relation.joins_values + relation.left_outer_joins_values + + buckets = buckets.group_by do |join| case join when String - STRING_JOIN + :string_join when Hash, Symbol, Array - ASSOCIATION_JOIN - when JoinDependency, JoinDependency::JoinAssociation - STASHED_JOIN + :association_join + when Polyamorous::JoinDependency, Polyamorous::JoinAssociation + :stashed_join when Arel::Nodes::Join - JOIN_NODE + :join_node else raise 'unknown class: %s' % join.class.name end end - - association_joins = - buckets[Ransack::Constants::ASSOCIATION_JOIN] || [] - - stashed_association_joins = - buckets[Ransack::Constants::STASHED_JOIN] || [] - - join_nodes = - buckets[Ransack::Constants::JOIN_NODE] || [] - - string_joins = - (buckets[Ransack::Constants::STRING_JOIN] || []) - .map { |x| x.strip } - .uniq - - join_list = relation.send :custom_join_ast, - relation.table.from(relation.table), string_joins - - join_dependency = JoinDependency.new( - relation.klass, association_joins, join_list - ) - + buckets.default = [] + association_joins = buckets[:association_join] + stashed_association_joins = buckets[:stashed_join] + join_nodes = buckets[:join_node].uniq + string_joins = buckets[:string_join].map(&:strip) + string_joins.uniq! + + join_list = join_nodes + convert_join_strings_to_ast(relation.table, string_joins) + + alias_tracker = relation.alias_tracker(join_list) + join_dependency = Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins, Arel::Nodes::OuterJoin) + join_dependency.instance_variable_set(:@alias_tracker, alias_tracker) join_nodes.each do |join| - join_dependency.alias_tracker.aliases[join.left.name.downcase] = 1 - end - - if ::ActiveRecord::VERSION::STRING >= '4.1'.freeze - join_dependency - else - join_dependency.graft(*stashed_association_joins) + join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1 end + join_dependency end - if ::ActiveRecord::VERSION::STRING >= '4.1'.freeze - - def build_or_find_association(name, parent = @base, klass = nil) - found_association = @join_dependency.join_root.children - .detect do |assoc| - assoc.reflection.name == name && - (@associations_pot.nil? || @associations_pot[assoc] == parent) && - (!klass || assoc.reflection.klass == klass) - end + def convert_join_strings_to_ast(table, joins) + joins.map! { |join| table.create_string_join(Arel.sql(join)) unless join.blank? } + joins.compact! + joins + end - unless found_association - jd = JoinDependency.new( - parent.base_klass, - Polyamorous::Join.new(name, @join_type, klass), - [] - ) - found_association = jd.join_root.children.last - associations found_association, parent - - # TODO maybe we dont need to push associations here, we could loop - # through the @associations_pot instead - @join_dependency.join_root.children.push found_association - - # Builds the arel nodes properly for this association - @join_dependency.send( - :construct_tables!, jd.join_root, found_association - ) + def build_or_find_association(name, parent = @base, klass = nil) + find_association(name, parent, klass) or build_association(name, parent, klass) + end - # Leverage the stashed association functionality in AR - @object = @object.joins(jd) - end - found_association + def find_association(name, parent = @base, klass = nil) + @join_dependency.instance_variable_get(:@join_root).children.detect do |assoc| + assoc.reflection.name == name && assoc.table && + (@associations_pot.empty? || @associations_pot[assoc] == parent || !@associations_pot.key?(assoc)) && + (!klass || assoc.reflection.klass == klass) end + end - def associations(assoc, parent) - @associations_pot ||= {} - @associations_pot[assoc] = parent - end + def build_association(name, parent = @base, klass = nil) + jd = Polyamorous::JoinDependency.new( + parent.base_klass, + parent.table, + Polyamorous::Join.new(name, @join_type, klass), + @join_type + ) + found_association = jd.instance_variable_get(:@join_root).children.last - else + @associations_pot[found_association] = parent - def build_or_find_association(name, parent = @base, klass = nil) - found_association = @join_dependency.join_associations - .detect do |assoc| - assoc.reflection.name == name && - assoc.parent == parent && - (!klass || assoc.reflection.klass == klass) - end - unless found_association - @join_dependency.send( - :build, - Polyamorous::Join.new(name, @join_type, klass), - parent - ) - found_association = @join_dependency.join_associations.last - # Leverage the stashed association functionality in AR - @object = @object.joins(found_association) - end - found_association - end + # TODO maybe we dont need to push associations here, we could loop + # through the @associations_pot instead + @join_dependency.instance_variable_get(:@join_root).children.push found_association + + # Builds the arel nodes properly for this association + @tables_pot[found_association] = @join_dependency.construct_tables_for_association!(jd.instance_variable_get(:@join_root), found_association) + # Leverage the stashed association functionality in AR + @object = @object.joins(jd) + found_association end + def extract_joins(association) + parent = @join_dependency.instance_variable_get(:@join_root) + reflection = association.reflection + join_constraints = association.join_constraints_with_tables( + parent.table, + parent.base_klass, + Arel::Nodes::OuterJoin, + @join_dependency.instance_variable_get(:@alias_tracker), + @tables_pot[association] + ) + join_constraints.to_a.flatten + end end end end diff --git a/lib/ransack/configuration.rb b/lib/ransack/configuration.rb index e50f78ec1..08a0b5405 100644 --- a/lib/ransack/configuration.rb +++ b/lib/ransack/configuration.rb @@ -5,10 +5,37 @@ module Ransack module Configuration mattr_accessor :predicates, :options - self.predicates = {} + + class PredicateCollection + attr_reader :sorted_names_with_underscores + + def initialize + @collection = {} + @sorted_names_with_underscores = [] + end + + delegate :[], :keys, :has_key?, to: :@collection + + def []=(key, value) + @sorted_names_with_underscores << [key, '_' + key] + @sorted_names_with_underscores.sort! { |(a, _), (b, _)| b.length <=> a.length } + + @collection[key] = value + end + end + + self.predicates = PredicateCollection.new + self.options = { - :search_key => :q, - :ignore_unknown_conditions => true + search_key: :q, + ignore_unknown_conditions: true, + hide_sort_order_indicators: false, + up_arrow: '▼'.freeze, + down_arrow: '▲'.freeze, + default_arrow: nil, + sanitize_scope_args: true, + postgres_fields_sort_option: nil, + strip_whitespace: true } def configure @@ -24,31 +51,152 @@ def add_predicate(name, opts = {}) self.predicates[name] = Predicate.new(opts) - Ransack::Constants::SUFFIXES.each do |suffix| + Constants::SUFFIXES.each do |suffix| compound_name = name + suffix self.predicates[compound_name] = Predicate.new( opts.merge( - :name => compound_name, - :arel_predicate => arel_predicate_with_suffix( + name: compound_name, + arel_predicate: arel_predicate_with_suffix( opts[:arel_predicate], suffix ), - :compound => true + compound: true ) ) end if compounds end - # default search_key that, it can be overridden on sort_link level + # The default `search_key` name is `:q`. The default key may be overridden + # in an initializer file like `config/initializers/ransack.rb` as follows: + # + # Ransack.configure do |config| + # # Name the search_key `:query` instead of the default `:q` + # config.search_key = :query + # end + # + # Sometimes there are situations when the default search parameter name + # cannot be used, for instance if there were two searches on one page. + # Another name can be set using the `search_key` option with Ransack + # `ransack`, `search` and `@search_form_for` methods in controllers & views. + # + # In the controller: + # @search = Log.ransack(params[:log_search], search_key: :log_search) + # + # In the view: + # <%= f.search_form_for @search, as: :log_search %> + # def search_key=(name) self.options[:search_key] = name end - # raise an error if an unknown predicate, condition or attribute is passed - # into a search + # By default Ransack ignores errors if an unknown predicate, condition or + # attribute is passed into a search. The default may be overridden in an + # initializer file like `config/initializers/ransack.rb` as follows: + # + # Ransack.configure do |config| + # # Raise if an unknown predicate, condition or attribute is passed + # config.ignore_unknown_conditions = false + # end + # def ignore_unknown_conditions=(boolean) self.options[:ignore_unknown_conditions] = boolean end + # By default Ransack ignores empty predicates. Ransack can also fallback to + # a default predicate by setting it in an initializer file + # like `config/initializers/ransack.rb` as follows: + # + # Ransack.configure do |config| + # # Use the 'eq' predicate if an unknown predicate is passed + # config.default_predicate = 'eq' + # end + # + def default_predicate=(name) + self.options[:default_predicate] = name + end + + # By default, Ransack displays sort order indicator arrows with HTML codes: + # + # up_arrow: '▼' + # down_arrow: '▲' + # + # There is also a default arrow which is displayed if a column is not sorted. + # By default this is nil so nothing will be displayed. + # + # Any of the defaults may be globally overridden in an initializer file + # like `config/initializers/ransack.rb` as follows: + # + # Ransack.configure do |config| + # # Globally set the up arrow to an icon, and the down and default arrows to unicode. + # config.custom_arrows = { + # up_arrow: '', + # down_arrow: 'U+02193', + # default_arrow: 'U+11047' + # } + # end + # + def custom_arrows=(opts = {}) + self.options[:up_arrow] = opts[:up_arrow].freeze if opts[:up_arrow] + self.options[:down_arrow] = opts[:down_arrow].freeze if opts[:down_arrow] + self.options[:default_arrow] = opts[:default_arrow].freeze if opts[:default_arrow] + end + + # Ransack sanitizes many values in your custom scopes into booleans. + # [1, '1', 't', 'T', 'true', 'TRUE'] all evaluate to true. + # [0, '0', 'f', 'F', 'false', 'FALSE'] all evaluate to false. + # + # This default may be globally overridden in an initializer file like + # `config/initializers/ransack.rb` as follows: + # + # Ransack.configure do |config| + # # Accept my custom scope values as what they are. + # config.sanitize_custom_scope_booleans = false + # end + # + def sanitize_custom_scope_booleans=(boolean) + self.options[:sanitize_scope_args] = boolean + end + + # The `NULLS FIRST` and `NULLS LAST` options can be used to determine + # whether nulls appear before or after non-null values in the sort ordering. + # + # User may want to configure it like this: + # + # Ransack.configure do |c| + # c.postgres_fields_sort_option = :nulls_first # or e.g. :nulls_always_last + # end + # + # See this feature: https://www.postgresql.org/docs/13/queries-order.html + # + def postgres_fields_sort_option=(setting) + self.options[:postgres_fields_sort_option] = setting + end + + # By default, Ransack displays sort order indicator arrows in sort links. + # The default may be globally overridden in an initializer file like + # `config/initializers/ransack.rb` as follows: + # + # Ransack.configure do |config| + # # Hide sort link order indicators globally across the application + # config.hide_sort_order_indicators = true + # end + # + def hide_sort_order_indicators=(boolean) + self.options[:hide_sort_order_indicators] = boolean + end + + # By default, Ransack displays strips all whitespace when searching for a string. + # The default may be globally changed in an initializer file like + # `config/initializers/ransack.rb` as follows: + # + # Ransack.configure do |config| + # # Enable whitespace stripping for string searches + # config.strip_whitespace = true + # end + # + def strip_whitespace=(boolean) + self.options[:strip_whitespace] = boolean + end + def arel_predicate_with_suffix(arel_predicate, suffix) if arel_predicate === Proc proc { |v| "#{arel_predicate.call(v)}#{suffix}" } diff --git a/lib/ransack/constants.rb b/lib/ransack/constants.rb index 6fe57cac6..027e87be3 100644 --- a/lib/ransack/constants.rb +++ b/lib/ransack/constants.rb @@ -1,23 +1,23 @@ module Ransack module Constants - ASC = 'asc'.freeze - DESC = 'desc'.freeze - ASC_ARROW = '▲'.freeze - DESC_ARROW = '▼'.freeze OR = 'or'.freeze AND = 'and'.freeze - SORT = 'sort'.freeze - SORT_LINK = 'sort_link'.freeze + + CAP_SEARCH = 'Search'.freeze SEARCH = 'search'.freeze - DEFAULT_SEARCH_KEY = 'q'.freeze + SEARCHES = 'searches'.freeze + ATTRIBUTE = 'attribute'.freeze - DISTINCT = 'DISTINCT '.freeze + ATTRIBUTES = 'attributes'.freeze COMBINATOR = 'combinator'.freeze - SPACE = ' '.freeze - COMMA_SPACE = ', '.freeze + + TWO_COLONS = '::'.freeze UNDERSCORE = '_'.freeze - NON_BREAKING_SPACE = ' '.freeze - EMPTY = ''.freeze + LEFT_PARENTHESIS = '('.freeze + Q = 'q'.freeze + I = 'i'.freeze + DOT_ASTERIX = '.*'.freeze + STRING_JOIN = 'string_join'.freeze ASSOCIATION_JOIN = 'association_join'.freeze STASHED_JOIN = 'stashed_join'.freeze @@ -25,125 +25,136 @@ module Constants TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set - BOOLEAN_VALUES = TRUE_VALUES + FALSE_VALUES + BOOLEAN_VALUES = (TRUE_VALUES + FALSE_VALUES).freeze - S_SORTS = %w(s sorts).freeze - ASC_DESC = %w(asc desc).freeze - AND_OR = %w(and or).freeze - IN_NOT_IN = %w(in not_in).freeze - SUFFIXES = %w(_any _all).freeze - AREL_PREDICATES = %w( - eq not_eq matches does_not_match lt lteq gt gteq in not_in - ).freeze + AND_OR = ['and'.freeze, 'or'.freeze].freeze + IN_NOT_IN = ['in'.freeze, 'not_in'.freeze].freeze + SUFFIXES = ['_any'.freeze, '_all'.freeze].freeze + AREL_PREDICATES = [ + 'eq'.freeze, 'not_eq'.freeze, + 'matches'.freeze, 'does_not_match'.freeze, + 'lt'.freeze, 'lteq'.freeze, + 'gt'.freeze, 'gteq'.freeze, + 'in'.freeze, 'not_in'.freeze + ].freeze + A_S_I = ['a'.freeze, 's'.freeze, 'i'.freeze].freeze EQ = 'eq'.freeze NOT_EQ = 'not_eq'.freeze EQ_ANY = 'eq_any'.freeze NOT_EQ_ALL = 'not_eq_all'.freeze + CONT = 'cont'.freeze + + RANSACK_SLASH_SEARCHES = 'ransack/searches'.freeze + RANSACK_SLASH_SEARCHES_SLASH_SEARCH = 'ransack/searches/search'.freeze + + DISTINCT = 'DISTINCT '.freeze DERIVED_PREDICATES = [ - ['cont'.freeze, { - :arel_predicate => 'matches'.freeze, - :formatter => proc { |v| "%#{escape_wildcards(v)}%" } + [CONT, { + arel_predicate: 'matches'.freeze, + formatter: proc { |v| "%#{escape_wildcards(v)}%" } } ], - ['i_cont'.freeze, { - :arel_predicate => 'i_matches'.freeze, - :formatter => proc { |v| "%#{escape_wildcards(v)}%" } - } - ], ['not_cont'.freeze, { - :arel_predicate => 'does_not_match'.freeze, - :formatter => proc { |v| "%#{escape_wildcards(v)}%" } + arel_predicate: 'does_not_match'.freeze, + formatter: proc { |v| "%#{escape_wildcards(v)}%" } + } + ], + ['i_cont'.freeze, { + arel_predicate: 'matches'.freeze, + formatter: proc { |v| "%#{escape_wildcards(v.downcase)}%" }, + case_insensitive: true } ], - ['i_not_cont'.freeze, { - :arel_predicate => 'i_does_not_match'.freeze, - :formatter => proc { |v| "%#{escape_wildcards(v)}%" } - } + ['not_i_cont'.freeze, { + arel_predicate: 'does_not_match'.freeze, + formatter: proc { |v| "%#{escape_wildcards(v.downcase)}%" }, + case_insensitive: true + } ], ['start'.freeze, { - :arel_predicate => 'matches'.freeze, - :formatter => proc { |v| "#{escape_wildcards(v)}%" } + arel_predicate: 'matches'.freeze, + formatter: proc { |v| "#{escape_wildcards(v)}%" } } ], ['not_start'.freeze, { - :arel_predicate => 'does_not_match'.freeze, - :formatter => proc { |v| "#{escape_wildcards(v)}%" } + arel_predicate: 'does_not_match'.freeze, + formatter: proc { |v| "#{escape_wildcards(v)}%" } } ], ['end'.freeze, { - :arel_predicate => 'matches'.freeze, - :formatter => proc { |v| "%#{escape_wildcards(v)}" } + arel_predicate: 'matches'.freeze, + formatter: proc { |v| "%#{escape_wildcards(v)}" } } ], ['not_end'.freeze, { - :arel_predicate => 'does_not_match'.freeze, - :formatter => proc { |v| "%#{escape_wildcards(v)}" } + arel_predicate: 'does_not_match'.freeze, + formatter: proc { |v| "%#{escape_wildcards(v)}" } } ], ['true'.freeze, { - :arel_predicate => proc { |v| v ? EQ : NOT_EQ }, - :compounds => false, - :type => :boolean, - :validator => proc { |v| BOOLEAN_VALUES.include?(v) }, - :formatter => proc { |v| true } + arel_predicate: proc { |v| v ? EQ : NOT_EQ }, + compounds: false, + type: :boolean, + validator: proc { |v| BOOLEAN_VALUES.include?(v) }, + formatter: proc { |v| true } } ], ['not_true'.freeze, { - :arel_predicate => proc { |v| v ? NOT_EQ : EQ }, - :compounds => false, - :type => :boolean, - :validator => proc { |v| BOOLEAN_VALUES.include?(v) }, - :formatter => proc { |v| true } + arel_predicate: proc { |v| v ? NOT_EQ : EQ }, + compounds: false, + type: :boolean, + validator: proc { |v| BOOLEAN_VALUES.include?(v) }, + formatter: proc { |v| true } } ], ['false'.freeze, { - :arel_predicate => proc { |v| v ? EQ : NOT_EQ }, - :compounds => false, - :type => :boolean, - :validator => proc { |v| BOOLEAN_VALUES.include?(v) }, - :formatter => proc { |v| false } + arel_predicate: proc { |v| v ? EQ : NOT_EQ }, + compounds: false, + type: :boolean, + validator: proc { |v| BOOLEAN_VALUES.include?(v) }, + formatter: proc { |v| false } } ], ['not_false'.freeze, { - :arel_predicate => proc { |v| v ? NOT_EQ : EQ }, - :compounds => false, - :type => :boolean, - :validator => proc { |v| BOOLEAN_VALUES.include?(v) }, - :formatter => proc { |v| false } + arel_predicate: proc { |v| v ? NOT_EQ : EQ }, + compounds: false, + type: :boolean, + validator: proc { |v| BOOLEAN_VALUES.include?(v) }, + formatter: proc { |v| false } } ], ['present'.freeze, { - :arel_predicate => proc { |v| v ? NOT_EQ_ALL : EQ_ANY }, - :compounds => false, - :type => :boolean, - :validator => proc { |v| BOOLEAN_VALUES.include?(v) }, - :formatter => proc { |v| [nil, Ransack::Constants::EMPTY] } + arel_predicate: proc { |v| v ? NOT_EQ_ALL : EQ_ANY }, + compounds: false, + type: :boolean, + validator: proc { |v| BOOLEAN_VALUES.include?(v) }, + formatter: proc { |v| [nil, ''.freeze].freeze } } ], ['blank'.freeze, { - :arel_predicate => proc { |v| v ? EQ_ANY : NOT_EQ_ALL }, - :compounds => false, - :type => :boolean, - :validator => proc { |v| BOOLEAN_VALUES.include?(v) }, - :formatter => proc { |v| [nil, Ransack::Constants::EMPTY] } + arel_predicate: proc { |v| v ? EQ_ANY : NOT_EQ_ALL }, + compounds: false, + type: :boolean, + validator: proc { |v| BOOLEAN_VALUES.include?(v) }, + formatter: proc { |v| [nil, ''.freeze].freeze } } ], ['null'.freeze, { - :arel_predicate => proc { |v| v ? EQ : NOT_EQ }, - :compounds => false, - :type => :boolean, - :validator => proc { |v| BOOLEAN_VALUES.include?(v)}, - :formatter => proc { |v| nil } + arel_predicate: proc { |v| v ? EQ : NOT_EQ }, + compounds: false, + type: :boolean, + validator: proc { |v| BOOLEAN_VALUES.include?(v) }, + formatter: proc { |v| nil } } ], ['not_null'.freeze, { - :arel_predicate => proc { |v| v ? NOT_EQ : EQ }, - :compounds => false, - :type => :boolean, - :validator => proc { |v| BOOLEAN_VALUES.include?(v) }, - :formatter => proc { |v| nil } } + arel_predicate: proc { |v| v ? NOT_EQ : EQ }, + compounds: false, + type: :boolean, + validator: proc { |v| BOOLEAN_VALUES.include?(v) }, + formatter: proc { |v| nil } } ] ].freeze @@ -151,9 +162,12 @@ module Constants # replace % \ to \% \\ def escape_wildcards(unescaped) case ActiveRecord::Base.connection.adapter_name - when "Mysql2".freeze, "PostgreSQL".freeze - # Necessary for PostgreSQL and MySQL - unescaped.to_s.gsub(/([\\|\%|.])/, '\\\\\\1') + when "Mysql2".freeze + # Necessary for MySQL + unescaped.to_s.gsub(/([\\%_])/, '\\\\\\1') + when "PostGIS".freeze, "PostgreSQL".freeze + # Necessary for PostgreSQL + unescaped.to_s.gsub(/([\\%_.])/, '\\\\\\1') else unescaped end diff --git a/lib/ransack/context.rb b/lib/ransack/context.rb index 0b9317da7..a55ef2735 100644 --- a/lib/ransack/context.rb +++ b/lib/ransack/context.rb @@ -2,19 +2,12 @@ module Ransack class Context - attr_reader :object, :klass, :base, :engine, :arel_visitor + attr_reader :search, :object, :klass, :base, :engine, :arel_visitor attr_accessor :auth_object, :search_key + attr_reader :arel_visitor class << self - def for(object, options = {}) - context = Class === object ? - for_class(object, options) : - for_object(object, options) - context or raise ArgumentError, - "Don't know what context to use for #{object}" - end - def for_class(klass, options = {}) if klass < ActiveRecord::Base Adapters::ActiveRecord::Context.new(klass, options) @@ -28,7 +21,18 @@ def for_object(object, options = {}) end end - end + def for(object, options = {}) + context = + if Class === object + for_class(object, options) + else + for_object(object, options) + end + context or raise ArgumentError, + "Don't know what context to use for #{object}" + end + + end # << self def initialize(object, options = {}) @object = relation_for(object) @@ -36,23 +40,19 @@ def initialize(object, options = {}) @join_dependency = join_dependency(@object) @join_type = options[:join_type] || Polyamorous::OuterJoin @search_key = options[:search_key] || Ransack.options[:search_key] + @associations_pot = {} + @tables_pot = {} + @lock_associations = [] - if ::ActiveRecord::VERSION::STRING >= "4.1".freeze - @base = @join_dependency.join_root - @engine = @base.base_klass.arel_engine - else - @base = @join_dependency.join_base - @engine = @base.arel_engine - end + @base = @join_dependency.instance_variable_get(:@join_root) + end + + def bind_pair_for(key) + @bind_pairs ||= {} - @default_table = Arel::Table.new( - @base.table_name, :as => @base.aliased_table_name, :engine => @engine - ) - @bind_pairs = Hash.new do |hash, key| + @bind_pairs[key] ||= begin parent, attr_name = get_parent_and_attribute_name(key.to_s) - if parent && attr_name - hash[key] = [parent, attr_name] - end + [parent, attr_name] if parent && attr_name end end @@ -61,10 +61,6 @@ def klassify(obj) obj elsif obj.respond_to? :klass obj.klass - elsif obj.respond_to? :active_record # Rails 3 - obj.active_record - elsif obj.respond_to? :base_klass # Rails 4 - obj.base_klass else raise ArgumentError, "Don't know how to klassify #{obj.inspect}" end @@ -73,7 +69,7 @@ def klassify(obj) # Convert a string representing a chain of associations and an attribute # into the attribute itself def contextualize(str) - parent, attr_name = @bind_pairs[str] + parent, attr_name = bind_pair_for(str) table_for(parent)[attr_name] end @@ -81,6 +77,9 @@ def chain_scope(scope, args) return unless @klass.method(scope) && args != false @object = if scope_arity(scope) < 1 && args == true @object.public_send(scope) + elsif scope_arity(scope) == 1 && args.is_a?(Array) + # For scopes with arity 1, pass the array as a single argument instead of splatting + @object.public_send(scope, args) else @object.public_send(scope, *args) end @@ -91,31 +90,34 @@ def scope_arity(scope) end def bind(object, str) - object.parent, object.attr_name = @bind_pairs[str] + return nil unless str + object.parent, object.attr_name = bind_pair_for(str) end def traverse(str, base = @base) - str ||= Ransack::Constants::EMPTY - - if (segments = str.split(/_/)).size > 0 + str ||= ''.freeze + segments = str.split(Constants::UNDERSCORE) + unless segments.empty? remainder = [] found_assoc = nil - while !found_assoc && segments.size > 0 do + until found_assoc || segments.empty? # Strip the _of_Model_type text from the association name, but hold # onto it in klass, for use as the next base - assoc, klass = unpolymorphize_association(segments - .join(Ransack::Constants::UNDERSCORE)) + assoc, klass = unpolymorphize_association( + segments.join(Constants::UNDERSCORE) + ) if found_assoc = get_association(assoc, base) base = traverse( - remainder.join( - Ransack::Constants::UNDERSCORE), klass || found_assoc.klass - ) + remainder.join(Constants::UNDERSCORE), klass || found_assoc.klass + ) end remainder.unshift segments.pop end - raise UntraversableAssociationError, - "No association matches #{str}" unless found_assoc + unless found_assoc + raise(UntraversableAssociationError, + "No association matches #{str}") + end end klassify(base) @@ -123,26 +125,25 @@ def traverse(str, base = @base) def association_path(str, base = @base) base = klassify(base) - str ||= Ransack::Constants::EMPTY + str ||= ''.freeze path = [] - segments = str.split(/_/) + segments = str.split(Constants::UNDERSCORE) association_parts = [] - if (segments = str.split(/_/)).size > 0 - while segments.size > 0 && - !base.columns_hash[segments.join(Ransack::Constants::UNDERSCORE)] && - association_parts << segments.shift do + unless segments.empty? + while !segments.empty? && + !base.columns_hash[segments.join(Constants::UNDERSCORE)] && + association_parts << segments.shift assoc, klass = unpolymorphize_association( - association_parts.join(Ransack::Constants::UNDERSCORE) - ) - if found_assoc = get_association(assoc, base) - path += association_parts - association_parts = [] - base = klassify(klass || found_assoc) - end + association_parts.join(Constants::UNDERSCORE) + ) + next unless found_assoc = get_association(assoc, base) + path += association_parts + association_parts = [] + base = klassify(klass || found_assoc) end end - path.join(Ransack::Constants::UNDERSCORE) + path.join(Constants::UNDERSCORE) end def unpolymorphize_association(str) @@ -153,28 +154,36 @@ def unpolymorphize_association(str) end end + def ransackable_alias(str) + klass._ransack_aliases.fetch(str, klass._ransack_aliases.fetch(str.to_sym, str)) + end + def ransackable_attribute?(str, klass) - klass.ransackable_attributes(auth_object).include?(str) || - klass.ransortable_attributes(auth_object).include?(str) + klass.ransackable_attributes(auth_object).any? { |s| s.to_sym == str.to_sym } || + klass.ransortable_attributes(auth_object).any? { |s| s.to_sym == str.to_sym } end def ransackable_association?(str, klass) - klass.ransackable_associations(auth_object).include? str + klass.ransackable_associations(auth_object).any? { |s| s.to_sym == str.to_sym } end def ransackable_scope?(str, klass) - klass.ransackable_scopes(auth_object).any? { |s| s.to_s == str } + klass.ransackable_scopes(auth_object).any? { |s| s.to_sym == str.to_sym } + end + + def ransackable_scope_skip_sanitize_args?(str, klass) + klass.ransackable_scopes_skip_sanitize_args.any? { |s| s.to_sym == str.to_sym } end - def searchable_attributes(str = Ransack::Constants::EMPTY) + def searchable_attributes(str = ''.freeze) traverse(str).ransackable_attributes(auth_object) end - def sortable_attributes(str = Ransack::Constants::EMPTY) + def sortable_attributes(str = ''.freeze) traverse(str).ransortable_attributes(auth_object) end - def searchable_associations(str = Ransack::Constants::EMPTY) + def searchable_associations(str = ''.freeze) traverse(str).ransackable_associations(auth_object) end end diff --git a/lib/ransack/helpers.rb b/lib/ransack/helpers.rb index 6e0c616bb..799e53e74 100644 --- a/lib/ransack/helpers.rb +++ b/lib/ransack/helpers.rb @@ -1,2 +1,2 @@ require 'ransack/helpers/form_builder' -require 'ransack/helpers/form_helper' \ No newline at end of file +require 'ransack/helpers/form_helper' diff --git a/lib/ransack/helpers/form_builder.rb b/lib/ransack/helpers/form_builder.rb index def9f00b8..54bbb2756 100644 --- a/lib/ransack/helpers/form_builder.rb +++ b/lib/ransack/helpers/form_builder.rb @@ -1,10 +1,26 @@ require 'action_view' +module ActionView::Helpers::Tags + # TODO: Find a better way to solve this issue! + # This patch is needed since this Rails commit: + # https://github.com/rails/rails/commit/c1a118a + class Base + private + + def value + if @allow_method_names_outside_object + object.send @method_name if object && object.respond_to?(@method_name, true) + else + object.send @method_name if object + end + end + end +end + RANSACK_FORM_BUILDER = 'RANSACK_FORM_BUILDER'.freeze require 'simple_form' if - (ENV[RANSACK_FORM_BUILDER] || Ransack::Constants::EMPTY) - .match('SimpleForm'.freeze) + (ENV[RANSACK_FORM_BUILDER] || ''.freeze).match('SimpleForm'.freeze) module Ransack module Helpers @@ -16,7 +32,7 @@ def label(method, *args, &block) text = args.first i18n = options[:i18n] || {} text ||= object.translate( - method, i18n.reverse_merge(:include_associations => true) + method, i18n.reverse_merge(include_associations: true) ) if object.respond_to? :translate super(method, text, options, &block) end @@ -28,15 +44,14 @@ def submit(value = nil, options = {}) end def attribute_select(options = nil, html_options = nil, action = nil) - options = options || {} - html_options = html_options || {} - action = action || Ransack::Constants::SEARCH + options ||= {} + html_options ||= {} + action ||= Constants::SEARCH default = options.delete(:default) raise ArgumentError, formbuilder_error_message( "#{action}_select") unless object.respond_to?(:context) options[:include_blank] = true unless options.has_key?(:include_blank) - bases = [Ransack::Constants::EMPTY] + - association_array(options[:associations]) + bases = [''.freeze].freeze + association_array(options[:associations]) if bases.size > 1 collection = attribute_collection_for_bases(action, bases) object.name ||= default if can_use_default?( @@ -53,13 +68,15 @@ def attribute_select(options = nil, html_options = nil, action = nil) end def sort_direction_select(options = {}, html_options = {}) - raise ArgumentError, formbuilder_error_message( - 'sort_direction'.freeze) unless object.respond_to?(:context) + unless object.respond_to?(:context) + raise ArgumentError, + formbuilder_error_message('sort_direction'.freeze) + end template_collection_select(:dir, sort_array, options, html_options) end def sort_select(options = {}, html_options = {}) - attribute_select(options, html_options, Ransack::Constants::SORT) + + attribute_select(options, html_options, 'sort'.freeze) + sort_direction_select(options, html_options) end @@ -71,6 +88,10 @@ def sort_link(attribute, *args) @template.sort_link @object, attribute, *args end + def sort_url(http://github.com/attribute,%20*args) + @template.sort_url @object, attribute, *args + end + def condition_fields(*args, &block) search_fields(:c, args, block) end @@ -100,35 +121,35 @@ def search_fields(name, args, block) objects ||= @object.send(name) objects = [objects] unless Array === objects name = "#{options[:object_name] || object_name}[#{name}]" - output = ActiveSupport::SafeBuffer.new - objects.each do |child| - output << @template.fields_for("#{name}[#{ - options[:child_index] || nested_child_index(name) - }]", child, options, &block) + objects.inject(ActiveSupport::SafeBuffer.new) do |output, child| + output << @template.fields_for("#{name}[#{options[:child_index] || + nested_child_index(name)}]", child, options, &block) end - output end def predicate_select(options = {}, html_options = {}) options[:compounds] = true if options[:compounds].nil? - default = options.delete(:default) || 'cont'.freeze + default = options.delete(:default) || Constants::CONT - keys = options[:compounds] ? Predicate.names : + keys = + if options[:compounds] + Predicate.names + else Predicate.names.reject { |k| k.match(/_(any|all)$/) } + end if only = options[:only] if only.respond_to? :call keys = keys.select { |k| only.call(k) } else only = Array.wrap(only).map(&:to_s) keys = keys.select { - |k| only.include? k.sub(/_(any|all)$/, Ransack::Constants::EMPTY) + |k| only.include? k.sub(/_(any|all)$/, ''.freeze) } end end collection = keys.map { |k| [k, Translate.predicate(k)] } - object.predicate ||= Predicate.named(default) if can_use_default?( - default, :predicate, keys - ) + object.predicate ||= Predicate.named(default) if + can_use_default?(default, :predicate, keys) template_collection_select(:p, collection, options, html_options) end @@ -164,21 +185,21 @@ def mapped_values(values) def sort_array [ - [Ransack::Constants::ASC, object.translate(Ransack::Constants::ASC)], - [Ransack::Constants::DESC, object.translate(Ransack::Constants::DESC)] - ] + ['asc'.freeze, object.translate('asc'.freeze)].freeze, + ['desc'.freeze, object.translate('desc'.freeze)].freeze + ].freeze end def combinator_choices if Nodes::Condition === object [ - [Ransack::Constants::OR, Translate.word(:any)], - [Ransack::Constants::AND, Translate.word(:all)] + [Constants::OR, Translate.word(:any)], + [Constants::AND, Translate.word(:all)] ] else [ - [Ransack::Constants::AND, Translate.word(:all)], - [Ransack::Constants::OR, Translate.word(:any)] + [Constants::AND, Translate.word(:all)], + [Constants::OR, Translate.word(:any)] ] end end @@ -186,8 +207,7 @@ def combinator_choices def association_array(obj, prefix = nil) ([prefix] + association_object(obj)) .compact - .flatten - .map { |v| [prefix, v].compact.join(Ransack::Constants::UNDERSCORE) } + .flat_map { |v| [prefix, v].compact.join(Constants::UNDERSCORE) } end def association_object(obj) @@ -207,7 +227,7 @@ def association_hash(obj) when Array, Hash association_array(value, key.to_s) else - [key.to_s, [key, value].join(Ransack::Constants::UNDERSCORE)] + [key.to_s, [key, value].join(Constants::UNDERSCORE)] end end end @@ -218,19 +238,21 @@ def attribute_collection_for_bases(action, bases) def get_attribute_element(action, base) begin - [Translate.association(base, :context => object.context), - collection_for_base(action, base)] - rescue UntraversableAssociationError => e + [ + Translate.association(base, context: object.context), + collection_for_base(action, base) + ] + rescue UntraversableAssociationError nil end end def attribute_collection_for_base(attributes, base = nil) attributes.map do |c| - [attr_from_base_and_column(base, c), + [ + attr_from_base_and_column(base, c), Translate.attribute( - attr_from_base_and_column(base, c), - :context => object.context + attr_from_base_and_column(base, c), context: object.context ) ] end @@ -242,13 +264,11 @@ def collection_for_base(action, base) end def attr_from_base_and_column(base, column) - [base, column].reject { |v| v.blank? } - .join(Ransack::Constants::UNDERSCORE) + [base, column].reject(&:blank?).join(Constants::UNDERSCORE) end def formbuilder_error_message(action) - "#{ - action.sub(Ransack::Constants::SEARCH, Ransack::Constants::ATTRIBUTE) + "#{action.sub(Constants::SEARCH, Constants::ATTRIBUTE) } must be called inside a search FormBuilder!" end diff --git a/lib/ransack/helpers/form_helper.rb b/lib/ransack/helpers/form_helper.rb index 421c43064..90665da84 100644 --- a/lib/ransack/helpers/form_helper.rb +++ b/lib/ransack/helpers/form_helper.rb @@ -2,161 +2,310 @@ module Ransack module Helpers module FormHelper + # +search_form_for+ + # + # <%= search_form_for(@q) do |f| %> + # def search_form_for(record, options = {}, &proc) - if record.is_a?(Ransack::Search) - search = record - options[:url] ||= polymorphic_path( - search.klass, format: options.delete(:format) - ) - elsif record.is_a?(Array) && - (search = record.detect { |o| o.is_a?(Ransack::Search) }) - options[:url] ||= polymorphic_path( - record.map { |o| o.is_a?(Ransack::Search) ? o.klass : o }, - format: options.delete(:format) - ) + search = extract_search_and_set_url(http://github.com/record,%20options,%20'search_form_for') + options[:html] ||= {} + html_options = build_html_options(search, options, :get) + finalize_form_options(options, html_options) + form_for(record, options, &proc) + end + + # +search_form_with+ + # + # <%= search_form_with(model: @q) do |f| %> + # + def search_form_with(record_or_options = {}, options = {}, &proc) + if record_or_options.is_a?(Hash) && record_or_options.key?(:model) + # Called with keyword arguments: search_form_with(model: @q) + options = record_or_options + record = options.delete(:model) else - raise ArgumentError, - "No Ransack::Search object was provided to search_form_for!" + # Called with positional arguments: search_form_with(@q) + record = record_or_options end + search = extract_search_and_set_url(http://github.com/record,%20options,%20'search_form_with') options[:html] ||= {} - html_options = { - :class => options[:class].present? ? - "#{options[:class]}" : - "#{search.klass.to_s.underscore}_search", - :id => options[:id].present? ? - "#{options[:id]}" : - "#{search.klass.to_s.underscore}_search", - :method => :get - } - options[:as] ||= Ransack::Constants::DEFAULT_SEARCH_KEY - options[:html].reverse_merge!(html_options) - options[:builder] ||= FormBuilder + html_options = build_html_options(search, options, :get) + finalize_form_with_options(options, html_options) + form_with(model: search, **options, &proc) + end + # +turbo_search_form_for+ + # + # <%= turbo_search_form_for(@q) do |f| %> + # + # This is a turbo-enabled version of search_form_for that submits via turbo streams + # instead of traditional HTML GET requests. Useful for seamless integration with + # paginated results and other turbo-enabled components. + # + def turbo_search_form_for(record, options = {}, &proc) + search = extract_search_and_set_url(http://github.com/record,%20options,%20'turbo_search_form_for') + options[:html] ||= {} + turbo_options = build_turbo_options(options) + method = options.delete(:method) || :post + html_options = build_html_options(search, options, method).merge(turbo_options) + finalize_form_options(options, html_options) form_for(record, options, &proc) end - # sort_link @q, :name, [:name, 'kind ASC'], 'Player Name' - def sort_link(search, attribute, *args) - # Extract out a routing proxy for url_for scoping later - if search.is_a?(Array) - routing_proxy = search.shift - search = search.first + # +sort_link+ + # + # <%= sort_link(@q, :name, [:name, 'kind ASC'], 'Player Name') %> + # + # You can also use a block: + # + # <%= sort_link(@q, :name, [:name, 'kind ASC']) do %> + # Player Name + # <% end %> + # + def sort_link(search_object, attribute, *args, &block) + search, routing_proxy = extract_search_and_routing_proxy(search_object) + unless Search === search + raise TypeError, 'First argument must be a Ransack::Search!' end + args[args.first.is_a?(Array) ? 1 : 0, 0] = capture(&block) if block_given? + s = SortLink.new(search, attribute, args, params, &block) + link_to(s.name, url(http://github.com/routing_proxy,%20s.url_options), s.html_options(args)) + end - raise TypeError, "First argument must be a Ransack::Search!" unless - Search === search + # +sort_url+ + # <%= sort_url(http://github.com/@q,%20:created_at,%20default_order:%20:desc) %> + # + def sort_url(http://github.com/search_object,%20attribute,%20*args) + search, routing_proxy = extract_search_and_routing_proxy(search_object) + unless Search === search + raise TypeError, 'First argument must be a Ransack::Search!' + end + s = SortLink.new(search, attribute, args, params) + url(http://github.com/routing_proxy,%20s.url_options) + end - # This is the field that this link represents. The direction of the sort icon (up/down arrow) will - # depend on the sort status of this field - field_name = attribute.to_s + private - # Determine the fields we want to sort on - sort_fields = if Array === args.first - args.shift - else - Array(field_name) + def extract_search_and_set_url(http://github.com/record,%20options,%20method_name) + if record.is_a? Ransack::Search + search = record + options[:url] ||= polymorphic_path( + search.klass, format: options.delete(:format) + ) + search + elsif record.is_a?(Array) && + (search = record.detect { |o| o.is_a?(Ransack::Search) }) + options[:url] ||= polymorphic_path( + options_for(record), format: options.delete(:format) + ) + search + else + raise ArgumentError, + "No Ransack::Search object was provided to #{method_name}!" + end end - label_text = - if String === args.first - args.shift.to_s - else - Translate.attribute(field_name, :context => search.context) + def build_turbo_options(options) + data_options = {} + if options[:turbo_frame] + data_options[:turbo_frame] = options.delete(:turbo_frame) end + data_options[:turbo_action] = options.delete(:turbo_action) || 'advance' + { data: data_options } + end + + def build_html_options(search, options, method) + { + class: html_option_for(options[:class], search), + id: html_option_for(options[:id], search), + method: method + } + end - options = args.first.is_a?(Hash) ? args.shift.dup : {} - default_order = options.delete :default_order - default_order_is_a_hash = Hash === default_order + def finalize_form_options(options, html_options) + options[:as] ||= Ransack.options[:search_key] + options[:html].reverse_merge!(html_options) + options[:builder] ||= FormBuilder + end - # If the default order is a hash of fields, duplicate it and let us access it with strings or symbols - default_order = default_order.dup.with_indifferent_access if - default_order_is_a_hash + def finalize_form_with_options(options, html_options) + options[:scope] ||= Ransack.options[:search_key] + options[:html].reverse_merge!(html_options) + options[:builder] ||= FormBuilder + end - search_params = params[search.context.search_key].presence || - {}.with_indifferent_access + def options_for(record) + record.map { |r| parse_record(r) } + end - # Find the current direction (if there is one) of the primary sort field - if existing_sort = search.sorts.detect { |s| s.name == field_name } - field_current_dir = existing_sort.dir + def parse_record(object) + return object.klass if object.is_a?(Ransack::Search) + object end - sort_params = [] + def html_option_for(option, search) + return option.to_s if option.present? + "#{search.klass.to_s.underscore}_search" + end - Array(sort_fields).each do |sort_field| - attr_name, new_dir = sort_field.to_s.downcase.split(/\s+/) - current_dir = nil + def extract_search_and_routing_proxy(search) + return [search[1], search[0]] if search.is_a?(Array) + [search, nil] + end - # if the user didn't specify the sort direction, detect the previous - # sort direction on this field and reverse it - if Ransack::Constants::ASC_DESC.none? { |d| d == new_dir } - if existing_sort = search.sorts.detect { |s| s.name == attr_name } - current_dir = existing_sort.dir - end + def url(http://github.com/routing_proxy,%20options_for_url) + if routing_proxy && respond_to?(routing_proxy) + send(routing_proxy).url_for(options_for_url) + else + url_for(options_for_url) + end + end - new_dir = - if current_dir - if current_dir == Ransack::Constants::DESC - Ransack::Constants::ASC - else - Ransack::Constants::DESC - end - elsif default_order_is_a_hash - default_order[attr_name] || Ransack::Constants::ASC - else - default_order || Ransack::Constants::ASC - end - end - - sort_params << "#{attr_name} #{new_dir}" - end - - # if there is only one sort parameter, remove it from the array and just - # use the string as the parameter - sort_params = sort_params.first if sort_params.size == 1 - - html_options = args.first.is_a?(Hash) ? args.shift.dup : {} - css = [Ransack::Constants::SORT_LINK, field_current_dir] - .compact.join(Ransack::Constants::SPACE) - html_options[:class] = [css, html_options[:class]] - .compact.join(Ransack::Constants::SPACE) - - query_hash = {} - query_hash[search.context.search_key] = search_params - .merge(:s => sort_params) - options.merge!(query_hash) - options_for_url = params.merge(options) - - url = - if routing_proxy && respond_to?(routing_proxy) - send(routing_proxy).url_for(options_for_url) - else - url_for(options_for_url) + class SortLink + def initialize(search, attribute, args, params) + @search = search + @params = parameters_hash(params) + @field = attribute.to_s + @sort_fields = extract_sort_fields_and_mutate_args!(args).compact + @current_dir = existing_sort_direction + @label_text = extract_label_and_mutate_args!(args) + @options = extract_options_and_mutate_args!(args) + @hide_indicator = @options.delete(:hide_indicator) || + Ransack.options[:hide_sort_order_indicators] + @default_order = @options.delete :default_order end - name = link_name(label_text, field_current_dir) + def up_arrow + Ransack.options[:up_arrow] + end - link_to(name, url, html_options) - end + def down_arrow + Ransack.options[:down_arrow] + end - private + def default_arrow + Ransack.options[:default_arrow] + end - def link_name(label_text, dir) - [ERB::Util.h(label_text), order_indicator_for(dir)] - .compact - .join(Ransack::Constants::NON_BREAKING_SPACE) - .html_safe - end + def name + [ERB::Util.h(@label_text), order_indicator] + .compact + .join(' '.freeze) + .html_safe + end - def order_indicator_for(dir) - if dir == Ransack::Constants::ASC - Ransack::Constants::ASC_ARROW - elsif dir == Ransack::Constants::DESC - Ransack::Constants::DESC_ARROW - else - nil + def url_options + @params.except(:host).merge( + @options.except(:class, :data, :host).merge( + @search.context.search_key => search_and_sort_params)) end - end + def html_options(args) + if args.empty? + html_options = @options + else + deprecation_message = "Passing two trailing hashes to `sort_link` is deprecated, merge the trailing hashes into a single one." + caller_location = caller_locations(2, 2).first + warn "#{deprecation_message} (called at #{caller_location.path}:#{caller_location.lineno})" + html_options = extract_options_and_mutate_args!(args) + end + + html_options.merge( + class: [['sort_link'.freeze, @current_dir], html_options[:class]] + .compact.join(' '.freeze) + ) + end + + private + + def parameters_hash(params) + if params.respond_to?(:to_unsafe_h) + params.to_unsafe_h + else + params + end + end + + def extract_sort_fields_and_mutate_args!(args) + return args.shift if args[0].is_a?(Array) + [@field] + end + + def extract_label_and_mutate_args!(args) + return args.shift if args[0].is_a?(String) + Translate.attribute(@field, context: @search.context) + end + + def extract_options_and_mutate_args!(args) + return args.shift.with_indifferent_access if args[0].is_a?(Hash) + {} + end + + def search_and_sort_params + search_params.merge(s: sort_params) + end + + def search_params + query_params = @params[@search.context.search_key] + query_params.is_a?(Hash) ? query_params : {} + end + + def sort_params + sort_array = recursive_sort_params_build(@sort_fields) + return sort_array[0] if sort_array.length == 1 + sort_array + end + + def recursive_sort_params_build(fields) + return [] if fields.empty? + [parse_sort(fields[0])] + recursive_sort_params_build(fields.drop 1) + end + + def parse_sort(field) + attr_name, new_dir = field.to_s.split(/\s+/) + if no_sort_direction_specified?(new_dir) + new_dir = detect_previous_sort_direction_and_invert_it(attr_name) + end + "#{attr_name} #{new_dir}" + end + + def detect_previous_sort_direction_and_invert_it(attr_name) + if sort_dir = existing_sort_direction(attr_name) + direction_text(sort_dir) + else + default_sort_order(attr_name) || 'asc'.freeze + end + end + + def existing_sort_direction(f = @field) + return unless sort = @search.sorts.detect { |s| s && s.name == f } + sort.dir + end + + def default_sort_order(attr_name) + return @default_order[attr_name] if Hash === @default_order + @default_order + end + + def order_indicator + return if @hide_indicator + return default_arrow if no_sort_direction_specified? + if @current_dir == 'desc'.freeze + up_arrow + else + down_arrow + end + end + + def no_sort_direction_specified?(dir = @current_dir) + dir != 'asc'.freeze && dir != 'desc'.freeze + end + + def direction_text(dir) + return 'asc'.freeze if dir == 'desc'.freeze + 'desc'.freeze + end + end end end end diff --git a/lib/ransack/invalid_search_error.rb b/lib/ransack/invalid_search_error.rb new file mode 100644 index 000000000..dc57960cb --- /dev/null +++ b/lib/ransack/invalid_search_error.rb @@ -0,0 +1,3 @@ +module Ransack + class InvalidSearchError < ArgumentError; end +end diff --git a/lib/ransack/locale/ar.yml b/lib/ransack/locale/ar.yml new file mode 100644 index 000000000..568018472 --- /dev/null +++ b/lib/ransack/locale/ar.yml @@ -0,0 +1,70 @@ +ar: + ransack: + search: "بحث" + predicate: "فاعل" + and: "و" + or: "أو" + any: "أيُّ" + all: "كل" + combinator: "دالة توافقية" + attribute: "خاصية" + value: "قيمة" + condition: "شرط" + sort: "ترتيب" + asc: "تصاعدي" + desc: "تنازلي" + predicates: + eq: "معادل" + eq_any: "معادل على اﻷقل لواحد" + eq_all: "معادل للجميع" + not_eq: "ليس معادلا لـ" + not_eq_any: "ليس معادلا على اﻷقل لواحد" + not_eq_all: "ليس معادلا للجميع" + matches: "موائم" + matches_any: "موائم لواحد على اﻷقل" + matches_all: "موائم للجميع" + does_not_match: "لا يتواءم" + does_not_match_any: "لا يتواءم مع واحد على اﻷقل" + does_not_match_all: "لا يتواءم مع الجميع" + lt: "أصغر من" + lt_any: "أصغر لواحد على اﻷقل" + lt_all: "أصغر من الجميع" + lteq: "أصغر أو مساو لـ" + lteq_any: "أصغر أو مساو لواحد على اﻷقل" + lteq_all: "أصغر أو مساو للجميع" + gt: "أكبر من" + gt_any: "أكبر من واحد على اﻷقل" + gt_all: "أكبر من الجميع" + gteq: "أكبر أو مساو لـ" + gteq_any: "أكبر أو مساو لواحد على اﻷقل" + gteq_all: "أكبر أو مساو للجميع" + in: "متضمن لـ" + in_any: "متضمن لواحد على اﻷقل" + in_all: "متضمن للجميع" + not_in: "غير متضمن" + not_in_any: "غير متضمن لواحد على اﻷقل" + not_in_all: "غير متضمن للجميع" + cont: "محتو" + cont_any: "محتو لواحد على اﻷقل" + cont_all: "محتو لجميع" + not_cont: "غير محتو" + not_cont_any: "غير محتو لواحد على اﻷقل" + not_cont_all: "غير محتو للجميع" + start: "يبدأ بـ" + start_any: "يبدأ بواحد على اﻷقل" + start_all: "يبدأ بالجميع" + not_start: "لا يبدأ بـ" + not_start_any: "لا يبدأ بواحد على اﻷقل" + not_start_all: "لا يبدأ بالجميع" + end: "ينتهي بـ" + end_any: "ينتهي بواحد على اﻷقل" + end_all: "ينتهي بالجميع" + not_end: "لا ينتهي بـ" + not_end_any: "لا ينتهي بواحد على اﻷقل" + not_end_all: "لا ينتهي بالجميع" + 'true': "صحيح" + 'false': "خطأ" + present: "مستقبل" + blank: "فراغ" + 'null': "عدم" + not_null: "غير مساو لقيمة عدم" diff --git a/lib/ransack/locale/az.yml b/lib/ransack/locale/az.yml new file mode 100644 index 000000000..c6290a50c --- /dev/null +++ b/lib/ransack/locale/az.yml @@ -0,0 +1,70 @@ +az: + ransack: + search: "axtar" + predicate: "təsdiqlə" + and: "və" + or: "və ya" + any: "hər hansı" + all: "hamısı" + combinator: "birləşdirici" + attribute: "xüsusiyyət" + value: "dəyər" + condition: "şərt" + sort: "sırala" + asc: "artan" + desc: "azalan" + predicates: + eq: "bərabər" + eq_any: "hər hansı birinə bərabər" + eq_all: "hamısına bərabər" + not_eq: "bərabər deyil" + not_eq_any: "hər hansı birinə bərabər deyil" + not_eq_all: "heç birinə bərabər deyil" + matches: "uyğunlaşan" + matches_any: "hər hansı biri ilə uyğunlaşan" + matches_all: "hamısı ilə uyğunlaşan" + does_not_match: "uyğunlaşmayan" + does_not_match_any: "hər hansı biri ilə uyğunlaşmayan" + does_not_match_all: "heç biri ilə uyğunlaşmayan" + lt: "daha kiçik" + lt_any: "hər hansı birindən kiçik" + lt_all: "hamısından kiçik" + lteq: "daha kiçik və ya bərabər" + lteq_any: "daha kiçik və ya hər hansı birinə bərabər" + lteq_all: "daha kiçik və ya hamısına bərabər" + gt: "daha böyük " + gt_any: "hər hansı birindən daha böyük" + gt_all: "hamısından daha böyük" + gteq: "daha böyük və ya bərabər" + gteq_any: "daha böyük və ya hər hansı birine bərabər" + gteq_all: "daha böyük və ya hamısıne bərabər" + in: "içində" + in_any: "hər hansı birində" + in_all: "hamısında" + not_in: "içində deyil" + not_in_any: "hər hansı birində deyil" + not_in_all: "heç birində deyil" + cont: "ehtiva edən" + cont_any: "hər hansı birini ehtiva edən" + cont_all: "hamısını ehtiva edən" + not_cont: "ehtiva etməyən" + not_cont_any: "hər hansı birini ehtiva etməyən" + not_cont_all: "heç birini birini ehtiva etməyən" + start: "ilə başlayan" + start_any: "hər hansı biri ilə başlayan" + start_all: "hamısı ilə başlayan" + not_start: "ilə başlamayan" + not_start_any: "hər hansı biri ilə başlamayan" + not_start_all: "hiçbiri ilə başlamayan" + end: "ilə bitən" + end_any: "hər hansı biri ilə bitən" + end_all: "hamısı ilə bitən" + not_end: "ilə bitməyən" + not_end_any: "hər hansı biri ilə bitməyən" + not_end_all: "heç biri ilə bitməyən" + 'true': "doğru" + 'false': "yanlış" + present: "mövcüd" + blank: "boş" + 'null': "keçərsiz" + not_null: "keçərli" diff --git a/lib/ransack/locale/bg.yml b/lib/ransack/locale/bg.yml new file mode 100644 index 000000000..b006b42fe --- /dev/null +++ b/lib/ransack/locale/bg.yml @@ -0,0 +1,70 @@ +bg: + ransack: + search: "търсене" + predicate: "предикат" + and: "и" + or: "или" + any: "всякакво" + all: "всички" + combinator: "комбинатор" + attribute: "атрибут" + value: "стойност" + condition: "условие" + sort: "сортиране" + asc: "възходящ" + desc: "низходящ" + predicates: + eq: "се равнява на" + eq_any: "се равнява на всяко" + eq_all: "се равнява на всички" + not_eq: "не е равно на" + not_eq_any: "не е равно на никое от" + not_eq_all: "не е равно на всички" + matches: "съвпада" + matches_any: "съвпада с някое" + matches_all: "съвпада с всички" + does_not_match: "не съвпада" + does_not_match_any: "не съвпада с никое" + does_not_match_all: "не съвпада с всички" + lt: "по-малко от" + lt_any: "по-малко от някое" + lt_all: "по-малко от всички" + lteq: "по-малко или равно на" + lteq_any: "по-малко или равно на някое" + lteq_all: "по-малко или равно на всички" + gt: "по-голямо от" + gt_any: "по-голямо от всякое" + gt_all: "по-голямо от всички" + gteq: "по-голямо или равно на" + gteq_any: "по-голямо или равно на всякое" + gteq_all: "по-голямо или равно на всички" + in: "в" + in_any: "във всякое" + in_all: "във всички" + not_in: "не в" + not_in_any: "не в никое" + not_in_all: "не във всички" + cont: "съдържа" + cont_any: "съдържа всякое" + cont_all: "съдържа всички" + not_cont: "не съдържа" + not_cont_any: "не съдържа никое" + not_cont_all: "не съдържа всички" + start: "започва с" + start_any: "започва с всякое" + start_all: "започва с всички" + not_start: "не започва с" + not_start_any: "не започва с никое" + not_start_all: "не започва с всички" + end: "завършва с" + end_any: "завършва с всякое" + end_all: "завършва с всички" + not_end: "не завършва с" + not_end_any: "не завършва с никое" + not_end_all: "не завършва с всички" + 'true': "истина" + 'false': "неистина" + present: "присъства" + blank: "е празно" + 'null': "е нула" + not_null: "не е нула" diff --git a/lib/ransack/locale/ca.yml b/lib/ransack/locale/ca.yml new file mode 100644 index 000000000..a8d7f6f04 --- /dev/null +++ b/lib/ransack/locale/ca.yml @@ -0,0 +1,70 @@ +ca: + ransack: + search: "cercar" + predicate: "predicat" + and: "i" + or: "o" + any: "qualsevol" + all: "tots" + combinator: "combinador" + attribute: "atribut" + value: "valor" + condition: "condició" + sort: "ordenar" + asc: "ascendent" + desc: "descendent" + predicates: + eq: "és igual a" + eq_any: "és igual a qualsevol" + eq_all: "és igual a tots" + not_eq: "no és igual a" + not_eq_any: "no és igual a qualsevol" + not_eq_all: "no és igual a tots" + matches: "coincideix" + matches_any: "coincideix a qualsevol" + matches_all: "coincideix a tots" + does_not_match: "no coincideix" + does_not_match_any: "no coincideix amb cap" + does_not_match_all: "no coincideix amb tots" + lt: "menor que" + lt_any: "menor que qualsevol" + lt_all: "menor o igual a" + lteq: "menor que o igual a" + lteq_any: "menor o igual a qualsevol" + lteq_all: "menor o igual a tots" + gt: "major que" + gt_any: "major que qualsevol" + gt_all: "major que tots" + gteq: "major que o igual a" + gteq_any: "major que o igual a qualsevol" + gteq_all: "major que o igual a tots" + in: "en" + in_any: "en qualsevol" + in_all: "en tots" + not_in: "no en" + not_in_any: "no en qualsevol" + not_in_all: "no en tots" + cont: "conté" + cont_any: "conté qualsevol" + cont_all: "conté tots" + not_cont: "no conté" + not_cont_any: "no conté cap" + not_cont_all: "no conté tota" + start: "comença per" + start_any: "comença per qualsevol" + start_all: "comença per tot" + not_start: "no comença per" + not_start_any: "no comença per qualsevol" + not_start_all: "no comença per tot" + end: "acaba en" + end_any: "acaba en qualsevol" + end_all: "acaba en tot" + not_end: "no acaba en" + not_end_any: "no acaba en qualsevol" + not_end_all: "no acaba en tot" + 'true': "és verdader" + 'false': "és fals" + present: "és present" + blank: "està en blanc" + 'null': "és nul" + not_null: "no és nul" diff --git a/lib/ransack/locale/da.yml b/lib/ransack/locale/da.yml new file mode 100644 index 000000000..185cfd161 --- /dev/null +++ b/lib/ransack/locale/da.yml @@ -0,0 +1,70 @@ +da: + ransack: + search: "søg" + predicate: "predicate" + and: "og" + or: "eller" + any: "anhver" + all: "alle" + combinator: "kombinering" + attribute: "attribut" + value: "værdi" + condition: "betingelse" + sort: "sorter" + asc: "opstigende" + desc: "faldende" + predicates: + eq: "lig med" + eq_any: "lig med enhver" + eq_all: "lig med alle" + not_eq: "ikke lig med" + not_eq_any: "ikke lig med nogen" + not_eq_all: "ikke lig med alle" + matches: "matcher" + matches_any: "matcher enhver" + matches_all: "matcher alle" + does_not_match: "matcher ikke" + does_not_match_any: "matcher ikke nogen" + does_not_match_all: "matcher ikke alle" + lt: "mindre end" + lt_any: "mindre end nogen" + lt_all: "mindre end alle" + lteq: "mindre end eller lig med" + lteq_any: "mindre end eller lig med nogen" + lteq_all: "mindre end eller lig med alle" + gt: "større end" + gt_any: "større end nogen" + gt_all: "større end alle" + gteq: "større end eller lig med" + gteq_any: "større end eller lig med nogen" + gteq_all: "større end eller lig med alle" + in: "i" + in_any: "i nogen" + in_all: "i alle" + not_in: "ikke i" + not_in_any: "ikke i nogen" + not_in_all: "ikke i alle" + cont: "indeholder" + cont_any: "indeholder nogen" + cont_all: "indeholder alle" + not_cont: "indeholder ikke" + not_cont_any: "indeholder ikke nogen" + not_cont_all: "indeholder ikke alle" + start: "starter med" + start_any: "starter med nogen" + start_all: "starter med alle" + not_start: "starter ikke med" + not_start_any: "starter ikke med nogen" + not_start_all: "starter ikke med alle" + end: "slutter med" + end_any: "slutter med nogen" + end_all: "slutter med alle" + not_end: "slutter ikke med" + not_end_any: "slutter ikke med nogen" + not_end_all: "slutter ikke med alle" + 'true': "er sand" + 'false': "er falsk" + present: "er til stede" + blank: "er blank" + 'null': "er nul" + not_null: "er ikke nul" diff --git a/lib/ransack/locale/de.yml b/lib/ransack/locale/de.yml new file mode 100644 index 000000000..2da9af463 --- /dev/null +++ b/lib/ransack/locale/de.yml @@ -0,0 +1,70 @@ +de: + ransack: + search: "suchen" + predicate: "Eigenschaft" + and: "und" + or: "oder" + any: "beliebige" + all: "alle" + combinator: "Kombinator" + attribute: "Attribut" + value: "Wert" + condition: "Bedingung" + sort: "sortieren" + asc: "aufsteigend" + desc: "absteigend" + predicates: + eq: "gleicht" + eq_any: "gleicht beliebigen" + eq_all: "gleicht allen" + not_eq: "ungleich" + not_eq_any: "ungleich beliebigen" + not_eq_all: "ungleich allen" + matches: "entspricht" + matches_any: "stimmt überein mit einem beliebigen" + matches_all: "stimmt mit allen überein" + does_not_match: "stimmt nicht überein" + does_not_match_any: "erfüllt ein beliebiger/s nicht" + does_not_match_all: "stimmt nicht mit allen überein" + lt: "kleiner als" + lt_any: "kleiner als ein beliebiger/s" + lt_all: "kleiner als alle als alle" + lteq: "kleiner oder gleich" + lteq_any: "kleiner oder gleich beliebige" + lteq_all: "kleiner oder gleich allen" + gt: "größer als" + gt_any: "größer als ein beliebiger/s" + gt_all: "größer als alle" + gteq: "größer oder gleich" + gteq_any: "größer oder gleich als ein beliebiger/s" + gteq_all: "größer oder gleich alle" + in: "in" + in_any: "ist nicht in einem beliebigen" + in_all: "in allen" + not_in: "nicht in" + not_in_any: "nicht in beliebige" + not_in_all: "nicht in allen" + cont: "enthält" + cont_any: "enthält beliebige" + cont_all: "enthält alle" + not_cont: "enthält nicht" + not_cont_any: "enthält ein beliebiger/s nicht" + not_cont_all: "enthält keine/s" + start: "beginnt mit" + start_any: "beginnt mit beliebigen" + start_all: "beginnt mit allen" + not_start: "beginnt nicht mit" + not_start_any: "beginnt nicht mit beliebigen" + not_start_all: "beginnt nicht mit allen" + end: "endet mit" + end_any: "endet mit beliebigen" + end_all: "endet mit allen" + not_end: "endet nicht mit" + not_end_any: "endet nicht mit beliebigen" + not_end_all: "endet nicht mit allen" + 'true': "ist wahr" + 'false': "ist falsch" + present: "ist vorhanden" + blank: "ist leer" + 'null': "ist null" + not_null: "ist nicht null" diff --git a/lib/ransack/locale/el.yml b/lib/ransack/locale/el.yml new file mode 100644 index 000000000..629398b74 --- /dev/null +++ b/lib/ransack/locale/el.yml @@ -0,0 +1,70 @@ +el: + ransack: + search: "αναζήτηση" + predicate: "κατηγορούμενο" + and: "και" + or: "ή" + any: "οποιοδήποτε" + all: "όλα" + combinator: "συνδυαστής" + attribute: "ιδιότητα" + value: "τιμή" + condition: "συνθήκη" + sort: "ταξινόμηση" + asc: "αύξουσα" + desc: "φθίνουσα" + predicates: + eq: "ίσον" + eq_any: "ισούται με οποιοδήποτε" + eq_all: "ισούται με όλα" + not_eq: "άνισο" + not_eq_any: "άνισο με οποιοδήποτε" + not_eq_all: "άνισο με όλα" + matches: "αντιστοιχεί" + matches_any: "αντιστοιχεί με οποιοδήποτε" + matches_all: "αντιστοιχεί με όλα" + does_not_match: "δεν αντιστοιχεί" + does_not_match_any: "δεν αντιστοιχεί με οποιοδήποτε" + does_not_match_all: "δεν αντιστοιχεί με όλα" + lt: "μικρότερο από" + lt_any: "μικρότερο από οποιοδήποτε" + lt_all: "μικρότερο από όλα" + lteq: "μικρότερο ή ίσον" + lteq_any: "μικρότερο ή ίσον από οποιοδήποτε" + lteq_all: "μικρότερο ή ίσον από όλα" + gt: "μεγαλύτερο από" + gt_any: "μεγαλύτερο από οποιοδήποτε" + gt_all: μεγαλύτερο από όλα" + gteq: "μεγαλύτερο ή ίσον από" + gteq_any: "μεγαλύτερο ή ίσον από οποιοδήποτε" + gteq_all: "μεγαλύτερο ή ίσον από όλα" + in: "περιέχεται" + in_any: "περιέχεται σε οποιοδήποτε" + in_all: "περιέχεται σε όλα" + not_in: "δεν περιέχεται" + not_in_any: "δεν περιέχεται σε οποιοδήποτε" + not_in_all: "δεν περιέχεται σε όλα" + cont: "περιέχει" + cont_any: "περιέχει οποιοδήποτε" + cont_all: "περιέχει όλα" + not_cont: "δεν περιέχει" + not_cont_any: "δεν περιέχει οποιοδήποτε" + not_cont_all: "δεν περιέχει όλα" + start: "αρχίζει με" + start_any: "αρχίζει με οποιοδήποτε" + start_all: "αρχίζει με όλα" + not_start: "δεν αρχίζει με" + not_start_any: "δεν αρχίζει με οποιοδήποτε" + not_start_all: "δεν αρχίζει με όλα" + end: "τελειώνει με" + end_any: "τελειώνει οποιοδήποτε" + end_all: "τελειώνει με όλα" + not_end: "δεν τελειώνει με" + not_end_any: "δεν τελειώνει οποιοδήποτε" + not_end_all: "δεν τελειώνει με όλα" + 'true': "αληθές" + 'false': "ψευδές" + present: "υπάρχει" + blank: "είναι κενό" + 'null': "άκυρο" + not_null: "δεν είναι άκυρο" diff --git a/lib/ransack/locale/es.yml b/lib/ransack/locale/es.yml index fa05bd084..7ab5fdd6c 100644 --- a/lib/ransack/locale/es.yml +++ b/lib/ransack/locale/es.yml @@ -6,11 +6,11 @@ es: or: "o" any: "cualquier" all: "todos" - combinator: "combinado" + combinator: "combinador" attribute: "atributo" value: "valor" condition: "condición" - sort: "ordernar" + sort: "ordenar" asc: "ascendente" desc: "descendente" predicates: @@ -19,12 +19,12 @@ es: eq_all: "es igual a todos" not_eq: "no es igual a" not_eq_any: "no es igual a cualquier" - not_eq_all: "no es iguala todos" - matches: "coincidir" - matches_any: "coincidir a cualquier" - matches_all: "coincidir a todos" - does_not_match: "no coincide" - does_not_match_any: "no coincide con ninguna" + not_eq_all: "no es igual a todos" + matches: "coincide con" + matches_any: "coincide con cualquier" + matches_all: "coincide con todos" + does_not_match: "no coincide con" + does_not_match_any: "no coincide con ninguno" does_not_match_all: "no coincide con todos" lt: "menor que" lt_any: "menor que cualquier" @@ -48,23 +48,23 @@ es: cont_any: "contiene cualquier" cont_all: "contiene todos" not_cont: "no contiene" - not_cont_any: "no contiene ninguna" - not_cont_all: "no contiene toda" + not_cont_any: "no contiene ninguno" + not_cont_all: "no contiene todos" start: "comienza con" start_any: "comienza con cualquier" - start_all: "comienza con toda" - not_start: "no inicia con" + start_all: "comienza con todos" + not_start: "no comienza con" not_start_any: "no comienza con cualquier" - not_start_all: "no inicia con toda" - end: "termina con" - end_any: "termina con cualquier" - end_all: "termina con todo" - not_end: "no termina con" - not_end_any: "no termina con cualquier" - not_end_all: "no termina con todo" + not_start_all: "no comienza con todos" + end: "termina en" + end_any: "termina en cualquier" + end_all: "termina en todos" + not_end: "no termina en" + not_end_any: "no termina en cualquier" + not_end_all: "no termina en todos" 'true': "es verdadero" 'false': "es falso" - present: "es presente" + present: "está presente" blank: "está en blanco" - 'null': "es nula" - not_null: "no es nula" + 'null': "es nulo" + not_null: "no es nulo" diff --git a/lib/ransack/locale/fa.yml b/lib/ransack/locale/fa.yml new file mode 100644 index 000000000..0ba4edab6 --- /dev/null +++ b/lib/ransack/locale/fa.yml @@ -0,0 +1,70 @@ +fa: + ransack: + search: "جستجو" + predicate: "پیش فرض" + and: "و" + or: "یا" + any: "هر کدام" + all: "همه" + combinator: "ترکیب کننده" + attribute: "ویژگی" + value: "مقدار" + condition: "شرایط" + sort: "مرتب سازی" + asc: "صعودی" + desc: "نزولی" + predicates: + eq: "مساوی با" + eq_any: "مساوی با حداقل یکی" + eq_all: "مساوی با همه" + not_eq: "غیر مساوی با" + not_eq_any: "غیر مساوی با هیچ کدام" + not_eq_all: "غیر مساوی با همه" + matches: "مشابهات" + matches_any: "شبیه حداقل یکی" + matches_all: "شبیه همه" + does_not_match: "بدون شباهت" + does_not_match_any: "شبیه یکی هم نیست" + does_not_match_all: "شبیه هیچ کدام نیست" + lt: "کمتر از" + lt_any: "کمتر از حداقل یکی" + lt_all: "کمتر از همه" + lteq: "کمتر یا مساوی با" + lteq_any: "کمتر یا مساوی با حداقل یکی" + lteq_all: "کمتر یا مساوی با همه" + gt: "بیشتر از" + gt_any: "بیشتر از حداقل یکی" + gt_all: "بیشتر از همه" + gteq: "بیشتر یا مساوی با" + gteq_any: "بیشتر یا مساوی با حداقل یکی" + gteq_all: "بیشتر یا مساوی با همه" + in: "در" + in_any: "در حداقل یکی" + in_all: "در همه" + not_in: "نه در" + not_in_any: "نه حتی در یکی" + not_in_all: "در هیچ کدام" + cont: "حاوی" + cont_any: "حاوی حداقل یکی" + cont_all: "حاوی همه" + not_cont: "حاوی این نمیشود" + not_cont_any: "حاوی حتی یکی هم نمیشود" + not_cont_all: "حاوی هیچ کدام نمیشود" + start: "شروع می شود با" + start_any: "شروع می شود با حداقل یکی" + start_all: "شروع می شود با همه" + not_start: "شروع نمی شود با" + not_start_any: "شروع نمی شود با حتی یکی" + not_start_all: "شروع نمی شود با هیچ کدام" + end: "به پایان می رسد با" + end_any: "به پایان می رسد با حداقل یکی" + end_all: "به پایان می رسد با همه" + not_end: "پایان نمی یابد با" + not_end_any: "پایان نمی یابد با حتی یکی" + not_end_all: "پایان نمی یابد با هیچ کدام" + 'true': "درست است" + 'false': "نادرست است" + present: "موجود است" + blank: "خالی است" + 'null': "صفر است" + not_null: "صفر نیست" diff --git a/lib/ransack/locale/fi.yml b/lib/ransack/locale/fi.yml new file mode 100644 index 000000000..f25649b59 --- /dev/null +++ b/lib/ransack/locale/fi.yml @@ -0,0 +1,71 @@ +fi: + ransack: + search: "haku" + predicate: "predikaatti" + and: "ja" + or: "tai" + any: "jokin" + all: "kaikki" + combinator: "kombinaattori" + attribute: "attribuutti" + value: "arvo" + condition: "ehto" + sort: "järjestys" + asc: "nouseva" + desc: "laskeva" + predicates: + eq: "sama kuin" + eq_any: "sama kuin jokin" + eq_all: "sama kuin kaikki" + not_eq: "eri kuin" + not_eq_any: "eri kuin jokin" + not_eq_all: "eri kuin kaikki" + matches: "täsmää" + matches_any: "täsmää jonkun kanssa" + matches_all: "täsmää kaikkien kanssa" + does_not_match: "ei täsmää" + does_not_match_any: "ei täsmää minkään kanssa" + does_not_match_all: "ei täsmää kaikkien kanssa" + lt: "vähemmän kuin" + lt_any: "vähemmän kuin jokin" + lt_all: "vähemmän kuin mikään" + lteq: "vähemmän tai sama kuin" + lteq_any: "vähemmän tai sama kuin jokin" + lteq_all: "vähemmän tai sama kuin mikään" + gt: "suurempi kuin" + gt_any: "suurempi kuin jokin" + gt_all: "suurempi kuin mikään" + gteq: "suurempi tai yhtä suuri kuin" + gteq_any: "suurempi tai yhtä suuri kuin jokin" + gteq_all: "suurempi tai yhtä suuri kuin mikään" + in: "kohteessa" + in_any: "jossakin kohteessa" + in_all: "kaikissa kohteissa" + not_in: "ei kohteessa" + not_in_any: "ei jossakin kohteista" + not_in_all: "ei missään kohteista" + cont: "sisältää" + cont_any: "sisältää jonkin" + cont_all: "sisältää kaikki" + not_cont: "ei sisällä" + not_cont_any: "ei sisällä jotakin" + not_cont_all: "ei sisällä mitään" + start: "alkaa" + start_any: "alkaa jollakin" + start_all: "alkaa kaikilla" + not_start: "ei ala" + not_start_any: "ei ala jollakin" + not_start_all: "ei ala millään" + end: "päättyy" + end_any: "päättyy jollakin" + end_all: "päättyy millään" + not_end: "ei pääty" + not_end_any: "ei pääty jollakin" + not_end_all: "ei pääty millään" + 'true': "on tosi" + 'false': "ei ole tosi" + present: "on läsnä" + blank: "on tyhjä" + 'null': "on määrittämätön" + not_null: "on määritetty" + diff --git a/lib/ransack/locale/id.yml b/lib/ransack/locale/id.yml new file mode 100644 index 000000000..20a93ccf5 --- /dev/null +++ b/lib/ransack/locale/id.yml @@ -0,0 +1,70 @@ +id: + ransack: + search: "cari" + predicate: "predikat" + and: "dan" + or: "atau" + any: "apapun" + all: "semua" + combinator: "kombinasi" + attribute: "atribut" + value: "data" + condition: "kondisi" + sort: "urutan" + asc: "ascending" + desc: "descending" + predicates: + eq: "sama dengan" + eq_any: "sama beberapa dengan" + eq_all: "sama seluruhnya dengan" + not_eq: "tidak sama dengan" + not_eq_any: "tidak sama beberapa dengan" + not_eq_all: "tidak semua seluruhnya dengan" + matches: "mirip" + matches_any: "mirip beberapa dengan" + matches_all: "mirip semua dengan" + does_not_match: "tidak mirip dengan" + does_not_match_any: "tidak mirip beberapa dengan" + does_not_match_all: "tidak mirip semua dengan" + lt: "kurang dari" + lt_any: "kurang beberapa dengan" + lt_all: "kurang seluruhnya dengan" + lteq: "kurang lebih" + lteq_any: "kurang lebih beberapa dengan" + lteq_all: "kurang lebih semua dengan" + gt: "lebih besar daripada" + gt_any: "lebih besar beberapa dengan" + gt_all: "lebih besar semua dengan" + gteq: "lebih besar atau sama dengan" + gteq_any: "beberapa lebih besar atau sama dengan" + gteq_all: "semua lebih besar atau sama dengan" + in: "di" + in_any: "di beberapa" + in_all: "di semua" + not_in: "tidak di" + not_in_any: "tidak di beberapa" + not_in_all: "tidak semua di" + cont: "mengandung" + cont_any: "mengandung beberapa" + cont_all: "mengandung semua" + not_cont: "tidak mengandung" + not_cont_any: "tidak mengandung beberapa" + not_cont_all: "tidak mengandung semua" + start: "diawali dengan" + start_any: "diawali beberapa dengan" + start_all: "diawali semua dengan" + not_start: "tidak diawali dengan" + not_start_any: "tidak diawali beberapa dengan" + not_start_all: "tidak diawali semua dengan" + end: "diakhiri dengan" + end_any: "diakhiri beberapa dengan" + end_all: "diakhiri semua dengan" + not_end: "tidak diakhiri dengan" + not_end_any: "tidak diakhiri dengan beberapa" + not_end_all: "tidak diakhiri dengan semua" + 'true': "bernilai benar" + 'false': "bernilai salah" + present: "ada" + blank: "kosong" + 'null': "null" + not_null: "tidak null" diff --git a/lib/ransack/locale/it.yml b/lib/ransack/locale/it.yml new file mode 100644 index 000000000..7b541b7a6 --- /dev/null +++ b/lib/ransack/locale/it.yml @@ -0,0 +1,70 @@ +it: + ransack: + search: "cerca" + predicate: "predicato" + and: "e" + or: "o" + any: "qualsiasi" + all: "tutti" + combinator: "combinatore" + attribute: "attributo" + value: "valore" + condition: "condizione" + sort: "ordinamento" + asc: "crescente" + desc: "decrescente" + predicates: + eq: "uguale a" + eq_any: "uguale ad almeno un" + eq_all: "uguale ad ognuno" + not_eq: "diverso da" + not_eq_any: "diverso da uno qualsiasi" + not_eq_all: "diverso da tutti" + matches: "combacia con" + matches_any: "combacia con almeno un" + matches_all: "combacia con tutti" + does_not_match: "non corrisponde" + does_not_match_any: "non corrisponde ad uno qualsiasi" + does_not_match_all: "non corrisponde con nessuno" + lt: "minore di" + lt_any: "minore di almeno un" + lt_all: "minore di tutti" + lteq: "minore o uguale a" + lteq_any: "minore o uguale ad almeno un" + lteq_all: "minore o uguale a tutti" + gt: "maggiore di" + gt_any: "maggiore di almeno un" + gt_all: "maggiore di tutti" + gteq: "maggiore o uguale a" + gteq_any: "maggiore o uguale ad almeno un" + gteq_all: "maggiore o uguale a tutti" + in: "in" + in_any: "in almeno un" + in_all: "in tutti" + not_in: "non in" + not_in_any: "non in almeno un" + not_in_all: "non in tutti" + cont: "contiene" + cont_any: "contiene almeno un" + cont_all: "contiene tutti" + not_cont: "non contiene" + not_cont_any: "non contiene un qualsiasi" + not_cont_all: "non contiene nessuno" + start: "inizia con" + start_any: "inizia con almeno un" + start_all: "inizia con tutti" + not_start: "non inizia con" + not_start_any: "non inizia con uno qualsiasi" + not_start_all: "non inizia con nessuno" + end: "finisce con" + end_any: "finisce con almeno un" + end_all: "finisce con tutti" + not_end: "non finisce con" + not_end_any: "non finisce con uno qualsiasi" + not_end_all: "non finisce con nessuno" + 'true': "è vero" + 'false': "è falso" + present: "è presente" + blank: "è vuoto" + 'null': "è nullo" + not_null: "non è nullo" diff --git a/lib/ransack/locale/ja.yml b/lib/ransack/locale/ja.yml new file mode 100644 index 000000000..563c7afa9 --- /dev/null +++ b/lib/ransack/locale/ja.yml @@ -0,0 +1,70 @@ +ja: + ransack: + search: "検索" + predicate: "は以下である" + and: "と" + or: "あるいは" + any: "いずれか" + all: "全て" + combinator: "組み合わせ" + attribute: "属性" + value: "値" + condition: "状態" + sort: "分類" + asc: "昇順" + desc: "降順" + predicates: + eq: "等しい" + eq_any: "いずれかに等しい" + eq_all: "全てに等しい" + not_eq: "等しくない" + not_eq_any: "いずれかに等しくない" + not_eq_all: "全てと等しくない" + matches: "合致している" + matches_any: "いずれかと合致している" + matches_all: "全てと合致している" + does_not_match: "合致していない" + does_not_match_any: "いずれかに合致していない" + does_not_match_all: "全てに合致していない" + lt: "小さい" + lt_any: "いずれかより小さい" + lt_all: "全てよりも小さい" + lteq: "小さいか等しい" + lteq_any: "いずれかより小さいか等しい" + lteq_all: "全てより小さいか等しい" + gt: "大きい" + gt_any: "いずれかより大きい" + gt_all: "全てより大きい" + gteq: "大きいか等しい" + gteq_any: "いずれかより大きいか等しい" + gteq_all: "全てより大きいか等しい" + in: "範囲内である" + in_any: "いずれかの範囲内である" + in_all: "全ての範囲内である" + not_in: "範囲内でない" + not_in_any: "いずれかの範囲内でない" + not_in_all: "全ての範囲内" + cont: "含む" + cont_any: "いずれかを含む" + cont_all: "全てを含む" + not_cont: "含まない" + not_cont_any: "いずれかを含まない" + not_cont_all: "全てを含まない" + start: "始まる" + start_any: "どれかで始まる" + start_all: "全てで始まる" + not_start: "始まらない" + not_start_any: "いずれかで始まらない" + not_start_all: "全てで始まらない" + end: "終わる" + end_any: "いずれかで終わる" + end_all: "全てで終わる" + not_end: "どれでも終わらない" + not_end_any: "いずれかで終わらない" + not_end_all: "全てで終わらない" + 'true': "真" + 'false': "偽" + present: "存在する" + blank: "空である" + 'null': "無効" + not_null: "無効ではない" diff --git a/lib/ransack/locale/ko.yml b/lib/ransack/locale/ko.yml new file mode 100644 index 000000000..2fd6cc127 --- /dev/null +++ b/lib/ransack/locale/ko.yml @@ -0,0 +1,70 @@ +ko: + ransack: + search: "검색" + predicate: "조건" + and: "그리고" + or: "또는" + any: "어떤 것이든" + all: "모두" + combinator: "조합기" + attribute: "속성" + value: "값" + condition: "조건" + sort: "정렬" + asc: "오름차순" + desc: "내림차순" + predicates: + eq: "같음" + eq_any: "어떤 것이든 같음" + eq_all: "모두 같음" + not_eq: "같지 않음" + not_eq_any: "어떤 것이든 같지 않음" + not_eq_all: "모두 같지 않음" + matches: "일치함" + matches_any: "어떤 것이든 일치함" + matches_all: "모두 일치함" + does_not_match: "일치하지 않음" + does_not_match_any: "어떤 것이든 일치하지 않음" + does_not_match_all: "모두 일치하지 않음" + lt: "보다 작음" + lt_any: "어떤 것이든 보다 작음" + lt_all: "모두 보다 작음" + lteq: "보다 작거나 같음" + lteq_any: "어떤 것이든 보다 작거나 같음" + lteq_all: "모두 보다 작거나 같음" + gt: "보다 큼" + gt_any: "어떤 것이든 보다 큼" + gt_all: "모두 보다 큼" + gteq: "보다 크거나 같음" + gteq_any: "어떤 것이든 보다 크거나 같음" + gteq_all: "모두 보다 크거나 같음" + in: "포함됨" + in_any: "어떤 것이든 포함됨" + in_all: "모두 포함됨" + not_in: "포함되지 않음" + not_in_any: "어떤 것이든 포함되지 않음" + not_in_all: "모두 포함되지 않음" + cont: "포함함" + cont_any: "어떤 것이든 포함함" + cont_all: "모두 포함함" + not_cont: "포함하지 않음" + not_cont_any: "어떤 것이든 포함하지 않음" + not_cont_all: "모두 포함하지 않음" + start: "시작함" + start_any: "어떤 것이든 시작함" + start_all: "모두 시작함" + not_start: "시작하지 않음" + not_start_any: "어떤 것이든 시작하지 않음" + not_start_all: "모두 시작하지 않음" + end: "끝남" + end_any: "어떤 것이든 끝남" + end_all: "모두 끝남" + not_end: "끝나지 않음" + not_end_any: "어떤 것이든 끝나지 않음" + not_end_all: "모두 끝나지 않음" + 'true': "참" + 'false': "거짓" + present: "존재함" + blank: "비어있음" + 'null': "널" + not_null: "널이 아님" diff --git a/lib/ransack/locale/nl.yml b/lib/ransack/locale/nl.yml index b0c4f2bb3..29adc4ced 100644 --- a/lib/ransack/locale/nl.yml +++ b/lib/ransack/locale/nl.yml @@ -35,9 +35,9 @@ nl: gt: "groter dan" gt_any: "groter dan enig" gt_all: "groter dan alle" - gteq: "groter dan or equal to" - gteq_any: "groter dan or equal to enig" - gteq_all: "groter dan or equal to alle" + gteq: "groter dan of gelijk aan" + gteq_any: "groter dan of gelijk aan enig" + gteq_all: "groter dan of gelijk aan alle" in: "in" in_any: "in enig" in_all: "in alle" @@ -64,7 +64,7 @@ nl: not_end_all: "eindigt niet met alle" 'true': "is waar" 'false': "is niet waar" - present: "is present" + present: "is aanwezig" blank: "is afwezig" 'null': "is null" not_null: "is niet null" diff --git a/lib/ransack/locale/pt-BR.yml b/lib/ransack/locale/pt-BR.yml new file mode 100644 index 000000000..9a6fd938e --- /dev/null +++ b/lib/ransack/locale/pt-BR.yml @@ -0,0 +1,70 @@ +pt-BR: + ransack: + search: "pesquisar" + predicate: "predicado" + and: "e" + or: "ou" + any: "algum" + all: "todos" + combinator: "combinador" + attribute: "atributo" + value: "valor" + condition: "condição" + sort: "classificar" + asc: "ascendente" + desc: "descendente" + predicates: + eq: "igual" + eq_any: "igual a algum" + eq_all: "igual a todos" + not_eq: "não é igual a" + not_eq_any: "não é igual a algum" + not_eq_all: "não é igual a todos" + matches: "corresponde" + matches_any: "corresponde a algum" + matches_all: "corresponde a todos" + does_not_match: "não corresponde" + does_not_match_any: "não corresponde a algum" + does_not_match_all: "não corresponde a todos" + lt: "menor que" + lt_any: "menor que algum" + lt_all: "menor que todos" + lteq: "menor ou igual a" + lteq_any: "menor ou igual a algum" + lteq_all: "menor ou igual a todos" + gt: "maior que" + gt_any: "maior que algum" + gt_all: "maior que todos" + gteq: "maior que ou igual a" + gteq_any: "maior que ou igual a algum" + gteq_all: "maior que ou igual a todos" + in: "em" + in_any: "em algum" + in_all: "em todos" + not_in: "não em" + not_in_any: "não em algum" + not_in_all: "não em todos" + cont: "contém" + cont_any: "contém algum" + cont_all: "contém todos" + not_cont: "não contém" + not_cont_any: "não contém algum" + not_cont_all: "não contém todos" + start: "começa com" + start_any: "começa com algum" + start_all: "começa com todos" + not_start: "não começa com" + not_start_any: "não começa com algum" + not_start_all: "não começa com algum" + end: "termina com" + end_any: "termina com algum" + end_all: "termina com todos" + not_end: "não termina com" + not_end_any: "não termina com algum" + not_end_all: "não termina com todos" + 'true': "é verdadeiro" + 'false': "é falso" + present: "está presente" + blank: "está em branco" + 'null': "é nulo" + not_null: "não é nulo" diff --git a/lib/ransack/locale/ru.yml b/lib/ransack/locale/ru.yml new file mode 100644 index 000000000..e3958f842 --- /dev/null +++ b/lib/ransack/locale/ru.yml @@ -0,0 +1,70 @@ +ru: + ransack: + search: "Поиск" + predicate: "predicate" + and: "и" + or: "или" + any: "любое" + all: "все" + combinator: "combinator" + attribute: "аттрибут" + value: "значение" + condition: "условие" + sort: "сортировка" + asc: "по возрастанию" + desc: "по убыванию" + predicates: + eq: "равный" + eq_any: "равный любому" + eq_all: "равный всем" + not_eq: "не равный любому" + not_eq_any: "не равный любому" + not_eq_all: "не равный всем" + matches: "совпадение" + matches_any: "совпадение любому" + matches_all: "совпадение всем" + does_not_match: "не совпадение" + does_not_match_any: "не совпадение любому" + does_not_match_all: "не совпадение всем" + lt: "меньше чем" + lt_any: "меньше чем любое" + lt_all: "меньше чем все" + lteq: "меньше чем или равен" + lteq_any: "меньше чем или равен любому" + lteq_all: "меньше чем или равен всем" + gt: "больше чем" + gt_any: "больше чем любое" + gt_all: "больше чем все" + gteq: "больше чем или равен" + gteq_any: "больше чем или равен любому" + gteq_all: "больше чем или равен всем" + in: "в" + in_any: "в любом из" + in_all: "во всех" + not_in: "нет в" + not_in_any: "не в каком из" + not_in_all: "нет во всех" + cont: "содержит" + cont_any: "содержит любое" + cont_all: "содержит все" + not_cont: "не содержит" + not_cont_any: "не содержит любое" + not_cont_all: "не содержит все" + start: "начинается с" + start_any: "начинается с любого" + start_all: "начинается с всего" + not_start: "не начинается с" + not_start_any: "не начинается с любого" + not_start_all: "не начинается со всего" + end: "кончается с" + end_any: "кончается с любого" + end_all: "кончается со всего" + not_end: "не кончается с" + not_end_any: "не кончается с любого" + not_end_all: "не кончается со всего" + 'true': "верно" + 'false': "не верно" + present: "настоящее" + blank: "пусто" + 'null': "нулевой" + not_null: "не нулевой" diff --git a/lib/ransack/locale/sk.yml b/lib/ransack/locale/sk.yml new file mode 100644 index 000000000..5c8363ae8 --- /dev/null +++ b/lib/ransack/locale/sk.yml @@ -0,0 +1,70 @@ +sk: + ransack: + search: "vyhľadávanie" + predicate: "predikát" + and: "a" + or: "alebo" + any: "akýkoľvek" + all: "každý" + combinator: "kombinátor" + attribute: "atribút" + value: "hodnota" + condition: "podmienka" + sort: "poradie" + asc: "vzostupne" + desc: "zostupne" + predicates: + eq: "sa rovná" + eq_any: "sa rovná akémukoľvek" + eq_all: "sa rovná všetkým" + not_eq: "sa nerovná" + not_eq_any: "sa nerovná akémukoľvek" + not_eq_all: "sa nerovná všetkým" + matches: "zodpovedá" + matches_any: "zodpovedá akémukoľvek" + matches_all: "zodpovedá všetkým" + does_not_match: "nezodpovedá" + does_not_match_any: "nezodpovedá akémukoľvek" + does_not_match_all: "nezodpovedá všetkým" + lt: "menší ako" + lt_any: "menší ako akýkoľvek" + lt_all: "menší ako všetky" + lteq: "menší alebo rovný" + lteq_any: "menší alebo rovný akémukoľvek" + lteq_all: "menší alebo rovný všetkým" + gt: "väčší ako" + gt_any: "väčší ako akýkoľvek" + gt_all: "väčší ako všetky" + gteq: "väčší alebo rovný" + gteq_any: "väčší alebo rovný akémukoľvek" + gteq_all: "väčší alebo rovný všetkým" + in: "v" + in_any: "v akejkoľvek" + in_all: "vo všetkých" + not_in: "nie je v" + not_in_any: "nie je v akejkoľvek" + not_in_all: "nie je vo všetkých" + cont: "obsahuje" + cont_any: "obsahuje akúkoľvek" + cont_all: "obsahuje všetky" + not_cont: "neobsahuje" + not_cont_any: "neobsahuje akúkoľvek" + not_cont_all: "neobsahuje všetky" + start: "začína na" + start_any: "začína s akoukoľvek" + start_all: "začína so všetkými" + not_start: "nezačíná s" + not_start_any: "nezačíná s akoukoľvek" + not_start_all: "nezačíná so všetkými" + end: "končí s" + end_any: "končí s akoukoľvek" + end_all: "končí so všetkými" + not_end: "nekončí s" + not_end_any: "nekončí s akoukoľvek" + not_end_all: "nekončí so všetkými" + 'true': "je pravdivé" + 'false': "nie je pravdivé" + present: "je vyplnené" + blank: "je prázdne" + 'null': "je null" + not_null: "nie je null" diff --git a/lib/ransack/locale/sv.yml b/lib/ransack/locale/sv.yml new file mode 100644 index 000000000..ed9c62c8c --- /dev/null +++ b/lib/ransack/locale/sv.yml @@ -0,0 +1,70 @@ +sv: + ransack: + search: "sök" + predicate: "predikat" + and: "och" + or: "eller" + any: "vilken som" + all: "alla" + combinator: "kombinator" + attribute: "attribut" + value: "värde" + condition: "villkor" + sort: "sortera" + asc: "stigande" + desc: "fallande" + predicates: + eq: "lika med" + eq_any: "lika med vilket som" + eq_all: "lika med alla" + not_eq: "inte lika med" + not_eq_any: "inte lika med någon" + not_eq_all: "inte lika med alla" + matches: "matchar" + matches_any: "matchar någon" + matches_all: "matchar alla" + does_not_match: "matchar inte" + does_not_match_any: "matchar inte någon" + does_not_match_all: "matchar inte alla" + lt: "mindre än" + lt_any: "mindre än någon" + lt_all: "mindre än alla" + lteq: "mindre än eller lika med" + lteq_any: "mindre än eller lika med någon" + lteq_all: "mindre än eller lika med alla" + gt: "större än" + gt_any: "större än någon" + gt_all: "större än alla" + gteq: "större än eller lika med" + gteq_any: "större än eller lika med någon" + gteq_all: "större än eller lika med alla" + in: "i" + in_any: "i någon" + in_all: "i alla" + not_in: "inte i" + not_in_any: "inte i någon" + not_in_all: "inte i alla" + cont: "innehåller" + cont_any: "innehåller någon" + cont_all: "innehåller alla" + not_cont: "innehåller inte" + not_cont_any: "innehåller inte någon" + not_cont_all: "innehåller inte alla" + start: "börjar med" + start_any: "börjar med någon" + start_all: "börjar med alla" + not_start: "börjar inte med" + not_start_any: "börjar inte med någon" + not_start_all: "börjar inte med alla" + end: "slutar med" + end_any: "slutar med någon" + end_all: "slutar med alla" + not_end: "slutar inte med" + not_end_any: "slutar inte med någon" + not_end_all: "slutar inte med alla" + 'true': "är sant" + 'false': "är falskt" + present: "existerar" + blank: "är tom" + 'null': "är null" + not_null: "är inte null" diff --git a/lib/ransack/locale/tr.yml b/lib/ransack/locale/tr.yml new file mode 100644 index 000000000..64c8e67c6 --- /dev/null +++ b/lib/ransack/locale/tr.yml @@ -0,0 +1,70 @@ +tr: + ransack: + search: "ara" + predicate: "doğrula" + and: "ve" + or: "veya" + any: "herhangi" + all: "hepsi" + combinator: "birleştirici" + attribute: "nitelik" + value: "değer" + condition: "şart" + sort: "sırala" + asc: "artan" + desc: "azalan" + predicates: + eq: "eşit" + eq_any: "herhangi birine eşit" + eq_all: "hepsine eşit" + not_eq: "eşit değil" + not_eq_any: "herhangi birine eşit değil" + not_eq_all: "hiçbirine eşit değil" + matches: "eşleşen" + matches_any: "herhangi biri ile eşleşen" + matches_all: "hepsi ile eşleşen" + does_not_match: "eşleşmeyen" + does_not_match_any: "herhangi biri ile eşleşmeyen" + does_not_match_all: "hiçbiri ile eşleşmeyen" + lt: "daha küçük" + lt_any: "herhangi birinden küçük" + lt_all: "hepsinden küçük" + lteq: "daha küçük veya eşit" + lteq_any: "daha küçük veya herhangi birine eşit" + lteq_all: "daha küçük veya hepsine eşit" + gt: "daha büyük " + gt_any: "herhangi birinden daha büyük" + gt_all: "hepsinden daha büyük" + gteq: "daha büyük veya eşit" + gteq_any: "daha büyük veya herhangi birine eşit" + gteq_all: "daha büyük veya hepsine eşit" + in: "içinde" + in_any: "herhangi birinde" + in_all: "hepsinde" + not_in: "içinde değil" + not_in_any: "herhangi birinde değil" + not_in_all: "hiçbirinde değil" + cont: "içeren" + cont_any: "herhangi birini içeren" + cont_all: "hepsini içeren" + not_cont: "içermeyen" + not_cont_any: "herhangi birini içermeyen" + not_cont_all: "hiçbirini birini içermeyen" + start: "ile başlayan" + start_any: "herhangi biriyle başlayan" + start_all: "hepsiyle başlayan" + not_start: "ile başlamayan" + not_start_any: "herhangi biriyle başlamayan" + not_start_all: "hiçbiriyle başlamayan" + end: "ile biten" + end_any: "herhangi biriyle biten" + end_all: "hepsi ile biten" + not_end: "ile bitmeyen" + not_end_any: "herhangi biriyle bitmeyen" + not_end_all: "hiçbiriyle bitmeyen" + 'true': "doğru" + 'false': "yanlış" + present: "mevcut" + blank: "boş" + 'null': "geçersiz" + not_null: "geçerli" diff --git a/lib/ransack/locale/uk.yml b/lib/ransack/locale/uk.yml new file mode 100644 index 000000000..a1163cd46 --- /dev/null +++ b/lib/ransack/locale/uk.yml @@ -0,0 +1,72 @@ +# Інші переклади на https://github.com/activerecord-hackery/ransack/blob/main/lib/ransack/locale +# +uk: + ransack: + search: пошук + predicate: предикат + and: і + or: або + any: будь-який + all: усі + combinator: комбінатор + attribute: атрибут + value: значення + condition: умова + sort: сортування + asc: за зростанням + desc: за спаданням + predicates: + eq: рівний + eq_any: рівний будь-якому + eq_all: рівний усім + not_eq: не рівний + not_eq_any: не рівний будь-якому + not_eq_all: не рівний усім + matches: збігається + matches_any: збігається з будь-яким + matches_all: збігається з усіма + does_not_match: не збігається + does_not_match_any: не збігається з будь-яким + does_not_match_all: не збігається з усіма + lt: менше ніж + lt_any: менше за будь-який + lt_all: менше за всі + lteq: менше або рівне + lteq_any: менше або рівне будь-якому + lteq_all: менше або рівне всім + gt: більше ніж + gt_any: більше ніж будь-який + gt_all: більше ніж усі + gteq: більше або рівне + gteq_any: більше або рівне будь-якому + gteq_all: більше або рівне всім + in: міститься у + in_any: міститься в будь-якому + in_all: міститься в усіх + not_in: не міститься у + not_in_any: не міститься в будь-якому + not_in_all: не міститься в усіх + cont: містить + cont_any: містить будь-який + cont_all: містить усі + not_cont: не містить + not_cont_any: не містить жодного + not_cont_all: не містить усіх + start: починається з + start_any: починається з будь-якого + start_all: починається з усіх + not_start: не починається з + not_start_any: не починається з будь-якого + not_start_all: не починається з усіх + end: закінчується на + end_any: закінчується на будь-який + end_all: закінчується на всі + not_end: не закінчується на + not_end_any: не закінчується на будь-який + not_end_all: не закінчується на всі + 'true': так + 'false': ні + present: присутній + blank: порожній + 'null': нульовий + not_null: не нульовий diff --git a/lib/ransack/locale/zh.yml b/lib/ransack/locale/zh-CN.yml similarity index 77% rename from lib/ransack/locale/zh.yml rename to lib/ransack/locale/zh-CN.yml index df6b5e085..5124d2faf 100644 --- a/lib/ransack/locale/zh.yml +++ b/lib/ransack/locale/zh-CN.yml @@ -1,4 +1,4 @@ -zh: +zh-CN: ransack: search: "搜索" predicate: "基于(predicate)" @@ -50,18 +50,18 @@ zh: not_cont: "不包含" not_cont_any: "不包含任意一个值" not_cont_all: "不包含所有值" - start: "以改值开始" - start_any: "以任意一个值开始" - start_all: "以所有值开始" - not_start: "不以改值开始" - not_start_any: "不以任意一个值开始" - not_start_all: "不以所有值开始" - end: "以改值结尾" - end_any: "以任意一个值结尾" - end_all: "以所有值结尾" - not_end: "不以改值结尾" - not_end_any: "不以任意一个值结尾" - not_end_all: "不以所有值结尾" + start: "始于" + start_any: "始于任一值" + start_all: "始于任意值" + not_start: "非始于" + not_start_any: "非始于任一值" + not_start_all: "非始于任意值" + end: "止于" + end_any: "止于任一值" + end_all: "止于任意值" + not_end: "非止于" + not_end_any: "非止于任一值" + not_end_all: "非止于任意值" 'true': "等于true" 'false': "等于false" present: "有值" diff --git a/lib/ransack/locale/zh-TW.yml b/lib/ransack/locale/zh-TW.yml new file mode 100644 index 000000000..2980f06e1 --- /dev/null +++ b/lib/ransack/locale/zh-TW.yml @@ -0,0 +1,70 @@ +zh-TW: + ransack: + search: "搜尋" + predicate: "基於" + and: "而且" + or: "或者" + any: "任何" + all: "所有" + combinator: "條件組合" + attribute: "屬性" + value: "數值" + condition: "條件" + sort: "排序" + asc: "升冪排序" + desc: "降冪排序" + predicates: + eq: "等於" + eq_any: "等於任何一個值" + eq_all: "等於所有值" + not_eq: "不等於" + not_eq_any: "不等於任何一個值" + not_eq_all: "不等於所有值" + matches: "符合" + matches_any: "符合任何一個條件" + matches_all: "符合所有條件" + does_not_match: "不符合" + does_not_match_any: "不符合任何一個條件" + does_not_match_all: "不符合所有條件" + lt: "小於" + lt_any: "小於任何一個值" + lt_all: "小於所有值" + lteq: "小於或等於" + lteq_any: "小於或等於任何一個值" + lteq_all: "小於或等於所有值" + gt: "大於" + gt_any: "大於任何一個值" + gt_all: "大於所有值" + gteq: "大於或等於" + gteq_any: "大於或等於任何一個值" + gteq_all: "大於或等於所有值" + in: "被包含於" + in_any: "被包含於任何一個值" + in_all: "被包含於所有值" + not_in: "不被包含於" + not_in_any: "不被包含於任何一個值" + not_in_all: "不被包含於所有值" + cont: "包含" + cont_any: "包含任何一個值" + cont_all: "包含所有值" + not_cont: "不包含" + not_cont_any: "不包含任何一個值" + not_cont_all: "不包含所有值" + start: "以某個值開始" + start_any: "以任何一個值開始" + start_all: "以所有值開始" + not_start: "不以某個值開始" + not_start_any: "不以任何一值開始" + not_start_all: "不以所有值開始" + end: "以某個值結尾" + end_any: "以任何一個值結尾" + end_all: "以所有值結尾" + not_end: "不以某個值結尾" + not_end_any: "不以任何一個值結尾" + not_end_all: "不以所有值結尾" + 'true': "為真" + 'false': "為假" + present: "有值" + blank: "為空" + 'null': "為 null" + not_null: "不為 null" diff --git a/lib/ransack/naming.rb b/lib/ransack/naming.rb index b9f90a8a4..06d67a57e 100644 --- a/lib/ransack/naming.rb +++ b/lib/ransack/naming.rb @@ -20,6 +20,10 @@ def to_param def to_model self end + + def model_name + self.class.model_name + end end class Name < String @@ -28,16 +32,16 @@ class Name < String alias_method :cache_key, :collection def initialize - super("Search".freeze) - @singular = "search".freeze - @plural = "searches".freeze - @element = "search".freeze - @human = "Search".freeze - @collection = "ransack/searches".freeze - @partial_path = "#{@collection}/#{@element}".freeze - @param_key = "q".freeze - @route_key = "searches".freeze - @i18n_key = :ransack + super(Constants::CAP_SEARCH) + @singular = Constants::SEARCH + @plural = Constants::SEARCHES + @element = Constants::SEARCH + @human = Constants::CAP_SEARCH + @collection = Constants::RANSACK_SLASH_SEARCHES + @partial_path = Constants::RANSACK_SLASH_SEARCHES_SLASH_SEARCH + @param_key = Constants::Q + @route_key = Constants::SEARCHES + @i18n_key = :ransack end end @@ -51,4 +55,4 @@ def i18n_scope end end -end \ No newline at end of file +end diff --git a/lib/ransack/nodes.rb b/lib/ransack/nodes.rb deleted file mode 100644 index ce667dd5b..000000000 --- a/lib/ransack/nodes.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'ransack/nodes/bindable' -require 'ransack/nodes/node' -require 'ransack/nodes/attribute' -require 'ransack/nodes/value' -require 'ransack/nodes/condition' -require 'ransack/nodes/sort' -require 'ransack/nodes/grouping' \ No newline at end of file diff --git a/lib/ransack/nodes/attribute.rb b/lib/ransack/nodes/attribute.rb index 2f01723f8..73b57f2a9 100644 --- a/lib/ransack/nodes/attribute.rb +++ b/lib/ransack/nodes/attribute.rb @@ -3,30 +3,34 @@ module Nodes class Attribute < Node include Bindable - attr_reader :name + attr_reader :name, :ransacker_args - delegate :blank?, :present?, :==, :to => :name - delegate :engine, :to => :context + delegate :blank?, :present?, to: :name + delegate :engine, to: :context - def initialize(context, name = nil) + def initialize(context, name = nil, ransacker_args = []) super(context) self.name = name unless name.blank? + @ransacker_args = ransacker_args end def name=(name) @name = name - context.bind(self, name) unless name.blank? end def valid? bound? && attr && context.klassify(parent).ransackable_attributes(context.auth_object) - .include?(attr_name) + .include?(attr_name.split('.').last) + end + + def associated_collection? + parent.respond_to?(:reflection) && parent.reflection.collection? end def type if ransacker - return ransacker.type + ransacker.type else context.type_for(self) end diff --git a/lib/ransack/nodes/bindable.rb b/lib/ransack/nodes/bindable.rb index ed18e6556..4615df1d8 100644 --- a/lib/ransack/nodes/bindable.rb +++ b/lib/ransack/nodes/bindable.rb @@ -5,9 +5,7 @@ module Bindable attr_accessor :parent, :attr_name def attr - @attr ||= ransacker ? - ransacker.attr_from(self) : - context.table_for(parent)[attr_name] + @attr ||= get_arel_attribute end alias :arel_attribute :attr @@ -27,6 +25,28 @@ def reset_binding! @parent = @attr_name = @attr = @klass = nil end + private + + def get_arel_attribute + if ransacker + ransacker.attr_from(self) + else + get_attribute + end + end + + def get_attribute + if is_alias_attribute? + context.table_for(parent)[parent.base_klass.attribute_aliases[attr_name]] + else + context.table_for(parent)[attr_name] + end + end + + def is_alias_attribute? + Ransack::SUPPORTS_ATTRIBUTE_ALIAS && + parent.base_klass.attribute_aliases.key?(attr_name) + end end end -end \ No newline at end of file +end diff --git a/lib/ransack/nodes/condition.rb b/lib/ransack/nodes/condition.rb index d1b724605..0b024fdc9 100644 --- a/lib/ransack/nodes/condition.rb +++ b/lib/ransack/nodes/condition.rb @@ -1,23 +1,26 @@ +require 'ransack/invalid_search_error' + module Ransack module Nodes class Condition < Node i18n_word :attribute, :predicate, :combinator, :value - i18n_alias :a => :attribute, :p => :predicate, - :m => :combinator, :v => :value + i18n_alias a: :attribute, p: :predicate, + m: :combinator, v: :value attr_accessor :predicate class << self def extract(context, key, values) - attributes, predicate = extract_attributes_and_predicate(key) + attributes, predicate, combinator = + extract_values_for_condition(key, context) + if attributes.size > 0 && predicate - combinator = key.match(/_(or|and)_/) ? $1 : nil condition = self.new(context) condition.build( - :a => attributes, - :p => predicate.name, - :m => combinator, - :v => predicate.wants_array ? Array(values) : [values] + a: attributes, + p: predicate.name, + m: combinator, + v: predicate.wants_array ? Array(values) : [values] ) # TODO: Figure out what to do with multiple types of attributes, # if anything. Tempted to go with "garbage in, garbage out" here. @@ -31,16 +34,34 @@ def extract(context, key, values) private - def extract_attributes_and_predicate(key) - str = key.dup - name = Predicate.detect_and_strip_from_string!(str) - predicate = Predicate.named(name) - unless predicate || Ransack.options[:ignore_unknown_conditions] - raise ArgumentError, "No valid predicate for #{key}" + def extract_values_for_condition(key, context = nil) + str = key.dup + name = Predicate.detect_and_strip_from_string!(str) + predicate = Predicate.named(name) + + unless predicate || Ransack.options[:ignore_unknown_conditions] + raise InvalidSearchError, "No valid predicate for #{key}" + end + + if context.present? + str = context.ransackable_alias(str) + end + + combinator = + if str.match(/_(or|and)_/) + $1 + else + nil + end + + if context.present? && context.attribute_method?(str) + attributes = [str] + else + attributes = str.split(/_and_|_or_/) + end + + [attributes, predicate, combinator] end - attributes = str.split(/_and_|_or_/) - [attributes, predicate] - end end def valid? @@ -60,14 +81,12 @@ def attributes def attributes=(args) case args when Array - args.each do |attr| - attr = Attribute.new(@context, attr) - self.attributes << attr if attr.valid? + args.each do |name| + build_attribute(name) end when Hash args.each do |index, attrs| - attr = Attribute.new(@context, attrs[:name]) - self.attributes << attr if attr.valid? + build_attribute(attrs[:name], attrs[:ransacker_args]) end else raise ArgumentError, @@ -105,14 +124,36 @@ def combinator end def combinator=(val) - @combinator = Ransack::Constants::AND_OR.detect { |v| v == val.to_s } || nil + @combinator = Constants::AND_OR.detect { |v| v == val.to_s } || nil end alias :m= :combinator= alias :m :combinator - def build_attribute(name = nil) - Attribute.new(@context, name).tap do |attribute| - self.attributes << attribute + # == build_attribute + # + # This method was originally called from Nodes::Grouping#new_condition + # only, without arguments, without #valid? checking, to build a new + # grouping condition. + # + # After refactoring in 235eae3, it is now called from 2 places: + # + # 1. Nodes::Condition#attributes=, with +name+ argument passed or +name+ + # and +ransacker_args+. Attributes are included only if #valid?. + # + # 2. Nodes::Grouping#new_condition without arguments. In this case, the + # #valid? conditional needs to be bypassed, otherwise nothing is + # built. The `name.nil?` conditional below currently does this. + # + # TODO: Add test coverage for this behavior and ensure that `name.nil?` + # isn't fixing issue #701 by introducing untested regressions. + # + def build_attribute(name = nil, ransacker_args = []) + Attribute.new(@context, name, ransacker_args).tap do |attribute| + @context.bind(attribute, attribute.name) + self.attributes << attribute if name.nil? || attribute.valid? + if predicate && !negative? + @context.lock_association(attribute.parent) + end end end @@ -123,9 +164,11 @@ def build_value(val = nil) end def value - predicate.wants_array ? - values.map { |v| v.cast(default_type) } : + if predicate.wants_array + values.map { |v| v.cast(default_type) } + else values.first.cast(default_type) + end end def build(params) @@ -162,6 +205,10 @@ def hash def predicate_name=(name) self.predicate = Predicate.named(name) + unless negative? + attributes.each { |a| context.lock_association(a.parent) } + end + @predicate end alias :p= :predicate_name= @@ -171,23 +218,7 @@ def predicate_name alias :p :predicate_name def arel_predicate - predicates = attributes.map do |attr| - attr.attr.send( - arel_predicate_for_attribute(attr), - formatted_values_for_attribute(attr) - ) - end - - if predicates.size > 1 - case combinator - when Ransack::Constants::AND - Arel::Nodes::Grouping.new(Arel::Nodes::And.new(predicates)) - when Ransack::Constants::OR - predicates.inject(&:or) - end - else - predicates.first - end + raise "not implemented" end def validated_values @@ -200,25 +231,41 @@ def casted_values_for_attribute(attr) def formatted_values_for_attribute(attr) formatted = casted_values_for_attribute(attr).map do |val| - val = attr.ransacker.formatter.call(val) if - attr.ransacker && attr.ransacker.formatter + if attr.ransacker && attr.ransacker.formatter + val = attr.ransacker.formatter.call(val) + end val = predicate.format(val) + if val.is_a?(String) && val.include?('%') + val = Arel::Nodes::Quoted.new(val) + end val end - predicate.wants_array ? formatted : formatted.first + if predicate.wants_array + formatted + else + formatted.first + end end def arel_predicate_for_attribute(attr) if predicate.arel_predicate === Proc values = casted_values_for_attribute(attr) - predicate.arel_predicate.call( - predicate.wants_array ? values : values.first - ) + unless predicate.wants_array + values = values.first + end + predicate.arel_predicate.call(values) else predicate.arel_predicate end end + def attr_value_for_attribute(attr) + return attr.attr if ActiveRecord::Base.connection.adapter_name == "PostgreSQL" + + predicate.case_insensitive ? attr.attr.lower : attr.attr + rescue + attr.attr + end def default_type predicate.type || (attributes.first && attributes.first.type) @@ -228,22 +275,125 @@ def inspect data = [ ['attributes'.freeze, a.try(:map, &:name)], ['predicate'.freeze, p], - [Ransack::Constants::COMBINATOR, m], + [Constants::COMBINATOR, m], ['values'.freeze, v.try(:map, &:value)] ] .reject { |e| e[1].blank? } .map { |v| "#{v[0]}: #{v[1]}" } - .join(Ransack::Constants::COMMA_SPACE) + .join(', '.freeze) "Condition <#{data}>" end + def negative? + predicate.negative? + end + + def arel_predicate + predicate = attributes.map { |attribute| + association = attribute.parent + parent_table = association.table + + if negative? && attribute.associated_collection? && not_nested_condition(attribute, parent_table) + query = context.build_correlated_subquery(association) + context.remove_association(association) + + case self.predicate_name + when 'not_null' + if self.value + query.where(format_predicate(attribute)) + Arel::Nodes::In.new(context.primary_key, Arel.sql(query.to_sql)) + else + query.where(format_predicate(attribute).not) + Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql)) + end + when 'not_cont' + query.where(attribute.attr.matches(formatted_values_for_attribute(attribute))) + Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql)) + else + query.where(format_predicate(attribute).not) + Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql)) + end + else + format_predicate(attribute) + end + }.reduce(combinator_method) + + if replace_right_node?(predicate) + # Replace right node object to plain integer value in order to avoid + # ActiveModel::RangeError from Arel::Node::Casted. + # The error can be ignored here because RDBMSs accept large numbers + # in condition clauses. + plain_value = predicate.right.value + predicate.right = plain_value + end + + predicate + end + + def not_nested_condition(attribute, parent_table) + parent_table.class != Arel::Nodes::TableAlias && attribute.name.starts_with?(parent_table.name) + end + private - def valid_combinator? - attributes.size < 2 || - Ransack::Constants::AND_OR.include?(combinator) + def combinator_method + combinator === Constants::OR ? :or : :and end + def format_predicate(attribute) + arel_pred = arel_predicate_for_attribute(attribute) + arel_values = formatted_values_for_attribute(attribute) + + # For LIKE predicates, wrap the value in Arel::Nodes.build_quoted to prevent + # ActiveRecord normalization from affecting wildcard patterns + if like_predicate?(arel_pred) + arel_values = Arel::Nodes.build_quoted(arel_values) + end + + predicate = attr_value_for_attribute(attribute).public_send(arel_pred, arel_values) + + if in_predicate?(predicate) + predicate.right = predicate.right.map do |pr| + casted_array?(pr) ? format_values_for(pr) : pr + end + end + + predicate + end + + def in_predicate?(predicate) + return unless defined?(Arel::Nodes::Casted) + predicate.class == Arel::Nodes::In || predicate.class == Arel::Nodes::NotIn + end + + def like_predicate?(arel_predicate) + arel_predicate == 'matches' || arel_predicate == 'does_not_match' + end + + def casted_array?(predicate) + predicate.is_a?(Arel::Nodes::Casted) && predicate.value.is_a?(Array) + end + + def format_values_for(predicate) + predicate.value.map do |val| + val.is_a?(String) ? Arel::Nodes.build_quoted(val) : val + end + end + + def replace_right_node?(predicate) + return false unless predicate.is_a?(Arel::Nodes::Binary) + + arel_node = predicate.right + return false unless arel_node.is_a?(Arel::Nodes::Casted) + + relation, name = arel_node.attribute.values + attribute_type = relation.type_for_attribute(name).type + attribute_type == :integer && arel_node.value.is_a?(Integer) + end + + def valid_combinator? + attributes.size < 2 || Constants::AND_OR.include?(combinator) + end end end end diff --git a/lib/ransack/nodes/grouping.rb b/lib/ransack/nodes/grouping.rb index 7da7d459a..916ee8b32 100644 --- a/lib/ransack/nodes/grouping.rb +++ b/lib/ransack/nodes/grouping.rb @@ -7,9 +7,9 @@ class Grouping < Node alias :m= :combinator= i18n_word :condition, :and, :or - i18n_alias :c => :condition, :n => :and, :o => :or + i18n_alias c: :condition, n: :and, o: :or - delegate :each, :to => :values + delegate :each, to: :values def initialize(context, combinator = nil) super(context) @@ -22,7 +22,7 @@ def persisted? def translate(key, options = {}) super or Translate.attribute( - key.to_s, options.merge(:context => context) + key.to_s, options.merge(context: context) ) end @@ -44,21 +44,16 @@ def conditions=(conditions) self.conditions << condition if condition.valid? end end - - self.conditions.uniq! + remove_duplicate_conditions! end alias :c= :conditions= def [](key) - if condition = conditions.detect { |c| c.key == key.to_s } - condition - else - nil - end + conditions.detect { |c| c.key == key.to_s } end def []=(key, value) - conditions.reject! { |c| c.key == key.to_s } + conditions.reject! { |c| c.key == key.to_s && c&.value == value&.value } self.conditions << value end @@ -69,7 +64,6 @@ def values def respond_to?(method_id) super or begin method_name = method_id.to_s - writer = method_name.sub!(/\=$/, Ransack::Constants::EMPTY) attribute_method?(method_name) ? true : false end end @@ -114,27 +108,30 @@ def groupings=(groupings) alias :g= :groupings= def method_missing(method_id, *args) - method_name = method_id.to_s - writer = method_name.sub!(/\=$/, Ransack::Constants::EMPTY) + method_name = method_id.to_s.dup + writer = method_name.sub!(/\=$/, ''.freeze) if attribute_method?(method_name) - writer ? - write_attribute(method_name, *args) : + if writer + write_attribute(method_name, *args) + else read_attribute(method_name) + end else super end end def attribute_method?(name) - name = strip_predicate_and_index(name) - return true if @context.attribute_method?(name) - case name + stripped_name = strip_predicate_and_index(name) + return true if @context.attribute_method?(stripped_name) || + @context.attribute_method?(name) + case stripped_name when /^(g|c|m|groupings|conditions|combinator)=?$/ true else - name.split(/_and_|_or_/) - .select { |n| !@context.attribute_method?(n) } - .empty? + stripped_name + .split(/_and_|_or_/) + .none? { |n| !@context.attribute_method?(n) } end end @@ -163,12 +160,11 @@ def build(params) def inspect data = [ - ['conditions'.freeze, conditions], - [Ransack::Constants::COMBINATOR, combinator] + ['conditions'.freeze, conditions], [Constants::COMBINATOR, combinator] ] .reject { |e| e[1].blank? } .map { |v| "#{v[0]}: #{v[1]}" } - .join(Ransack::Constants::COMMA_SPACE) + .join(', '.freeze) "Grouping <#{data}>" end @@ -190,10 +186,21 @@ def read_attribute(name) end def strip_predicate_and_index(str) - string = str.split(/\(/).first + string = str[/(.+?)\(/, 1] || str.dup Predicate.detect_and_strip_from_string!(string) string end + + def remove_duplicate_conditions! + # If self.conditions.uniq! is called without passing a block, then + # conditions differing only by ransacker_args within attributes are + # wrongly considered equal and are removed. + self.conditions.uniq! do |c| + c.attributes.map { |a| [a.name, a.ransacker_args] }.flatten + + [c.predicate.name] + + c.values.map { |v| v.value } + end + end end end end diff --git a/lib/ransack/nodes/node.rb b/lib/ransack/nodes/node.rb index 3af1e8bce..e614e9ced 100644 --- a/lib/ransack/nodes/node.rb +++ b/lib/ransack/nodes/node.rb @@ -2,7 +2,7 @@ module Ransack module Nodes class Node attr_reader :context - delegate :contextualize, :to => :context + delegate :contextualize, to: :context class_attribute :i18n_words class_attribute :i18n_aliases self.i18n_words = [] diff --git a/lib/ransack/nodes/sort.rb b/lib/ransack/nodes/sort.rb index dc383d9be..52d1a266f 100644 --- a/lib/ransack/nodes/sort.rb +++ b/lib/ransack/nodes/sort.rb @@ -3,19 +3,20 @@ module Nodes class Sort < Node include Bindable - attr_reader :name, :dir + attr_reader :name, :dir, :ransacker_args i18n_word :asc, :desc class << self def extract(context, str) - attr, direction = str.split(/\s+/,2) + return if str.blank? + attr, direction = str.split(/\s+/, 2) self.new(context).build(name: attr, dir: direction) end end def build(params) params.with_indifferent_access.each do |key, value| - if key.match(/^(name|dir)$/) + if key.match(/^(name|dir|ransacker_args)$/) self.send("#{key}=", value) end end @@ -30,20 +31,24 @@ def valid? end def name=(name) - @name = name - context.bind(self, name) unless name.blank? + @name = context.ransackable_alias(name) || name + context.bind(self, @name) end def dir=(dir) dir = dir.downcase if dir @dir = - if Ransack::Constants::ASC_DESC.include?(dir) + if dir == 'asc'.freeze || dir == 'desc'.freeze dir else - Ransack::Constants::ASC + 'asc'.freeze end end + def ransacker_args=(ransack_args) + @ransacker_args = ransack_args + end + end end end diff --git a/lib/ransack/nodes/value.rb b/lib/ransack/nodes/value.rb index 1b8be0518..6313defb7 100644 --- a/lib/ransack/nodes/value.rb +++ b/lib/ransack/nodes/value.rb @@ -2,7 +2,7 @@ module Ransack module Nodes class Value < Node attr_accessor :value - delegate :present?, :blank?, :to => :value + delegate :present?, :blank?, to: :value def initialize(context, value = nil) super(context) @@ -14,8 +14,7 @@ def persisted? end def eql?(other) - self.class == other.class && - self.value == other.value + self.class == other.class && self.value == other.value end alias :== :eql? @@ -24,87 +23,93 @@ def hash end def cast(type) - case type - when :date - cast_to_date(value) - when :datetime, :timestamp, :time - cast_to_time(value) - when :boolean - cast_to_boolean(value) - when :integer - cast_to_integer(value) - when :float - cast_to_float(value) - when :decimal - cast_to_decimal(value) - else - cast_to_string(value) - end - end + case type + when :date + cast_to_date(value) + when :datetime, :timestamp, :time, :timestamptz + cast_to_time(value) + when :boolean + cast_to_boolean(value) + when :integer + cast_to_integer(value) + when :float + cast_to_float(value) + when :decimal + cast_to_decimal(value) + when :money + cast_to_money(value) + else + cast_to_string(value) + end + end - def cast_to_date(val) - if val.respond_to?(:to_date) - val.to_date rescue nil - else - y, m, d = *[val].flatten - m ||= 1 - d ||= 1 - Date.new(y,m,d) rescue nil - end - end + def cast_to_date(val) + if val.respond_to?(:to_date) + val.to_date rescue nil + else + y, m, d = *[val].flatten + m ||= 1 + d ||= 1 + Date.new(y, m, d) rescue nil + end + end - def cast_to_time(val) - if val.is_a?(Array) - Time.zone.local(*val) rescue nil - else - unless val.acts_like?(:time) - val = val.is_a?(String) ? Time.zone.parse(val) : val.to_time rescue val - end - val.in_time_zone rescue nil - end - end + def cast_to_time(val) + if val.is_a?(Array) + Time.zone.local(*val) rescue nil + else + unless val.acts_like?(:time) + val = val.is_a?(String) ? Time.zone.parse(val) : val.to_time rescue val + end + val.in_time_zone rescue nil + end + end - def cast_to_boolean(val) - if Constants::TRUE_VALUES.include?(val) - true - elsif Constants::FALSE_VALUES.include?(val) - false - else - nil - end - end + def cast_to_boolean(val) + if Constants::TRUE_VALUES.include?(val) + true + elsif Constants::FALSE_VALUES.include?(val) + false + else + nil + end + end - def cast_to_string(val) - val.respond_to?(:to_s) ? val.to_s : String.new(val) - end + def cast_to_string(val) + val.respond_to?(:to_s) ? val.to_s : String.new(val) + end - def cast_to_integer(val) - val.blank? ? nil : val.to_i - end + def cast_to_integer(val) + val.respond_to?(:to_i) && !val.blank? ? val.to_i : nil + end - def cast_to_float(val) - val.blank? ? nil : val.to_f - end + def cast_to_float(val) + val.blank? ? nil : val.to_f + end - def cast_to_decimal(val) - if val.blank? - nil - elsif val.class == BigDecimal - val - elsif val.respond_to?(:to_d) - val.to_d - else - val.to_s.to_d - end + def cast_to_decimal(val) + if val.blank? + nil + elsif val.class == BigDecimal + val + elsif val.respond_to?(:to_d) + val.to_d + else + val.to_s.to_d end + end + + def cast_to_money(val) + val.blank? ? nil : val.to_f.to_s + end def inspect "Value <#{value}>" end - def array_of_arrays?(val) - Array === val && Array === val.first - end + def array_of_arrays?(val) + Array === val && Array === val.first + end end end end diff --git a/lib/ransack/predicate.rb b/lib/ransack/predicate.rb index 434b39535..649418116 100644 --- a/lib/ransack/predicate.rb +++ b/lib/ransack/predicate.rb @@ -1,7 +1,7 @@ module Ransack class Predicate attr_reader :name, :arel_predicate, :type, :formatter, :validator, - :compound, :wants_array + :compound, :wants_array, :case_insensitive class << self @@ -9,34 +9,26 @@ def names Ransack.predicates.keys end - def names_by_decreasing_length - names.sort { |a,b| b.length <=> a.length } - end - def named(name) - Ransack.predicates[name.to_s] + Ransack.predicates[(name || Ransack.options[:default_predicate]).to_s] end def detect_and_strip_from_string!(str) - if p = detect_from_string(str) - str.sub! /_#{p}$/, Ransack::Constants::EMPTY - p - end + detect_from_string str, chomp: true end - def detect_from_string(str) - names_by_decreasing_length.detect { |p| str.end_with?("_#{p}") } - end + def detect_from_string(str, chomp: false) + return unless str -# def name_from_attribute_name(attribute_name) -# names_by_decreasing_length.detect { -# |p| attribute_name.to_s.match(/_#{p}$/) -# } -# end + Ransack.predicates.sorted_names_with_underscores.each do |predicate, underscored| + if str.end_with? underscored + str.chomp! underscored if chomp + return predicate + end + end -# def for_attribute_name(attribute_name) -# self.named(detect_from_string(attribute_name.to_s)) -# end + nil + end end @@ -48,8 +40,9 @@ def initialize(opts = {}) @validator = opts[:validator] || lambda { |v| v.respond_to?(:empty?) ? !v.empty? : !v.nil? } @compound = opts[:compound] - @wants_array = opts[:wants_array] == true || @compound || - Ransack::Constants::IN_NOT_IN.include?(@arel_predicate) + @wants_array = opts.fetch(:wants_array, + @compound || Constants::IN_NOT_IN.include?(@arel_predicate)) + @case_insensitive = opts[:case_insensitive] end def eql?(other) @@ -71,7 +64,11 @@ def format(val) end def validate(vals, type = @type) - vals.select { |v| validator.call(type ? v.cast(type) : v.value) }.any? + vals.any? { |v| validator.call(type ? v.cast(type) : v.value) } + end + + def negative? + @name.include?("not_".freeze) end end diff --git a/lib/ransack/ransacker.rb b/lib/ransack/ransacker.rb index 3ae0c20dd..a677880ed 100644 --- a/lib/ransack/ransacker.rb +++ b/lib/ransack/ransacker.rb @@ -3,7 +3,7 @@ class Ransacker attr_reader :name, :type, :formatter, :args - delegate :call, :to => :@callable + delegate :call, to: :@callable def initialize(klass, name, opts = {}, &block) @klass, @name = klass, name diff --git a/lib/ransack/search.rb b/lib/ransack/search.rb index 34dde5410..03115c5ac 100644 --- a/lib/ransack/search.rb +++ b/lib/ransack/search.rb @@ -1,6 +1,13 @@ -require 'ransack/nodes' +require 'ransack/nodes/bindable' +require 'ransack/nodes/node' +require 'ransack/nodes/attribute' +require 'ransack/nodes/value' +require 'ransack/nodes/condition' +require 'ransack/nodes/sort' +require 'ransack/nodes/grouping' require 'ransack/context' require 'ransack/naming' +require 'ransack/invalid_search_error' module Ransack class Search @@ -8,14 +15,17 @@ class Search attr_reader :base, :context - delegate :object, :klass, :to => :context + delegate :object, :klass, to: :context delegate :new_grouping, :new_condition, :build_grouping, :build_condition, - :translate, :to => :base + :translate, to: :base def initialize(object, params = {}, options = {}) + strip_whitespace = options.fetch(:strip_whitespace, Ransack.options[:strip_whitespace]) + params = params.to_unsafe_h if params.respond_to?(:to_unsafe_h) if params.is_a? Hash params = params.dup + params = params.transform_values { |v| v.is_a?(String) && strip_whitespace ? v.strip : v } params.delete_if { |k, v| [*v].all?{ |i| i.blank? && i != false } } else params = {} @@ -23,10 +33,11 @@ def initialize(object, params = {}, options = {}) @context = options[:context] || Context.for(object, options) @context.auth_object = options[:auth_object] @base = Nodes::Grouping.new( - @context, - options[:grouping] || Ransack::Constants::AND + @context, options[:grouping] || Constants::AND ) @scope_args = {} + @sorts ||= [] + @ignore_unknown_conditions = options[:ignore_unknown_conditions] == false ? false : true build(params.with_indifferent_access) end @@ -36,14 +47,14 @@ def result(opts = {}) def build(params) collapse_multiparameter_attributes!(params).each do |key, value| - if Ransack::Constants::S_SORTS.include?(key) + if ['s'.freeze, 'sorts'.freeze].freeze.include?(key) send("#{key}=", value) - elsif base.attribute_method?(key) - base.send("#{key}=", value) elsif @context.ransackable_scope?(key, @context.object) add_scope(key, value) - elsif !Ransack.options[:ignore_unknown_conditions] - raise ArgumentError, "Invalid search term #{key}" + elsif base.attribute_method?(key) + base.send("#{key}=", value) + elsif !Ransack.options[:ignore_unknown_conditions] || !@ignore_unknown_conditions + raise InvalidSearchError, "Invalid search term #{key}" end end self @@ -58,7 +69,7 @@ def sorts=(args) else sort = Nodes::Sort.extract(@context, sort) end - self.sorts << sort + self.sorts << sort if sort end when Hash args.each do |index, attrs| @@ -68,14 +79,14 @@ def sorts=(args) when String self.sorts = [args] else - raise ArgumentError, + raise InvalidSearchError, "Invalid argument (#{args.class}) supplied to sorts=" end end alias :s= :sorts= def sorts - @sorts ||= [] + @sorts end alias :s :sorts @@ -91,7 +102,7 @@ def new_sort(opts = {}) def method_missing(method_id, *args) method_name = method_id.to_s - getter_name = method_name.sub(/=$/, Ransack::Constants::EMPTY) + getter_name = method_name.sub(/=$/, ''.freeze) if base.attribute_method?(getter_name) base.send(method_id, *args) elsif @context.ransackable_scope?(getter_name, @context.object) @@ -111,8 +122,9 @@ def inspect ([:scope, @scope_args] if @scope_args.present?), [:base, base.inspect] ] - .compact.map { |d| d.join(': '.freeze) } - .join(Ransack::Constants::COMMA_SPACE) + .compact + .map { |d| d.join(': '.freeze) } + .join(', '.freeze) "Ransack::Search<#{details}>" end @@ -120,25 +132,54 @@ def inspect private def add_scope(key, args) + sanitized_args = if Ransack.options[:sanitize_scope_args] && !@context.ransackable_scope_skip_sanitize_args?(key, @context.object) + sanitized_scope_args(args) + else + args + end + if @context.scope_arity(key) == 1 @scope_args[key] = args.is_a?(Array) ? args[0] : args else - @scope_args[key] = args + @scope_args[key] = args.is_a?(Array) ? sanitized_args : args + end + @context.chain_scope(key, sanitized_args) + end + + def sanitized_scope_args(args) + if args.is_a?(Array) + args = args.map(&method(:sanitized_scope_args)) + end + + if Constants::TRUE_VALUES.include? args + true + elsif Constants::FALSE_VALUES.include? args + false + else + args end - @context.chain_scope(key, args) end def collapse_multiparameter_attributes!(attrs) attrs.keys.each do |k| - if k.include?('('.freeze) + if k.include?(Constants::LEFT_PARENTHESIS) real_attribute, position = k.split(/\(|\)/) - cast = %w(a s i).freeze.include?(position.last) ? position.last : nil + cast = + if Constants::A_S_I.include?(position.last) + position.last + else + nil + end position = position.to_i - 1 value = attrs.delete(k) attrs[real_attribute] ||= [] attrs[real_attribute][position] = if cast - value.blank? && cast == 'i'.freeze ? nil : value.send("to_#{cast}") + if value.blank? && cast == Constants::I + nil + else + value.send("to_#{cast}") + end else value end diff --git a/lib/ransack/translate.rb b/lib/ransack/translate.rb index 922e7d954..9906c3f84 100644 --- a/lib/ransack/translate.rb +++ b/lib/ransack/translate.rb @@ -6,154 +6,150 @@ module Ransack module Translate - def self.word(key, options = {}) - I18n.translate(:"ransack.#{key}", :default => key.to_s) - end - - def self.predicate(key, options = {}) - I18n.translate(:"ransack.predicates.#{key}", :default => key.to_s) - end - - def self.attribute(key, options = {}) - unless context = options.delete(:context) - raise ArgumentError, "A context is required to translate attributes" + class << self + def word(key, options = {}) + I18n.translate(:"ransack.#{key}", default: key.to_s) end - original_name = key.to_s - base_class = context.klass - base_ancestors = base_class.ancestors.select { - |x| x.respond_to?(:model_name) - } - predicate = Predicate.detect_from_string(original_name) - attributes_str = original_name - .sub(/_#{predicate}$/, Ransack::Constants::EMPTY) - attribute_names = attributes_str.split(/_and_|_or_/) - combinator = attributes_str.match(/_and_/) ? :and : :or - defaults = base_ancestors.map do |klass| - "ransack.attributes.#{i18n_key(klass)}.#{original_name}".to_sym + def predicate(key, options = {}) + I18n.translate(:"ransack.predicates.#{key}", default: key.to_s) end - translated_names = attribute_names.map do |name| - attribute_name(context, name, options[:include_associations]) - end + def attribute(key, options = {}) + unless context = options.delete(:context) + raise ArgumentError, "A context is required to translate attributes" + end - interpolations = { - :attributes => translated_names.join(" #{Translate.word(combinator)} ") - } + original_name = key.to_s + base_class = context.klass + base_ancestors = base_class.ancestors.select { + |x| x.respond_to?(:model_name) + } + attributes_str = original_name.dup # will be modified by ⬇ + predicate = Predicate.detect_and_strip_from_string!(attributes_str) + attribute_names = attributes_str.split(/_and_|_or_/) + combinator = attributes_str =~ /_and_/ ? :and : :or + defaults = base_ancestors.map do |klass| + "ransack.attributes.#{i18n_key(klass)}.#{original_name}".to_sym + end + defaults << options.delete(:default) if options[:default] - if predicate - defaults << "%{attributes} %{predicate}".freeze - interpolations[:predicate] = Translate.predicate(predicate) - else - defaults << "%{attributes}".freeze - end + translated_names = attribute_names.map do |name| + attribute_name(context, name, options[:include_associations]) + end - defaults << options.delete(:default) if options[:default] - options.reverse_merge! :count => 1, :default => defaults - I18n.translate(defaults.shift, options.merge(interpolations)) - end + interpolations = { + attributes: translated_names.join(" #{Translate.word(combinator)} ") + } - def self.association(key, options = {}) - unless context = options.delete(:context) - raise ArgumentError, "A context is required to translate associations" + if predicate + defaults << "%{attributes} %{predicate}".freeze + interpolations[:predicate] = Translate.predicate(predicate) + else + defaults << "%{attributes}".freeze + end + + options.reverse_merge! count: 1, default: defaults + I18n.translate(defaults.shift, **options.merge(interpolations)) end - defaults = - if key.blank? - [:"#{context.klass.i18n_scope}.models.#{i18n_key(context.klass)}"] - else - [:"ransack.associations.#{i18n_key(context.klass)}.#{key}"] + def association(key, options = {}) + unless context = options.delete(:context) + raise ArgumentError, "A context is required to translate associations" end - defaults << context.traverse(key).model_name.human - options = { :count => 1, :default => defaults } - I18n.translate(defaults.shift, options) - end - private + defaults = + if key.blank? + [:"ransack.models.#{i18n_key(context.klass)}", + :"#{context.klass.i18n_scope}.models.#{i18n_key(context.klass)}"] + else + [:"ransack.associations.#{i18n_key(context.klass)}.#{key}"] + end + defaults << context.traverse(key).model_name.human + options = { count: 1, default: defaults } + I18n.translate(defaults.shift, **options) + end - def self.attribute_name(context, name, include_associations = nil) - @context, @name = context, name - @assoc_path = context.association_path(name) - @attr_name = @name.sub(/^#{@assoc_path}_/, Ransack::Constants::EMPTY) - associated_class = @context.traverse(@assoc_path) if @assoc_path.present? - @include_associated = include_associations && associated_class + private - defaults = default_attribute_name << fallback_args - options = { :count => 1, :default => defaults } - interpolations = build_interpolations(associated_class) + def attribute_name(context, name, include_associations = nil) + @context, @name = context, name + @assoc_path = context.association_path(name) + @attr_name = @name.sub(/^#{@assoc_path}_/, ''.freeze) + associated_class = @context.traverse(@assoc_path) if @assoc_path.present? + @include_associated = include_associations && associated_class - I18n.translate(defaults.shift, options.merge(interpolations)) - end + defaults = default_attribute_name << fallback_args + options = { count: 1, default: defaults } + interpolations = build_interpolations(associated_class) - def self.default_attribute_name - ["ransack.attributes.#{i18n_key(@context.klass)}.#{@name}".to_sym] - end + I18n.translate(defaults.shift, **options.merge(interpolations)) + end - def self.fallback_args - if @include_associated - '%{association_name} %{attr_fallback_name}'.freeze - else - '%{attr_fallback_name}'.freeze + def default_attribute_name + ["ransack.attributes.#{i18n_key(@context.klass)}.#{@name}".to_sym] end - end - def self.build_interpolations(associated_class) - { - :attr_fallback_name => attr_fallback_name(associated_class), - :association_name => association_name - } - .reject { |_, value| value.nil? } - end + def fallback_args + if @include_associated + '%{association_name} %{attr_fallback_name}'.freeze + else + '%{attr_fallback_name}'.freeze + end + end + + def build_interpolations(associated_class) + { + attr_fallback_name: attr_fallback_name(associated_class), + association_name: association_name + }.reject { |_, value| value.nil? } + end - def self.attr_fallback_name(associated_class) - I18n.t( - :"ransack.attributes.#{fallback_class(associated_class)}.#{@attr_name}", - :default => default_interpolation(associated_class) + def attr_fallback_name(associated_class) + I18n.t( + :"ransack.attributes.#{fallback_class(associated_class)}.#{@attr_name}", + default: default_interpolation(associated_class) ) - end + end - def self.fallback_class(associated_class) - i18n_key(associated_class || @context.klass) - end + def fallback_class(associated_class) + i18n_key(associated_class || @context.klass) + end - def self.association_name - association(@assoc_path, :context => @context) if @include_associated - end + def association_name + association(@assoc_path, context: @context) if @include_associated + end - def self.default_interpolation(associated_class) - [ - associated_attribute(associated_class), - ".attributes.#{@attr_name}".to_sym, - @attr_name.humanize - ] - .flatten - end + def default_interpolation(associated_class) + [ + associated_attribute(associated_class), + ".attributes.#{@attr_name}".to_sym, + @attr_name.humanize + ].flatten + end - def self.associated_attribute(associated_class) - if associated_class - translated_attribute(associated_class) - else - translated_ancestor_attributes + def associated_attribute(associated_class) + if associated_class + translated_attribute(associated_class) + else + translated_ancestor_attributes + end end - end - def self.translated_attribute(associated_class) - key = "#{associated_class.i18n_scope}.attributes.#{ + def translated_attribute(associated_class) + key = "#{associated_class.i18n_scope}.attributes.#{ i18n_key(associated_class)}.#{@attr_name}" - ["#{key}.one".to_sym, key.to_sym] - end + ["#{key}.one".to_sym, key.to_sym] + end - def self.translated_ancestor_attributes - @context.klass.ancestors - .select { |ancestor| ancestor.respond_to?(:model_name) } - .map { |ancestor| translated_attribute(ancestor) } - end + def translated_ancestor_attributes + @context.klass.ancestors + .select { |ancestor| ancestor.respond_to?(:model_name) } + .map { |ancestor| translated_attribute(ancestor) } + end - def self.i18n_key(klass) - if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0 - klass.model_name.i18n_key.to_s.tr('.'.freeze, '/'.freeze) - else - klass.model_name.i18n_key.to_s + def i18n_key(klass) + klass.model_name.i18n_key end end end diff --git a/lib/ransack/version.rb b/lib/ransack/version.rb index 9c15d3834..ccc9e973b 100644 --- a/lib/ransack/version.rb +++ b/lib/ransack/version.rb @@ -1,3 +1,3 @@ module Ransack - VERSION = "1.4.1" + VERSION = '4.4.1' end diff --git a/lib/ransack/visitor.rb b/lib/ransack/visitor.rb index aabd4b192..d079bcfe0 100644 --- a/lib/ransack/visitor.rb +++ b/lib/ransack/visitor.rb @@ -18,7 +18,7 @@ def visit_Ransack_Nodes_Condition(object) end def visit_Ransack_Nodes_Grouping(object) - if object.combinator == Ransack::Constants::OR + if object.combinator == Constants::OR visit_or(object) else visit_and(object) @@ -38,17 +38,7 @@ def visit_and(object) def visit_or(object) nodes = object.values.map { |o| accept(o) }.compact - return nil unless nodes.size > 0 - - if nodes.size > 1 - nodes.inject(&:or) - else - nodes.first - end - end - - def visit_Ransack_Nodes_Sort(object) - object.attr.send(object.dir) if object.valid? + nodes.inject(&:or) end def quoted?(object) @@ -64,11 +54,34 @@ def visit(object) send(DISPATCH[object.class], object) end + def visit_Ransack_Nodes_Sort(object) + if object.valid? + if object.attr.is_a?(Arel::Attributes::Attribute) + object.attr.send(object.dir) + else + ordered(object) + end + else + scope_name = :"sort_by_#{object.name}_#{object.dir}" + scope_name if object.context.object.respond_to?(scope_name) + end + end + DISPATCH = Hash.new do |hash, klass| hash[klass] = "visit_#{ - klass.name.gsub('::'.freeze, Ransack::Constants::UNDERSCORE) + klass.name.gsub(Constants::TWO_COLONS, Constants::UNDERSCORE) }" end + private + + def ordered(object) + case object.dir + when 'asc'.freeze + Arel::Nodes::Ascending.new(object.attr) + when 'desc'.freeze + Arel::Nodes::Descending.new(object.attr) + end + end end end diff --git a/ransack.gemspec b/ransack.gemspec index cd63cac4f..13cec86c0 100644 --- a/ransack.gemspec +++ b/ransack.gemspec @@ -1,4 +1,5 @@ # -*- encoding: utf-8 -*- + $:.push File.expand_path("../lib", __FILE__) require "ransack/version" @@ -6,37 +7,28 @@ Gem::Specification.new do |s| s.name = "ransack" s.version = Ransack::VERSION s.platform = Gem::Platform::RUBY - s.authors = ["Ernie Miller", "Ryan Bigg", "Jon Atack"] - s.email = ["ernie@erniemiller.org", "radarlistener@gmail.com", "jonnyatack@gmail.com"] + s.authors = ["Ernie Miller", "Ryan Bigg", "Jon Atack", "Sean Carroll", "David Rodríguez"] + s.email = ["ernie@erniemiller.org", "radarlistener@gmail.com", "jonnyatack@gmail.com", "magma.craters2h@icloud.com"] s.homepage = "https://github.com/activerecord-hackery/ransack" - s.summary = %q{Object-based searching for ActiveRecord (currently).} - s.description = %q{Ransack is the successor to the MetaSearch gem. It improves and expands upon MetaSearch's functionality, but does not have a 100%-compatible API.} + s.summary = %q{Object-based searching for Active Record.} + s.description = %q{Powerful object-based searching and filtering for Active Record with advanced features like complex boolean queries, association searching, custom predicates and i18n support.} + # BREAKING CHANGE: Ruby version requirement changed from >= 3.0 to >= 3.1. + # Be sure to mention this breaking change in the PR description and changelog. + s.required_ruby_version = '>= 3.1' s.license = 'MIT' + + s.metadata = { + 'changelog_uri' => "#{s.homepage}/releases/tag/v#{s.version}" + } - s.rubyforge_project = "ransack" + s.metadata['changelog_uri'] = 'https://github.com/activerecord-hackery/ransack/blob/main/CHANGELOG.md' - s.add_dependency 'actionpack', '>= 3.0' - s.add_dependency 'activerecord', '>= 3.0' - s.add_dependency 'activesupport', '>= 3.0' + s.add_dependency 'activerecord', '>= 7.2' + s.add_dependency 'activesupport', '>= 7.2' s.add_dependency 'i18n' - s.add_dependency 'polyamorous', '~> 1.1' - s.add_development_dependency 'rspec', '~> 2.14.0' - s.add_development_dependency 'machinist', '~> 1.0.6' - s.add_development_dependency 'faker', '~> 0.9.5' - s.add_development_dependency 'sqlite3', '~> 1.3.3' - s.add_development_dependency 'pg' - s.add_development_dependency 'mysql2', '0.3.14' - s.add_development_dependency 'pry', '0.9.12.2' - - s.files = `git ls-files` - .split("\n") - - s.test_files = `git ls-files -- {test,spec,features}/*` - .split("\n") - - s.executables = `git ls-files -- bin/*` - .split("\n") - .map { |f| File.basename(f) } + s.files = Dir["README.md", "LICENSE", "lib/**/*"] + s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") + s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] end diff --git a/spec/blueprints/articles.rb b/spec/blueprints/articles.rb deleted file mode 100644 index 42e2f14be..000000000 --- a/spec/blueprints/articles.rb +++ /dev/null @@ -1,5 +0,0 @@ -Article.blueprint do - person - title - body -end \ No newline at end of file diff --git a/spec/blueprints/comments.rb b/spec/blueprints/comments.rb deleted file mode 100644 index 1ecc7eb69..000000000 --- a/spec/blueprints/comments.rb +++ /dev/null @@ -1,5 +0,0 @@ -Comment.blueprint do - article - person - body -end \ No newline at end of file diff --git a/spec/blueprints/notes.rb b/spec/blueprints/notes.rb deleted file mode 100644 index f6702ca15..000000000 --- a/spec/blueprints/notes.rb +++ /dev/null @@ -1,5 +0,0 @@ -Note.blueprint do - note - notable_type { "Article" } - notable_id -end \ No newline at end of file diff --git a/spec/blueprints/people.rb b/spec/blueprints/people.rb deleted file mode 100644 index 5fc983a19..000000000 --- a/spec/blueprints/people.rb +++ /dev/null @@ -1,8 +0,0 @@ -Person.blueprint do - name - email { "test@example.com" } - salary - only_sort - only_search - only_admin -end diff --git a/spec/blueprints/tags.rb b/spec/blueprints/tags.rb deleted file mode 100644 index f0fb3d869..000000000 --- a/spec/blueprints/tags.rb +++ /dev/null @@ -1,3 +0,0 @@ -Tag.blueprint do - name { Sham.tag_name } -end \ No newline at end of file diff --git a/spec/console.rb b/spec/console.rb index 3dd9ebca4..881ec9aeb 100644 --- a/spec/console.rb +++ b/spec/console.rb @@ -1,21 +1,13 @@ Bundler.setup -require 'machinist/active_record' -require 'sham' +require 'factory_bot' require 'faker' require 'ransack' -Dir[File.expand_path('../../spec/{helpers,support,blueprints}/*.rb', __FILE__)] +Dir[File.expand_path('../../spec/{helpers,support,factories}/*.rb', __FILE__)] .each do |f| require f end -Sham.define do - name { Faker::Name.name } - title { Faker::Lorem.sentence } - body { Faker::Lorem.paragraph } - salary { |index| 30000 + (index * 1000) } - tag_name { Faker::Lorem.words(3).join(' ') } - note { Faker::Lorem.words(7).join(' ') } -end +Faker::Config.random = Random.new(0) Schema.create diff --git a/spec/factories/articles.rb b/spec/factories/articles.rb new file mode 100644 index 000000000..24c8a5d9d --- /dev/null +++ b/spec/factories/articles.rb @@ -0,0 +1,7 @@ +FactoryBot.define do + factory :article do + association :person + title { Faker::Lorem.sentence } + body { Faker::Lorem.paragraph } + end +end diff --git a/spec/factories/comments.rb b/spec/factories/comments.rb new file mode 100644 index 000000000..8079e956d --- /dev/null +++ b/spec/factories/comments.rb @@ -0,0 +1,7 @@ +FactoryBot.define do + factory :comment do + association :article + association :person + body { Faker::Lorem.paragraph } + end +end diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb new file mode 100644 index 000000000..0bd668c8f --- /dev/null +++ b/spec/factories/notes.rb @@ -0,0 +1,13 @@ +FactoryBot.define do + factory :note do + note { Faker::Lorem.words(number: 7).join(' ') } + + trait :for_person do + association :notable, factory: :person + end + + trait :for_article do + association :notable, factory: :article + end + end +end diff --git a/spec/factories/people.rb b/spec/factories/people.rb new file mode 100644 index 000000000..4ce5294c7 --- /dev/null +++ b/spec/factories/people.rb @@ -0,0 +1,10 @@ +FactoryBot.define do + factory :person do + name { Faker::Name.name } + email { "test@example.com" } + sequence(:salary) { |n| 30000 + (n * 1000) } + only_sort { Faker::Lorem.words(number: 3).join(' ') } + only_search { Faker::Lorem.words(number: 3).join(' ') } + only_admin { Faker::Lorem.words(number: 3).join(' ') } + end +end diff --git a/spec/factories/tags.rb b/spec/factories/tags.rb new file mode 100644 index 000000000..fe97b0f76 --- /dev/null +++ b/spec/factories/tags.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :tag do + name { Faker::Lorem.words(number: 3).join(' ') } + end +end diff --git a/spec/helpers/polyamorous_helper.rb b/spec/helpers/polyamorous_helper.rb new file mode 100644 index 000000000..4e4542a8a --- /dev/null +++ b/spec/helpers/polyamorous_helper.rb @@ -0,0 +1,13 @@ +module PolyamorousHelper + def new_join_association(reflection, children, klass) + Polyamorous::JoinAssociation.new reflection, children, klass + end + + def new_join_dependency(klass, associations = {}) + Polyamorous::JoinDependency.new klass, klass.arel_table, associations, Polyamorous::InnerJoin + end + + def new_join(name, type = Polyamorous::InnerJoin, klass = nil) + Polyamorous::Join.new name, type, klass + end +end diff --git a/spec/helpers/ransack_helper.rb b/spec/helpers/ransack_helper.rb index dfdaceb4c..f80e883c1 100644 --- a/spec/helpers/ransack_helper.rb +++ b/spec/helpers/ransack_helper.rb @@ -6,4 +6,4 @@ def quote_table_name(table) def quote_column_name(column) ActiveRecord::Base.connection.quote_column_name(column) end -end \ No newline at end of file +end diff --git a/spec/polyamorous/activerecord_compatibility_spec.rb b/spec/polyamorous/activerecord_compatibility_spec.rb new file mode 100644 index 000000000..614c338d9 --- /dev/null +++ b/spec/polyamorous/activerecord_compatibility_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +module Polyamorous + describe "ActiveRecord Compatibility" do + it 'works with self joins and includes' do + trade_account = Account.create! + Account.create!(trade_account: trade_account) + + accounts = Account.joins(:trade_account).includes(:trade_account, :agent_account) + account = accounts.first + + expect(account.agent_account).to be_nil + end + end +end diff --git a/spec/polyamorous/join_association_spec.rb b/spec/polyamorous/join_association_spec.rb new file mode 100644 index 000000000..a1fcead8a --- /dev/null +++ b/spec/polyamorous/join_association_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +module Polyamorous + describe JoinAssociation do + let(:join_dependency) { new_join_dependency Note, {} } + let(:reflection) { Note.reflect_on_association(:notable) } + let(:parent) { join_dependency.send(:join_root) } + let(:join_association) { + new_join_association(reflection, parent.children, Article) + } + + subject { new_join_association(reflection, parent.children, Person) } + + it 'leaves the original reflection intact for thread safety' do + reflection.instance_variable_set(:@klass, Article) + join_association + .swapping_reflection_klass(reflection, Person) do |new_reflection| + expect(new_reflection.options).not_to equal reflection.options + expect(new_reflection.options).not_to have_key(:polymorphic) + expect(new_reflection.klass).to eq(Person) + expect(reflection.klass).to eq(Article) + end + end + + it 'sets the polymorphic option to true after initializing' do + expect(join_association.reflection.options[:polymorphic]).to be true + end + end +end diff --git a/spec/polyamorous/join_dependency_spec.rb b/spec/polyamorous/join_dependency_spec.rb new file mode 100644 index 000000000..77c66c055 --- /dev/null +++ b/spec/polyamorous/join_dependency_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +module Polyamorous + describe JoinDependency do + context 'with symbol joins' do + subject { new_join_dependency Person, articles: :comments } + + specify { expect(subject.send(:join_root).drop(1).size) + .to eq(2) } + specify { expect(subject.send(:join_root).drop(1).map(&:join_type).uniq) + .to eq [Polyamorous::InnerJoin] } + end + + context 'with has_many :through association' do + subject { new_join_dependency Person, :authored_article_comments } + + specify { expect(subject.send(:join_root).drop(1).size) + .to eq 1 } + specify { expect(subject.send(:join_root).drop(1).first.table_name) + .to eq 'comments' } + end + + context 'with outer join' do + subject { new_join_dependency Person, new_join(:articles, :outer) } + + specify { expect(subject.send(:join_root).drop(1).size) + .to eq 1 } + specify { expect(subject.send(:join_root).drop(1).first.join_type) + .to eq Polyamorous::OuterJoin } + end + + context 'with nested outer joins' do + subject { new_join_dependency Person, + new_join(:articles, :outer) => new_join(:comments, :outer) } + + specify { expect(subject.send(:join_root).drop(1).size) + .to eq 2 } + specify { expect(subject.send(:join_root).drop(1).map(&:join_type)) + .to eq [Polyamorous::OuterJoin, Polyamorous::OuterJoin] } + specify { expect(subject.send(:join_root).drop(1).map(&:join_type).uniq) + .to eq [Polyamorous::OuterJoin] } + end + + context 'with polymorphic belongs_to join' do + subject { new_join_dependency Note, new_join(:notable, :inner, Person) } + + specify { expect(subject.send(:join_root).drop(1).size) + .to eq 1 } + specify { expect(subject.send(:join_root).drop(1).first.join_type) + .to eq Polyamorous::InnerJoin } + specify { expect(subject.send(:join_root).drop(1).first.table_name) + .to eq 'people' } + end + + context 'with polymorphic belongs_to join and nested symbol join' do + subject { new_join_dependency Note, + new_join(:notable, :inner, Person) => :comments } + + specify { expect(subject.send(:join_root).drop(1).size) + .to eq 2 } + specify { expect(subject.send(:join_root).drop(1).map(&:join_type).uniq) + .to eq [Polyamorous::InnerJoin] } + specify { expect(subject.send(:join_root).drop(1).first.table_name) + .to eq 'people' } + specify { expect(subject.send(:join_root).drop(1)[1].table_name) + .to eq 'comments' } + end + + context 'with polymorphic belongs_to join and nested join' do + subject { new_join_dependency Note, + new_join(:notable, :outer, Person) => :comments } + specify { expect(subject.send(:join_root).drop(1).size).to eq 2 } + specify { expect(subject.send(:join_root).drop(1).map(&:join_type)).to eq [Polyamorous::OuterJoin, Polyamorous::InnerJoin] } + specify { expect(subject.send(:join_root).drop(1).first.table_name) + .to eq 'people' } + specify { expect(subject.send(:join_root).drop(1)[1].table_name) + .to eq 'comments' } + end + end +end diff --git a/spec/polyamorous/join_spec.rb b/spec/polyamorous/join_spec.rb new file mode 100644 index 000000000..5e2bfc3ec --- /dev/null +++ b/spec/polyamorous/join_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +module Polyamorous + describe Join do + it 'is a tree node' do + join = new_join(:articles, :outer) + expect(join).to be_kind_of(TreeNode) + end + + it 'can be added to a tree' do + join = new_join(:articles, :outer) + + tree_hash = {} + join.add_to_tree(tree_hash) + + expect(tree_hash[join]).to be {} + end + end +end diff --git a/spec/ransack/adapters/active_record/base_spec.rb b/spec/ransack/adapters/active_record/base_spec.rb index 2a2b4ceda..ff7d0fab8 100644 --- a/spec/ransack/adapters/active_record/base_spec.rb +++ b/spec/ransack/adapters/active_record/base_spec.rb @@ -4,59 +4,308 @@ module Ransack module Adapters module ActiveRecord describe Base do - subject { ::ActiveRecord::Base } it { should respond_to :ransack } - it { should respond_to :search } describe '#search' do - subject { Person.search } + subject { Person.ransack } it { should be_a Search } it 'has a Relation as its object' do expect(subject.object).to be_an ::ActiveRecord::Relation end + context "multiple database connection" do + it "does not raise error" do + expect { Person.ransack(name_cont: "test") }.not_to raise_error + expect { SubDB::OperationHistory.ransack(people_id_eq: 1) }.not_to raise_error + end + end + context 'with scopes' do before do - Person.stub :ransackable_scopes => [:active, :over_age] + allow(Person) + .to receive(:ransackable_scopes) + .and_return([:active, :over_age, :of_age]) + end + + it 'applies true scopes' do + s = Person.ransack('active' => true) + expect(s.result.to_sql).to (include 'active = 1') + end + + it 'applies stringy true scopes' do + s = Person.ransack('active' => 'true') + expect(s.result.to_sql).to (include 'active = 1') + end + + it 'applies stringy boolean scopes with true value in an array' do + s = Person.ransack('of_age' => ['true']) + expect(s.result.to_sql).to (include rails7_and_mysql ? %q{(age >= '18')} : 'age >= 18') + end + + it 'applies stringy boolean scopes with false value in an array' do + s = Person.ransack('of_age' => ['false']) + expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age < '18'} : 'age < 18') + end + + it 'ignores unlisted scopes' do + s = Person.ransack('restricted' => true) + expect(s.result.to_sql).to_not (include 'restricted') + end + + it 'ignores false scopes' do + s = Person.ransack('active' => false) + expect(s.result.to_sql).not_to (include 'active') end - it "applies true scopes" do - search = Person.search('active' => true) - search.result.to_sql.should include "active = 1" + it 'ignores stringy false scopes' do + s = Person.ransack('active' => 'false') + expect(s.result.to_sql).to_not (include 'active') end - it "ignores unlisted scopes" do - search = Person.search('restricted' => true) - search.result.to_sql.should_not include "restricted" + it 'passes values to scopes' do + s = Person.ransack('over_age' => 18) + expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '18'} : 'age > 18') end - it "ignores false scopes" do - search = Person.search('active' => false) - search.result.to_sql.should_not include "active" + it 'chains scopes' do + s = Person.ransack('over_age' => 18, 'active' => true) + expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '18'} : 'age > 18') + expect(s.result.to_sql).to (include 'active = 1') end - it "passes values to scopes" do - search = Person.search('over_age' => 18) - search.result.to_sql.should include "age > 18" + it 'applies scopes that define string SQL joins' do + allow(Article) + .to receive(:ransackable_scopes) + .and_return([:latest_comment_cont]) + + # Including a negative condition to test removing the scope + s = Search.new(Article, notes_note_not_eq: 'Test', latest_comment_cont: 'Test') + expect(s.result.to_sql).to include 'latest_comment' end - it "chains scopes" do - search = Person.search('over_age' => 18, 'active' => true) - search.result.to_sql.should include "age > 18" - search.result.to_sql.should include "active = 1" + context "with sanitize_custom_scope_booleans set to false" do + before(:all) do + Ransack.configure { |c| c.sanitize_custom_scope_booleans = false } + end + + after(:all) do + Ransack.configure { |c| c.sanitize_custom_scope_booleans = true } + end + + it 'passes true values to scopes' do + s = Person.ransack('over_age' => 1) + expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '1'} : 'age > 1') + end + + it 'passes false values to scopes' do + s = Person.ransack('over_age' => 0) + expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '0'} : 'age > 0') + end + end + + context "with ransackable_scopes_skip_sanitize_args enabled for scope" do + before do + allow(Person) + .to receive(:ransackable_scopes_skip_sanitize_args) + .and_return([:over_age]) + end + + it 'passes true values to scopes' do + s = Person.ransack('over_age' => 1) + expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '1'} : 'age > 1') + end + + it 'passes false values to scopes' do + s = Person.ransack('over_age' => 0) + expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '0'} : 'age > 0') + end end end it 'does not raise exception for string :params argument' do - lambda { Person.search('') }.should_not raise_error + expect { Person.ransack('') }.to_not raise_error + end + + it 'raises ArgumentError exception if ransack! called with unknown condition' do + expect { Person.ransack!(unknown_attr_eq: 'Ernie') }.to raise_error(ArgumentError) + end + + it 'raises InvalidSearchError exception if ransack! called with unknown condition' do + expect { Person.ransack!(unknown_attr_eq: 'Ernie') }.to raise_error(InvalidSearchError) end it 'does not modify the parameters' do - params = { :name_eq => '' } - expect { Person.search(params) }.not_to change { params } + params = { name_eq: '' } + expect { Person.ransack(params) }.not_to change { params } + end + end + + context 'has_one through associations' do + let(:address) { Address.create!(city: 'Boston') } + let(:org) { Organization.create!(name: 'Testorg', address: address) } + let!(:employee) { Employee.create!(name: 'Ernie', organization: org) } + + it 'works when has_one through association is first' do + s = Employee.ransack(address_city_eq: 'Boston', organization_name_eq: 'Testorg') + expect(s.result.to_a).to include(employee) + end + + it 'works when has_one through association is last' do + s = Employee.ransack(organization_name_eq: 'Testorg', address_city_eq: 'Boston') + expect(s.result.to_a).to include(employee) + end + end + + context 'negative conditions on HABTM associations' do + let(:medieval) { Tag.create!(name: 'Medieval') } + let(:fantasy) { Tag.create!(name: 'Fantasy') } + let(:arthur) { Article.create!(title: 'King Arthur') } + let(:marco) { Article.create!(title: 'Marco Polo') } + + before do + marco.tags << medieval + arthur.tags << medieval + arthur.tags << fantasy + end + + it 'removes redundant joins from top query' do + s = Article.ransack(tags_name_not_eq: "Fantasy") + sql = s.result.to_sql + expect(sql).to_not include('LEFT OUTER JOIN') + end + + it 'handles != for single values' do + s = Article.ransack(tags_name_not_eq: "Fantasy") + articles = s.result.to_a + expect(articles).to include marco + expect(articles).to_not include arthur + end + + it 'handles NOT IN for multiple attributes' do + s = Article.ransack(tags_name_not_in: ["Fantasy", "Scifi"]) + articles = s.result.to_a + + expect(articles).to include marco + expect(articles).to_not include arthur + end + end + + context 'negative conditions on related object with HABTM associations' do + let(:medieval) { Tag.create!(name: 'Medieval') } + let(:fantasy) { Tag.create!(name: 'Fantasy') } + let(:arthur) { Article.create!(title: 'King Arthur') } + let(:marco) { Article.create!(title: 'Marco Polo') } + let(:comment_arthur) { marco.comments.create!(body: 'King Arthur comment') } + let(:comment_marco) { arthur.comments.create!(body: 'Marco Polo comment') } + + before do + comment_arthur.tags << medieval + comment_marco.tags << fantasy + end + + it 'removes redundant joins from top query' do + s = Article.ransack(comments_tags_name_not_eq: "Fantasy") + sql = s.result.to_sql + expect(sql).to include('LEFT OUTER JOIN') + end + + it 'handles != for single values' do + s = Article.ransack(comments_tags_name_not_eq: "Fantasy") + articles = s.result.to_a + expect(articles).to include marco + expect(articles).to_not include arthur + end + + it 'handles NOT IN for multiple attributes' do + s = Article.ransack(comments_tags_name_not_in: ["Fantasy", "Scifi"]) + articles = s.result.to_a + + expect(articles).to include marco + expect(articles).to_not include arthur + end + end + + context 'negative conditions on self-referenced associations' do + let(:pop) { Person.create!(name: 'Grandpa') } + let(:dad) { Person.create!(name: 'Father') } + let(:mom) { Person.create!(name: 'Mother') } + let(:son) { Person.create!(name: 'Grandchild') } + + before do + son.parent = dad + dad.parent = pop + dad.children << son + mom.children << son + pop.children << dad + son.save! && dad.save! && mom.save! && pop.save! + end + + it 'handles multiple associations and aliases' do + s = Person.ransack( + c: { + '0' => { a: ['name'], p: 'not_eq', v: ['Father'] }, + '1' => { + a: ['children_name', 'parent_name'], + p: 'not_eq', v: ['Father'], m: 'or' + }, + '2' => { a: ['children_salary'], p: 'eq', v: [nil] } + }) + people = s.result + + expect(people.to_a).to include son + expect(people.to_a).to include mom + expect(people.to_a).to_not include dad # rule '0': 'name' + expect(people.to_a).to_not include pop # rule '1': 'children_name' + end + end + + describe '#ransack_alias' do + it 'translates an alias to the correct attributes' do + p = Person.create!(name: 'Meatloaf', email: 'babies@example.com') + + s = Person.ransack(term_cont: 'atlo') + expect(s.result.to_a).to eq [p] + + s = Person.ransack(term_cont: 'babi') + expect(s.result.to_a).to eq [p] + + s = Person.ransack(term_cont: 'nomatch') + expect(s.result.to_a).to eq [] + end + + it 'also works with associations' do + dad = Person.create!(name: 'Birdman') + son = Person.create!(name: 'Weezy', parent: dad) + + s = Person.ransack(daddy_eq: 'Birdman') + expect(s.result.to_a).to eq [son] + + s = Person.ransack(daddy_eq: 'Drake') + expect(s.result.to_a).to eq [] + end + + it 'makes aliases available to subclasses' do + yngwie = Musician.create!(name: 'Yngwie Malmsteen') + + musicians = Musician.ransack(term_cont: 'ngw').result + expect(musicians).to eq([yngwie]) + end + + it 'handles naming collisions gracefully' do + frank = Person.create!(name: 'Frank Stallone') + + people = Person.ransack(term_cont: 'allon').result + expect(people).to eq([frank]) + + Class.new(Article) do + ransack_alias :term, :title + end + + people = Person.ransack(term_cont: 'allon').result + expect(people).to eq([frank]) end end @@ -64,7 +313,7 @@ module ActiveRecord # For infix tests def self.sane_adapter? case ::ActiveRecord::Base.connection.adapter_name - when "SQLite3", "PostgreSQL" + when 'SQLite3', 'PostgreSQL' true else false @@ -82,68 +331,279 @@ def self.sane_adapter? # end it 'creates ransack attributes' do - s = Person.search(:reversed_name_eq => 'htimS cirA') + person = Person.create!(name: 'Aric Smith') + + s = Person.ransack(reversed_name_eq: 'htimS cirA') expect(s.result.size).to eq(1) - expect(s.result.first).to eq Person.where(name: 'Aric Smith').first + expect(s.result.first).to eq person end it 'can be accessed through associations' do - s = Person.search(:children_reversed_name_eq => 'htimS cirA') + s = Person.ransack(children_reversed_name_eq: 'htimS cirA') expect(s.result.to_sql).to match( /#{quote_table_name("children_people")}.#{ quote_column_name("name")} = 'Aric Smith'/ ) end - it 'allows an "attribute" to be an InfixOperation' do - s = Person.search(:doubled_name_eq => 'Aric SmithAric Smith') + it 'allows an attribute to be an InfixOperation' do + s = Person.ransack(doubled_name_eq: 'Aric SmithAric Smith') expect(s.result.first).to eq Person.where(name: 'Aric Smith').first end if defined?(Arel::Nodes::InfixOperation) && sane_adapter? - it "doesn't break #count if using InfixOperations" do - s = Person.search(:doubled_name_eq => 'Aric SmithAric Smith') + it 'does not break #count if using InfixOperations' do + s = Person.ransack(doubled_name_eq: 'Aric SmithAric Smith') expect(s.result.count).to eq 1 end if defined?(Arel::Nodes::InfixOperation) && sane_adapter? - it "should remove empty key value pairs from the params hash" do - s = Person.search(:children_reversed_name_eq => '') + it 'should remove empty key value pairs from the params hash' do + s = Person.ransack(children_reversed_name_eq: '') expect(s.result.to_sql).not_to match /LEFT OUTER JOIN/ end - it "should keep proper key value pairs in the params hash" do - s = Person.search(:children_reversed_name_eq => 'Testing') + it 'should keep proper key value pairs in the params hash' do + s = Person.ransack(children_reversed_name_eq: 'Testing') expect(s.result.to_sql).to match /LEFT OUTER JOIN/ end - it "should function correctly when nil is passed in" do - s = Person.search(nil) + it 'should function correctly when nil is passed in' do + s = Person.ransack(nil) end - it "should function correctly when a blank string is passed in" do - s = Person.search('') + it 'should function correctly when a blank string is passed in' do + s = Person.ransack('') end - it "should function correctly when using fields with dots in them" do - s = Person.search(:email_cont => "example.com") - expect(s.result.exists?).to be true + it 'should function correctly with a multi-parameter attribute' do + if ::ActiveRecord::VERSION::MAJOR >= 7 + ::ActiveRecord.default_timezone = :utc + else + ::ActiveRecord::Base.default_timezone = :utc + end + Time.zone = 'UTC' + + date = Date.current + s = Person.ransack( + { 'created_at_gteq(1i)' => date.year, + 'created_at_gteq(2i)' => date.month, + 'created_at_gteq(3i)' => date.day + } + ) + expect(s.result.to_sql).to match />=/ + expect(s.result.to_sql).to match date.to_s end - it "should function correctly when using fields with % in them" do - Person.create!(:name => "110%-er") - s = Person.search(:name_cont => "10%") + it 'should function correctly when using fields with dots in them' do + s = Person.ransack(email_cont: 'example.com') expect(s.result.exists?).to be true end - it "should function correctly when using fields with backslashes in them" do - Person.create!(:name => "\\WINNER\\") - s = Person.search(:name_cont => "\\WINNER\\") - expect(s.result.exists?).to be true + it 'should function correctly when using fields with % in them' do + p = Person.create!(name: '110%-er') + s = Person.ransack(name_cont: '10%') + expect(s.result.to_a).to eq [p] + end + + it 'should function correctly when using fields with backslashes in them' do + p = Person.create!(name: "\\WINNER\\") + s = Person.ransack(name_cont: "\\WINNER\\") + expect(s.result.to_a).to eq [p] + end + + if ::ActiveRecord::VERSION::MAJOR >= 7 && ActiveRecord::Base.respond_to?(:normalizes) + context 'with ActiveRecord::normalizes' do + around(:each) do |example| + # Create a temporary model class with normalization for testing + test_class = Class.new(ActiveRecord::Base) do + self.table_name = 'people' + normalizes :name, with: ->(name) { name.gsub(/[^a-z0-9]/, '_') } + + def self.ransackable_attributes(auth_object = nil) + Person.ransackable_attributes(auth_object) + end + + def self.name + 'TestPersonWithNormalization' + end + end + + stub_const('TestPersonWithNormalization', test_class) + example.run + end + + it 'should not apply normalization to LIKE wildcards for cont predicate' do + # Create a person with characters that would be normalized + p = TestPersonWithNormalization.create!(name: 'foo%bar') + expect(p.reload.name).to eq('foo_bar') # Verify normalization happened on storage + + # Search should find the person using the original search term + s = TestPersonWithNormalization.ransack(name_cont: 'foo') + expect(s.result.to_a).to eq [p] + + # Verify the SQL contains proper LIKE wildcards, not normalized ones + sql = s.result.to_sql + expect(sql).to include("LIKE '%foo%'") + expect(sql).not_to include("LIKE '_foo_'") + end + + it 'should not apply normalization to LIKE wildcards for other LIKE predicates' do + p = TestPersonWithNormalization.create!(name: 'foo%bar') + + # Test start predicate + s = TestPersonWithNormalization.ransack(name_start: 'foo') + expect(s.result.to_a).to eq [p] + expect(s.result.to_sql).to include("LIKE 'foo%'") + + # Test end predicate + s = TestPersonWithNormalization.ransack(name_end: 'bar') + expect(s.result.to_a).to eq [p] + expect(s.result.to_sql).to include("LIKE '%bar'") + + # Test i_cont predicate + s = TestPersonWithNormalization.ransack(name_i_cont: 'FOO') + expect(s.result.to_a).to eq [p] + expect(s.result.to_sql).to include("LIKE '%foo%'") + end + end end - it 'allows sort by "only_sort" field' do - s = Person.search( - "s" => { "0" => { "dir" => "asc", "name" => "only_sort" } } + context 'searching by underscores' do + # when escaping is supported right in LIKE expression without adding extra expressions + def self.simple_escaping? + case ::ActiveRecord::Base.connection.adapter_name + when 'Mysql2', 'PostgreSQL' + true + else + false + end + end + + it 'should search correctly if matches exist' do + p = Person.create!(name: 'name_with_underscore') + s = Person.ransack(name_cont: 'name_') + expect(s.result.to_a).to eq [p] + end if simple_escaping? + + it 'should return empty result if no matches' do + Person.create!(name: 'name_with_underscore') + s = Person.ransack(name_cont: 'n_') + expect(s.result.to_a).to eq [] + end if simple_escaping? + end + + context 'searching on an `in` predicate with a ransacker' do + it 'should function correctly when passing an array of ids' do + s = Person.ransack(array_people_ids_in: true) + expect(s.result.count).to be > 0 + + s = Person.ransack(array_where_people_ids_in: [1, '2', 3]) + expect(s.result.count).to be 3 + expect(s.result.map(&:id)).to eq [3, 2, 1] + end + + it 'should function correctly with HABTM associations' do + article = Article.first + tag = article.tags.first + s = Person.ransack(article_tags_in: [tag.id]) + + expect(s.result.count).to be 1 + expect(s.result.map(&:id)).to eq [article.person.id] + end + + it 'should function correctly when passing an array of strings' do + a, b = Person.select(:id).order(:id).limit(2).map { |a| a.id.to_s } + + Person.create!(name: a) + s = Person.ransack(array_people_names_in: true) + expect(s.result.count).to be > 0 + s = Person.ransack(array_where_people_names_in: a) + expect(s.result.count).to be 1 + + Person.create!(name: b) + s = Person.ransack(array_where_people_names_in: [a, b]) + expect(s.result.count).to be 2 + end + + it 'should function correctly with an Arel SqlLiteral' do + s = Person.ransack(sql_literal_id_in: 1) + expect(s.result.count).to be 1 + s = Person.ransack(sql_literal_id_in: ['2', 4, '5', 8]) + expect(s.result.count).to be 4 + end + end + + context 'search on an `in` predicate with an array' do + it 'should function correctly when passing an array of ids' do + array = Person.all.map(&:id) + s = Person.ransack(id_in: array) + expect(s.result.count).to eq array.size + end + end + + it 'should work correctly when an attribute name ends with _start' do + p = Person.create!(new_start: 'Bar and foo', name: 'Xiang') + + s = Person.ransack(new_start_end: ' and foo') + expect(s.result.to_a).to eq [p] + + s = Person.ransack(name_or_new_start_start: 'Xia') + expect(s.result.to_a).to eq [p] + + s = Person.ransack(new_start_or_name_end: 'iang') + expect(s.result.to_a).to eq [p] + end + + it 'should work correctly when an attribute name ends with _end' do + p = Person.create!(stop_end: 'Foo and bar', name: 'Marianne') + + s = Person.ransack(stop_end_start: 'Foo and') + expect(s.result.to_a).to eq [p] + + s = Person.ransack(stop_end_or_name_end: 'anne') + expect(s.result.to_a).to eq [p] + + s = Person.ransack(name_or_stop_end_end: ' bar') + expect(s.result.to_a).to eq [p] + end + + it 'should work correctly when an attribute name has `and` in it' do + p = Person.create!(terms_and_conditions: true) + s = Person.ransack(terms_and_conditions_eq: true) + expect(s.result.to_a).to eq [p] + end + + context 'attribute aliased column names', + if: Ransack::SUPPORTS_ATTRIBUTE_ALIAS do + it 'should be translated to original column name' do + s = Person.ransack(full_name_eq: 'Nicolas Cage') + expect(s.result.to_sql).to match( + /WHERE #{quote_table_name("people")}.#{quote_column_name("name")}/ + ) + end + + it 'should translate on associations' do + s = Person.ransack(articles_content_cont: 'Nicolas Cage') + expect(s.result.to_sql).to match( + /#{quote_table_name("articles")}.#{ + quote_column_name("body")} I?LIKE '%Nicolas Cage%'/ + ) + end + end + + it 'sorts with different join variants' do + comments = [ + Comment.create(article: Article.create(title: 'Avenger'), person: Person.create(salary: 100_000)), + Comment.create(article: Article.create(title: 'Avenge'), person: Person.create(salary: 50_000)), + ] + expect(Comment.ransack(article_title_cont: 'aven', s: 'person_salary desc').result).to eq(comments) + expect(Comment.joins(:person).ransack(s: 'persons_salarydesc', article_title_cont: 'aven').result).to eq(comments) + expect(Comment.joins(:person).ransack(article_title_cont: 'aven', s: 'persons_salary desc').result).to eq(comments) + end + + it 'allows sort by `only_sort` field' do + s = Person.ransack( + 's' => { '0' => { 'dir' => 'asc', 'name' => 'only_sort' } } ) expect(s.result.to_sql).to match( /ORDER BY #{quote_table_name("people")}.#{ @@ -151,9 +611,9 @@ def self.sane_adapter? ) end - it "doesn't sort by 'only_search' field" do - s = Person.search( - "s" => { "0" => { "dir" => "asc", "name" => "only_search" } } + it 'does not sort by `only_search` field' do + s = Person.ransack( + 's' => { '0' => { 'dir' => 'asc', 'name' => 'only_search' } } ) expect(s.result.to_sql).not_to match( /ORDER BY #{quote_table_name("people")}.#{ @@ -161,25 +621,25 @@ def self.sane_adapter? ) end - it 'allows search by "only_search" field' do - s = Person.search(:only_search_eq => 'htimS cirA') + it 'allows search by `only_search` field' do + s = Person.ransack(only_search_eq: 'htimS cirA') expect(s.result.to_sql).to match( /WHERE #{quote_table_name("people")}.#{ quote_column_name("only_search")} = 'htimS cirA'/ ) end - it "can't be searched by 'only_sort'" do - s = Person.search(:only_sort_eq => 'htimS cirA') + it 'cannot be searched by `only_sort`' do + s = Person.ransack(only_sort_eq: 'htimS cirA') expect(s.result.to_sql).not_to match( /WHERE #{quote_table_name("people")}.#{ quote_column_name("only_sort")} = 'htimS cirA'/ ) end - it 'allows sort by "only_admin" field, if auth_object: :admin' do - s = Person.search( - { "s" => { "0" => { "dir" => "asc", "name" => "only_admin" } } }, + it 'allows sort by `only_admin` field, if auth_object: :admin' do + s = Person.ransack( + { 's' => { '0' => { 'dir' => 'asc', 'name' => 'only_admin' } } }, { auth_object: :admin } ) expect(s.result.to_sql).to match( @@ -188,9 +648,9 @@ def self.sane_adapter? ) end - it "doesn't sort by 'only_admin' field, if auth_object: nil" do - s = Person.search( - "s" => { "0" => { "dir" => "asc", "name" => "only_admin" } } + it 'does not sort by `only_admin` field, if auth_object: nil' do + s = Person.ransack( + 's' => { '0' => { 'dir' => 'asc', 'name' => 'only_admin' } } ) expect(s.result.to_sql).not_to match( /ORDER BY #{quote_table_name("people")}.#{ @@ -198,10 +658,10 @@ def self.sane_adapter? ) end - it 'allows search by "only_admin" field, if auth_object: :admin' do - s = Person.search( - { :only_admin_eq => 'htimS cirA' }, - { :auth_object => :admin } + it 'allows search by `only_admin` field, if auth_object: :admin' do + s = Person.ransack( + { only_admin_eq: 'htimS cirA' }, + { auth_object: :admin } ) expect(s.result.to_sql).to match( /WHERE #{quote_table_name("people")}.#{ @@ -209,13 +669,122 @@ def self.sane_adapter? ) end - it "can't be searched by 'only_admin', if auth_object: nil" do - s = Person.search(:only_admin_eq => 'htimS cirA') + it 'cannot be searched by `only_admin`, if auth_object: nil' do + s = Person.ransack(only_admin_eq: 'htimS cirA') expect(s.result.to_sql).not_to match( /WHERE #{quote_table_name("people")}.#{ quote_column_name("only_admin")} = 'htimS cirA'/ ) end + + it 'should allow passing ransacker arguments to a ransacker' do + s = Person.ransack( + c: [{ + a: { + '0' => { + name: 'with_arguments', ransacker_args: [10, 100] + } + }, + p: 'cont', + v: ['Passing arguments to ransackers!'] + }] + ) + expect(s.result.to_sql).to match( + /LENGTH\(articles.body\) BETWEEN 10 AND 100/ + ) + expect(s.result.to_sql).to match( + /LIKE \'\%Passing arguments to ransackers!\%\'/ + ) + expect { s.result.first }.to_not raise_error + end + + it 'should allow sort passing arguments to a ransacker' do + s = Person.ransack( + s: { + '0' => { + name: 'with_arguments', dir: 'desc', ransacker_args: [2, 6] + } + } + ) + expect(s.result.to_sql).to match( + /ORDER BY \(SELECT MAX\(articles.title\) FROM articles/ + ) + expect(s.result.to_sql).to match( + /WHERE articles.person_id = people.id AND LENGTH\(articles.body\)/ + ) + expect(s.result.to_sql).to match( + /BETWEEN 2 AND 6 GROUP BY articles.person_id \) DESC/ + ) + end + + context 'case insensitive sorting' do + it 'allows sort by desc' do + search = Person.ransack(sorts: ['name_case_insensitive desc']) + expect(search.result.to_sql).to match /ORDER BY LOWER(.*) DESC/ + end + + it 'allows sort by asc' do + search = Person.ransack(sorts: ['name_case_insensitive asc']) + expect(search.result.to_sql).to match /ORDER BY LOWER(.*) ASC/ + end + end + + context 'ransacker with different types' do + it 'handles string type ransacker correctly' do + s = Person.ransack(name_case_insensitive_eq: 'test') + expect(s.result.to_sql).to match(/LOWER\(.*\) = 'test'/) + end + + it 'handles integer type ransacker correctly' do + s = Person.ransack(sql_literal_id_eq: 1) + expect(s.result.to_sql).to match(/people\.id = 1/) + end + end + + context 'ransacker with formatter returning nil' do + it 'handles formatter returning nil gracefully' do + # This tests the edge case where a formatter might return nil + s = Person.ransack(article_tags_eq: 999999) # Non-existent tag ID + expect { s.result.to_sql }.not_to raise_error + end + end + + context 'ransacker with array formatters' do + it 'handles array_people_ids formatter correctly' do + person1 = Person.create!(name: 'Test1') + person2 = Person.create!(name: 'Test2') + + s = Person.ransack(array_people_ids_eq: 'test') + expect { s.result }.not_to raise_error + end + + it 'handles array_where_people_ids formatter correctly' do + person1 = Person.create!(name: 'Test1') + person2 = Person.create!(name: 'Test2') + + s = Person.ransack(array_where_people_ids_eq: [person1.id, person2.id]) + expect { s.result }.not_to raise_error + end + end + + context 'regular sorting' do + it 'allows sort by desc' do + search = Person.ransack(sorts: ['name desc']) + expect(search.result.to_sql).to match /ORDER BY .* DESC/ + end + + it 'allows sort by asc' do + search = Person.ransack(sorts: ['name asc']) + expect(search.result.to_sql).to match /ORDER BY .* ASC/ + end + end + + context 'sorting by a scope' do + it 'applies the correct scope' do + search = Person.ransack(sorts: ['reverse_name asc']) + expect(search.result.to_sql).to include("ORDER BY REVERSE(name) ASC") + end + end end describe '#ransackable_attributes' do @@ -225,9 +794,14 @@ def self.sane_adapter? it { should include 'name' } it { should include 'reversed_name' } it { should include 'doubled_name' } + it { should include 'term' } it { should include 'only_search' } it { should_not include 'only_sort' } it { should_not include 'only_admin' } + + if Ransack::SUPPORTS_ATTRIBUTE_ALIAS + it { should include 'full_name' } + end end context 'with auth_object :admin' do @@ -240,6 +814,37 @@ def self.sane_adapter? it { should_not include 'only_sort' } it { should include 'only_admin' } end + + context 'when not defined in model, nor in ApplicationRecord' do + subject { Article.ransackable_attributes } + + it "raises a helpful error" do + without_application_record_method(:ransackable_attributes) do + expect { subject }.to raise_error(RuntimeError, /Ransack needs Article attributes explicitly allowlisted/) + end + end + end + + context 'when defined only in model by delegating to super' do + subject { Article.ransackable_attributes } + + around do |example| + Article.singleton_class.define_method(:ransackable_attributes) do + super(nil) - super(nil) + end + + example.run + ensure + Article.singleton_class.remove_method(:ransackable_attributes) + end + + it "returns the allowlist in the model, and warns" do + without_application_record_method(:ransackable_attributes) do + expect { subject }.to output(/Ransack's builtin `ransackable_attributes` method is deprecated/).to_stderr + expect(subject).to be_empty + end + end + end end describe '#ransortable_attributes' do @@ -272,6 +877,37 @@ def self.sane_adapter? it { should include 'parent' } it { should include 'children' } it { should include 'articles' } + + context 'when not defined in model, nor in ApplicationRecord' do + subject { Article.ransackable_associations } + + it "raises a helpful error" do + without_application_record_method(:ransackable_associations) do + expect { subject }.to raise_error(RuntimeError, /Ransack needs Article associations explicitly allowlisted/) + end + end + end + + context 'when defined only in model by delegating to super' do + subject { Article.ransackable_associations } + + around do |example| + Article.singleton_class.define_method(:ransackable_associations) do + super(nil) - super(nil) + end + + example.run + ensure + Article.singleton_class.remove_method(:ransackable_associations) + end + + it "returns the allowlist in the model, and warns" do + without_application_record_method(:ransackable_associations) do + expect { subject }.to output(/Ransack's builtin `ransackable_associations` method is deprecated/).to_stderr + expect(subject).to be_empty + end + end + end end describe '#ransackable_scopes' do @@ -280,6 +916,27 @@ def self.sane_adapter? it { should eq [] } end + describe '#ransackable_scopes_skip_sanitize_args' do + subject { Person.ransackable_scopes_skip_sanitize_args } + + it { should eq [] } + end + + private + + def without_application_record_method(method) + ApplicationRecord.singleton_class.alias_method :"original_#{method}", :"#{method}" + ApplicationRecord.singleton_class.remove_method :"#{method}" + + yield + ensure + ApplicationRecord.singleton_class.alias_method :"#{method}", :"original_#{method}" + ApplicationRecord.singleton_class.remove_method :"original_#{method}" + end + + def rails7_and_mysql + ::ActiveRecord::VERSION::MAJOR >= 7 && ENV['DB'] == 'mysql' + end end end end diff --git a/spec/ransack/adapters/active_record/context_spec.rb b/spec/ransack/adapters/active_record/context_spec.rb index 3d9fb418d..783988fad 100644 --- a/spec/ransack/adapters/active_record/context_spec.rb +++ b/spec/ransack/adapters/active_record/context_spec.rb @@ -9,10 +9,9 @@ module ActiveRecord describe Context do subject { Context.new(Person) } - if AR_version >= "3.1" - its(:alias_tracker) { - should be_a ::ActiveRecord::Associations::AliasTracker - } + it 'has an Active Record alias tracker method' do + expect(subject.alias_tracker) + .to be_an ::ActiveRecord::Associations::AliasTracker end describe '#relation_for' do @@ -23,8 +22,8 @@ module ActiveRecord describe '#evaluate' do it 'evaluates search objects' do - search = Search.new(Person, :name_eq => 'Joe Blow') - result = subject.evaluate(search) + s = Search.new(Person, name_eq: 'Joe Blow') + result = subject.evaluate(s) expect(result).to be_an ::ActiveRecord::Relation expect(result.to_sql) @@ -32,54 +31,149 @@ module ActiveRecord end it 'SELECTs DISTINCT when distinct: true' do - search = Search.new(Person, :name_eq => 'Joe Blow') - result = subject.evaluate(search, :distinct => true) + s = Search.new(Person, name_eq: 'Joe Blow') + result = subject.evaluate(s, distinct: true) expect(result).to be_an ::ActiveRecord::Relation expect(result.to_sql).to match /SELECT DISTINCT/ end end - describe "sharing context across searches" do - let(:shared_context) { Context.for(Person) } + describe '#build_correlated_subquery' do + it 'build correlated subquery for Root STI model' do + search = Search.new(Person, { articles_title_not_eq: 'some_title' }, context: subject) + attribute = search.conditions.first.attributes.first + constraints = subject.build_correlated_subquery(attribute.parent).constraints + constraint = constraints.first + + expect(constraints.length).to eql 1 + expect(constraint.left.name).to eql 'person_id' + expect(constraint.left.relation.name).to eql 'articles' + expect(constraint.right.name).to eql 'id' + expect(constraint.right.relation.name).to eql 'people' + end - before do - Search.new(Person, { :parent_name_eq => 'A' }, - context: shared_context) - Search.new(Person, { :children_name_eq => 'B' }, - context: shared_context) + it 'build correlated subquery for Child STI model when predicate is not_eq' do + search = Search.new(Person, { story_articles_title_not_eq: 'some_title' }, context: subject) + attribute = search.conditions.first.attributes.first + constraints = subject.build_correlated_subquery(attribute.parent).constraints + constraint = constraints.first + + expect(constraints.length).to eql 1 + expect(constraint.left.relation.name).to eql 'articles' + expect(constraint.left.name).to eql 'person_id' + expect(constraint.right.relation.name).to eql 'people' + expect(constraint.right.name).to eql 'id' end - describe '#join_associations', :if => AR_version <= '4.0' do - it 'returns dependent join associations for all searches run - against the context' do - parents, children = shared_context.join_associations + it 'build correlated subquery for Child STI model when predicate is eq' do + search = Search.new(Person, { story_articles_title_not_eq: 'some_title' }, context: subject) + attribute = search.conditions.first.attributes.first + constraints = subject.build_correlated_subquery(attribute.parent).constraints + constraint = constraints.first + + expect(constraints.length).to eql 1 + expect(constraint.left.relation.name).to eql 'articles' + expect(constraint.left.name).to eql 'person_id' + expect(constraint.right.relation.name).to eql 'people' + expect(constraint.right.name).to eql 'id' + end - expect(children.aliased_table_name).to eq "children_people" - expect(parents.aliased_table_name).to eq "parents_people" - end + it 'build correlated subquery for multiple conditions (default scope)' do + search = Search.new(Person, { comments_body_not_eq: 'some_title' }) + + # Was + # SELECT "people".* FROM "people" WHERE "people"."id" NOT IN ( + # SELECT "comments"."disabled" FROM "comments" + # WHERE "comments"."disabled" = "people"."id" + # AND NOT ("comments"."body" != 'some_title') + # ) ORDER BY "people"."id" DESC + # Should Be + # SELECT "people".* FROM "people" WHERE "people"."id" NOT IN ( + # SELECT "comments"."person_id" FROM "comments" + # WHERE "comments"."person_id" = "people"."id" + # AND NOT ("comments"."body" != 'some_title') + # ) ORDER BY "people"."id" DESC + + expect(search.result.to_sql).to match /.comments.\..person_id. = .people.\..id./ + end - it 'can be rejoined to execute a valid query' do - parents, children = shared_context.join_associations + it 'handles Arel::Nodes::And with children' do + # Create a mock Arel::Nodes::And with children for testing + search = Search.new(Person, { articles_title_not_eq: 'some_title', articles_body_not_eq: 'some_body' }, context: subject) + attribute = search.conditions.first.attributes.first + constraints = subject.build_correlated_subquery(attribute.parent).constraints + constraint = constraints.first + + expect(constraints.length).to eql 1 + expect(constraint.left.name).to eql 'person_id' + expect(constraint.left.relation.name).to eql 'articles' + expect(constraint.right.name).to eql 'id' + expect(constraint.right.relation.name).to eql 'people' + end - expect { Person.joins(parents).joins(children).to_a } - .to_not raise_error - end + it 'correctly extracts correlated key from complex AND conditions' do + # Test with multiple nested conditions to ensure the children traversal works + search = Search.new( + Person, + { + articles_title_not_eq: 'title', + articles_body_not_eq: 'body', + articles_published_eq: true + }, + context: subject + ) + + attribute = search.conditions.first.attributes.first + constraints = subject.build_correlated_subquery(attribute.parent).constraints + constraint = constraints.first + + expect(constraints.length).to eql 1 + expect(constraint.left.relation.name).to eql 'articles' + expect(constraint.left.name).to eql 'person_id' + expect(constraint.right.relation.name).to eql 'people' + expect(constraint.right.name).to eql 'id' + end + + it 'build correlated subquery for polymorphic & default_scope when predicate is not_cont_all' do + search = Search.new(Article, + g: [ + { + m: "and", + c: [ + { + a: ["recent_notes_note"], + p: "not_eq", + v: ["some_note"], + } + ] + } + ], + ) + + expect(search.result.to_sql).to match /(.notes.\..note. != \'some_note\')/ + end + end + + describe 'sharing context across searches' do + let(:shared_context) { Context.for(Person) } + + before do + Search.new(Person, { parent_name_eq: 'A' }, + context: shared_context) + Search.new(Person, { children_name_eq: 'B' }, + context: shared_context) end describe '#join_sources' do - # FIXME: fix this test for Rails 4.2. it 'returns dependent arel join nodes for all searches run against - the context', - :if => %w(3.1 3.2 4.0 4.1).include?(AR_version) do + the context' do parents, children = shared_context.join_sources - expect(children.left.name).to eq "children_people" expect(parents.left.name).to eq "parents_people" end - it 'can be rejoined to execute a valid query', - :if => AR_version >= '3.1' do + it 'can be rejoined to execute a valid query' do parents, children = shared_context.join_sources expect { Person.joins(parents).joins(children).to_a } @@ -103,6 +197,22 @@ module ActiveRecord expect(attribute.relation.table_alias).to be_nil end + describe '#type_for' do + it 'returns nil when column does not exist instead of raising NoMethodError' do + # Create a mock attribute that references a non-existent column + mock_attr = double('attribute') + allow(mock_attr).to receive(:valid?).and_return(true) + + mock_arel_attr = double('arel_attribute') + allow(mock_arel_attr).to receive(:relation).and_return(Person.arel_table) + allow(mock_arel_attr).to receive(:name).and_return('nonexistent_column') + allow(mock_attr).to receive(:arel_attribute).and_return(mock_arel_attr) + allow(mock_attr).to receive(:klass).and_return(Person) + + # This should return nil instead of raising an error + expect(subject.type_for(mock_attr)).to be_nil + end + end end end end diff --git a/spec/ransack/configuration_spec.rb b/spec/ransack/configuration_spec.rb index c860b3db2..9d232143b 100644 --- a/spec/ransack/configuration_spec.rb +++ b/spec/ransack/configuration_spec.rb @@ -3,9 +3,7 @@ module Ransack describe Configuration do it 'yields Ransack on configure' do - Ransack.configure do |config| - expect(config).to eq Ransack - end + Ransack.configure { |config| expect(config).to eq Ransack } end it 'adds predicates' do @@ -22,7 +20,7 @@ module Ransack Ransack.configure do |config| config.add_predicate( :test_predicate_without_compound, - :compounds => false + compounds: false ) end expect(Ransack.predicates) @@ -38,25 +36,110 @@ module Ransack end it 'changes default search key parameter' do - # store original state so we can restore it later - before = Ransack.options.clone + default = Ransack.options.clone - Ransack.configure do |config| - config.search_key = :query - end + Ransack.configure { |c| c.search_key = :query } expect(Ransack.options[:search_key]).to eq :query - # restore original state so we don't break other tests - Ransack.options = before + Ransack.options = default + end + + it 'should have default value for strip_whitespace' do + expect(Ransack.options[:strip_whitespace]).to eq true + end + + it 'changes default search key parameter' do + default = Ransack.options.clone + + Ransack.configure { |c| c.strip_whitespace = false } + + expect(Ransack.options[:strip_whitespace]).to eq false + + Ransack.options = default + end + + it 'should have default values for arrows' do + expect(Ransack.options[:up_arrow]).to eq '▼' + expect(Ransack.options[:down_arrow]).to eq '▲' + expect(Ransack.options[:default_arrow]).to eq nil + end + + it 'changes the default value for the up arrow only' do + default, new_up_arrow = Ransack.options.clone, 'U+02191' + + Ransack.configure { |c| c.custom_arrows = { up_arrow: new_up_arrow } } + + expect(Ransack.options[:down_arrow]).to eq default[:down_arrow] + expect(Ransack.options[:up_arrow]).to eq new_up_arrow + + Ransack.options = default + end + + it 'changes the default value for the down arrow only' do + default, new_down_arrow = Ransack.options.clone, '' + + Ransack.configure { |c| c.custom_arrows = { down_arrow: new_down_arrow } } + + expect(Ransack.options[:up_arrow]).to eq default[:up_arrow] + expect(Ransack.options[:down_arrow]).to eq new_down_arrow + + Ransack.options = default + end + + it 'changes the default value for the default arrow only' do + default, new_default_arrow = Ransack.options.clone, '' + + Ransack.configure { |c| c.custom_arrows = { default_arrow: new_default_arrow } } + + expect(Ransack.options[:up_arrow]).to eq default[:up_arrow] + expect(Ransack.options[:down_arrow]).to eq default[:down_arrow] + expect(Ransack.options[:default_arrow]).to eq new_default_arrow + + Ransack.options = default + end + + it 'changes the default value for all arrows' do + default = Ransack.options.clone + new_up_arrow = '' + new_down_arrow = 'U+02193' + new_default_arrow = 'defaultarrow' + + Ransack.configure do |c| + c.custom_arrows = { up_arrow: new_up_arrow, down_arrow: new_down_arrow, default_arrow: new_default_arrow } + end + + expect(Ransack.options[:up_arrow]).to eq new_up_arrow + expect(Ransack.options[:down_arrow]).to eq new_down_arrow + expect(Ransack.options[:default_arrow]).to eq new_default_arrow + + Ransack.options = default + end + + it 'consecutive arrow customizations respect previous customizations' do + default = Ransack.options.clone + + Ransack.configure { |c| c.custom_arrows = { up_arrow: 'up' } } + expect(Ransack.options[:down_arrow]).to eq default[:down_arrow] + + Ransack.configure { |c| c.custom_arrows = { down_arrow: 'DOWN' } } + expect(Ransack.options[:up_arrow]).to eq 'up' + + Ransack.configure { |c| c.custom_arrows = { up_arrow: 'U-Arrow' } } + expect(Ransack.options[:down_arrow]).to eq 'DOWN' + + Ransack.configure { |c| c.custom_arrows = { down_arrow: 'down arrow-2' } } + expect(Ransack.options[:up_arrow]).to eq 'U-Arrow' + + Ransack.options = default end it 'adds predicates that take arrays, overriding compounds' do Ransack.configure do |config| config.add_predicate( :test_array_predicate, - :wants_array => true, - :compounds => true + wants_array: true, + compounds: true ) end @@ -64,5 +147,55 @@ module Ransack expect(Ransack.predicates).not_to have_key 'test_array_predicate_any' expect(Ransack.predicates).not_to have_key 'test_array_predicate_all' end + + describe '`wants_array` option takes precedence over Arel predicate' do + it 'implicitly wants an array for in/not in predicates' do + Ransack.configure do |config| + config.add_predicate( + :test_in_predicate, + arel_predicate: 'in' + ) + config.add_predicate( + :test_not_in_predicate, + arel_predicate: 'not_in' + ) + end + + expect(Ransack.predicates['test_in_predicate'].wants_array) + .to eq true + expect(Ransack.predicates['test_not_in_predicate'].wants_array) + .to eq true + end + + it 'explicitly does not want array for in/not_in predicates' do + Ransack.configure do |config| + config.add_predicate( + :test_in_predicate_no_array, + arel_predicate: 'in', + wants_array: false + ) + config.add_predicate( + :test_not_in_predicate_no_array, + arel_predicate: 'not_in', + wants_array: false + ) + end + + expect(Ransack.predicates['test_in_predicate_no_array'].wants_array) + .to eq false + expect(Ransack.predicates['test_not_in_predicate_no_array'].wants_array) + .to eq false + end + end + + it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do + default = Ransack.options.clone + + Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first } + + expect(Ransack.options[:postgres_fields_sort_option]).to eq :nulls_first + + Ransack.options = default + end end end diff --git a/spec/ransack/dependencies_spec.rb b/spec/ransack/dependencies_spec.rb deleted file mode 100644 index ca669b933..000000000 --- a/spec/ransack/dependencies_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -unless ::ActiveRecord::VERSION::STRING >= '4' - describe 'Ransack' do - it 'can be required without errors' do - output = `bundle exec ruby -e "require 'ransack'" 2>&1` - expect(output).to be_empty - end - end -end diff --git a/spec/ransack/helpers/form_builder_spec.rb b/spec/ransack/helpers/form_builder_spec.rb index b02bdd756..90b01c138 100644 --- a/spec/ransack/helpers/form_builder_spec.rb +++ b/spec/ransack/helpers/form_builder_spec.rb @@ -3,11 +3,9 @@ module Ransack module Helpers describe FormBuilder do - router = ActionDispatch::Routing::RouteSet.new router.draw do resources :people, :comments, :notes - get ':controller(/:action(/:id(.:format)))' end include router.url_helpers @@ -18,16 +16,16 @@ module Helpers @controller.instance_variable_set(:@_routes, router) @controller.class_eval { include router.url_helpers } @controller.view_context_class.class_eval { include router.url_helpers } - @s = Person.search + @s = Person.ransack @controller.view_context.search_form_for(@s) { |f| @f = f } end it 'selects previously-entered time values with datetime_select' do - date_values = %w(2011 1 2 03 04 05).freeze + date_values = %w(2011 1 2 03 04 05) # @s.created_at_eq = date_values # This works in Rails 4.x but not 3.x @s.created_at_eq = [2011, 1, 2, 3, 4, 5] # so we have to do this html = @f.datetime_select( - :created_at_eq, :use_month_numbers => true, :include_seconds => true + :created_at_eq, use_month_numbers: true, include_seconds: true ) date_values.each { |val| expect(html).to include date_select_html(val) } end @@ -57,7 +55,7 @@ module Helpers end context 'with `belongs_to` association attributes' do before do - @controller.view_context.search_form_for(Comment.search) { |f| @f = f } + @controller.view_context.search_form_for(Comment.ransack) { |f| @f = f } end it 'localizes :"#{singularized model}_#{attribute name}_#{predicate}"' do test_label(@f, :article_body_start, /Article maiN BoDy starts with/) @@ -71,17 +69,13 @@ module Helpers describe '#sort_link' do it 'sort_link for ransack attribute' do - sort_link = @f.sort_link :name, :controller => 'people' - if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./ - expect(sort_link).to match /people\?q%5Bs%5D=name\+asc/ - else - expect(sort_link).to match /people\?q(%5B|\[)s(%5D|\])=name\+asc/ - end + sort_link = @f.sort_link :name, controller: 'people' + expect(sort_link).to match /people\?q(%5B|\[)s(%5D|\])=name\+asc/ expect(sort_link).to match /sort_link/ expect(sort_link).to match /Full Name<\/a>/ end it 'sort_link for common attribute' do - sort_link = @f.sort_link :id, :controller => 'people' + sort_link = @f.sort_link :id, controller: 'people' expect(sort_link).to match /id<\/a>/ end end @@ -104,14 +98,14 @@ module Helpers it 'returns ransackable attributes for associations with :associations' do attributes = Person.ransackable_attributes + Article.ransackable_attributes.map { |a| "articles_#{a}" } - html = @f.attribute_select(:associations => ['articles']) + html = @f.attribute_select(associations: ['articles']) expect(html.split(/\n/).size).to eq(attributes.size) attributes.each do |attribute| expect(html).to match // end @@ -126,7 +120,7 @@ module Helpers end end it 'filters predicates with single-value :only' do - html = @f.predicate_select :only => 'eq' + html = @f.predicate_select only: 'eq' Predicate.names.reject { |k| k =~ /^eq/ }.each do |key| expect(html).not_to match /) - else - %() - end + %() end - end end end diff --git a/spec/ransack/helpers/form_helper_spec.rb b/spec/ransack/helpers/form_helper_spec.rb index ffa0b4b30..f0b78d407 100644 --- a/spec/ransack/helpers/form_helper_spec.rb +++ b/spec/ransack/helpers/form_helper_spec.rb @@ -3,11 +3,12 @@ module Ransack module Helpers describe FormHelper do - router = ActionDispatch::Routing::RouteSet.new router.draw do - resources :people - get ':controller(/:action(/:id(.:format)))' + resources :people, :notes + namespace :admin do + resources :comments + end end include router.url_helpers @@ -16,306 +17,1059 @@ module Helpers before do @controller = ActionView::TestCase::TestController.new @controller.instance_variable_set(:@_routes, router) - @controller.class_eval do - include router.url_helpers - end - - @controller.view_context_class.class_eval do - include router.url_helpers - end + @controller.class_eval { include router.url_helpers } + @controller.view_context_class.class_eval { include router.url_helpers } end describe '#sort_link with default search_key' do subject { @controller.view_context .sort_link( - [:main_app, Person.search(:sorts => ['name desc'])], + [:main_app, Person.ransack(sorts: ['name desc'])], :name, - :controller => 'people' + controller: 'people' ) } - it { - should match( - if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./ - /people\?q%5Bs%5D=name\+asc/ - else - /people\?q(%5B|\[)s(%5D|\])=name\+asc/ - end + it { should match /people\?q(%5B|\[)s(%5D|\])=name\+asc/ } + it { should match /sort_link desc/ } + it { should match /Full Name ▼/ } + end + + describe '#sort_url with default search_key' do + subject { @controller.view_context + .sort_url( + [:main_app, Person.ransack(sorts: ['name desc'])], + :name, + controller: 'people' ) } - it { - should match /sort_link desc/ - } - it { - should match /Full Name ▼/ - } + it { should match /people\?q(%5B|\[)s(%5D|\])=name\+asc/ } end describe '#sort_link with default search_key defined as symbol' do subject { @controller.view_context .sort_link( - Person.search( - { :sorts => ['name desc'] }, :search_key => :people_search - ), - :name, - :controller => 'people' + Person.ransack({ sorts: ['name desc'] }, search_key: :people_search), + :name, controller: 'people' ) } - it { - should match( - if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./ - /people\?people_search%5Bs%5D=name\+asc/ - else - /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/ - end + it { should match /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/ } + end + + describe '#sort_url with default search_key defined as symbol' do + subject { @controller.view_context + .sort_url( + Person.ransack({ sorts: ['name desc'] }, search_key: :people_search), + :name, controller: 'people' ) } + it { should match /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/ } end - describe '#sort_link desc through association table defined as a symbol' do + describe '#sort_link desc through association table defined as symbol' do subject { @controller.view_context .sort_link( - Person.search({ :sorts => 'comments_body asc' }), - :comments_body, :controller => 'people' - ) - } - it { - should match( - if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./ - /people\?q%5Bs%5D=comments.body\+desc/ - else - /people\?q(%5B|\[)s(%5D|\])=comments.body\+desc/ - end - ) - } + Person.ransack({ sorts: 'comments_body asc' }), + :comments_body, + controller: 'people' + ) + } + it { should match /people\?q(%5B|\[)s(%5D|\])=comments.body\+desc/ } it { should match /sort_link asc/ } it { should match /Body ▲/ } end + describe '#sort_url desc through association table defined as symbol' do + subject { @controller.view_context + .sort_url( + Person.ransack({ sorts: 'comments_body asc' }), + :comments_body, + controller: 'people' + ) + } + it { should match /people\?q(%5B|\[)s(%5D|\])=comments.body\+desc/ } + end + describe '#sort_link through association table defined as a string' do subject { @controller.view_context .sort_link( - Person.search({ :sorts => 'comments.body desc' }), - 'comments.body', :controller => 'people' - ) - } - it { - should match( - if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./ - /people\?q%5Bs%5D=comments.body\+asc/ - else - /people\?q(%5B|\[)s(%5D|\])=comments.body\+asc/ - end - ) - } + Person.ransack({ sorts: 'comments.body desc' }), + 'comments.body', + controller: 'people' + ) + } + it { should match /people\?q(%5B|\[)s(%5D|\])=comments.body\+asc/ } it { should match /sort_link desc/ } it { should match /Comments.body ▼/ } end + describe '#sort_url through association table defined as a string' do + subject { @controller.view_context + .sort_url( + Person.ransack({ sorts: 'comments.body desc' }), + 'comments.body', + controller: 'people' + ) + } + it { should match /people\?q(%5B|\[)s(%5D|\])=comments.body\+asc/ } + end + describe '#sort_link works even if search params are a blank string' do before { @controller.view_context.params[:q] = '' } specify { - expect { - @controller.view_context.sort_link( - Person.search(@controller.view_context.params[:q]), + expect { @controller.view_context + .sort_link( + Person.ransack(@controller.view_context.params[:q]), :name, - :controller => 'people' + controller: 'people' ) }.not_to raise_error } end - describe '#sort_link with default search_key defined as string' do - subject { - @controller.view_context.sort_link( - Person.search( - { :sorts => ['name desc'] }, :search_key => 'people_search' - ), + describe '#sort_url works even if search params are a blank string' do + before { @controller.view_context.params[:q] = '' } + specify { + expect { @controller.view_context + .sort_url( + Person.ransack(@controller.view_context.params[:q]), + :name, + controller: 'people' + ) + }.not_to raise_error + } + end + + describe '#sort_link works even if search params are a string' do + before { @controller.view_context.params[:q] = 'input error' } + specify { + expect { @controller.view_context + .sort_link( + Person.ransack({}), + :name, + controller: 'people' + ) + }.not_to raise_error + } + end + + describe '#sort_url works even if search params are a string' do + before { @controller.view_context.params[:q] = 'input error' } + specify { + expect { @controller.view_context + .sort_url( + Person.ransack({}), + :name, + controller: 'people' + ) + }.not_to raise_error + } + end + + describe '#sort_link with search_key defined as a string' do + subject { @controller.view_context + .sort_link( + Person.ransack( + { sorts: ['name desc'] }, search_key: 'people_search' + ), :name, - :controller => 'people' + controller: 'people' ) } - it { - should match( - if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./ - /people\?people_search%5Bs%5D=name\+asc/ - else - /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/ - end + it { should match /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/ } + end + + describe '#sort_link with default_order defined with a string key' do + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack()], + :name, + controller: 'people', + default_order: 'desc' + ) + } + it { should_not match /default_order/ } + end + + describe '#sort_url with default_order defined with a string key' do + subject { @controller.view_context + .sort_url( + [:main_app, Person.ransack()], + :name, + controller: 'people', + default_order: 'desc' ) } + it { should_not match /default_order/ } end describe '#sort_link with multiple search_keys defined as an array' do subject { @controller.view_context .sort_link( - [:main_app, Person.search(:sorts => ['name desc', 'email asc'])], + [:main_app, Person.ransack(sorts: ['name desc', 'email asc'])], :name, [:name, 'email DESC'], - :controller => 'people' + controller: 'people' ) } it { - should match( - /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ + should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ + ) + } + it { should match /sort_link desc/ } + it { should match /Full Name ▼/ } + end + + describe '#sort_url with multiple search_keys defined as an array' do + subject { @controller.view_context + .sort_url( + [:main_app, Person.ransack(sorts: ['name desc', 'email asc'])], + :name, [:name, 'email DESC'], + controller: 'people' ) } it { - should match /sort_link desc/ + should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ + ) + } + end + + describe '#sort_link with multiple search_keys does not break on nil values & ignores them' do + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack(sorts: ['name desc', nil, 'email', nil])], + :name, [nil, :name, nil, 'email DESC', nil], + controller: 'people' + ) } it { - should match /Full Name ▼/ + should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ + ) + } + it { should match /sort_link desc/ } + it { should match /Full Name ▼/ } + end + + describe '#sort_url with multiple search_keys does not break on nil values & ignores them' do + subject { @controller.view_context + .sort_url( + [:main_app, Person.ransack(sorts: ['name desc', nil, 'email', nil])], + :name, [nil, :name, nil, 'email DESC', nil], + controller: 'people' + ) + } + it { + should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ + ) } end describe '#sort_link with multiple search_keys should allow a label to be specified' do subject { @controller.view_context .sort_link( - [:main_app, Person.search(:sorts => ['name desc', 'email asc'])], + [:main_app, Person.ransack(sorts: ['name desc', 'email asc'])], :name, [:name, 'email DESC'], 'Property Name', - :controller => 'people' + controller: 'people' ) } - it { - should match /Property Name ▼/ - } + it { should match /Property Name ▼/ } end describe '#sort_link with multiple search_keys should flip multiple fields specified without a direction' do subject { @controller.view_context .sort_link( - [:main_app, Person.search(:sorts => ['name desc', 'email asc'])], + [:main_app, Person.ransack(sorts: ['name desc', 'email asc'])], :name, [:name, :email], - :controller => 'people' + controller: 'people' ) } it { - should match( - /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ + should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ ) } - it { - should match /sort_link desc/ + it { should match /sort_link desc/ } + it { should match /Full Name ▼/ } + end + + describe '#sort_url with multiple search_keys should flip multiple fields specified without a direction' do + subject { @controller.view_context + .sort_url( + [:main_app, Person.ransack(sorts: ['name desc', 'email asc'])], + :name, [:name, :email], + controller: 'people' + ) } it { - should match /Full Name ▼/ + should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ + ) } end - describe '#sort_link with multiple search_keys should allow a default_order to be specified' do + describe '#sort_link with multiple search_keys and default_order specified as a string' do subject { @controller.view_context .sort_link( - [:main_app, Person.search()], + [:main_app, Person.ransack()], :name, [:name, :email], - :controller => 'people', - :default_order => 'desc' + controller: 'people', + default_order: 'desc' ) } it { - should match( - /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ + should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ + ) + } + it { should match /sort_link/ } + it { should match /Full Name/ } + end + + describe '#sort_url with multiple search_keys and default_order specified as a string' do + subject { @controller.view_context + .sort_url( + [:main_app, Person.ransack()], + :name, [:name, :email], + controller: 'people', + default_order: 'desc' ) } it { - should match /sort_link/ + should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ + ) + } + end + + describe '#sort_link with multiple search_keys and default_order specified as a symbol' do + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack()], + :name, [:name, :email], + controller: 'people', + default_order: :desc + ) + } + it { + should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ + ) + } + it { should match /sort_link/ } + it { should match /Full Name/ } + end + + describe '#sort_url with multiple search_keys and default_order specified as a symbol' do + subject { @controller.view_context + .sort_url( + [:main_app, Person.ransack], + :name, [:name, :email], + controller: 'people', + default_order: :desc + ) } it { - should match /Full Name/ + should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ + ) } end describe '#sort_link with multiple search_keys should allow multiple default_orders to be specified' do subject { @controller.view_context .sort_link( - [:main_app, Person.search()], + [:main_app, Person.ransack], :name, [:name, :email], - :controller => 'people', - :default_order => { 'name' => 'desc', :email => 'asc' } + controller: 'people', + default_order: { name: 'desc', email: 'asc' } ) } it { - should match( - /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+asc/ + should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+asc/ ) } - it { - should match /sort_link/ + it { should match /sort_link/ } + it { should match /Full Name/ } + end + + describe '#sort_url with multiple search_keys should allow multiple default_orders to be specified' do + subject { @controller.view_context + .sort_url( + [:main_app, Person.ransack], + :name, [:name, :email], + controller: 'people', + default_order: { name: 'desc', email: 'asc' } + ) } it { - should match /Full Name/ + should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+asc/ + ) } end describe '#sort_link with multiple search_keys with multiple default_orders should not override a specified order' do subject { @controller.view_context .sort_link( - [:main_app, Person.search()], + [:main_app, Person.ransack], :name, [:name, 'email desc'], - :controller => 'people', - :default_order => { 'name' => 'desc', :email => 'asc' } + controller: 'people', + default_order: { name: 'desc', email: 'asc' } ) } it { - should match( - /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ + should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ ) } - it { - should match /sort_link/ + it { should match /sort_link/ } + it { should match /Full Name/ } + end + + describe '#sort_url with multiple search_keys with multiple default_orders should not override a specified order' do + subject { @controller.view_context + .sort_url( + [:main_app, Person.ransack], + :name, [:name, 'email desc'], + controller: 'people', + default_order: { name: 'desc', email: 'asc' } + ) } it { - should match /Full Name/ + should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ + ) + } + end + + describe "#sort_link on polymorphic association should preserve association model name case" do + subject { @controller.view_context + .sort_link( + [:main_app, Note.ransack], + :notable_of_Person_type_name, "Notable", + controller: 'notes' + ) } + it { should match /notes\?q(%5B|\[)s(%5D|\])=notable_of_Person_type_name\+asc/ } + it { should match /sort_link/ } + it { should match /Notable/ } end + + describe "#sort_url on polymorphic association should preserve association model name case" do + subject { @controller.view_context + .sort_link( + [:main_app, Note.ransack], + :notable_of_Person_type_name, "Notable", + controller: 'notes' + ) + } + it { should match /notes\?q(%5B|\[)s(%5D|\])=notable_of_Person_type_name\+asc/ } + end + context 'view has existing parameters' do - before do - @controller.view_context.params.merge!({ :exist => 'existing' }) - end describe '#sort_link should not remove existing params' do + before { @controller.view_context.params[:exist] = 'existing' } + subject { @controller.view_context.sort_link( - Person.search( - { :sorts => ['name desc'] }, :search_key => 'people_search' - ), + Person.ransack( + { sorts: ['name desc'] }, + search_key: 'people_search' + ), :name, - :controller => 'people' + controller: 'people' ) } - it { - should match /exist\=existing/ + + it { should match /exist\=existing/ } + end + + describe '#sort_url should not remove existing params' do + before { @controller.view_context.params[:exist] = 'existing' } + + subject { + @controller.view_context.sort_url( + Person.ransack( + { sorts: ['name desc'] }, + search_key: 'people_search' + ), + :name, + controller: 'people' + ) } + + it { should match /exist\=existing/ } + end + + context 'using a real ActionController::Parameter object' do + describe 'with symbol q:, #sort_link should include search params' do + subject { @controller.view_context.sort_link(Person.ransack, :name) } + let(:params) { ActionController::Parameters.new( + { q: { name_eq: 'TEST' }, controller: 'people' } + ) } + + before { @controller.instance_variable_set(:@params, params) } + + it { + should match( + /people\?q(%5B|\[)name_eq(%5D|\])=TEST&q(%5B|\[)s(%5D|\]) + =name\+asc/x, + ) + } + end + + describe 'with symbol q:, #sort_url should include search params' do + subject { @controller.view_context.sort_url(http://github.com/Person.ransack,%20:name) } + let(:params) { ActionController::Parameters.new( + { q: { name_eq: 'TEST' }, controller: 'people' } + ) } + + before { @controller.instance_variable_set(:@params, params) } + + it { + should match( + /people\?q(%5B|\[)name_eq(%5D|\])=TEST&q(%5B|\[)s(%5D|\]) + =name\+asc/x, + ) + } + end + + describe "with string 'q', #sort_link should include search params" do + subject { @controller.view_context.sort_link(Person.ransack, :name) } + let(:params) { + ActionController::Parameters.new( + { 'q' => { name_eq: 'Test2' }, controller: 'people' } + ) } + + before { @controller.instance_variable_set(:@params, params) } + + it { + should match( + /people\?q(%5B|\[)name_eq(%5D|\])=Test2&q(%5B|\[)s(%5D|\]) + =name\+asc/x, + ) + } + end + + describe "with string 'q', #sort_url should include search params" do + subject { @controller.view_context.sort_url(http://github.com/Person.ransack,%20:name) } + let(:params) { + ActionController::Parameters.new( + { 'q' => { name_eq: 'Test2' }, controller: 'people' } + ) } + + before { @controller.instance_variable_set(:@params, params) } + + it { + should match( + /people\?q(%5B|\[)name_eq(%5D|\])=Test2&q(%5B|\[)s(%5D|\]) + =name\+asc/x, + ) + } + end + end + end + + describe '#sort_link with hide order indicator set to true' do + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack(sorts: ['name desc'])], + :name, + controller: 'people', + hide_indicator: true + ) + } + it { should match /Full Name/ } + it { should_not match /▼|▲/ } + end + + describe '#sort_link with hide order indicator set to false' do + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack(sorts: ['name desc'])], + :name, + controller: 'people', + hide_indicator: false + ) + } + it { should match /Full Name ▼/ } + end + + describe '#sort_link with config set with custom up_arrow' do + before do + Ransack.configure { |c| c.custom_arrows = { up_arrow: "\u{1F446}" } } + end + + after do + Ransack.configure { |c| c.custom_arrows = { up_arrow: "▼" } } + end + + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack(sorts: ['name desc'])], + :name, + controller: 'people', + hide_indicator: false + ) + } + + it { should match /Full Name \u{1F446}/ } + end + + describe '#sort_link with config set with custom down_arrow' do + before do + Ransack.configure { |c| c.custom_arrows = { down_arrow: "\u{1F447}" } } end + + after do + Ransack.configure { |c| c.custom_arrows = { down_arrow: "▲" } } + end + + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack(sorts: ['name asc'])], + :name, + controller: 'people', + hide_indicator: false + ) + } + + it { should match /Full Name \u{1F447}/ } + end + + describe '#sort_link with config set to hide arrows' do + before do + Ransack.configure { |c| c.hide_sort_order_indicators = true } + end + + after do + Ransack.configure { |c| c.hide_sort_order_indicators = false } + end + + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack(sorts: ['name desc'])], + :name, + controller: 'people' + ) + } + + it { should_not match /▼|▲/ } + end + + describe '#sort_link with config set to show arrows (default setting)' do + before do + Ransack.configure { |c| c.hide_sort_order_indicators = false } + end + + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack(sorts: ['name desc'])], + :name, + controller: 'people' + ) + } + + it { should match /Full Name ▼/ } + end + + describe '#sort_link with config set to show arrows and a default arrow set' do + before do + Ransack.configure do |c| + c.hide_sort_order_indicators = false + c.custom_arrows = { default_arrow: "defaultarrow" } + end + end + + after do + Ransack.configure do |c| + c.custom_arrows = { default_arrow: nil } + end + end + + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack], + :name, + controller: 'people' + ) + } + + it { should match /Full Name defaultarrow/ } + end + + describe '#sort_link w/config to hide arrows + custom arrow, hides all' do + before do + Ransack.configure do |c| + c.hide_sort_order_indicators = true + c.custom_arrows = { down_arrow: 'down', default_arrow: "defaultarrow" } + end + end + + after do + Ransack.configure do |c| + c.hide_sort_order_indicators = false + c.custom_arrows = { down_arrow: '▲' } + end + end + + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack(sorts: ['name desc'])], + :name, + controller: 'people' + ) + } + + it { should_not match /▼|down|defaultarrow/ } + end + + describe '#sort_link with config set to show arrows + custom arrow' do + before do + Ransack.configure do |c| + c.hide_sort_order_indicators = false + c.custom_arrows = { up_arrow: 'up-value' } + end + end + + after do + Ransack.configure do |c| + c.hide_sort_order_indicators = false + c.custom_arrows = { up_arrow: '▼' } + end + end + + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack(sorts: ['name desc'])], + :name, + controller: 'people' + ) + } + + it { should match /▲|up-value/ } + end + + describe '#sort_link with a block' do + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack(sorts: ['name desc'])], + :name, + controller: 'people' + ) { 'Block label' } + } + it { should match /Block label ▼/ } + end + + describe '#sort_link with class option' do + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack(sorts: ['name desc'])], + :name, + class: 'people', controller: 'people' + ) + } + it { should match /class="sort_link desc people"/ } + it { should_not match /people\?class=people/ } + end + + describe '#sort_link with class option workaround' do + it "generates a correct link and prints a deprecation" do + expect do + link = @controller.view_context + .sort_link( + [:main_app, Person.ransack(sorts: ['name desc'])], + :name, + 'name', + { controller: 'people' }, + class: 'people' + ) + + expect(link).to match(/class="sort_link desc people"/) + expect(link).not_to match(/people\?class=people/) + end.to output( + /Passing two trailing hashes to `sort_link` is deprecated, merge the trailing hashes into a single one\. \(called at #{Regexp.escape(__FILE__)}:/ + ).to_stderr + end + end + + describe '#sort_link with data option' do + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack(sorts: ['name desc'])], + :name, + data: { turbo_action: :advance }, controller: 'people' + ) + } + it { should match /data-turbo-action="http://github.com/advance"/ } + it { should_not match /people\?data%5Bturbo_action%5D=advance/ } + end + + describe "#sort_link with host option" do + subject { @controller.view_context + .sort_link( + [:main_app, Person.ransack(sorts: ['name desc'])], + :name, + host: 'foo', controller: 'people' + ) + } + it { should match /href="\/people\?q/ } + it { should_not match /href=".*foo/ } + end + + describe "#sort_link ignores host in params" do + before { @controller.view_context.params[:host] = 'other_domain' } + subject { @controller.view_context.sort_link(Person.ransack, :name, controller: 'people') } + + it { should match /href="\/people\?q/ } + it { should_not match /href=".*other_domain/ } end describe '#search_form_for with default format' do + subject { @controller.view_context + .search_form_for(Person.ransack) {} } + it { should match /action="http://people/"/ } + end + + describe '#search_form_for with pdf format' do subject { @controller.view_context - .search_form_for(Person.search) {} + .search_form_for(Person.ransack, format: :pdf) {} } - it { - should match /action="http://people/"/ + it { should match /action="http://people.pdf/"/ } + end + + describe '#search_form_for with json format' do + subject { + @controller.view_context + .search_form_for(Person.ransack, format: :json) {} } + it { should match /action="http://people.json/"/ } end - describe '#search_form_for with pdf format' do + describe '#search_form_for with an array of routes' do subject { @controller.view_context - .search_form_for(Person.search, :format => :pdf) {} + .search_form_for([:admin, Comment.ransack]) {} } - it { - should match /action="http://people.pdf/"/ + it { should match /action="http://admin//comments"/ } + end + + describe '#search_form_for with custom default search key' do + before do + Ransack.configure { |c| c.search_key = :example } + end + after do + Ransack.configure { |c| c.search_key = :q } + end + subject { + @controller.view_context + .search_form_for(Person.ransack) { |f| f.text_field :name_eq } } + it { should match /example_name_eq/ } end - describe '#search_form_for with json format' do + describe '#search_form_with with default format' do + subject { @controller.view_context + .search_form_with(model: Person.ransack) {} } + it { should match /action="http://people/"/ } + end + + describe '#search_form_with with pdf format' do subject { @controller.view_context - .search_form_for(Person.search, :format => :json) {} + .search_form_with(model: Person.ransack, format: :pdf) {} } - it { - should match /action="http://people.json/"/ + it { should match /action="http://people.pdf/"/ } + end + + describe '#search_form_with with json format' do + subject { + @controller.view_context + .search_form_with(model: Person.ransack, format: :json) {} + } + it { should match /action="http://people.json/"/ } + end + + describe '#search_form_with with an array of routes' do + subject { + @controller.view_context + .search_form_with(model: [:admin, Comment.ransack]) {} + } + it { should match /action="http://admin//comments"/ } + end + + describe '#search_form_with with custom default search key' do + before do + Ransack.configure { |c| c.search_key = :example } + end + after do + Ransack.configure { |c| c.search_key = :q } + end + subject { + @controller.view_context + .search_form_with(model: Person.ransack) { |f| f.text_field :name_eq } + } + it { should match /example\[name_eq\]/ } + end + + describe '#search_form_with without Ransack::Search object' do + it 'raises ArgumentError' do + expect { + @controller.view_context.search_form_with(model: "not a search object") {} + }.to raise_error(ArgumentError, 'No Ransack::Search object was provided to search_form_with!') + end + end + + describe '#turbo_search_form_for with default options' do + subject { + @controller.view_context + .turbo_search_form_for(Person.ransack) {} + } + it { should match /action="http://people/"/ } + it { should match /method="post"/ } + it { should match /data-turbo-action="http://github.com/advance"/ } + end + + describe '#turbo_search_form_for with custom method' do + subject { + @controller.view_context + .turbo_search_form_for(Person.ransack, method: :patch) {} + } + it { should match /method="post"/ } + it { should match /name="_method" value="patch"/ } + it { should match /data-turbo-action="http://github.com/advance"/ } + end + + describe '#turbo_search_form_for with turbo_frame' do + subject { + @controller.view_context + .turbo_search_form_for(Person.ransack, turbo_frame: 'search_results') {} + } + it { should match /data-turbo-frame="search_results"/ } + end + + describe '#turbo_search_form_for with custom turbo_action' do + subject { + @controller.view_context + .turbo_search_form_for(Person.ransack, turbo_action: 'replace') {} + } + it { should match /data-turbo-action="http://github.com/replace"/ } + end + + describe '#turbo_search_form_for with format' do + subject { + @controller.view_context + .turbo_search_form_for(Person.ransack, format: :json) {} + } + it { should match /action="http://people.json/"/ } + end + + describe '#turbo_search_form_for with array of routes' do + subject { + @controller.view_context + .turbo_search_form_for([:admin, Comment.ransack]) {} + } + it { should match /action="http://admin//comments"/ } + end + + describe '#turbo_search_form_for with custom search key' do + before do + Ransack.configure { |c| c.search_key = :example } + end + after do + Ransack.configure { |c| c.search_key = :q } + end + subject { + @controller.view_context + .turbo_search_form_for(Person.ransack) { |f| f.text_field :name_eq } } + it { should match /example_name_eq/ } end + describe '#turbo_search_form_for without Ransack::Search object' do + it 'raises ArgumentError' do + expect { + @controller.view_context.turbo_search_form_for("not a search object") {} + }.to raise_error(ArgumentError, 'No Ransack::Search object was provided to turbo_search_form_for!') + end + end + + describe 'private helper methods' do + let(:helper) { @controller.view_context } + let(:search) { Person.ransack } + + describe '#build_turbo_options' do + it 'builds turbo options with frame' do + options = { turbo_frame: 'results', turbo_action: 'replace' } + result = helper.send(:build_turbo_options, options) + expect(result).to eq({ + data: { + turbo_frame: 'results', + turbo_action: 'replace' + } + }) + expect(options).to be_empty + end + + it 'builds turbo options without frame' do + options = { turbo_action: 'advance' } + result = helper.send(:build_turbo_options, options) + expect(result).to eq({ data: { turbo_action: 'advance' } }) + end + + it 'uses default turbo action' do + options = {} + result = helper.send(:build_turbo_options, options) + expect(result).to eq({ data: { turbo_action: 'advance' } }) + end + end + + describe '#build_html_options' do + it 'builds HTML options with correct method' do + options = { class: 'custom' } + result = helper.send(:build_html_options, search, options, :post) + expect(result[:method]).to eq(:post) + expect(result[:class]).to include('custom') + end + end + + describe '#extract_search_and_set_url' do + it 'extracts search from Ransack::Search object' do + options = {} + result = helper.send(:extract_search_and_set_url, search, options, 'search_form_for') + expect(result).to eq(search) + expect(options[:url]).to match(/people/) + end + + it 'extracts search from array with Search object' do + options = {} + comment_search = Comment.ransack + result = helper.send(:extract_search_and_set_url, [:admin, comment_search], options, 'search_form_for') + expect(result).to eq(comment_search) + expect(options[:url]).to match(/admin/) + end + + it 'raises error for invalid record with correct method name' do + options = {} + expect { + helper.send(:extract_search_and_set_url, "invalid", options, 'turbo_search_form_for') + }.to raise_error(ArgumentError, 'No Ransack::Search object was provided to turbo_search_form_for!') + end + + it 'extracts search from Ransack::Search object for search_form_with' do + options = {} + result = helper.send(:extract_search_and_set_url, search, options, 'search_form_with') + expect(result).to eq(search) + expect(options[:url]).to match(/people/) + end + + it 'extracts search from array with Search object for search_form_with' do + options = {} + comment_search = Comment.ransack + result = helper.send(:extract_search_and_set_url, [:admin, comment_search], options, 'search_form_with') + expect(result).to eq(comment_search) + expect(options[:url]).to match(/admin/) + end + + it 'raises error for invalid record with correct method name for search_form_with' do + options = {} + expect { + helper.send(:extract_search_and_set_url, "invalid", options, 'search_form_with') + }.to raise_error(ArgumentError, 'No Ransack::Search object was provided to search_form_with!') + end + end + end end end end diff --git a/spec/ransack/invalid_search_error_spec.rb b/spec/ransack/invalid_search_error_spec.rb new file mode 100644 index 000000000..340d11035 --- /dev/null +++ b/spec/ransack/invalid_search_error_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +module Ransack + describe InvalidSearchError do + it 'inherits from ArgumentError' do + expect(InvalidSearchError.superclass).to eq(ArgumentError) + end + + it 'can be instantiated with a message' do + error = InvalidSearchError.new('Test error message') + expect(error.message).to eq('Test error message') + end + + it 'can be instantiated without a message' do + error = InvalidSearchError.new + expect(error.message).to eq('Ransack::InvalidSearchError') + end + + it 'can be raised and caught' do + expect { raise InvalidSearchError.new('Test') }.to raise_error(InvalidSearchError, 'Test') + end + + it 'can be raised and caught as ArgumentError' do + expect { raise InvalidSearchError.new('Test') }.to raise_error(ArgumentError, 'Test') + end + end +end diff --git a/spec/ransack/nodes/condition_spec.rb b/spec/ransack/nodes/condition_spec.rb index 8b5603bd4..bebdfcbbe 100644 --- a/spec/ransack/nodes/condition_spec.rb +++ b/spec/ransack/nodes/condition_spec.rb @@ -3,6 +3,34 @@ module Ransack module Nodes describe Condition do + context 'bug report #1245' do + it 'preserves tuple behavior' do + ransack_hash = { + m: 'and', + g: [ + { title_type_in: ['["title 1", ""]'] } + ] + } + + sql = Article.ransack(ransack_hash).result.to_sql + expect(sql).to include("IN (('title 1', ''))") + end + end + + context 'with an alias' do + subject { + Condition.extract( + Context.for(Person), 'term_start', Person.first(2).map(&:name) + ) + } + + specify { expect(subject.combinator).to eq 'or' } + specify { expect(subject.predicate.name).to eq 'start' } + + it 'converts the alias to the correct attributes' do + expect(subject.attributes.map(&:name)).to eq(['name', 'email']) + end + end context 'with multiple values and an _any predicate' do subject { @@ -14,6 +42,15 @@ module Nodes specify { expect(subject.values.size).to eq(2) } end + describe '#negative?' do + let(:context) { Context.for(Person) } + let(:eq) { Condition.extract(context, 'name_eq', 'A') } + let(:not_eq) { Condition.extract(context, 'name_not_eq', 'A') } + + specify { expect(not_eq.negative?).to be true } + specify { expect(eq.negative?).to be false } + end + context 'with an invalid predicate' do subject { Condition.extract( @@ -23,18 +60,272 @@ module Nodes context "when ignore_unknown_conditions is false" do before do - Ransack.configure { |config| config.ignore_unknown_conditions = false } + Ransack.configure { |c| c.ignore_unknown_conditions = false } end specify { expect { subject }.to raise_error ArgumentError } + specify { expect { subject }.to raise_error InvalidSearchError } end context "when ignore_unknown_conditions is true" do before do - Ransack.configure { |config| config.ignore_unknown_conditions = true } + Ransack.configure { |c| c.ignore_unknown_conditions = true } + end + + specify { expect(subject).to be_nil } + end + end + + context 'with an empty predicate' do + subject { + Condition.extract( + Context.for(Person), 'full_name', Person.first.name + ) + } + + context "when default_predicate = nil" do + before do + Ransack.configure { |c| c.default_predicate = nil } + end + + specify { expect(subject).to be_nil } + end + + context "when default_predicate = 'eq'" do + before do + Ransack.configure { |c| c.default_predicate = 'eq' } + end + + specify { expect(subject).to eq Condition.extract(Context.for(Person), 'full_name_eq', Person.first.name) } + end + end + + context 'with wildcard string values' do + it 'properly quotes values with wildcards for LIKE predicates' do + ransack_hash = { name_cont: 'test%' } + sql = Person.ransack(ransack_hash).result.to_sql + + # The % should be properly quoted in the SQL + case ActiveRecord::Base.connection.adapter_name + when "Mysql2" + expect(sql).to include("LIKE '%test\\\\%%'") + expect(sql).not_to include("NOT LIKE '%test\\\\%%'") + when "PostGIS", "PostgreSQL" + expect(sql).to include("ILIKE '%test\\%%'") + expect(sql).not_to include("NOT ILIKE '%test\\%%'") + else + expect(sql).to include("LIKE '%test%%'") + expect(sql).not_to include("NOT LIKE '%test%%'") + end + end + + it 'properly quotes values with wildcards for NOT LIKE predicates' do + ransack_hash = { name_not_cont: 'test%' } + sql = Person.ransack(ransack_hash).result.to_sql + + # The % should be properly quoted in the SQL + case ActiveRecord::Base.connection.adapter_name + when "Mysql2" + expect(sql).to include("NOT LIKE '%test\\\\%%'") + when "PostGIS", "PostgreSQL" + expect(sql).to include("NOT ILIKE '%test\\%%'") + else + expect(sql).to include("NOT LIKE '%test%%'") + end + end + end + + context 'with negative conditions on associations' do + it 'handles not_null predicate with true value correctly' do + ransack_hash = { comments_id_not_null: true } + sql = Person.ransack(ransack_hash).result.to_sql + + # Should generate an IN query with IS NOT NULL condition + expect(sql).to include('IN (') + expect(sql).to include('IS NOT NULL') + expect(sql).not_to include('IS NULL') + end + + it 'handles not_null predicate with false value correctly' do + ransack_hash = { comments_id_not_null: false } + sql = Person.ransack(ransack_hash).result.to_sql + + # Should generate a NOT IN query with IS NULL condition + expect(sql).to include('NOT IN (') + expect(sql).to include('IS NULL') + expect(sql).not_to include('IS NOT NULL') + end + + it 'handles not_cont predicate correctly' do + ransack_hash = { comments_body_not_cont: 'test' } + sql = Person.ransack(ransack_hash).result.to_sql + + # Should generate a NOT IN query with LIKE condition (not NOT LIKE) + expect(sql).to include('NOT IN (') + expect(sql).to include("LIKE '%test%'") + expect(sql).not_to include("NOT LIKE '%test%'") + end + end + + context 'with nested conditions' do + it 'correctly identifies non-nested conditions' do + condition = Condition.extract( + Context.for(Person), 'name_eq', 'Test' + ) + + # Create a mock parent table + parent_table = Person.arel_table + + # Get the attribute name and make sure it starts with the table name + attribute = condition.attributes.first + expect(attribute.name).to eq('name') + expect(parent_table.name).to eq('people') + + # The method should return false because 'name' doesn't start with 'people' + result = condition.send(:not_nested_condition, attribute, parent_table) + expect(result).to be false + end + + it 'correctly identifies truly non-nested conditions when attribute name starts with table name' do + # Create a condition with an attribute that starts with the table name + condition = Condition.extract( + Context.for(Person), 'name_eq', 'Test' + ) + + # Modify the attribute name to start with the table name for testing purposes + attribute = condition.attributes.first + allow(attribute).to receive(:name).and_return('people_name') + + # Create a parent table + parent_table = Person.arel_table + + # Now the method should return true because 'people_name' starts with 'people' + result = condition.send(:not_nested_condition, attribute, parent_table) + expect(result).to be true + end + + it 'correctly identifies nested conditions' do + condition = Condition.extract( + Context.for(Person), 'articles_title_eq', 'Test' + ) + + # Create a mock table alias + parent_table = Arel::Nodes::TableAlias.new( + Article.arel_table, + Article.arel_table + ) + + # Access the private method using send + result = condition.send(:not_nested_condition, condition.attributes.first, parent_table) + + # Should return false for nested condition + expect(result).to be false + end + end + + context 'with polymorphic associations and not_in predicate' do + before do + # Define test models for polymorphic associations + class ::TestTask < ActiveRecord::Base + self.table_name = 'tasks' + has_many :follows, primary_key: :uid, inverse_of: :followed, foreign_key: :followed_uid, class_name: 'TestFollow' + has_many :users, through: :follows, source: :follower, source_type: 'TestUser' + + # Add ransackable_attributes method + def self.ransackable_attributes(auth_object = nil) + ["created_at", "id", "name", "uid", "updated_at"] + end + + # Add ransackable_associations method + def self.ransackable_associations(auth_object = nil) + ["follows", "users"] + end + end + + class ::TestFollow < ActiveRecord::Base + self.table_name = 'follows' + belongs_to :follower, polymorphic: true, foreign_key: :follower_uid, primary_key: :uid + belongs_to :followed, polymorphic: true, foreign_key: :followed_uid, primary_key: :uid + + # Add ransackable_attributes method + def self.ransackable_attributes(auth_object = nil) + ["created_at", "followed_type", "followed_uid", "follower_type", "follower_uid", "id", "updated_at"] + end + + # Add ransackable_associations method + def self.ransackable_associations(auth_object = nil) + ["followed", "follower"] + end + end + + class ::TestUser < ActiveRecord::Base + self.table_name = 'users' + has_many :follows, primary_key: :uid, inverse_of: :follower, foreign_key: :follower_uid, class_name: 'TestFollow' + has_many :tasks, through: :follows, source: :followed, source_type: 'TestTask' + + # Add ransackable_attributes method + def self.ransackable_attributes(auth_object = nil) + ["created_at", "id", "name", "uid", "updated_at"] + end + + # Add ransackable_associations method + def self.ransackable_associations(auth_object = nil) + ["follows", "tasks"] + end + end + + # Create tables if they don't exist + ActiveRecord::Base.connection.create_table(:tasks, force: true) do |t| + t.string :uid + t.string :name + t.timestamps null: false + end + + ActiveRecord::Base.connection.create_table(:follows, force: true) do |t| + t.string :followed_uid, null: false + t.string :followed_type, null: false + t.string :follower_uid, null: false + t.string :follower_type, null: false + t.timestamps null: false + t.index [:followed_uid, :followed_type] + t.index [:follower_uid, :follower_type] end - specify { subject.should be_nil } + ActiveRecord::Base.connection.create_table(:users, force: true) do |t| + t.string :uid + t.string :name + t.timestamps null: false + end + end + + after do + # Clean up test models and tables + Object.send(:remove_const, :TestTask) + Object.send(:remove_const, :TestFollow) + Object.send(:remove_const, :TestUser) + + ActiveRecord::Base.connection.drop_table(:tasks, if_exists: true) + ActiveRecord::Base.connection.drop_table(:follows, if_exists: true) + ActiveRecord::Base.connection.drop_table(:users, if_exists: true) + end + + it 'correctly handles not_in predicate with polymorphic associations' do + # Create the search + search = TestTask.ransack(users_uid_not_in: ['uid_example']) + sql = search.result.to_sql + + # Verify the SQL contains the expected NOT IN clause + expect(sql).to include('NOT IN') + expect(sql).to include("follower_uid") + expect(sql).to include("followed_uid") + expect(sql).to include("'uid_example'") + + # The SQL should include a reference to tasks.uid + expect(sql).to include("tasks") + expect(sql).to include("uid") + + # The SQL should include a reference to follows table + expect(sql).to include("follows") end end end diff --git a/spec/ransack/nodes/grouping_spec.rb b/spec/ransack/nodes/grouping_spec.rb index 6d6fb0427..9ff005964 100644 --- a/spec/ransack/nodes/grouping_spec.rb +++ b/spec/ransack/nodes/grouping_spec.rb @@ -14,19 +14,95 @@ module Nodes describe '#attribute_method?' do context 'for attributes of the context' do it 'is true' do - expect(subject.attribute_method?('name')).to be_true + expect(subject.attribute_method?('name')).to be true end - context "where the attribute contains '_and_'" do + context "when the attribute contains '_and_'" do it 'is true' do - expect(subject.attribute_method?('terms_and_conditions')).to be_true + expect(subject.attribute_method?('terms_and_conditions')).to be true + end + end + + context "when the attribute contains '_or_'" do + it 'is true' do + expect(subject.attribute_method?('true_or_false')).to be true + end + end + + context "when the attribute ends with '_start'" do + it 'is true' do + expect(subject.attribute_method?('life_start')).to be true + end + end + + context "when the attribute ends with '_end'" do + it 'is true' do + expect(subject.attribute_method?('stop_end')).to be true end end end context 'for unknown attributes' do it 'is false' do - expect(subject.attribute_method?('not_an_attribute')).to be_false + expect(subject.attribute_method?('not_an_attribute')).to be false + end + end + end + + describe '#conditions=' do + context 'when conditions are identical' do + let(:conditions) do + { + '0' => { + 'a' => { '0'=> { 'name' => 'name', 'ransacker_args' => '' } }, + 'p' => 'cont', + 'v' => { '0' => { 'value' => 'John' } } + }, + '1' => { + 'a' => { '0' => { 'name' => 'name', 'ransacker_args' => '' } }, + 'p' => 'cont', + 'v' => { '0' => { 'value' => 'John' } } + } + } + end + + before { subject.conditions = conditions } + + it 'expect duplicates to be removed' do + expect(subject.conditions.count).to eq 1 + end + end + + context 'when conditions differ only by ransacker_args' do + let(:conditions) do + { + '0' => { + 'a' => { + '0' => { + 'name' => 'with_arguments', + 'ransacker_args' => [1, 2] + } + }, + 'p' => 'eq', + 'v' => { '0' => { 'value' => '10' } } + }, + '1' => { + 'a' => { + '0' => { + 'name' => 'with_arguments', + 'ransacker_args' => [3, 4] + } + }, + 'p' => 'eq', + 'v' => { '0' => { 'value' => '10' } } + } + } + end + + before { subject.conditions = conditions } + + it 'expect them to be parsed as different and not as duplicates' do + expect(subject.conditions.count).to eq 2 end end end diff --git a/spec/ransack/nodes/value_spec.rb b/spec/ransack/nodes/value_spec.rb new file mode 100644 index 000000000..1b5b7b251 --- /dev/null +++ b/spec/ransack/nodes/value_spec.rb @@ -0,0 +1,126 @@ +require 'spec_helper' + +module Ransack + module Nodes + describe Value do + let(:context) { Context.for(Person) } + + subject do + Value.new(context, raw_value) + end + + context "with a date value" do + let(:raw_value) { "2022-05-23" } + + [:date].each do |type| + it "should cast #{type} correctly" do + result = subject.cast(type) + + expect(result).to be_a_kind_of(Date) + expect(result).to eq(Date.parse(raw_value)) + end + end + end + + context "with a timestamp value" do + let(:raw_value) { "2022-05-23 10:40:02 -0400" } + + [:datetime, :timestamp, :time, :timestamptz].each do |type| + it "should cast #{type} correctly" do + result = subject.cast(type) + + expect(result).to be_a_kind_of(Time) + expect(result).to eq(Time.zone.parse(raw_value)) + end + end + end + + Constants::TRUE_VALUES.each do |value| + context "with a true boolean value (#{value})" do + let(:raw_value) { value.to_s } + + it "should cast boolean correctly" do + result = subject.cast(:boolean) + expect(result).to eq(true) + end + end + end + + Constants::FALSE_VALUES.each do |value| + context "with a false boolean value (#{value})" do + let(:raw_value) { value.to_s } + + it "should cast boolean correctly" do + result = subject.cast(:boolean) + + expect(result).to eq(false) + end + end + end + + ["12", "101.5"].each do |value| + context "with an integer value (#{value})" do + let(:raw_value) { value } + + it "should cast #{value} to integer correctly" do + result = subject.cast(:integer) + + expect(result).to be_an(Integer) + expect(result).to eq(value.to_i) + end + end + end + + [[], ["12"], ["101.5"]].each do |value| + context "with an array value (#{value.inspect})" do + let(:raw_value) { value } + + it "should cast to integer as nil" do + result = subject.cast(:integer) + + expect(result).to be nil + end + end + end + + ["12", "101.5"].each do |value| + context "with a float value (#{value})" do + let(:raw_value) { value } + + it "should cast #{value} to float correctly" do + result = subject.cast(:float) + + expect(result).to be_an(Float) + expect(result).to eq(value.to_f) + end + end + end + + ["12", "101.5"].each do |value| + context "with a decimal value (#{value})" do + let(:raw_value) { value } + + it "should cast #{value} to decimal correctly" do + result = subject.cast(:decimal) + + expect(result).to be_a(BigDecimal) + expect(result).to eq(value.to_d) + end + end + end + + ["12", "101.513"].each do |value| + context "with a money value (#{value})" do + let(:raw_value) { value } + + it "should cast #{value} to money correctly" do + result = subject.cast(:money) + + expect(result).to be_a(String) + expect(result).to eq(value.to_f.to_s) + end + end + end + end + end +end diff --git a/spec/ransack/predicate_spec.rb b/spec/ransack/predicate_spec.rb index 176b67515..cf84e200d 100644 --- a/spec/ransack/predicate_spec.rb +++ b/spec/ransack/predicate_spec.rb @@ -1,8 +1,10 @@ require 'spec_helper' module Ransack - describe Predicate do + TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set + FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set + describe Predicate do before do @s = Search.new(Person) end @@ -13,31 +15,32 @@ module Ransack expect { subject.result }.to_not raise_error end - it "escapes '%', '.' and '\\\\' in value" do + it "escapes '%', '.', '_' and '\\\\' in value" do subject.send(:"#{method}=", '%._\\') expect(subject.result.to_sql).to match(regexp) end end describe 'eq' do - it 'generates an equality condition for boolean true' do - @s.awesome_eq = true - field = "#{quote_table_name("people")}.#{quote_column_name("awesome")}" - expect(@s.result.to_sql).to match /#{field} = #{ - ActiveRecord::Base.connection.quoted_true}/ + it 'generates an equality condition for boolean true values' do + test_boolean_equality_for(true) end - it 'generates an equality condition for boolean false' do - @s.awesome_eq = false - field = "#{quote_table_name("people")}.#{quote_column_name("awesome")}" - expect(@s.result.to_sql).to match /#{field} = #{ - ActiveRecord::Base.connection.quoted_false}/ + it 'generates an equality condition for boolean false values' do + test_boolean_equality_for(false) end it 'does not generate a condition for nil' do @s.awesome_eq = nil expect(@s.result.to_sql).not_to match /WHERE/ end + + it 'generates a = condition with a huge integer value' do + val = 123456789012345678901 + @s.salary_eq = val + field = "#{quote_table_name("people")}.#{quote_column_name("salary")}" + expect(@s.result.to_sql).to match /#{field} = #{val}/ + end end describe 'lteq' do @@ -59,6 +62,13 @@ module Ransack @s.salary_lteq = nil expect(@s.result.to_sql).not_to match /WHERE/ end + + it 'generates a <= condition with a huge integer value' do + val = 123456789012345678901 + @s.salary_lteq = val + field = "#{quote_table_name("people")}.#{quote_column_name("salary")}" + expect(@s.result.to_sql).to match /#{field} <= #{val}/ + end end describe 'lt' do @@ -80,6 +90,13 @@ module Ransack @s.salary_lt = nil expect(@s.result.to_sql).not_to match /WHERE/ end + + it 'generates a = condition with a huge integer value' do + val = 123456789012345678901 + @s.salary_lt = val + field = "#{quote_table_name("people")}.#{quote_column_name("salary")}" + expect(@s.result.to_sql).to match /#{field} < #{val}/ + end end describe 'gteq' do @@ -101,6 +118,13 @@ module Ransack @s.salary_gteq = nil expect(@s.result.to_sql).not_to match /WHERE/ end + + it 'generates a >= condition with a huge integer value' do + val = 123456789012345678901 + @s.salary_gteq = val + field = "#{quote_table_name("people")}.#{quote_column_name("salary")}" + expect(@s.result.to_sql).to match /#{field} >= #{val}/ + end end describe 'gt' do @@ -122,14 +146,22 @@ module Ransack @s.salary_gt = nil expect(@s.result.to_sql).not_to match /WHERE/ end + + it 'generates a > condition with a huge integer value' do + val = 123456789012345678901 + @s.salary_gt = val + field = "#{quote_table_name("people")}.#{quote_column_name("salary")}" + expect(@s.result.to_sql).to match /#{field} > #{val}/ + end end describe 'cont' do it_has_behavior 'wildcard escaping', :name_cont, - (if ActiveRecord::Base.connection.adapter_name == "PostgreSQL" - /"people"."name" ILIKE '%\\%\\._\\\\%'/ - elsif ActiveRecord::Base.connection.adapter_name == "Mysql2" - /`people`.`name` LIKE '%\\\\%\\\\._\\\\\\\\%'/ + (case ActiveRecord::Base.connection.adapter_name + when "PostGIS", "PostgreSQL" + /"people"."name" ILIKE '%\\%\\.\\_\\\\%'/ + when "Mysql2" + /`people`.`name` LIKE '%\\\\%.\\\\_\\\\\\\\%'/ else /"people"."name" LIKE '%%._\\%'/ end) do @@ -145,10 +177,11 @@ module Ransack describe 'not_cont' do it_has_behavior 'wildcard escaping', :name_not_cont, - (if ActiveRecord::Base.connection.adapter_name == "PostgreSQL" - /"people"."name" NOT ILIKE '%\\%\\._\\\\%'/ - elsif ActiveRecord::Base.connection.adapter_name == "Mysql2" - /`people`.`name` NOT LIKE '%\\\\%\\\\._\\\\\\\\%'/ + (case ActiveRecord::Base.connection.adapter_name + when "PostGIS", "PostgreSQL" + /"people"."name" NOT ILIKE '%\\%\\.\\_\\\\%'/ + when "Mysql2" + /`people`.`name` NOT LIKE '%\\\\%.\\\\_\\\\\\\\%'/ else /"people"."name" NOT LIKE '%%._\\%'/ end) do @@ -162,6 +195,130 @@ module Ransack end end + describe 'i_cont' do + it_has_behavior 'wildcard escaping', :name_i_cont, + (case ActiveRecord::Base.connection.adapter_name + when "PostGIS" + /LOWER\("people"."name"\) ILIKE '%\\%\\.\\_\\\\%'/ + when "PostgreSQL" + /"people"."name" ILIKE '%\\%\\.\\_\\\\%'/ + when "Mysql2" + /LOWER\(`people`.`name`\) LIKE '%\\\\%.\\\\_\\\\\\\\%'/ + else + /LOWER\("people"."name"\) LIKE '%%._\\%'/ + end) do + subject { @s } + end + + it 'generates a LIKE query with LOWER(column) and value surrounded by %' do + @s.name_i_cont = 'Ric' + field = "#{quote_table_name("people")}.#{quote_column_name("name")}" + expect(@s.result.to_sql).to match /[LOWER\(]?#{field}\)? I?LIKE '%ric%'/ + end + end + + describe 'not_i_cont' do + it_has_behavior 'wildcard escaping', :name_not_i_cont, + (case ActiveRecord::Base.connection.adapter_name + when "PostGIS" + /LOWER\("people"."name"\) NOT ILIKE '%\\%\\.\\_\\\\%'/ + when "PostgreSQL" + /"people"."name" NOT ILIKE '%\\%\\.\\_\\\\%'/ + when "Mysql2" + /LOWER\(`people`.`name`\) NOT LIKE '%\\\\%.\\\\_\\\\\\\\%'/ + else + /LOWER\("people"."name"\) NOT LIKE '%%._\\%'/ + end) do + subject { @s } + end + + it 'generates a NOT LIKE query with LOWER(column) and value surrounded by %' do + @s.name_not_i_cont = 'Ric' + field = "#{quote_table_name("people")}.#{quote_column_name("name")}" + expect(@s.result.to_sql).to match /[LOWER\(]?#{field}\)? NOT I?LIKE '%ric%'/ + end + end + + describe 'start' do + it 'generates a LIKE query with value followed by %' do + @s.name_start = 'Er' + field = "#{quote_table_name("people")}.#{quote_column_name("name")}" + expect(@s.result.to_sql).to match /#{field} I?LIKE 'Er%'/ + end + + it "works with attribute names ending with '_start'" do + @s.new_start_start = 'hEy' + field = "#{quote_table_name("people")}.#{quote_column_name("new_start")}" + expect(@s.result.to_sql).to match /#{field} I?LIKE 'hEy%'/ + end + + it "works with attribute names ending with '_end'" do + @s.stop_end_start = 'begin' + field = "#{quote_table_name("people")}.#{quote_column_name("stop_end")}" + expect(@s.result.to_sql).to match /#{field} I?LIKE 'begin%'/ + end + end + + describe 'not_start' do + it 'generates a NOT LIKE query with value followed by %' do + @s.name_not_start = 'Eri' + field = "#{quote_table_name("people")}.#{quote_column_name("name")}" + expect(@s.result.to_sql).to match /#{field} NOT I?LIKE 'Eri%'/ + end + + it "works with attribute names ending with '_start'" do + @s.new_start_not_start = 'hEy' + field = "#{quote_table_name("people")}.#{quote_column_name("new_start")}" + expect(@s.result.to_sql).to match /#{field} NOT I?LIKE 'hEy%'/ + end + + it "works with attribute names ending with '_end'" do + @s.stop_end_not_start = 'begin' + field = "#{quote_table_name("people")}.#{quote_column_name("stop_end")}" + expect(@s.result.to_sql).to match /#{field} NOT I?LIKE 'begin%'/ + end + end + + describe 'end' do + it 'generates a LIKE query with value preceded by %' do + @s.name_end = 'Miller' + field = "#{quote_table_name("people")}.#{quote_column_name("name")}" + expect(@s.result.to_sql).to match /#{field} I?LIKE '%Miller'/ + end + + it "works with attribute names ending with '_start'" do + @s.new_start_end = 'finish' + field = "#{quote_table_name("people")}.#{quote_column_name("new_start")}" + expect(@s.result.to_sql).to match /#{field} I?LIKE '%finish'/ + end + + it "works with attribute names ending with '_end'" do + @s.stop_end_end = 'Ending' + field = "#{quote_table_name("people")}.#{quote_column_name("stop_end")}" + expect(@s.result.to_sql).to match /#{field} I?LIKE '%Ending'/ + end + end + + describe 'not_end' do + it 'generates a NOT LIKE query with value preceded by %' do + @s.name_not_end = 'Miller' + field = "#{quote_table_name("people")}.#{quote_column_name("name")}" + expect(@s.result.to_sql).to match /#{field} NOT I?LIKE '%Miller'/ + end + + it "works with attribute names ending with '_start'" do + @s.new_start_not_end = 'finish' + field = "#{quote_table_name("people")}.#{quote_column_name("new_start")}" + expect(@s.result.to_sql).to match /#{field} NOT I?LIKE '%finish'/ + end + + it "works with attribute names ending with '_end'" do + @s.stop_end_not_end = 'Ending' + field = "#{quote_table_name("people")}.#{quote_column_name("stop_end")}" + expect(@s.result.to_sql).to match /#{field} NOT I?LIKE '%Ending'/ + end + end + describe 'true' do it 'generates an equality condition for boolean true' do @s.awesome_true = true @@ -252,6 +409,28 @@ module Ransack field = "#{quote_table_name("people")}.#{quote_column_name("name")}" expect(@s.result.to_sql).to match /#{field} IS NULL/ end + + describe 'with association query' do + it 'generates a value IS NOT NULL query' do + @s.comments_id_not_null = true + sql = @s.result.to_sql + parent_field = "#{quote_table_name("people")}.#{quote_column_name("id")}" + expect(sql).to match /#{parent_field} IN/ + field = "#{quote_table_name("comments")}.#{quote_column_name("id")}" + expect(sql).to match /#{field} IS NOT NULL/ + expect(sql).not_to match /AND NOT/ + end + + it 'generates a value IS NULL query when assigned false' do + @s.comments_id_not_null = false + sql = @s.result.to_sql + parent_field = "#{quote_table_name("people")}.#{quote_column_name("id")}" + expect(sql).to match /#{parent_field} NOT IN/ + field = "#{quote_table_name("comments")}.#{quote_column_name("id")}" + expect(sql).to match /#{field} IS NULL/ + expect(sql).to match /AND NOT/ + end + end end describe 'present' do @@ -281,5 +460,45 @@ module Ransack expect(@s.result.to_sql).to match /#{field} IS NOT NULL AND #{field} != ''/ end end - end + + context "defining custom predicates" do + describe "with 'not_in' arel predicate" do + before do + Ransack.configure { |c| c.add_predicate "not_in_csv", arel_predicate: "not_in", formatter: proc { |v| v.split(",") } } + end + + it 'generates a value IS NOT NULL query' do + @s.name_not_in_csv = ["a", "b"] + field = "#{quote_table_name("people")}.#{quote_column_name("name")}" + expect(@s.result.to_sql).to match /#{field} NOT IN \('a', 'b'\)/ + end + end + end + + private + + def test_boolean_equality_for(boolean_value) + query = expected_query(boolean_value) + test_values_for(boolean_value).each do |value| + s = Search.new(Person, awesome_eq: value) + expect(s.result.to_sql).to match query + end + end + + def test_values_for(boolean_value) + case boolean_value + when true + TRUE_VALUES + when false + FALSE_VALUES + end + end + + def expected_query(value, attribute = 'awesome', operator = '=') + field = "#{quote_table_name("people")}.#{quote_column_name(attribute)}" + quoted_value = ActiveRecord::Base.connection.quote(value) + /#{field} #{operator} #{quoted_value}/ + end + end + end diff --git a/spec/ransack/ransacker_spec.rb b/spec/ransack/ransacker_spec.rb new file mode 100644 index 000000000..a0fa5a696 --- /dev/null +++ b/spec/ransack/ransacker_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +module Ransack + describe Ransacker do + let(:klass) { Person } + let(:name) { :test_ransacker } + let(:opts) { {} } + + describe '#initialize' do + context 'with minimal options' do + subject { Ransacker.new(klass, name, opts) } + + it 'sets the name' do + expect(subject.name).to eq(name) + end + + it 'sets default type to string' do + expect(subject.type).to eq(:string) + end + + it 'sets default args to [:parent]' do + expect(subject.args).to eq([:parent]) + end + end + + context 'with custom options' do + let(:opts) { { type: :integer, args: [:parent, :custom_arg], formatter: proc { |v| v.to_i } } } + + subject { Ransacker.new(klass, name, opts) } + + it 'sets the custom type' do + expect(subject.type).to eq(:integer) + end + + it 'sets the custom args' do + expect(subject.args).to eq([:parent, :custom_arg]) + end + + it 'sets the formatter' do + expect(subject.formatter).to eq(opts[:formatter]) + end + end + + context 'with callable option' do + let(:callable) { proc { |parent| parent.table[:id] } } + let(:opts) { { callable: callable } } + + subject { Ransacker.new(klass, name, opts) } + + it 'initializes successfully' do + expect(subject).to be_a(Ransacker) + end + end + end + + describe 'basic functionality' do + subject { Ransacker.new(klass, name, opts) } + + it 'responds to required methods' do + expect(subject).to respond_to(:name) + expect(subject).to respond_to(:type) + expect(subject).to respond_to(:args) + expect(subject).to respond_to(:formatter) + expect(subject).to respond_to(:attr_from) + expect(subject).to respond_to(:call) + end + end + end +end diff --git a/spec/ransack/search_spec.rb b/spec/ransack/search_spec.rb index a892afc48..d7fd1c862 100644 --- a/spec/ransack/search_spec.rb +++ b/spec/ransack/search_spec.rb @@ -3,38 +3,76 @@ module Ransack describe Search do describe '#initialize' do - it "removes empty conditions before building" do + it 'removes empty conditions before building' do expect_any_instance_of(Search).to receive(:build).with({}) - Search.new(Person, :name_eq => '') + Search.new(Person, name_eq: '') end - it "keeps conditions with a false value before building" do + it 'keeps conditions with a false value before building' do expect_any_instance_of(Search).to receive(:build) - .with({ "name_eq" => false }) - Search.new(Person, :name_eq => false) + .with({ 'name_eq' => false }) + Search.new(Person, name_eq: false) end - it "keeps conditions with a value before building" do + it 'keeps conditions with a value before building' do expect_any_instance_of(Search).to receive(:build) - .with({ "name_eq" => 'foobar' }) - Search.new(Person, :name_eq => 'foobar') + .with({ 'name_eq' => 'foobar' }) + Search.new(Person, name_eq: 'foobar') end - it "removes empty suffixed conditions before building" do + context 'whitespace stripping' do + context 'when whitespace_strip option is true' do + before do + Ransack.configure { |c| c.strip_whitespace = true } + end + + it 'strips leading & trailing whitespace before building' do + expect_any_instance_of(Search).to receive(:build) + .with({ 'name_eq' => 'foobar' }) + Search.new(Person, name_eq: ' foobar ') + end + end + + context 'when whitespace_strip option is false' do + before do + Ransack.configure { |c| c.strip_whitespace = false } + end + + it 'doesn\'t strip leading & trailing whitespace before building' do + expect_any_instance_of(Search).to receive(:build) + .with({ 'name_eq' => ' foobar ' }) + Search.new(Person, name_eq: ' foobar ') + end + end + + it 'strips leading & trailing whitespace when strip_whitespace search parameter is true' do + expect_any_instance_of(Search).to receive(:build) + .with({ 'name_eq' => 'foobar' }) + Search.new(Person, { name_eq: ' foobar ' }, { strip_whitespace: true }) + end + + it 'doesn\'t strip leading & trailing whitespace when strip_whitespace search parameter is false' do + expect_any_instance_of(Search).to receive(:build) + .with({ 'name_eq' => ' foobar ' }) + Search.new(Person, { name_eq: ' foobar ' }, { strip_whitespace: false }) + end + end + + it 'removes empty suffixed conditions before building' do expect_any_instance_of(Search).to receive(:build).with({}) - Search.new(Person, :name_eq_any => ['']) + Search.new(Person, name_eq_any: ['']) end - it "keeps suffixed conditions with a false value before building" do + it 'keeps suffixed conditions with a false value before building' do expect_any_instance_of(Search).to receive(:build) - .with({ "name_eq_any" => [false] }) - Search.new(Person, :name_eq_any => [false]) + .with({ 'name_eq_any' => [false] }) + Search.new(Person, name_eq_any: [false]) end - it "keeps suffixed conditions with a value before building" do + it 'keeps suffixed conditions with a value before building' do expect_any_instance_of(Search).to receive(:build) - .with({ "name_eq_any" => ['foobar'] }) - Search.new(Person, :name_eq_any => ['foobar']) + .with({ 'name_eq_any' => ['foobar'] }) + Search.new(Person, name_eq_any: ['foobar']) end it 'does not raise exception for string :params argument' do @@ -43,20 +81,16 @@ module Ransack it 'accepts a context option' do shared_context = Context.for(Person) - search1 = Search.new( - Person, { "name_eq" => "A" }, context: shared_context - ) - search2 = Search.new( - Person, { "name_eq" => "B" }, context: shared_context - ) - expect(search1.context).to be search2.context + s1 = Search.new(Person, { name_eq: 'A' }, context: shared_context) + s2 = Search.new(Person, { name_eq: 'B' }, context: shared_context) + expect(s1.context).to be s2.context end end describe '#build' do it 'creates conditions for top-level attributes' do - search = Search.new(Person, :name_eq => 'Ernie') - condition = search.base[:name_eq] + s = Search.new(Person, name_eq: 'Ernie') + condition = s.base[:name_eq] expect(condition).to be_a Nodes::Condition expect(condition.predicate.name).to eq 'eq' expect(condition.attributes.first.name).to eq 'name' @@ -64,8 +98,8 @@ module Ransack end it 'creates conditions for association attributes' do - search = Search.new(Person, :children_name_eq => 'Ernie') - condition = search.base[:children_name_eq] + s = Search.new(Person, children_name_eq: 'Ernie') + condition = s.base[:children_name_eq] expect(condition).to be_a Nodes::Condition expect(condition.predicate.name).to eq 'eq' expect(condition.attributes.first.name).to eq 'children_name' @@ -73,34 +107,85 @@ module Ransack end it 'creates conditions for polymorphic belongs_to association attributes' do - search = Search.new(Note, :notable_of_Person_type_name_eq => 'Ernie') - condition = search.base[:notable_of_Person_type_name_eq] + s = Search.new(Note, notable_of_Person_type_name_eq: 'Ernie') + condition = s.base[:notable_of_Person_type_name_eq] expect(condition).to be_a Nodes::Condition expect(condition.predicate.name).to eq 'eq' - expect(condition.attributes.first.name).to eq 'notable_of_Person_type_name' + expect(condition.attributes.first.name) + .to eq 'notable_of_Person_type_name' expect(condition.value).to eq 'Ernie' end - it 'creates conditions for multiple polymorphic belongs_to association attributes' do - search = Search.new(Note, - :notable_of_Person_type_name_or_notable_of_Article_type_title_eq => 'Ernie') - condition = search. + it 'creates conditions for multiple polymorphic belongs_to association + attributes' do + s = Search.new(Note, + notable_of_Person_type_name_or_notable_of_Article_type_title_eq: 'Ernie') + condition = s. base[:notable_of_Person_type_name_or_notable_of_Article_type_title_eq] expect(condition).to be_a Nodes::Condition expect(condition.predicate.name).to eq 'eq' - expect(condition.attributes.first.name).to eq 'notable_of_Person_type_name' - expect(condition.attributes.last.name).to eq 'notable_of_Article_type_title' + expect(condition.attributes.first.name) + .to eq 'notable_of_Person_type_name' + expect(condition.attributes.last.name) + .to eq 'notable_of_Article_type_title' expect(condition.value).to eq 'Ernie' end - it 'preserves default scope conditions for associations' do - search = Search.new(Person, :articles_title_eq => 'Test') - expect(search.result.to_sql).to include "default_scope" + it 'creates conditions for aliased attributes', + if: Ransack::SUPPORTS_ATTRIBUTE_ALIAS do + s = Search.new(Person, full_name_eq: 'Ernie') + condition = s.base[:full_name_eq] + expect(condition).to be_a Nodes::Condition + expect(condition.predicate.name).to eq 'eq' + expect(condition.attributes.first.name).to eq 'full_name' + expect(condition.value).to eq 'Ernie' + end + + it 'preserves default scope and conditions for associations' do + s = Search.new(Person, published_articles_title_eq: 'Test') + expect(s.result.to_sql).to include 'default_scope' + expect(s.result.to_sql).to include 'published' + end + + # The failure/oversight in Ransack::Nodes::Condition#arel_predicate or deeper is beyond my understanding of the structures + it 'preserves (inverts) default scope and conditions for negative subqueries' do + # the positive case (published_articles_title_eq) is + # SELECT "people".* FROM "people" + # LEFT OUTER JOIN "articles" ON "articles"."person_id" = "people"."id" + # AND "articles"."published" = 't' + # AND ('default_scope' = 'default_scope') + # WHERE "articles"."title" = 'Test' ORDER BY "people"."id" DESC + # + # negative case was + # SELECT "people".* FROM "people" WHERE "people"."id" NOT IN ( + # SELECT "articles"."person_id" FROM "articles" + # WHERE "articles"."person_id" = "people"."id" + # AND NOT ("articles"."title" != 'Test') + # ) ORDER BY "people"."id" DESC + # + # Should have been like + # SELECT "people".* FROM "people" WHERE "people"."id" NOT IN ( + # SELECT "articles"."person_id" FROM "articles" + # WHERE "articles"."person_id" = "people"."id" + # AND "articles"."title" = 'Test' AND "articles"."published" = 't' AND ('default_scope' = 'default_scope') + # ) ORDER BY "people"."id" DESC + # + # With tenanting (eg default_scope with column reference), NOT IN should be like + # SELECT "people".* FROM "people" WHERE "people"."tenant_id" = 'tenant_id' AND "people"."id" NOT IN ( + # SELECT "articles"."person_id" FROM "articles" + # WHERE "articles"."person_id" = "people"."id" + # AND "articles"."tenant_id" = 'tenant_id' + # AND "articles"."title" = 'Test' AND "articles"."published" = 't' AND ('default_scope' = 'default_scope') + # ) ORDER BY "people"."id" DESC + + s = Search.new(Person, published_articles_title_not_eq: 'Test') + expect(s.result.to_sql).to include 'default_scope' + expect(s.result.to_sql).to include 'published' end it 'discards empty conditions' do - search = Search.new(Person, :children_name_eq => '') - condition = search.base[:children_name_eq] + s = Search.new(Person, children_name_eq: '') + condition = s.base[:children_name_eq] expect(condition).to be_nil end @@ -110,13 +195,13 @@ module Ransack end it 'accepts arrays of groupings' do - search = Search.new(Person, + s = Search.new(Person, g: [ - { :m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie' }, - { :m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert' }, + { m: 'or', name_eq: 'Ernie', children_name_eq: 'Ernie' }, + { m: 'or', name_eq: 'Bert', children_name_eq: 'Bert' }, ] ) - ors = search.groupings + ors = s.groupings expect(ors.size).to eq(2) or1, or2 = ors expect(or1).to be_a Nodes::Grouping @@ -125,14 +210,14 @@ module Ransack expect(or2.combinator).to eq 'or' end - it 'accepts "attributes" hashes for groupings' do - search = Search.new(Person, + it 'accepts attributes hashes for groupings' do + s = Search.new(Person, g: { '0' => { m: 'or', name_eq: 'Ernie', children_name_eq: 'Ernie' }, '1' => { m: 'or', name_eq: 'Bert', children_name_eq: 'Bert' }, } ) - ors = search.groupings + ors = s.groupings expect(ors.size).to eq(2) or1, or2 = ors expect(or1).to be_a Nodes::Grouping @@ -141,15 +226,17 @@ module Ransack expect(or2.combinator).to eq 'or' end - it 'accepts "attributes" hashes for conditions' do - search = Search.new(Person, - :c => { - '0' => { :a => ['name'], :p => 'eq', :v => ['Ernie'] }, - '1' => { :a => ['children_name', 'parent_name'], - :p => 'eq', :v => ['Ernie'], :m => 'or' } - } + it 'accepts attributes hashes for conditions' do + s = Search.new(Person, + c: { + '0' => { a: ['name'], p: 'eq', v: ['Ernie'] }, + '1' => { + a: ['children_name', 'parent_name'], + p: 'eq', v: ['Ernie'], m: 'or' + } + } ) - conditions = search.base.conditions + conditions = s.base.conditions expect(conditions.size).to eq(2) expect(conditions.map { |c| c.class }) .to eq [Nodes::Condition, Nodes::Condition] @@ -157,11 +244,11 @@ module Ransack it 'creates conditions for custom predicates that take arrays' do Ransack.configure do |config| - config.add_predicate 'ary_pred', :wants_array => true + config.add_predicate 'ary_pred', wants_array: true end - search = Search.new(Person, :name_ary_pred => ['Ernie', 'Bert']) - condition = search.base[:name_ary_pred] + s = Search.new(Person, name_ary_pred: ['Ernie', 'Bert']) + condition = s.base[:name_ary_pred] expect(condition).to be_a Nodes::Condition expect(condition.predicate.name).to eq 'ary_pred' expect(condition.attributes.first.name).to eq 'name' @@ -169,39 +256,194 @@ module Ransack end it 'does not evaluate the query on #inspect' do - search = Search.new(Person, :children_id_in => [1, 2, 3]) - expect(search.inspect).not_to match /ActiveRecord/ + s = Search.new(Person, children_id_in: [1, 2, 3]) + expect(s.inspect).not_to match /ActiveRecord/ end context 'with an invalid condition' do - subject { Search.new(Person, :unknown_attr_eq => 'Ernie') } + subject { Search.new(Person, unknown_attr_eq: 'Ernie') } - context "when ignore_unknown_conditions is false" do + context 'when ignore_unknown_conditions configuration option is false' do before do - Ransack.configure { |config| - config.ignore_unknown_conditions = false - } + Ransack.configure { |c| c.ignore_unknown_conditions = false } end specify { expect { subject }.to raise_error ArgumentError } + specify { expect { subject }.to raise_error InvalidSearchError } end - context "when ignore_unknown_conditions is true" do + context 'when ignore_unknown_conditions configuration option is true' do before do - Ransack.configure { |config| - config.ignore_unknown_conditions = true - } + Ransack.configure { |c| c.ignore_unknown_conditions = true } end specify { expect { subject }.not_to raise_error } end + + subject(:with_ignore_unknown_conditions_false) { + Search.new(Person, + { unknown_attr_eq: 'Ernie' }, + { ignore_unknown_conditions: false } + ) + } + + subject(:with_ignore_unknown_conditions_true) { + Search.new(Person, + { unknown_attr_eq: 'Ernie' }, + { ignore_unknown_conditions: true } + ) + } + + context 'when ignore_unknown_conditions search parameter is absent' do + specify { expect { subject }.not_to raise_error } + end + + context 'when ignore_unknown_conditions search parameter is false' do + specify { expect { with_ignore_unknown_conditions_false }.to raise_error ArgumentError } + specify { expect { with_ignore_unknown_conditions_false }.to raise_error InvalidSearchError } + end + + context 'when ignore_unknown_conditions search parameter is true' do + specify { expect { with_ignore_unknown_conditions_true }.not_to raise_error } + end end it 'does not modify the parameters' do - params = { :name_eq => '' } + params = { name_eq: '' } expect { Search.new(Person, params) }.not_to change { params } end + context 'with empty search parameters' do + it 'handles completely empty parameters' do + search = Search.new(Person, {}) + expect(search.result.to_sql).not_to match(/WHERE/) + end + + it 'handles nil parameters' do + search = Search.new(Person, nil) + expect(search.result.to_sql).not_to match(/WHERE/) + end + end + + context 'with whitespace-only values' do + before do + Ransack.configure { |c| c.strip_whitespace = true } + end + + it 'removes whitespace-only values' do + expect_any_instance_of(Search).to receive(:build).with({}) + Search.new(Person, name_eq: ' ') + end + + it 'keeps values with content after whitespace stripping' do + expect_any_instance_of(Search).to receive(:build).with({ 'name_eq' => 'test' }) + Search.new(Person, name_eq: ' test ') + end + end + + context 'with special characters in values' do + it 'handles values with special regex characters' do + search = Search.new(Person, name_cont: 'test[(){}^$|?*+.\\') + expect { search.result }.not_to raise_error + end + + it 'handles values with SQL injection attempts' do + search = Search.new(Person, name_cont: "'; DROP TABLE people; --") + expect { search.result }.not_to raise_error + end + end + + context "ransackable_scope" do + around(:each) do |example| + Person.define_singleton_method(:name_eq) do |name| + self.where(name: name) + end + + begin + example.run + ensure + Person.singleton_class.undef_method :name_eq + end + end + + it "is prioritized over base predicates" do + allow(Person).to receive(:ransackable_scopes) + .and_return(Person.ransackable_scopes + [:name_eq]) + + s = Search.new(Person, name_eq: "Johny") + expect(s.instance_variable_get(:@scope_args)["name_eq"]).to eq("Johny") + expect(s.base[:name_eq]).to be_nil + end + end + + context "ransackable_scope with array arguments" do + around(:each) do |example| + Person.define_singleton_method(:domestic) do |countries| + self.where(name: countries) + end + + Person.define_singleton_method(:flexible_scope) do |*args| + self.where(id: args) + end + + Person.define_singleton_method(:two_param_scope) do |param1, param2| + self.where(name: param1, id: param2) + end + + begin + example.run + ensure + Person.singleton_class.undef_method :domestic + Person.singleton_class.undef_method :flexible_scope + Person.singleton_class.undef_method :two_param_scope + end + end + + it "handles scopes that take arrays as single arguments (arity 1)" do + allow(Person).to receive(:ransackable_scopes) + .and_return(Person.ransackable_scopes + [:domestic]) + + # This should not raise ArgumentError + expect { + s = Search.new(Person, domestic: ['US', 'JP']) + s.result # This triggers the actual scope call + }.not_to raise_error + + s = Search.new(Person, domestic: ['US', 'JP']) + expect(s.instance_variable_get(:@scope_args)["domestic"]).to eq("US") + end + + it "handles scopes with flexible arity (negative arity)" do + allow(Person).to receive(:ransackable_scopes) + .and_return(Person.ransackable_scopes + [:flexible_scope]) + + expect { + s = Search.new(Person, flexible_scope: ['US', 'JP']) + s.result + }.not_to raise_error + end + + it "handles scopes with arity > 1" do + allow(Person).to receive(:ransackable_scopes) + .and_return(Person.ransackable_scopes + [:two_param_scope]) + + expect { + s = Search.new(Person, two_param_scope: ['param1', 'param2']) + s.result + }.not_to raise_error + end + + it "still supports the workaround with nested arrays" do + allow(Person).to receive(:ransackable_scopes) + .and_return(Person.ransackable_scopes + [:domestic]) + + # The workaround from the issue should still work + expect { + s = Search.new(Person, domestic: [['US', 'JP']]) + s.result + }.not_to raise_error + end + end end describe '#result' do @@ -211,83 +453,133 @@ module Ransack let(:children_people_name_field) { "#{quote_table_name("children_people")}.#{quote_column_name("name")}" } + let(:notable_type_field) { + "#{quote_table_name("notes")}.#{quote_column_name("notable_type")}" + } + it 'evaluates conditions contextually' do - search = Search.new(Person, :children_name_eq => 'Ernie') - expect(search.result).to be_an ActiveRecord::Relation - where = search.result.where_values.first - expect(where.to_sql).to match /#{children_people_name_field} = 'Ernie'/ + s = Search.new(Person, children_name_eq: 'Ernie') + expect(s.result).to be_an ActiveRecord::Relation + expect(s.result.to_sql).to match /#{ + children_people_name_field} = 'Ernie'/ + end + + it 'use appropriate table alias' do + s = Search.new(Person, { + name_eq: "person_name_query", + articles_title_eq: "person_article_title_query", + parent_name_eq: "parent_name_query", + parent_articles_title_eq: 'parents_article_title_query' + }).result + + real_query = remove_quotes_and_backticks(s.to_sql) + + expect(real_query) + .to match(%r{LEFT OUTER JOIN articles ON (\('default_scope' = 'default_scope'\) AND )?articles.person_id = people.id}) + expect(real_query) + .to match(%r{LEFT OUTER JOIN articles articles_people ON (\('default_scope' = 'default_scope'\) AND )?articles_people.person_id = parents_people.id}) + + expect(real_query) + .to include "people.name = 'person_name_query'" + expect(real_query) + .to include "articles.title = 'person_article_title_query'" + expect(real_query) + .to include "parents_people.name = 'parent_name_query'" + expect(real_query) + .to include "articles_people.title = 'parents_article_title_query'" + end + + it 'evaluates conditions for multiple `belongs_to` associations to the same table contextually' do + s = Search.new( + Recommendation, + person_name_eq: 'Ernie', + target_person_parent_name_eq: 'Test' + ).result + expect(s).to be_an ActiveRecord::Relation + real_query = remove_quotes_and_backticks(s.to_sql) + expected_query = <<-SQL + SELECT recommendations.* FROM recommendations + LEFT OUTER JOIN people ON people.id = recommendations.person_id + LEFT OUTER JOIN people target_people_recommendations + ON target_people_recommendations.id = recommendations.target_person_id + LEFT OUTER JOIN people parents_people + ON parents_people.id = target_people_recommendations.parent_id + WHERE (people.name = 'Ernie' AND parents_people.name = 'Test') + SQL + .squish + expect(real_query).to eq expected_query end it 'evaluates compound conditions contextually' do - search = Search.new(Person, :children_name_or_name_eq => 'Ernie') - expect(search.result).to be_an ActiveRecord::Relation - where = search.result.where_values.first - expect(where.to_sql).to match /#{children_people_name_field + s = Search.new(Person, children_name_or_name_eq: 'Ernie').result + expect(s).to be_an ActiveRecord::Relation + expect(s.to_sql).to match /#{children_people_name_field } = 'Ernie' OR #{people_name_field} = 'Ernie'/ end it 'evaluates polymorphic belongs_to association conditions contextually' do - search = Search.new(Note, :notable_of_Person_type_name_eq => 'Ernie') - expect(search.result).to be_an ActiveRecord::Relation - where = search.result.where_values.first - expect(where.to_sql).to match /#{people_name_field} = 'Ernie'/ + s = Search.new(Note, notable_of_Person_type_name_eq: 'Ernie').result + expect(s).to be_an ActiveRecord::Relation + expect(s.to_sql).to match /#{people_name_field} = 'Ernie'/ + expect(s.to_sql).to match /#{notable_type_field} = 'Person'/ end it 'evaluates nested conditions' do - search = Search.new(Person, :children_name_eq => 'Ernie', - :g => [ - { :m => 'or', - :name_eq => 'Ernie', - :children_children_name_eq => 'Ernie' - } + s = Search.new(Person, children_name_eq: 'Ernie', + g: [ + { m: 'or', name_eq: 'Ernie', children_children_name_eq: 'Ernie' } ] - ) - expect(search.result).to be_an ActiveRecord::Relation - where = search.result.where_values.first - expect(where.to_sql).to match /#{children_people_name_field} = 'Ernie'/ - expect(where.to_sql).to match /#{people_name_field} = 'Ernie'/ - expect(where.to_sql).to match /#{quote_table_name("children_people_2") - }.#{quote_column_name("name")} = 'Ernie'/ + ).result + expect(s).to be_an ActiveRecord::Relation + first, last = s.to_sql.split(/ AND /) + expect(first).to match /#{children_people_name_field} = 'Ernie'/ + expect(last).to match /#{ + people_name_field} = 'Ernie' OR #{ + quote_table_name("children_people_2")}.#{ + quote_column_name("name")} = 'Ernie'/ end it 'evaluates arrays of groupings' do - search = Search.new(Person, - :g => [ - { :m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie' }, - { :m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert' } + s = Search.new(Person, + g: [ + { m: 'or', name_eq: 'Ernie', children_name_eq: 'Ernie' }, + { m: 'or', name_eq: 'Bert', children_name_eq: 'Bert' } ] - ) - expect(search.result).to be_an ActiveRecord::Relation - where = search.result.where_values.first - sql = where.to_sql - first, second = sql.split(/ AND /) - expect(first).to match /#{people_name_field} = 'Ernie'/ - expect(first).to match /#{children_people_name_field} = 'Ernie'/ - expect(second).to match /#{people_name_field} = 'Bert'/ - expect(second).to match /#{children_people_name_field} = 'Bert'/ + ).result + expect(s).to be_an ActiveRecord::Relation + first, last = s.to_sql.split(/ AND /) + expect(first).to match /#{people_name_field} = 'Ernie' OR #{ + children_people_name_field} = 'Ernie'/ + expect(last).to match /#{people_name_field} = 'Bert' OR #{ + children_people_name_field} = 'Bert'/ end - it 'returns distinct records when passed :distinct => true' do - search = Search.new( - Person, :g => [ - { :m => 'or', - :comments_body_cont => 'e', - :articles_comments_body_cont => 'e' - } + it 'returns distinct records when passed distinct: true' do + s = Search.new(Person, + g: [ + { m: 'or', comments_body_cont: 'e', articles_comments_body_cont: 'e' } ] ) - if ActiveRecord::VERSION::MAJOR == 3 - all_or_load, uniq_or_distinct = :all, :uniq - else - all_or_load, uniq_or_distinct = :load, :distinct - end - expect(search.result.send(all_or_load).size) - .to eq(9000) - expect(search.result(:distinct => true).size) + + all_or_load, uniq_or_distinct = :load, :distinct + expect(s.result.send(all_or_load).size) + .to eq(8998) + expect(s.result(distinct: true).size) .to eq(10) - expect(search.result.send(all_or_load).send(uniq_or_distinct)) - .to eq search.result(:distinct => true).send(all_or_load) + expect(s.result.send(all_or_load).send(uniq_or_distinct)) + .to eq s.result(distinct: true).send(all_or_load) end + + it 'evaluates joins with belongs_to join' do + s = Person.joins(:parent).ransack(parent_name_eq: 'Ernie').result(distinct: true) + expect(s).to be_an ActiveRecord::Relation + end + + private + + def remove_quotes_and_backticks(str) + str.gsub(/["`]/, '') + end end describe '#sorts=' do @@ -295,6 +587,11 @@ module Ransack @s = Search.new(Person) end + it 'doesn\'t creates sorts' do + @s.sorts = '' + expect(@s.sorts.size).to eq(0) + end + it 'creates sorts based on a single attribute/direction' do @s.sorts = 'id desc' expect(@s.sorts.size).to eq(1) @@ -322,85 +619,181 @@ module Ransack expect(sort.dir).to eq 'asc' end - it 'creates sorts based on multiple attributes/directions in array format' do - @s.sorts = ['id desc', { :name => 'name', :dir => 'asc' }] + it 'creates sorts based on a single alias/direction' do + @s.sorts = 'daddy desc' + expect(@s.sorts.size).to eq(1) + sort = @s.sorts.first + expect(sort).to be_a Nodes::Sort + expect(sort.name).to eq 'parent_name' + expect(sort.dir).to eq 'desc' + end + + it 'creates sorts based on a single alias and uppercase direction' do + @s.sorts = 'daddy DESC' + expect(@s.sorts.size).to eq(1) + sort = @s.sorts.first + expect(sort).to be_a Nodes::Sort + expect(sort.name).to eq 'parent_name' + expect(sort.dir).to eq 'desc' + end + + it 'creates sorts based on a single alias and without direction' do + @s.sorts = 'daddy' + expect(@s.sorts.size).to eq(1) + sort = @s.sorts.first + expect(sort).to be_a Nodes::Sort + expect(sort.name).to eq 'parent_name' + expect(sort.dir).to eq 'asc' + end + + it 'creates sorts based on attributes, alias and directions in array format' do + @s.sorts = ['id desc', { name: 'daddy', dir: 'asc' }] expect(@s.sorts.size).to eq(2) sort1, sort2 = @s.sorts expect(sort1).to be_a Nodes::Sort expect(sort1.name).to eq 'id' expect(sort1.dir).to eq 'desc' expect(sort2).to be_a Nodes::Sort - expect(sort2.name).to eq 'name' + expect(sort2.name).to eq 'parent_name' expect(sort2.dir).to eq 'asc' end - it 'creates sorts based on multiple attributes and uppercase directions in array format' do - @s.sorts = ['id DESC', { :name => 'name', :dir => 'ASC' }] + it 'creates sorts based on attributes, alias and uppercase directions in array format' do + @s.sorts = ['id DESC', { name: 'daddy', dir: 'ASC' }] expect(@s.sorts.size).to eq(2) sort1, sort2 = @s.sorts expect(sort1).to be_a Nodes::Sort expect(sort1.name).to eq 'id' expect(sort1.dir).to eq 'desc' expect(sort2).to be_a Nodes::Sort - expect(sort2.name).to eq 'name' + expect(sort2.name).to eq 'parent_name' expect(sort2.dir).to eq 'asc' end - it 'creates sorts based on multiple attributes and different directions in array format' do - @s.sorts = ['id DESC', { name: 'name', dir: nil }] + it 'creates sorts based on attributes, alias and different directions + in array format' do + @s.sorts = ['id DESC', { name: 'daddy', dir: nil }] expect(@s.sorts.size).to eq(2) sort1, sort2 = @s.sorts expect(sort1).to be_a Nodes::Sort expect(sort1.name).to eq 'id' expect(sort1.dir).to eq 'desc' expect(sort2).to be_a Nodes::Sort - expect(sort2.name).to eq 'name' + expect(sort2.name).to eq 'parent_name' expect(sort2.dir).to eq 'asc' end - it 'creates sorts based on multiple attributes/directions in hash format' do + it 'creates sorts based on attributes, alias and directions in hash format' do @s.sorts = { - '0' => { :name => 'id', :dir => 'desc' }, - '1' => { :name => 'name', :dir => 'asc' } + '0' => { name: 'id', dir: 'desc' }, + '1' => { name: 'daddy', dir: 'asc' } } expect(@s.sorts.size).to eq(2) expect(@s.sorts).to be_all { |s| Nodes::Sort === s } id_sort = @s.sorts.detect { |s| s.name == 'id' } - name_sort = @s.sorts.detect { |s| s.name == 'name' } + daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' } expect(id_sort.dir).to eq 'desc' - expect(name_sort.dir).to eq 'asc' + expect(daddy_sort.dir).to eq 'asc' end - it 'creates sorts based on multiple attributes and uppercase directions in hash format' do + it 'creates sorts based on attributes, alias and uppercase directions + in hash format' do @s.sorts = { - '0' => { :name => 'id', :dir => 'DESC' }, - '1' => { :name => 'name', :dir => 'ASC' } + '0' => { name: 'id', dir: 'DESC' }, + '1' => { name: 'daddy', dir: 'ASC' } } expect(@s.sorts.size).to eq(2) expect(@s.sorts).to be_all { |s| Nodes::Sort === s } id_sort = @s.sorts.detect { |s| s.name == 'id' } - name_sort = @s.sorts.detect { |s| s.name == 'name' } + daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' } expect(id_sort.dir).to eq 'desc' - expect(name_sort.dir).to eq 'asc' + expect(daddy_sort.dir).to eq 'asc' end - it 'creates sorts based on multiple attributes and different directions in hash format' do + it 'creates sorts based on attributes, alias and different directions + in hash format' do @s.sorts = { - '0' => { :name => 'id', :dir => 'DESC' }, - '1' => { :name => 'name', :dir => nil } + '0' => { name: 'id', dir: 'DESC' }, + '1' => { name: 'daddy', dir: nil } } expect(@s.sorts.size).to eq(2) expect(@s.sorts).to be_all { |s| Nodes::Sort === s } id_sort = @s.sorts.detect { |s| s.name == 'id' } - name_sort = @s.sorts.detect { |s| s.name == 'name' } + daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' } expect(id_sort.dir).to eq 'desc' - expect(name_sort.dir).to eq 'asc' + expect(daddy_sort.dir).to eq 'asc' end it 'overrides existing sort' do @s.sorts = 'id asc' expect(@s.result.first.id).to eq 1 end + + it 'raises ArgumentError when an invalid argument is sent' do + expect do + @s.sorts = 1234 + end.to raise_error(ArgumentError, "Invalid argument (Integer) supplied to sorts=") + end + + it 'raises InvalidSearchError when an invalid argument is sent' do + expect do + @s.sorts = 1234 + end.to raise_error(Ransack::InvalidSearchError, "Invalid argument (Integer) supplied to sorts=") + end + + it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do + default = Ransack.options.clone + + s = Search.new(Person, s: 'name asc') + expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC" + + Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first } + s = Search.new(Person, s: 'name asc') + expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC NULLS FIRST" + s = Search.new(Person, s: 'name desc') + expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" DESC NULLS LAST" + + Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_last } + s = Search.new(Person, s: 'name asc') + expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC NULLS LAST" + s = Search.new(Person, s: 'name desc') + expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" DESC NULLS FIRST" + + Ransack.options = default + end + + it "PG's sort option with double name", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do + default = Ransack.options.clone + + s = Search.new(Person, s: 'doubled_name asc') + expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC" + + Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first } + s = Search.new(Person, s: 'doubled_name asc') + expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST" + s = Search.new(Person, s: 'doubled_name desc') + expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS LAST" + + Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_last } + s = Search.new(Person, s: 'doubled_name asc') + expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST" + s = Search.new(Person, s: 'doubled_name desc') + expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST" + + Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_first } + s = Search.new(Person, s: 'doubled_name asc') + expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST" + s = Search.new(Person, s: 'doubled_name desc') + expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST" + + Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_last } + s = Search.new(Person, s: 'doubled_name asc') + expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST" + s = Search.new(Person, s: 'doubled_name desc') + expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS LAST" + + Ransack.options = default + end end describe '#method_missing' do @@ -419,7 +812,7 @@ module Ransack it 'allows chaining to access nested conditions' do @s.groupings = [ - { :m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie' } + { m: 'or', name_eq: 'Ernie', children_name_eq: 'Ernie' } ] expect(@s.groupings.first.children_name_eq).to eq 'Ernie' end diff --git a/spec/ransack/translate_spec.rb b/spec/ransack/translate_spec.rb index 0961e95e0..5e91d72ea 100644 --- a/spec/ransack/translate_spec.rb +++ b/spec/ransack/translate_spec.rb @@ -2,13 +2,12 @@ module Ransack describe Translate do - describe '.attribute' do it 'translate namespaced attribute like AR does' do ar_translation = ::Namespace::Article.human_attribute_name(:title) ransack_translation = Ransack::Translate.attribute( :title, - :context => ::Namespace::Article.search.context + context: ::Namespace::Article.ransack.context ) expect(ransack_translation).to eq ar_translation end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9338d52b4..313991a6e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,46 +1,41 @@ -require 'machinist/active_record' -require 'sham' -require 'faker' require 'ransack' - +require 'factory_bot' +require 'faker' +require 'action_controller' +require 'ransack/helpers' +require 'pry' +require 'simplecov' +require 'byebug' +require 'rspec' + +SimpleCov.start I18n.enforce_available_locales = false Time.zone = 'Eastern Time (US & Canada)' I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'support', '*.yml')] -Dir[File.expand_path('../{helpers,support,blueprints}/*.rb', __FILE__)] -.each do |f| - require f -end +Dir[File.expand_path('../{helpers,support,factories}/*.rb', __FILE__)] +.each { |f| require f } -Sham.define do - name { Faker::Name.name } - title { Faker::Lorem.sentence } - body { Faker::Lorem.paragraph } - salary { |index| 30000 + (index * 1000) } - tag_name { Faker::Lorem.words(3).join(' ') } - note { Faker::Lorem.words(7).join(' ') } - only_admin { Faker::Lorem.words(3).join(' ') } - only_search { Faker::Lorem.words(3).join(' ') } - only_sort { Faker::Lorem.words(3).join(' ') } - notable_id { |id| id } -end +Faker::Config.random = Random.new(0) RSpec.configure do |config| config.alias_it_should_behave_like_to :it_has_behavior, 'has behavior' + + config.include FactoryBot::Syntax::Methods config.before(:suite) do - puts '=' * 80 - connection_name = ActiveRecord::Base.connection.adapter_name - puts "Running specs against #{connection_name}, ActiveRecord #{ - ActiveRecord::VERSION::STRING} and ARel #{Arel::VERSION}..." - puts '=' * 80 + message = "Running Ransack specs with #{ + ActiveRecord::Base.connection.adapter_name + }, Active Record #{::ActiveRecord::VERSION::STRING}, Arel #{Arel::VERSION + } and Ruby #{RUBY_VERSION}" + line = '=' * message.length + puts line, message, line Schema.create + SubDB::Schema.create if defined?(SubDB) end - config.before(:all) { Sham.reset(:before_all) } - config.before(:each) { Sham.reset(:before_each) } - config.include RansackHelper + config.include PolyamorousHelper end RSpec::Matchers.define :be_like do |expected| diff --git a/spec/support/schema.rb b/spec/support/schema.rb index 1b448d469..ca48773a0 100644 --- a/spec/support/schema.rb +++ b/spec/support/schema.rb @@ -1,46 +1,112 @@ require 'active_record' +require 'activerecord-postgis-adapter' -case ENV['DB'] -when "mysql" +case ENV['DB'].try(:downcase) +when 'mysql', 'mysql2' + # To test with MySQL: `DB=mysql bundle exec rake spec` ActiveRecord::Base.establish_connection( adapter: 'mysql2', database: 'ransack', + username: ENV.fetch("http://github.com/MYSQL_USERNAME") { "root" }, + password: ENV.fetch("http://github.com/MYSQL_PASSWORD") { "" }, encoding: 'utf8' ) -when "postgres" +when 'pg', 'postgres', 'postgresql' + # To test with PostgreSQL: `DB=postgresql bundle exec rake spec` ActiveRecord::Base.establish_connection( adapter: 'postgresql', database: 'ransack', - username: 'postgres', + username: ENV.fetch("http://github.com/DATABASE_USERNAME") { "postgres" }, + password: ENV.fetch("http://github.com/DATABASE_PASSWORD") { "" }, + host: ENV.fetch("http://github.com/DATABASE_HOST") { "localhost" }, + min_messages: 'warning' + ) +when 'postgis' + # To test with PostGIS: `DB=postgis bundle exec rake spec` + ActiveRecord::Base.establish_connection( + adapter: 'postgis', + postgis_extension: 'postgis', + database: 'ransack', + username: ENV.fetch("http://github.com/DATABASE_USERNAME") { "postgres" }, + password: ENV.fetch("http://github.com/DATABASE_PASSWORD") { "" }, + host: ENV.fetch("http://github.com/DATABASE_HOST") { "localhost" }, min_messages: 'warning' ) else - # Assume SQLite3 + # Otherwise, assume SQLite3: `bundle exec rake spec` ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: ':memory:' ) end -class Person < ActiveRecord::Base - if ActiveRecord::VERSION::MAJOR == 3 - default_scope order('id DESC') - else - default_scope { order(id: :desc) } +# This is just a test app with no sensitive data, so we explicitly allowlist all +# attributes and associations for search. In general, end users should +# explicitly authorize each model, but this shows a way to configure the +# unrestricted default behavior of versions prior to Ransack 4. +# +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true + + def self.ransackable_attributes(auth_object = nil) + authorizable_ransackable_attributes + end + + def self.ransackable_associations(auth_object = nil) + authorizable_ransackable_associations end - belongs_to :parent, :class_name => 'Person', :foreign_key => :parent_id - has_many :children, :class_name => 'Person', :foreign_key => :parent_id +end + +class Person < ApplicationRecord + default_scope { order(id: :desc) } + belongs_to :parent, class_name: 'Person', foreign_key: :parent_id + has_many :children, class_name: 'Person', foreign_key: :parent_id has_many :articles + has_many :story_articles + + has_many :published_articles, ->{ where(published: true) }, + class_name: "Article" has_many :comments - has_many :authored_article_comments, :through => :articles, - :source => :comments, :foreign_key => :person_id - has_many :notes, :as => :notable + has_many :authored_article_comments, through: :articles, + source: :comments, foreign_key: :person_id + has_many :notes, as: :notable scope :restricted, lambda { where("restricted = 1") } scope :active, lambda { where("active = 1") } scope :over_age, lambda { |y| where(["age > ?", y]) } + scope :of_age, lambda { |of_age| + of_age ? where("age >= ?", 18) : where("age < ?", 18) + } + + scope :sort_by_reverse_name_asc, lambda { order(Arel.sql("REVERSE(name) ASC")) } + scope :sort_by_reverse_name_desc, lambda { order("REVERSE(name) DESC") } - ransacker :reversed_name, :formatter => proc { |v| v.reverse } do |parent| + alias_attribute :full_name, :name + + ransack_alias :term, :name_or_email + ransack_alias :daddy, :parent_name + + ransacker :reversed_name, formatter: proc { |v| v.reverse } do |parent| + parent.table[:name] + end + + ransacker :array_people_ids, + formatter: proc { |v| Person.first(2).map(&:id) } do |parent| + parent.table[:id] + end + + ransacker :array_where_people_ids, + formatter: proc { |v| Person.where(id: v).map(&:id) } do |parent| + parent.table[:id] + end + + ransacker :array_people_names, + formatter: proc { |v| Person.first(2).map { |p| p.id.to_s } } do |parent| + parent.table[:name] + end + + ransacker :array_where_people_names, + formatter: proc { |v| Person.where(id: v).map { |p| p.id.to_s } } do |parent| parent.table[:name] end @@ -50,27 +116,44 @@ class Person < ActiveRecord::Base ) end - def self.ransackable_attributes(auth_object = nil) - if auth_object == :admin - column_names + _ransackers.keys - ['only_sort'] - else - column_names + _ransackers.keys - ['only_sort', 'only_admin'] - end + ransacker :sql_literal_id, type: :integer do + Arel.sql('people.id') end - def self.ransortable_attributes(auth_object = nil) - if auth_object == :admin - column_names + _ransackers.keys - ['only_search'] - else - column_names + _ransackers.keys - ['only_search', 'only_admin'] + ransacker :name_case_insensitive, type: :string do + arel_table[:name].lower + end + + ransacker :with_arguments, args: [:parent, :ransacker_args] do |parent, args| + min, max = args + query = <<-SQL + (SELECT MAX(articles.title) + FROM articles + WHERE articles.person_id = people.id + AND LENGTH(articles.body) BETWEEN #{min} AND #{max} + GROUP BY articles.person_id + ) + SQL + .squish + Arel.sql(query) + end + + ransacker :article_tags, formatter: proc { |id| + if Tag.exists?(id) + joins(articles: :tags) + .where(tags: { id: id }) + .distinct + .select(:id).arel end + } do |parent| + parent.table[:id] end def self.ransackable_attributes(auth_object = nil) if auth_object == :admin - column_names + _ransackers.keys - ['only_sort'] + authorizable_ransackable_attributes - ['only_sort'] else - column_names + _ransackers.keys - ['only_sort', 'only_admin'] + authorizable_ransackable_attributes - ['only_sort', 'only_admin'] end end @@ -83,19 +166,69 @@ def self.ransortable_attributes(auth_object = nil) end end -class Article < ActiveRecord::Base +class Musician < Person +end + +class Article < ApplicationRecord belongs_to :person has_many :comments has_and_belongs_to_many :tags - has_many :notes, :as => :notable + has_many :notes, as: :notable + has_many :recent_notes, as: :notable - if ActiveRecord::VERSION::STRING >= '3.1' - default_scope { where("'default_scope' = 'default_scope'") } - else # Rails 3.0 does not accept a block - default_scope where("'default_scope' = 'default_scope'") + alias_attribute :content, :body + + default_scope { where("'default_scope' = 'default_scope'") } + scope :latest_comment_cont, lambda { |msg| + join = <<-SQL + (LEFT OUTER JOIN ( + SELECT + comments.*, + row_number() OVER (PARTITION BY comments.article_id ORDER BY comments.id DESC) AS rownum + FROM comments + ) AS latest_comment + ON latest_comment.article_id = article.id + AND latest_comment.rownum = 1 + ) + SQL + .squish + + joins(join).where("latest_comment.body ILIKE ?", "%#{msg}%") + } + + ransacker :title_type, formatter: lambda { |tuples| + title, type = JSON.parse(tuples) + Arel::Nodes::Grouping.new( + [ + Arel::Nodes.build_quoted(title), + Arel::Nodes.build_quoted(type) + ] + ) + } do |_parent| + articles = Article.arel_table + Arel::Nodes::Grouping.new( + %i[title type].map do |field| + Arel::Nodes::NamedFunction.new( + 'COALESCE', + [ + Arel::Nodes::NamedFunction.new('TRIM', [articles[field]]), + Arel::Nodes.build_quoted('') + ] + ) + end + ) end end +class StoryArticle < Article +end + +class Recommendation < ApplicationRecord + belongs_to :person + belongs_to :target_person, class_name: 'Person' + belongs_to :article +end + module Namespace class Article < ::Article @@ -108,17 +241,48 @@ class Article < ::Article end end -class Comment < ActiveRecord::Base +class Comment < ApplicationRecord belongs_to :article belongs_to :person + has_and_belongs_to_many :tags + + default_scope { where(disabled: false) } end -class Tag < ActiveRecord::Base +class Tag < ApplicationRecord has_and_belongs_to_many :articles + has_and_belongs_to_many :comments +end + +class Note < ApplicationRecord + belongs_to :notable, polymorphic: true end -class Note < ActiveRecord::Base - belongs_to :notable, :polymorphic => true +class RecentNote < ApplicationRecord + DEFAULT_NOTABLE_ID = 1 + self.table_name = "notes" + default_scope { where(notable_id: DEFAULT_NOTABLE_ID) } + + belongs_to :notable, polymorphic: true +end + +class Account < ApplicationRecord + belongs_to :agent_account, class_name: "Account" + belongs_to :trade_account, class_name: "Account" +end + +class Address < ApplicationRecord + has_one :organization +end + +class Organization < ApplicationRecord + belongs_to :address + has_many :employees +end + +class Employee < ApplicationRecord + belongs_to :organization + has_one :address, through: :organization end module Schema @@ -126,67 +290,130 @@ def self.create ActiveRecord::Migration.verbose = false ActiveRecord::Schema.define do - create_table :people, :force => true do |t| + create_table :people, force: true do |t| t.integer :parent_id t.string :name t.string :email t.string :only_search t.string :only_sort t.string :only_admin + t.string :new_start + t.string :stop_end t.integer :salary + t.date :life_start t.boolean :awesome, default: false t.boolean :terms_and_conditions, default: false + t.boolean :true_or_false, default: true t.timestamps null: false end - create_table :articles, :force => true do |t| - t.integer :person_id - t.string :title - t.text :subject_header - t.text :body + create_table :articles, force: true do |t| + t.integer :person_id + t.string :title + t.text :subject_header + t.text :body + t.string :type + t.boolean :published, default: true end - create_table :comments, :force => true do |t| - t.integer :article_id - t.integer :person_id - t.text :body + create_table :comments, force: true do |t| + t.integer :article_id + t.integer :person_id + t.text :body + t.boolean :disabled, default: false end - create_table :tags, :force => true do |t| - t.string :name + create_table :tags, force: true do |t| + t.string :name + end + + create_table :articles_tags, force: true, id: false do |t| + t.integer :article_id + t.integer :tag_id end - create_table :articles_tags, :force => true, :id => false do |t| - t.integer :article_id - t.integer :tag_id + create_table :comments_tags, force: true, id: false do |t| + t.integer :comment_id + t.integer :tag_id end - create_table :notes, :force => true do |t| - t.integer :notable_id - t.string :notable_type - t.string :note + create_table :notes, force: true do |t| + t.integer :notable_id + t.string :notable_type + t.string :note end + create_table :recommendations, force: true do |t| + t.integer :person_id + t.integer :target_person_id + t.integer :article_id + end + + create_table :accounts, force: true do |t| + t.belongs_to :agent_account + t.belongs_to :trade_account + end + + create_table :addresses, force: true do |t| + t.string :city + end + + create_table :organizations, force: true do |t| + t.string :name + t.integer :address_id + end + + create_table :employees, force: true do |t| + t.string :name + t.integer :organization_id + end end 10.times do - person = Person.make - Note.make(:notable => person) + person = FactoryBot.create(:person) + FactoryBot.create(:note, :for_person, notable: person) 3.times do - article = Article.make(:person => person) + article = FactoryBot.create(:article, person: person) 3.times do - article.tags = [Tag.make, Tag.make, Tag.make] + article.tags = [FactoryBot.create(:tag), FactoryBot.create(:tag), FactoryBot.create(:tag)] end - Note.make(:notable => article) + FactoryBot.create(:note, :for_article, notable: article) 10.times do - Comment.make(:article => article, :person => person) + FactoryBot.create(:comment, article: article, person: person) end end end - Comment.make( - :body => 'First post!', - :article => Article.make(:title => 'Hello, world!') - ) + FactoryBot.create(:comment, + body: 'First post!', + article: FactoryBot.create(:article, title: 'Hello, world!') + ) + end +end + +module SubDB + class Base < ApplicationRecord + self.abstract_class = true + establish_connection( + adapter: 'sqlite3', + database: ':memory:' + ) + end + + class OperationHistory < Base + end + + module Schema + def self.create + s = ::ActiveRecord::Schema.new + s.instance_variable_set(:@connection, SubDB::Base.connection) + s.verbose = false + s.define({}) do + create_table :operation_histories, force: true do |t| + t.string :operation_type + t.integer :people_id + end + end + end end end