From a0a7c6a7e53ba473ad43105972008d58d1635d72 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 17 Jun 2024 14:34:25 +0200 Subject: [PATCH 01/76] remove unneeded files --- .devcontainer/devcontainer.json | 18 - .document | 5 - .rakeTasks | 7 - .rubocop.yml | 43 -- .ruby-version | 1 - .vscode/tasks.json | 34 -- Gemfile | 18 - Gemfile.lock | 109 ----- Rakefile | 58 --- bin/peptfilter | 7 - bin/prot2pept | 7 - bin/unipept | 8 - bin/uniprot | 7 - lib/batch_iterator.rb | 89 ---- lib/batch_order.rb | 21 - lib/commands.rb | 10 - lib/commands/peptfilter.rb | 117 ----- lib/commands/prot2pept.rb | 61 --- lib/commands/unipept.rb | 400 ----------------- lib/commands/unipept/api_runner.rb | 265 ------------ lib/commands/unipept/config.rb | 29 -- lib/commands/unipept/pept2ec.rb | 16 - lib/commands/unipept/pept2funct.rb | 16 - lib/commands/unipept/pept2go.rb | 16 - lib/commands/unipept/pept2interpro.rb | 16 - lib/commands/unipept/pept2lca.rb | 16 - lib/commands/unipept/pept2prot.rb | 26 -- lib/commands/unipept/pept2taxa.rb | 12 - lib/commands/unipept/peptinfo.rb | 16 - lib/commands/unipept/protinfo.rb | 12 - lib/commands/unipept/taxa2lca.rb | 18 - lib/commands/unipept/taxa2tree.rb | 74 ---- lib/commands/unipept/taxonomy.rb | 12 - lib/commands/uniprot.rb | 68 --- lib/configuration.rb | 44 -- lib/formatters.rb | 411 ------------------ lib/output_writer.rb | 13 - lib/retryable_typhoeus.rb | 34 -- lib/server_message.rb | 44 -- lib/version.rb | 3 - test/commands/test_peptfilter.rb | 180 -------- test/commands/test_prot2pept.rb | 87 ---- test/commands/test_unipept.rb | 37 -- test/commands/test_uniprot.rb | 136 ------ test/commands/unipept/test_api_runner.rb | 451 -------------------- test/commands/unipept/test_config.rb | 64 --- test/commands/unipept/test_pept2ec.rb | 140 ------ test/commands/unipept/test_pept2funct.rb | 140 ------ test/commands/unipept/test_pept2go.rb | 140 ------ test/commands/unipept/test_pept2interpro.rb | 140 ------ test/commands/unipept/test_pept2lca.rb | 109 ----- test/commands/unipept/test_pept2prot.rb | 110 ----- test/commands/unipept/test_pept2taxa.rb | 92 ---- test/commands/unipept/test_peptinfo.rb | 140 ------ test/commands/unipept/test_protinfo.rb | 45 -- test/commands/unipept/test_taxa2lca.rb | 69 --- test/commands/unipept/test_taxa2tree.rb | 68 --- test/commands/unipept/test_taxonomy.rb | 92 ---- test/helper.rb | 88 ---- test/support/api_stub.rb | 60 --- test/support/resources/pept2ec.json | 55 --- test/support/resources/pept2funct.json | 73 ---- test/support/resources/pept2go.json | 43 -- test/support/resources/pept2interpro.json | 43 -- test/support/resources/pept2lca.json | 14 - test/support/resources/pept2prot.json | 422 ------------------ test/support/resources/pept2taxa.json | 194 --------- test/support/resources/peptinfo.json | 70 --- test/support/resources/taxa2tree.json | 194 --------- test/support/resources/taxonomy.json | 22 - test/test_batch_iterator.rb | 87 ---- test/test_batch_order.rb | 57 --- test/test_configuration.rb | 43 -- test/test_formatters.rb | 240 ----------- test/test_output_writer.rb | 29 -- test/test_retryable_typhoeus.rb | 108 ----- test/test_server_message.rb | 92 ---- unipept.gemspec | 124 ------ 78 files changed, 6479 deletions(-) delete mode 100644 .devcontainer/devcontainer.json delete mode 100644 .document delete mode 100644 .rakeTasks delete mode 100644 .rubocop.yml delete mode 100644 .ruby-version delete mode 100644 .vscode/tasks.json delete mode 100644 Gemfile delete mode 100644 Gemfile.lock delete mode 100644 Rakefile delete mode 100755 bin/peptfilter delete mode 100755 bin/prot2pept delete mode 100755 bin/unipept delete mode 100755 bin/uniprot delete mode 100644 lib/batch_iterator.rb delete mode 100644 lib/batch_order.rb delete mode 100644 lib/commands.rb delete mode 100644 lib/commands/peptfilter.rb delete mode 100644 lib/commands/prot2pept.rb delete mode 100644 lib/commands/unipept.rb delete mode 100644 lib/commands/unipept/api_runner.rb delete mode 100644 lib/commands/unipept/config.rb delete mode 100644 lib/commands/unipept/pept2ec.rb delete mode 100644 lib/commands/unipept/pept2funct.rb delete mode 100644 lib/commands/unipept/pept2go.rb delete mode 100644 lib/commands/unipept/pept2interpro.rb delete mode 100644 lib/commands/unipept/pept2lca.rb delete mode 100644 lib/commands/unipept/pept2prot.rb delete mode 100644 lib/commands/unipept/pept2taxa.rb delete mode 100644 lib/commands/unipept/peptinfo.rb delete mode 100644 lib/commands/unipept/protinfo.rb delete mode 100644 lib/commands/unipept/taxa2lca.rb delete mode 100644 lib/commands/unipept/taxa2tree.rb delete mode 100644 lib/commands/unipept/taxonomy.rb delete mode 100644 lib/commands/uniprot.rb delete mode 100644 lib/configuration.rb delete mode 100644 lib/formatters.rb delete mode 100644 lib/output_writer.rb delete mode 100644 lib/retryable_typhoeus.rb delete mode 100644 lib/server_message.rb delete mode 100644 lib/version.rb delete mode 100644 test/commands/test_peptfilter.rb delete mode 100644 test/commands/test_prot2pept.rb delete mode 100644 test/commands/test_unipept.rb delete mode 100644 test/commands/test_uniprot.rb delete mode 100644 test/commands/unipept/test_api_runner.rb delete mode 100644 test/commands/unipept/test_config.rb delete mode 100644 test/commands/unipept/test_pept2ec.rb delete mode 100644 test/commands/unipept/test_pept2funct.rb delete mode 100644 test/commands/unipept/test_pept2go.rb delete mode 100644 test/commands/unipept/test_pept2interpro.rb delete mode 100644 test/commands/unipept/test_pept2lca.rb delete mode 100644 test/commands/unipept/test_pept2prot.rb delete mode 100644 test/commands/unipept/test_pept2taxa.rb delete mode 100644 test/commands/unipept/test_peptinfo.rb delete mode 100644 test/commands/unipept/test_protinfo.rb delete mode 100644 test/commands/unipept/test_taxa2lca.rb delete mode 100644 test/commands/unipept/test_taxa2tree.rb delete mode 100644 test/commands/unipept/test_taxonomy.rb delete mode 100644 test/helper.rb delete mode 100644 test/support/api_stub.rb delete mode 100644 test/support/resources/pept2ec.json delete mode 100644 test/support/resources/pept2funct.json delete mode 100644 test/support/resources/pept2go.json delete mode 100644 test/support/resources/pept2interpro.json delete mode 100644 test/support/resources/pept2lca.json delete mode 100644 test/support/resources/pept2prot.json delete mode 100644 test/support/resources/pept2taxa.json delete mode 100644 test/support/resources/peptinfo.json delete mode 100644 test/support/resources/taxa2tree.json delete mode 100644 test/support/resources/taxonomy.json delete mode 100644 test/test_batch_iterator.rb delete mode 100644 test/test_batch_order.rb delete mode 100644 test/test_configuration.rb delete mode 100644 test/test_formatters.rb delete mode 100644 test/test_output_writer.rb delete mode 100644 test/test_retryable_typhoeus.rb delete mode 100644 test/test_server_message.rb delete mode 100644 unipept.gemspec diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 158cb1dc..00000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,18 +0,0 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the -// README at: https://github.com/devcontainers/templates/tree/main/src/ruby -{ - "name": "Ruby", - // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/ruby:1-3.2-bookworm", - // Features to add to the dev container. More info: https://containers.dev/features. - // "features": {}, - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "ruby --version", - "postStartCommand": "bundle install" - // Configure tool-specific properties. - // "customizations": {}, - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" -} diff --git a/.document b/.document deleted file mode 100644 index 3d618dd8..00000000 --- a/.document +++ /dev/null @@ -1,5 +0,0 @@ -lib/**/*.rb -bin/* -- -features/**/*.feature -LICENSE.txt diff --git a/.rakeTasks b/.rakeTasks deleted file mode 100644 index e4870ce1..00000000 --- a/.rakeTasks +++ /dev/null @@ -1,7 +0,0 @@ - - diff --git a/.rubocop.yml b/.rubocop.yml deleted file mode 100644 index 02bc2c2a..00000000 --- a/.rubocop.yml +++ /dev/null @@ -1,43 +0,0 @@ -AllCops: - TargetRubyVersion: 2.7 - NewCops: enable - Exclude: - - 'unipept.gemspec' - - 'vendor/**/*' - - 'Rakefile' - -Style/ClassAndModuleChildren: - EnforcedStyle: compact - Enabled: false -Style/FrozenStringLiteralComment: - Enabled: false - -# disable for now -Lint/NestedMethodDefinition: - Enabled: false -Lint/Lint/RedundantStringCoercion: - Enabled: false -Style/ClassVars: - Enabled: false -Style/Documentation: - Enabled: false -Style/RescueModifier: - Enabled: false -Metrics/AbcSize: - Enabled: false -Metrics/ClassLength: - Enabled: false -Layout/LineLength: - Enabled: false -Metrics/MethodLength: - Enabled: false -Metrics/BlockLength: - Enabled: false -Metrics/CyclomaticComplexity: - Enabled: false -Metrics/PerceivedComplexity: - Enabled: false -Naming/HeredocDelimiterNaming: - Enabled: false -Naming/MethodParameterName: - Enabled: false diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 4a36342f..00000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -3.0.0 diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index ee038b55..00000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "type": "rake", - "task": "test_style:autocorrect_all", - "group": "test", - "problemMatcher": [], - "label": "rake: test_style:autocorrect_all" - }, - { - "type": "rake", - "task": "test", - "group": { - "kind": "test", - "isDefault": true - }, - "problemMatcher": [], - "label": "rake: test" - }, - { - "type": "shell", - "command": "bundle exec rake gemspec", - "label": "generate gemspec", - "problemMatcher": [] - }, - { - "type": "shell", - "command": "BRANCH=main bundle exec rake release", - "label": "release gem", - "problemMatcher": [] - } - ] -} diff --git a/Gemfile b/Gemfile deleted file mode 100644 index b9a8aa44..00000000 --- a/Gemfile +++ /dev/null @@ -1,18 +0,0 @@ -source 'http://rubygems.org' -# Add dependencies required to use your gem here. - -gem 'cri', '~>2.15.10' -gem 'typhoeus', '~> 1.4.0' - -# this is not explicitly used, but an older incompatible version comes bundled with ruby -gem 'psych', '~> 5.1.0' - -group :development do - gem 'minitest', '~> 5.18' - gem 'rake', '~> 13.0.1' - gem 'rubocop', '~>1.50' -end - -group :release do - gem 'juwelier' -end diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 85dda46b..00000000 --- a/Gemfile.lock +++ /dev/null @@ -1,109 +0,0 @@ -GEM - remote: http://rubygems.org/ - specs: - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) - ast (2.4.2) - builder (3.2.4) - cri (2.15.11) - descendants_tracker (0.0.4) - thread_safe (~> 0.3, >= 0.3.1) - ethon (0.16.0) - ffi (>= 1.15.0) - faraday (1.3.0) - faraday-net_http (~> 1.0) - multipart-post (>= 1.2, < 3) - ruby2_keywords - faraday-net_http (1.0.1) - ffi (1.15.5) - git (1.18.0) - addressable (~> 2.8) - rchardet (~> 1.8) - github_api (0.19.0) - addressable (~> 2.4) - descendants_tracker (~> 0.0.4) - faraday (>= 0.8, < 2) - hashie (~> 3.5, >= 3.5.2) - oauth2 (~> 1.0) - hashie (3.6.0) - highline (2.0.3) - json (2.6.3) - juwelier (2.4.9) - builder - bundler - git - github_api - highline - kamelcase (~> 0) - nokogiri - psych - rake - rdoc - semver2 - jwt (2.2.2) - kamelcase (0.0.2) - semver2 (~> 3) - mini_portile2 (2.8.0) - minitest (5.18.1) - multi_json (1.15.0) - multi_xml (0.6.0) - multipart-post (2.1.1) - nokogiri (1.13.10) - mini_portile2 (~> 2.8.0) - racc (~> 1.4) - oauth2 (1.4.11) - faraday (>= 0.17.3, < 3.0) - jwt (>= 1.0, < 3.0) - multi_json (~> 1.3) - multi_xml (~> 0.5) - rack (>= 1.2, < 4) - parallel (1.23.0) - parser (3.2.2.1) - ast (~> 2.4.1) - psych (5.1.0) - stringio - public_suffix (4.0.6) - racc (1.6.1) - rack (3.0.7) - rainbow (3.1.1) - rake (13.0.6) - rchardet (1.8.0) - rdoc (6.4.0) - psych (>= 4.0.0) - regexp_parser (2.8.0) - rexml (3.2.5) - rubocop (1.50.2) - json (~> 2.3) - parallel (~> 1.10) - parser (>= 3.2.0.0) - rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.28.0, < 2.0) - ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.28.0) - parser (>= 3.2.1.0) - ruby-progressbar (1.13.0) - ruby2_keywords (0.0.4) - semver2 (3.4.2) - stringio (3.0.6) - thread_safe (0.3.6) - typhoeus (1.4.0) - ethon (>= 0.9.0) - unicode-display_width (2.4.2) - -PLATFORMS - ruby - -DEPENDENCIES - cri (~> 2.15.10) - juwelier - minitest (~> 5.18) - psych (~> 5.1.0) - rake (~> 13.0.1) - rubocop (~> 1.50) - typhoeus (~> 1.4.0) - -BUNDLED WITH - 2.4.12 diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 35780f89..00000000 --- a/Rakefile +++ /dev/null @@ -1,58 +0,0 @@ -require 'rubygems' -require 'bundler' -begin - Bundler.setup(:default, :development) -rescue Bundler::BundlerError => e - warn e.message - warn 'Run `bundle install` to install missing gems' - exit e.status_code -end -require 'rake' -require 'rake/testtask' -require 'rubocop/rake_task' -begin - require 'juwelier' - Juwelier::Tasks.new do |gem| - # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options - gem.name = 'unipept' - gem.executables = %w[unipept prot2pept peptfilter uniprot] - gem.homepage = 'http://unipept.ugent.be' - gem.license = 'MIT' - gem.summary = 'Command line interface to Unipept web services.' - gem.description = <<-EOS - Command line interface to the Unipept (http://unipept.ugent.be) web services - (pept2lca, taxa2lca, pept2taxa, pept2prot and taxonomy) and some utility - commands for handling proteins using the command line. - EOS - gem.email = 'unipept@ugent.be' - gem.authors = ['Bart Mesuere', 'Pieter Verschaffelt', 'Tibo Vande Moortele', 'Toon Willems', 'Tom Naessens'] - gem.required_ruby_version = '>= 2.7.0' - end - Juwelier::RubygemsDotOrgTasks.new -rescue LoadError - # do nothing -end - -task :test_unit do - require './test/helper.rb' - - FileList['./test/**/test_*.rb', './test/**/*_spec.rb'].each do |fn| - require fn - end -end - -RuboCop::RakeTask.new(:test_style) - -task test: %i[test_unit] - -task default: :test - -require 'rdoc/task' -Rake::RDocTask.new do |rdoc| - version = File.exist?('VERSION') ? File.read('VERSION') : '' - - rdoc.rdoc_dir = 'rdoc' - rdoc.title = "unipept #{version}" - rdoc.rdoc_files.include('README*') - rdoc.rdoc_files.include('lib/**/*.rb') -end diff --git a/bin/peptfilter b/bin/peptfilter deleted file mode 100755 index 1d98950a..00000000 --- a/bin/peptfilter +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env ruby -require_relative '../lib/commands' - -Signal.trap('PIPE', 'EXIT') if Signal.list.include? 'PIPE' -Signal.trap('INT', 'EXIT') if Signal.list.include? 'INT' - -Unipept::Commands::Peptfilter.run(ARGV) diff --git a/bin/prot2pept b/bin/prot2pept deleted file mode 100755 index 23d24bb4..00000000 --- a/bin/prot2pept +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env ruby -require_relative '../lib/commands' - -Signal.trap('PIPE', 'EXIT') if Signal.list.include? 'PIPE' -Signal.trap('INT', 'EXIT') if Signal.list.include? 'INT' - -Unipept::Commands::Prot2pept.run(ARGV) diff --git a/bin/unipept b/bin/unipept deleted file mode 100755 index 42d7f3c7..00000000 --- a/bin/unipept +++ /dev/null @@ -1,8 +0,0 @@ -#!usr/bin/env ruby -require_relative '../lib/commands' - -# Prevent broken pipe errors -Signal.trap('PIPE', 'EXIT') if Signal.list.include? 'PIPE' -Signal.trap('INT', 'EXIT') if Signal.list.include? 'INT' - -Unipept::Commands::Unipept.run(ARGV) diff --git a/bin/uniprot b/bin/uniprot deleted file mode 100755 index e687cd21..00000000 --- a/bin/uniprot +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env ruby -require_relative '../lib/commands' - -Signal.trap('PIPE', 'EXIT') if Signal.list.include? 'PIPE' -Signal.trap('INT', 'EXIT') if Signal.list.include? 'INT' - -Unipept::Commands::Uniprot.run(ARGV) diff --git a/lib/batch_iterator.rb b/lib/batch_iterator.rb deleted file mode 100644 index ee6b3da3..00000000 --- a/lib/batch_iterator.rb +++ /dev/null @@ -1,89 +0,0 @@ -require 'set' - -module Unipept - class BatchIterator - attr_reader :batch_size - - def initialize(batch_size) - @batch_size = batch_size - end - - # Splits the input lines into slices, based on the batch_size of the current - # command. Executes the given block for each of the batches. - # - # Supports both normal input and input in the fasta format. - # - # @input [Iterator] lines An iterator containing the input lines - # - # @input [lambda] block The code to execute on the slices - def iterate(lines, &block) - first_line = lines.next rescue return - if fasta? first_line - fasta_iterator(first_line, lines, &block) - elsif csv_taxa2tree? first_line - csv_taxa_iterator(first_line, lines, &block) - else - normal_iterator(first_line, lines, &block) - end - end - - # Checks if the geven line is a fasta header. - # - # @param [String] line The input line - # - # @return [Boolean] Whether te input is a fasta header - def fasta?(line) - line.start_with? '>' - end - - def csv_taxa2tree?(line) - line.include? 'taxon_id' - end - - private - - # Splits the input lines in fasta format into slices, based on the - # batch_size of the current command. Executes the given block for each of - # the batches. - def fasta_iterator(first_line, next_lines) - current_fasta_header = first_line.chomp - next_lines.each_slice(batch_size).with_index do |slice, i| - fasta_mapper = [] - input_set = Set.new - - slice.each do |line| - line = line.chomp - if fasta? line - current_fasta_header = line - else - fasta_mapper << [current_fasta_header, line] - input_set << line - end - end - - yield(input_set.to_a, i, fasta_mapper) - end - end - - # Splits the input lines into slices, based on the batch_size of the current - # command. Executes the given block for each of the batches. - def normal_iterator(first_line, next_lines, &block) - Enumerator.new do |y| - y << first_line - loop do - y << next_lines.next - end - end.each_slice(batch_size).with_index(&block) - end - - def csv_taxa_iterator(first_line, next_lines, &block) - # Find index of taxon_id in the first_line and only parse this part from the next lines - taxon_idx = first_line.rstrip.split(',').find_index('taxon_id') - Enumerator.new do |y| - loop do - y << next_lines.next.rstrip.split(',')[taxon_idx] - end - end.each_slice(batch_size).with_index(&block) - end - end -end diff --git a/lib/batch_order.rb b/lib/batch_order.rb deleted file mode 100644 index 1e84da2b..00000000 --- a/lib/batch_order.rb +++ /dev/null @@ -1,21 +0,0 @@ -module Unipept - class BatchOrder - attr_reader :order - - def initialize - @order = {} - @current = 0 - end - - # Executes block if it's its turn, queues the block in the other case. - def wait(i, &block) - @order[i] = block - return unless i == @current - - while order[@current] - order.delete(@current).call - @current += 1 - end - end - end -end diff --git a/lib/commands.rb b/lib/commands.rb deleted file mode 100644 index bf87a4b6..00000000 --- a/lib/commands.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'cri' - -module Unipept - module Commands - require_relative 'commands/peptfilter' - require_relative 'commands/prot2pept' - require_relative 'commands/uniprot' - require_relative 'commands/unipept' - end -end diff --git a/lib/commands/peptfilter.rb b/lib/commands/peptfilter.rb deleted file mode 100644 index 99326f3d..00000000 --- a/lib/commands/peptfilter.rb +++ /dev/null @@ -1,117 +0,0 @@ -module Unipept::Commands - class Peptfilter - attr_reader :root_command - - @root_command = Cri::Command.define do - name 'peptfilter' - summary 'Filter peptides based on specific criteria.' - usage 'peptfilter [options]' - description <<-EOS - The peptfilter command filters a list of peptides according to specific criteria. The command expects a list of peptides that are passed to standard input. - - The input should have one peptide per line. FASTA headers are preserved in the output, so that peptides remain bundled. - EOS - # flag :u, :unique, "filter duplicate peptides." - required nil, :minlen, 'only retain tryptic peptides that have at least min (default: 5) amino acids.' - required nil, :maxlen, 'only retain tryptic peptides that have at most max (default: 50) amino acids.' - required :l, :lacks, 'only retain tryptic peptides that lack all amino acids from the string of residues.' - required :c, :contains, 'only retain tryptic peptides that contain all amino acids from the string of residues.' - flag :h, :help, 'show help for this command' do |_value, cmd| - puts cmd.help - exit 0 - end - run do |opts, args, _cmd| - abort "error: peptfilter doesn't support input as arguments. Use standard input instead." unless args.empty? - minlen = opts.fetch(:minlen, '5').to_i - maxlen = opts.fetch(:maxlen, '50').to_i - lacks = opts.fetch(:lacks, '').chars.to_a - contains = opts.fetch(:contains, '').chars.to_a - $stdin.each_line do |pept| - # FASTA headers - if pept.start_with? '>' - puts pept - next - end - - pept = pept.chomp - puts pept if Peptfilter.filter(pept, minlen, maxlen, lacks, contains) - end - end - end - - # Invokes the peptfilter command-line tool with the given arguments. - # - # @param [Array] args An array of command-line arguments - # - # @return [void] - def self.run(args) - @root_command.run(args) - end - - # Checks if a peptide satisfies the min length, max length, lacks and contains requirements. - # Returns true if - # - the peptide length is equal or higher than min - # - the peptide length is equal or lower than max - # - the peptide doesn't contain any of the amino acids in lacks - # - the peptide contains all of the amino acids in contains - # - # @param [String] peptide The peptide to check - # - # @param [Integer] min The minimal length requirement - # - # @param [Integer] max The maximal length requirement - # - # @param [Array] lacks The forbidden amino acids - # - # @param [Array] contains The required amino acids - # - # @return [Boolean] true if the peptide satisfies all requirements - def self.filter(peptide, min, max, lacks, contains) - filter_length(peptide, min, max) && - filter_lacks(peptide, lacks) && - filter_contains(peptide, contains) - end - - # Checks if a peptide satisfies the min length and max length requirements. - # Returns true if - # - the peptide length is equal or higher than min - # - the peptide length is equal or lower than max - # - # @param [String] peptide The peptide to check - # - # @param [Integer] min The minimal length requirement - # - # @param [Integer] max The maximal length requirement - # - # @return [Boolean] true if the peptide satisfies all requirements - def self.filter_length(peptide, min, max) - peptide.length >= min && peptide.length <= max - end - - # Checks if a peptide satisfies lacks requirement. - # Returns true if - # - the peptide doesn't contain any of the amino acids in lacks - # - # @param [String] peptide The peptide to check - # - # @param [Array] lacks The forbidden amino acids - # - # @return [Boolean] true if the peptide satisfies all requirements - def self.filter_lacks(peptide, lacks) - (peptide.chars.to_a & lacks).empty? - end - - # Checks if a peptide satisfies the contains requirement. - # Returns true if - # - the peptide contains all of the amino acids in contains - # - # @param [String] peptide The peptide to check - # - # @param [Array] contains The required amino acids - # - # @return [Boolean] true if the peptide satisfies all requirements - def self.filter_contains(peptide, contains) - (peptide.chars.to_a & contains).size == contains.size - end - end -end diff --git a/lib/commands/prot2pept.rb b/lib/commands/prot2pept.rb deleted file mode 100644 index 974aad99..00000000 --- a/lib/commands/prot2pept.rb +++ /dev/null @@ -1,61 +0,0 @@ -module Unipept::Commands - class Prot2pept - attr_reader :root_command, :valid_formats - - @root_command = Cri::Command.define do - name 'prot2pept' - summary 'Split protein sequences into peptides.' - usage 'prot2pept [options]' - description <<-EOS - The prot2pept command splits each protein sequence into a list of peptides according to a given cleavage-pattern. The command expects a list of protein sequences that are passed to standard input. - - The input should have either one protein sequence per line or contain a FASTA formatted list of protein sequences. FASTA headers are preserved in the output, so that peptides can be bundled per protein sequence. - - EOS - required :p, :pattern, 'specify cleavage-pattern (regex) as the pattern after which the next peptide will be cleaved (default: ([KR])([^P]) for tryptic peptides).' - flag :h, :help, 'show help for this command' do |_value, cmd| - puts cmd.help - exit 0 - end - run do |opts, _args, _cmd| - pattern = opts.fetch(:pattern, '([KR])([^P])') - pattern = Regexp.compile(pattern) - - # decide if we have FASTA input - first_char = $stdin.getc - $stdin.ungetc(first_char) - if first_char == '>' - # fasta mode! - protein = '' - while (line = $stdin.gets) - if line.start_with? '>' - puts Prot2pept.split(protein, pattern) - protein = '' - puts line - else - protein << line.chomp - end - end - puts Prot2pept.split(protein, pattern) - else - $stdin.each_line do |prot| - puts Prot2pept.split(prot, pattern) - end - end - end - end - - def self.split(protein, pattern) - protein.tr('*', "\n").gsub(pattern, "\\1\n\\2").gsub(pattern, "\\1\n\\2").split("\n").reject(&:empty?) - end - - # Invokes the uniprot command-line tool with the given arguments. - # - # @param [Array] args An array of command-line arguments - # - # @return [void] - def self.run(args) - @root_command.run(args) - end - end -end diff --git a/lib/commands/unipept.rb b/lib/commands/unipept.rb deleted file mode 100644 index ebc199bc..00000000 --- a/lib/commands/unipept.rb +++ /dev/null @@ -1,400 +0,0 @@ -require 'typhoeus' - -require_relative '../batch_order' -require_relative '../batch_iterator' -require_relative '../configuration' -require_relative '../formatters' -require_relative '../output_writer' -require_relative '../server_message' -require_relative '../version' - -require_relative 'unipept/config' -require_relative 'unipept/pept2ec' -require_relative 'unipept/pept2funct' -require_relative 'unipept/pept2go' -require_relative 'unipept/pept2interpro' -require_relative 'unipept/pept2lca' -require_relative 'unipept/pept2prot' -require_relative 'unipept/pept2taxa' -require_relative 'unipept/peptinfo' -require_relative 'unipept/protinfo' -require_relative 'unipept/taxa2lca' -require_relative 'unipept/taxonomy' -require_relative 'unipept/taxa2tree' - -module Unipept - class Commands::Unipept - def initialize - @root_command = create_root_command - add_config_command - add_pept2taxa_command - add_pept2ec_command - add_pept2funct_command - add_pept2go_command - add_pept2interpro_command - add_pept2lca_command - add_peptinfo_command - add_protinfo_command - add_taxa2lca_command - add_pept2prot_command - add_taxonomy_command - add_taxa2tree_command - end - - def run(args) - @root_command.run(args) - end - - def create_root_command - Cri::Command.new_basic_root.modify do - name 'unipept' - summary 'Command line interface to Unipept web services.' - usage 'unipept subcommand [options]' - description <<-EOS - The unipept subcommands are command line wrappers around the Unipept web services. - - Subcommands that start with pept expect a list of tryptic peptides as input. Subcommands that start with tax expect a list of NCBI Taxonomy Identifiers as input. Input is passed - - - as separate command line arguments - - - in a text file that is passed as an argument to the -i option - - - to standard input - - The command will give priority to the first way the input is passed, in the order as listed above. Text files and standard input should have one tryptic peptide or one NCBI Taxonomy Identifier per line. - EOS - flag :v, :version, 'displays the version' - flag :q, :quiet, 'disable service messages' - flag nil, :'no-header', 'disable header in csv output', hidden: true - option :i, :input, 'read input from file', argument: :required - option nil, :batch, 'specify the batch size', argument: :required, hidden: true - option nil, :parallel, 'specify the number of parallel requests', argument: :required, hidden: true - option :o, :output, 'write output to file', argument: :required - option :f, :format, "define the output format (available: #{Unipept::Formatter.available.select { |f| f != 'html' && f != 'url' }.join(', ')}) (default: #{Unipept::Formatter.default}).", argument: :required - - # Configuration options - option nil, 'host', 'specify the server running the Unipept web service', argument: :required - - run do |opts, _args, cmd| - if opts[:version] - puts Unipept::VERSION - else - abort cmd.help - end - end - end - end - - def add_config_command - @root_command.define_command('config') do - summary 'Set configuration options.' - usage 'config option [value]' - description <<-EOS - Sets or shows the value for configuration options. All settings are stored in the .unipeptrc file in the home directory of the user. - - Running the command with a value will set that value for the given option, running it without will show the current value. - - These options are currently supported: - - - host: Set the default host for api calls. - - Example: "unipept config host http://api.unipept.ugent.be" will set the default host to the public unipept server. - EOS - - runner Commands::Config - end - end - - def add_pept2taxa_command - @root_command.define_command('pept2taxa') do - usage 'pept2taxa [options]' - summary 'Fetch taxa of UniProt entries that match tryptic peptides.' - description <<-EOS - For each tryptic peptide the unipept pept2taxa command retrieves from Unipept the set of taxa from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The command expects a list of tryptic peptides that are passed - - - as separate command line arguments - - - in a text file that is passed as an argument to the -i option - - - to standard input - - The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line. - EOS - - flag :e, :equate, 'equate isoleucine (I) and leucine (L) when matching peptides' - flag :a, :all, 'report all information fields of NCBI Taxonomy records available in Unipept. Note that this may have a performance penalty.' - option :s, :select, 'select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.', argument: :required, multiple: true - - runner Commands::Pept2taxa - end - end - - def add_pept2ec_command - @root_command.define_command('pept2ec') do - usage 'pept2ec[options]' - summary 'Fetch EC numbers of UniProt entries that match tryptic peptides.' - description <<-EOS - For each tryptic peptide the unipept pept2ec command retrieves from Unipept the set of EC numbers from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The command expects a list of tryptic peptides that are passed - - - as separate command line arguments - - - in a text file that is passed as an argument to the -i option - - - to standard input - - The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line. - EOS - - flag :e, :equate, 'equate isoleucine (I) and leucine (L) when matching peptides' - flag :a, :all, 'Also return the names of the EC numbers. Note that this may have a performance penalty.' - option :s, :select, 'select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.', argument: :required, multiple: true - - runner Commands::Pept2ec - end - end - - def add_pept2funct_command - @root_command.define_command('pept2funct') do - usage 'pept2funct[options]' - summary 'Fetch EC numbers, GO terms and InterPro codes of UniProt entries that match tryptic peptides.' - description <<-EOS - For each tryptic peptide the unipept pept2funct command retrieves from Unipept the set of EC numbers and GO terms from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The command expects a list of tryptic peptides that are passed - - - as separate command line arguments - - - in a text file that is passed as an argument to the -i option - - - to standard input - - The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line. - EOS - - flag :e, :equate, 'equate isoleucine (I) and leucine (L) when matching peptides' - flag :a, :all, 'Also return the names of the EC numbers, GO terms and InterPro codes. Note that this may have a performance penalty.' - option :s, :select, 'select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.', argument: :required, multiple: true - - runner Commands::Pept2funct - end - end - - def add_pept2go_command - @root_command.define_command('pept2go') do - usage 'pept2go [options]' - summary 'Fetch GO terms of UniProt entries that match tryptic peptides.' - description <<-EOS - For each tryptic peptide the unipept pept2go command retrieves from Unipept the set of GO terms from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The command expects a list of tryptic peptides that are passed - - - as separate command line arguments - - - in a text file that is passed as an argument to the -i option - - - to standard input - - The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line. - EOS - - flag :e, :equate, 'equate isoleucine (I) and leucine (L) when matching peptides' - flag :a, :all, 'Also return the names of the GO terms. Note that this may have a performance penalty.' - option :s, :select, 'select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.', argument: :required, multiple: true - - runner Commands::Pept2go - end - end - - def add_pept2interpro_command - @root_command.define_command('pept2interpro') do - usage 'pept2interpro [options]' - summary 'Fetch InterPro entries of UniProt entries that match tryptic peptides.' - description <<-EOS - For each tryptic peptide the unipept pept2interpro command retrieves from Unipept the set of InterPro entries from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The command expects a list of tryptic peptides that are passed - - - as separate command line arguments - - - in a text file that is passed as an argument to the -i option - - - to standard input - - The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line. - EOS - - flag :e, :equate, 'equate isoleucine (I) and leucine (L) when matching peptides' - flag :a, :all, 'Also return the names and types of the InterPro entries. Note that this may have a performance penalty.' - option :s, :select, 'select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.', argument: :required, multiple: true - - runner Commands::Pept2interpro - end - end - - def add_pept2lca_command - @root_command.define_command('pept2lca') do - usage 'pept2lca [options]' - summary 'Fetch taxonomic lowest common ancestor of UniProt entries that match tryptic peptides.' - description <<-EOS - For each tryptic peptide the unipept pept2lca command retrieves from Unipept the lowest common ancestor of the set of taxa from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The lowest common ancestor is based on the topology of the Unipept Taxonomy -- a cleaned up version of the NCBI Taxonomy -- and is itself a record from the NCBI Taxonomy. The command expects a list of tryptic peptides that are passed - - - as separate command line arguments - - - in a text file that is passed as an argument to the -i option - - - to standard input - - The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line. - EOS - - flag :e, :equate, 'equate isoleucine (I) and leucine (L) when matching peptides' - flag :a, :all, 'report all information fields of NCBI Taxonomy records available in Unipept. Note that this may have a performance penalty.' - option :s, :select, 'select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.', argument: :required, multiple: true - - runner Commands::Pept2lca - end - end - - def add_peptinfo_command - @root_command.define_command('peptinfo') do - usage 'peptinfo [options]' - summary 'Fetch functional information and the taxonomic lowest common ancestor of UniProt entries that match tryptic peptides.' - description <<-EOS - For each tryptic peptide the unipept peptinfo command retrieves from Unipept the functional information and the lowest common ancestor of the set of taxa from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The lowest common ancestor is based on the topology of the Unipept Taxonomy -- a cleaned up version of the NCBI Taxonomy -- and is itself a record from the NCBI Taxonomy. The command expects a list of tryptic peptides that are passed - - - as separate command line arguments - - - in a text file that is passed as an argument to the -i option - - - to standard input - - The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line. - EOS - - flag :e, :equate, 'equate isoleucine (I) and leucine (L) when matching peptides' - flag :a, :all, 'report the names of the functional annotations and all information fields of NCBI Taxonomy records available in Unipept. Note that this may have a performance penalty.' - option :s, :select, 'select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.', argument: :required, multiple: true - - runner Commands::Peptinfo - end - end - - def add_protinfo_command - @root_command.define_command('protinfo') do - usage 'protinfo [options]' - summary 'Fetch functional and taxonomic information of UniProt ids' - description <<-EOS - For each UniProt id the unipept protinfo command retrieves from Unipept the functional information and the NCBI id. The command expects a list of UniProt ids that are passed - - - as separate command line arguments - - - in a text file that is passed as an argument to the -i option - - - to standard input - - The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line. - EOS - - option :s, :select, 'select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.', argument: :required, multiple: true - - runner Commands::Protinfo - end - end - - def add_taxa2lca_command - @root_command.define_command('taxa2lca') do - usage 'taxa2lca [options]' - summary 'Compute taxonomic lowest common ancestor for given list of taxa.' - description <<-EOS - The unipept taxa2lca command computes the lowest common ancestor of a given list of NCBI Taxonomy Identifiers. The lowest common ancestor is based on the topology of the Unipept Taxonomy -- a cleaned up version of the NCBI Taxonomy -- and is itself a record from the NCBI Taxonomy. The command expects a list of NCBI Taxonomy Identifiers that are passed - - - as separate command line arguments - - - in a text file that is passed as an argument to the -i option - - - to standard input - - The command will give priority to the first way NCBI Taxonomy Identifiers are passed, in the order as listed above. Text files and standard input should have one NCBI Taxonomy Identifier per line. - EOS - - flag :a, :all, 'report all information fields of NCBI Taxonomy records available in Unipept. Note that this may have a performance penalty.' - option :s, :select, 'select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.', argument: :required, multiple: true - - runner Commands::Taxa2lca - end - end - - def add_taxa2tree_command - @root_command.define_command('taxa2tree') do - usage 'taxa2tree [options]' - summary 'Compute lineage tree for given list of taxa' - description <<-EOS - The unipept taxa2tree command computes a lineage tree of a given list of NCBI Taxonomy Identifiers. A frequency table is computed for the given list of taxa. Secondly, the lineages for all taxa are looked up. These are then used to build a lineage tree with all counts set. The command expects a list of NCBI Taxonomy Identifiers that are passed - - - as separate command line arguments - - - in a text file that is passed as an argument to the -i option - - - to standard input - - The command will give priority to the first way NCBI Taxonomy Identifiers are passed, in the order as listed above. Text files and standard input should have one NCBI Taxonomy Identifier per line. - EOS - - option :f, :format, "define the output format (available: json, url, html) (default: 'json'). Note that xml and csv are not available for taxa2tree. html and url are used as an output format for visualizations.", argument: :required - - runner Commands::Taxa2Tree - end - end - - def add_pept2prot_command - @root_command.define_command('pept2prot') do - usage 'pept2prot [options]' - summary 'Fetch UniProt entries that match tryptic peptides.' - description <<-EOS - For each tryptic peptide the unipept pept2prot command retrieves from Unipept all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The command expects a list of tryptic peptides that are passed - - - as separate command line arguments - - - in a text file that is passed as an argument to the -i option - - - to standard input - - The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line. - EOS - - flag :e, :equate, 'equate isoleucine (I) and leucine (L) when matching peptides' - flag :a, :all, 'report all information fields of UniProt entries available in Unipept. Note that this may have a performance penalty.' - option :s, :select, 'select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.', argument: :required, multiple: true - option nil, :meganize, 'output the results in a BlastTab-like format that MEGAN can understand' - - runner Commands::Pept2prot - end - end - - def add_taxonomy_command - @root_command.define_command('taxonomy') do - usage 'taxonomy [options]' - summary 'Fetch taxonomic information from Unipept Taxonomy.' - description <<-EOS - The unipept taxonomy command yields information from the Unipept Taxonomy records for a given list of NCBI Taxonomy Identifiers. The Unipept Taxonomy is a cleaned up version of the NCBI Taxonomy, and its records are also records of the NCBI Taxonomy. The command expects a list of NCBI Taxonomy Identifiers that are passed - - - as separate command line arguments - - - in a text file that is passed as an argument to the -i option - - - to standard input - - The command will give priority to the first way NCBI Taxonomy Identifiers are passed, in the order as listed above. Text files and standard input should have one NCBI Taxonomy Identifier per line. - EOS - - flag :a, :all, 'report all information fields of NCBI Taxonomy records available in Unipept. Note that this may have a performance penalty.' - option :s, :select, 'select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.', argument: :required, multiple: true - - runner Commands::Taxonomy - end - end - - # Invokes the unipept command-line tool with the given arguments. - # - # @param [Array] args An array of command-line arguments - # - # @return [void] - def self.run(args) - new.run(args) - end - end -end diff --git a/lib/commands/unipept/api_runner.rb b/lib/commands/unipept/api_runner.rb deleted file mode 100644 index e2b15a04..00000000 --- a/lib/commands/unipept/api_runner.rb +++ /dev/null @@ -1,265 +0,0 @@ -require_relative '../../retryable_typhoeus' - -module Unipept - class Commands::ApiRunner < Cri::CommandRunner - attr_reader :configuration, :url, :user_agent - - def initialize(args, opts, cmd) - super - @configuration = Unipept::Configuration.new - - @host = host - @user_agent = "Unipept CLI - unipept #{Unipept::VERSION}" - @url = "#{@host}/api/v2/#{cmd.name}.json" - @fasta = false - end - - # Returns the host. If a value is defined by both an option and the config - # file, the value of the option is used. - def host - # find host in opts first - host = options[:host] || @configuration['host'] - host = 'http://api.unipept.ugent.be' if host.nil? || host.empty? - - # add http:// if needed - if host.start_with?('http://', 'https://') - host - else - "http://#{host}" - end - end - - # Returns an input iterator to use for the request. - # - if arguments are given, uses arguments - # - if the input file option is given, uses file input - # - if none of the previous are given, uses stdin - def input_iterator - return arguments.each unless arguments.empty? - return File.foreach(options[:input]) if options[:input] - - $stdin.each_line - end - - def output_writer - @output_writer ||= OutputWriter.new(options[:output]) - end - - # Returns the default default_batch_size of a command. - def default_batch_size - raise NotImplementedError, 'This must be implemented in a subclass.' - end - - # returns the effective batch_size of a command - def batch_size - if options[:batch] - options[:batch].to_i - else - default_batch_size - end - end - - # returns the required fields to do any mapping - def required_fields - [] - end - - # Returns a new batch_iterator based on the batch_size - def batch_iterator - Unipept::BatchIterator.new(batch_size) - end - - def concurrent_requests - if options[:parallel] - options[:parallel].to_i - else - 10 - end - end - - def queue_size - concurrent_requests * 20 - end - - # Returns an array of regular expressions containing all the selected fields - def selected_fields - return @selected_fields unless @selected_fields.nil? - - fields = [*options[:select]].map { |f| f.split(',') }.flatten - fields.concat(required_fields) if @fasta && !fields.empty? - @selected_fields = fields.map { |f| glob_to_regex(f) } - end - - # Returns a formatter, based on the format specified in the options - def formatter - @formatter ||= Unipept::Formatter.new_for_format(options[:format]) - end - - # Constructs a request body (a Hash) for set of input strings, using the - # options supplied by the user. - def construct_request_body(input) - names = selected_fields.empty? || selected_fields.any? { |f| f.to_s.include?('name') || f.to_s.include?('.*$') } - { input: input, - equate_il: options[:equate] == true, - extra: options[:all] == true, - names: options[:all] == true && names } - end - - # Runs the command - def run - ServerMessage.new(@host).print unless options[:quiet] - hydra = Typhoeus::Hydra.new(max_concurrency: concurrent_requests) - batch_order = Unipept::BatchOrder.new - last_id = 0 - - # puts input_iterator.inspect - - batch_iterator.iterate(input_iterator) do |input_slice, batch_id, fasta_mapper| - last_id = batch_id - @fasta = !fasta_mapper.nil? - request = ::RetryableTyphoeus::Request.new( - @url, - method: :post, - body: construct_request_body(input_slice), - accept_encoding: 'gzip', - followlocation: true, - postredir: :post_all, - headers: { 'User-Agent' => @user_agent } - ) - - request.on_complete do |resp| - block = handle_response(resp, batch_id, fasta_mapper) - batch_order.wait(batch_id, &block) - end - - hydra.queue request - hydra.run if (batch_id % queue_size).zero? - end - - hydra.run - batch_order.wait(last_id + 1) { output_writer.write_line formatter.footer } - end - - # Saves an error to a new file in the .unipept directory in the users home - # directory. - def save_error(message) - path = error_file_path - FileUtils.mkdir_p File.dirname(path) - File.write(path, message) - warn "API request failed! log can be found in #{path}" - end - - protected - - def error_file_path - File.expand_path(File.join(Dir.home, '.unipept', "unipept-#{Time.now.strftime('%F-%T')}.log")) - end - - # Handles the response of an API request. - # Returns a block to execute. - def handle_response(response, batch_id, fasta_mapper) - if response.success? - handle_success_response(response, batch_id, fasta_mapper) - else - handle_failed_response(response) - end - end - - def handle_success_response(response, batch_id, fasta_mapper) - result = filter_result(response.response_body) - - lambda do - unless result.empty? - output_writer.write_line formatter.header(result, fasta_mapper) if batch_id.zero? && !options[:'no-header'] - output_writer.write_line formatter.format(result, fasta_mapper, batch_id.zero?) - end - end - end - - def handle_failed_response(response) - if response.timed_out? - -> { save_error('request timed out, continuing anyway, but results might be incomplete') } - elsif response.code.zero? - -> { save_error("could not get an http response, continuing anyway, but results might be incomplete#{response.return_message}") } - else - -> { save_error("Got #{response.code}: #{response.response_body}\nRequest headers: #{response.request.options}\nRequest body:\n#{response.request.encoded_body}\n\n") } - end - end - - # Parses the json_response, wraps it in an array if needed and filters the - # fields based on the selected_fields - def filter_result(json_response) - result = JSON[json_response] rescue [] - result = [result] unless result.is_a? Array - key_order = result.first.keys if result.first - result = flatten_functional_fields(result) if formatter.instance_of?(Unipept::CSVFormatter) - result.map! { |r| r.select! { |k, _v| selected_fields.any? { |f| f.match k } } } unless selected_fields.empty? - result = inflate_functional_fields(result, key_order) if formatter.instance_of?(Unipept::CSVFormatter) && result.first - result - end - - # Transforms the hierarchical input to something without hierarchy. All fields - # associated with functional annotations are transformed to a flat alternative. - # Example: {"go" => {"go_term": xxx, "protein_count": yyy}} --> {"go_term" => [xxx], "protein_count" => [yyy]} - def flatten_functional_fields(data) - output = [] - data.each do |row| - output_row = {} - row.each do |k, v| - if %w[ec go ipr].include? k - v.each do |item| - item.each do |field_name, field_value| - new_field_name = %w[ec_number go_term ipr_code].include?(field_name) ? field_name : "#{k}_#{field_name}" - output_row[new_field_name] = [] unless output_row.key? new_field_name - output_row[new_field_name] << field_value - end - end - else - output_row[k] = v - end - end - output << output_row - end - output - end - - # Transforms a flattened input created by flatten_functional_fields to the original - # hierarchy. - def inflate_functional_fields(data, original_key_order) - output = [] - data.each do |row| - output_row = {} - - processed_keys = [] - original_key_order.each do |original_key| - if %w[ec go ipr].include? original_key - # First, we take all distinct keys that start with "ec", "go" or "ipr" - annotation_keys = row.keys.select { |key| key.start_with? original_key } - processed_keys += annotation_keys - unless annotation_keys.empty? - # Each of the values of the annotation_keys is an array. All respective values of each of - # these arrays need to be put together into one hash. (E.g. {a => [1, 2], b=> [x, y]} --> [{a: 1, b: x}, {a: 2, b: y}]) - reconstructed_objects = [] - (0..row[annotation_keys[0]].length).each do |i| - reconstructed_object = {} - annotation_keys.each do |annotation_key| - reconstructed_object[%w[ec_number go_term ipr_code].include?(annotation_key) ? annotation_key : annotation_key[annotation_key.index('_') + 1, annotation_key.length]] = row[annotation_key][i] - end - reconstructed_objects << reconstructed_object - end - output_row[original_key] = reconstructed_objects - end - elsif row.key? original_key - output_row[original_key] = row[original_key] - end - end - - output << output_row - end - output - end - - def glob_to_regex(string) - /^#{string.gsub('*', '.*')}$/ - end - end -end diff --git a/lib/commands/unipept/config.rb b/lib/commands/unipept/config.rb deleted file mode 100644 index 83494b96..00000000 --- a/lib/commands/unipept/config.rb +++ /dev/null @@ -1,29 +0,0 @@ -module Unipept - class Commands::Config < Cri::CommandRunner - def run - abort command.help if arguments.empty? || arguments.size > 2 - - key, value = *arguments - - if arguments.size == 2 - set_config(key, value) - puts "#{key} was set to #{value}" - else - puts get_config(key) - end - end - - def config - @config ||= Unipept::Configuration.new - end - - def set_config(key, value) - config[key] = value - config.save - end - - def get_config(key) - config[key] - end - end -end diff --git a/lib/commands/unipept/pept2ec.rb b/lib/commands/unipept/pept2ec.rb deleted file mode 100644 index bab32c13..00000000 --- a/lib/commands/unipept/pept2ec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require_relative 'api_runner' -module Unipept::Commands - class Pept2ec < ApiRunner - def required_fields - ['peptide'] - end - - def default_batch_size - if options[:all] - 100 - else - 1000 - end - end - end -end diff --git a/lib/commands/unipept/pept2funct.rb b/lib/commands/unipept/pept2funct.rb deleted file mode 100644 index 74bffa59..00000000 --- a/lib/commands/unipept/pept2funct.rb +++ /dev/null @@ -1,16 +0,0 @@ -require_relative 'api_runner' -module Unipept::Commands - class Pept2funct < ApiRunner - def required_fields - ['peptide'] - end - - def default_batch_size - if options[:all] - 100 - else - 1000 - end - end - end -end diff --git a/lib/commands/unipept/pept2go.rb b/lib/commands/unipept/pept2go.rb deleted file mode 100644 index 26d81d17..00000000 --- a/lib/commands/unipept/pept2go.rb +++ /dev/null @@ -1,16 +0,0 @@ -require_relative 'api_runner' -module Unipept::Commands - class Pept2go < ApiRunner - def required_fields - ['peptide'] - end - - def default_batch_size - if options[:all] - 100 - else - 1000 - end - end - end -end diff --git a/lib/commands/unipept/pept2interpro.rb b/lib/commands/unipept/pept2interpro.rb deleted file mode 100644 index fc273904..00000000 --- a/lib/commands/unipept/pept2interpro.rb +++ /dev/null @@ -1,16 +0,0 @@ -require_relative 'api_runner' -module Unipept::Commands - class Pept2interpro < ApiRunner - def required_fields - ['peptide'] - end - - def default_batch_size - if options[:all] - 100 - else - 1000 - end - end - end -end diff --git a/lib/commands/unipept/pept2lca.rb b/lib/commands/unipept/pept2lca.rb deleted file mode 100644 index 76d49beb..00000000 --- a/lib/commands/unipept/pept2lca.rb +++ /dev/null @@ -1,16 +0,0 @@ -require_relative 'api_runner' -module Unipept::Commands - class Pept2lca < ApiRunner - def required_fields - ['peptide'] - end - - def default_batch_size - if options[:all] - 100 - else - 1000 - end - end - end -end diff --git a/lib/commands/unipept/pept2prot.rb b/lib/commands/unipept/pept2prot.rb deleted file mode 100644 index d6d7b513..00000000 --- a/lib/commands/unipept/pept2prot.rb +++ /dev/null @@ -1,26 +0,0 @@ -require_relative 'api_runner' - -module Unipept::Commands - class Pept2prot < ApiRunner - def initialize(args, opts, cmd) - if args[:meganize] - args[:all] = true - args[:select] = ['peptide,refseq_protein_ids'] - args[:format] = 'blast' - end - super - end - - def required_fields - ['peptide'] - end - - def default_batch_size - if options[:all] - 5 - else - 10 - end - end - end -end diff --git a/lib/commands/unipept/pept2taxa.rb b/lib/commands/unipept/pept2taxa.rb deleted file mode 100644 index efbb6f78..00000000 --- a/lib/commands/unipept/pept2taxa.rb +++ /dev/null @@ -1,12 +0,0 @@ -require_relative 'api_runner' -module Unipept::Commands - class Pept2taxa < ApiRunner - def required_fields - ['peptide'] - end - - def default_batch_size - 5 - end - end -end diff --git a/lib/commands/unipept/peptinfo.rb b/lib/commands/unipept/peptinfo.rb deleted file mode 100644 index be90c152..00000000 --- a/lib/commands/unipept/peptinfo.rb +++ /dev/null @@ -1,16 +0,0 @@ -require_relative 'api_runner' -module Unipept::Commands - class Peptinfo < ApiRunner - def required_fields - ['peptide'] - end - - def default_batch_size - if options[:all] - 100 - else - 1000 - end - end - end -end diff --git a/lib/commands/unipept/protinfo.rb b/lib/commands/unipept/protinfo.rb deleted file mode 100644 index fc85c4d2..00000000 --- a/lib/commands/unipept/protinfo.rb +++ /dev/null @@ -1,12 +0,0 @@ -require_relative 'api_runner' -module Unipept::Commands - class Protinfo < ApiRunner - def required_fields - ['protein'] - end - - def default_batch_size - 1000 - end - end -end diff --git a/lib/commands/unipept/taxa2lca.rb b/lib/commands/unipept/taxa2lca.rb deleted file mode 100644 index 32bb4af6..00000000 --- a/lib/commands/unipept/taxa2lca.rb +++ /dev/null @@ -1,18 +0,0 @@ -require_relative 'api_runner' -module Unipept::Commands - class Taxa2lca < ApiRunner - def batch_iterator - SimpleBatchIterator.new - end - - def default_batch_size - raise 'NOT NEEDED FOR TAXA2LCA' - end - end - - class SimpleBatchIterator - def iterate(input) - yield(input.to_a, 0) - end - end -end diff --git a/lib/commands/unipept/taxa2tree.rb b/lib/commands/unipept/taxa2tree.rb deleted file mode 100644 index 30d0993a..00000000 --- a/lib/commands/unipept/taxa2tree.rb +++ /dev/null @@ -1,74 +0,0 @@ -require_relative 'api_runner' -module Unipept::Commands - class Taxa2Tree < ApiRunner - def initialize(args, opts, cmd) - super - - # JSON is the default format for this command - args[:format] = 'json' unless args[:format] - - unless %w[url html json].include? args[:format] - warn "Format #{args[:format]} is not supported by taxa2tree. Use html, url or json (default)." - exit 1 - end - - if options[:format] == 'html' - # Overwrite the URL for this command, since it's possible that it uses HTML generated by the server. - @url = "#{@host}/api/v2/#{cmd.name}.html" - elsif args[:format] == 'url' - @link = true - end - end - - def required_fields - ['taxon_id'] - end - - def batch_size - return arguments.length unless arguments.empty? - return File.foreach(options[:input]).inject(0) { |c, _| c + 1 } if options[:input] - - @stdin_contents = $stdin.readlines - @stdin_contents.length - end - - def input_iterator - return arguments.each unless arguments.empty? - return File.foreach(options[:input]) if options[:input] - - @stdin_contents.each - end - - protected - - def filter_result(response) - return response if response.start_with?('] args An array of command-line arguments - # - # @return [void] - def self.run(args) - @root_command.run(args) - end - - # Fetches a UniProt entry from the UniProt website with the given accession - # number in the requested format. - # - # @param [String] accession The accession number of the record to fetch - # - # @param [String] format The format of of the record. If the format is 'sequence', the sequence will be returned in as a single line - # - # @return [String] The requested UniProt entry in the requested format - def self.get_uniprot_entry(accession, format) - if format == 'sequence' - get_uniprot_entry(accession, 'fasta').lines.map(&:chomp)[1..].join - else - # other format has been specified, just download and output - resp = Typhoeus.get("https://rest.uniprot.org/uniprotkb/#{accession}.#{format}") - resp.response_body if resp.success? - end - end - end -end diff --git a/lib/configuration.rb b/lib/configuration.rb deleted file mode 100644 index 078df616..00000000 --- a/lib/configuration.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'yaml' - -module Unipept - class Configuration - attr_reader :config, :file_name - - # Creates a new config object, based on a given YAML file. If no filename - # given, '.unipeptrc' in the home dir of the user will be used. - # - # If the file doesn't exist, an empty config will be loaded. - # - # @param [String] file An optional file name of the YAML file to create the - # config from - def initialize(file = nil) - @file_name = file || File.join(Dir.home, '.unipeptrc') - @config = if File.exist? file_name - YAML.load_file file_name, permitted_classes: [Time] - else - {} - end - end - - # Saves the config to disk. If the file doesn't exist yet, a new one will be - # created - def save - File.write(file_name, config.to_yaml) - end - - # Deletes a key - def delete(key) - config.delete(key) - end - - # forwards [] to the internal config hash - def [](*args) - config.[](*args) - end - - # forwards =[] to the internal config hash - def []=(*args) - config.[]=(*args) # rubocop:disable Layout/SpaceBeforeBrackets - end - end -end diff --git a/lib/formatters.rb b/lib/formatters.rb deleted file mode 100644 index 951b4887..00000000 --- a/lib/formatters.rb +++ /dev/null @@ -1,411 +0,0 @@ -require 'json' - -module Unipept - class Formatter - # The Hash of available formatters - # - # @return [Hash] A hash of the available formatters - def self.formatters - @@formatters ||= {} - end - - # Returns a new formatter of the given format. If the given format is not available, the - # default formatter is returned - # - # @param [String] format The type of the formatter we want - # - # @return [Formatter] The requested formatter - def self.new_for_format(format) - formatters[format].new - rescue StandardError - formatters[default].new - end - - # Adds a new formatter to the list of available formats - # - # @param [Symbol] format The type of the format we want to register - def self.register(format) - formatters[format.to_s] = self - end - - # Returns a list of the available formatters - # - # @return [Array] The list of available formatters - def self.available - formatters.reject { |_key, value| value.hidden? }.keys - end - - # @return [String] The type of the default formatter: csv - def self.default - 'csv' - end - - # @return [String] The type of the current formatter - def type - raise NotImplementedError, 'This must be implemented in a subclass.' - end - - def self.hidden? - false - end - - # Returns the header row for the given sample_data and fasta_mapper. This - # row is output only once at the beginning of the output - # - # @param [Object] _sample_data The data that we will output after this - # header. Can be used to extract the keys. - # - # @param [Array>] _fasta_mapper Optional mapping between input - # data and corresponding fasta header. The data is represented as a list - # containing tuples where the first element is the fasta header and second - # element is the input data - # - # @return [String] The header row - def header(_sample_data, _fasta_mapper = nil) - raise NotImplementedError, 'This must be implemented in a subclass.' - end - - # Returns the footer row. This row is output only once at the end of the - # output - # - # @return [String] The footer row - def footer - raise NotImplementedError, 'This must be implemented in a subclass.' - end - - # Converts the given input data and corresponding fasta headers to another - # format. - # - # @param [Array] data The data we wish to convert - # - # @param [Array>] fasta_mapper Optional mapping between input - # data and corresponding fasta header. The data is represented as a list - # containing tuples where the first element is the fasta header and second - # element is the input data - # - # @param [Boolean] Is this the first output batch? - # - # @return [String] The converted input data - def format(data, fasta_mapper, first) - data = integrate_fasta_headers(data, fasta_mapper) if fasta_mapper - convert(data, first) - end - - # Converts the given input data to another format. - # - # @param [Array] data The data we wish to convert - # - # @param [Boolean] Is this the first output batch? - # - # @return [String] The converted input data - def convert(_data, _first) - raise NotImplementedError, 'This must be implemented in a subclass.' - end - - # Integrates the fasta headers into the data object - def integrate_fasta_headers(data, fasta_mapper) - data_dict = group_by_first_key(data) - data = fasta_mapper.map do |header, key| - result = data_dict[key] - unless result.nil? - result = result.map do |row| - copy = { fasta_header: header } - copy.merge(row) - end - end - result - end - data.compact.flatten(1) - end - - # Groups the data by the first key of each element, for example - # [{key1: v1, key2: v2},{key1: v1, key2: v3},{key1: v4, key2: v2}] - # to {v1 => [{key1: v1, key2: v2},{key1: v1, key2: v3}], v4 => [{key1: v4, key2: v2}]} - # - # @param [Array] data The data we wish to group - # - # @return [Hash] The input data grouped by the first key - def group_by_first_key(data) - data.group_by { |el| el.values.first.to_s } - end - end - - class JSONFormatter < Formatter - require 'json' - register :json - - # @return [String] The type of the current formatter: json - def type - 'json' - end - - def header(_data, _fasta_mapper = nil) - '[' - end - - def footer - "]\n" - end - - # Converts the given input data to the JSON format. - # - # @param [Array] data The data we wish to convert - # - # @param [Boolean] Is this the first output batch? - # - # @return [String] The converted input data in the JSON format - def convert(data, first) - output = data.map(&:to_json).join(',') - first ? output : ",#{output}" - end - end - - class CSVFormatter < Formatter - require 'csv' - register :csv - - # @return [String] The type of the current formatter: csv - def type - 'csv' - end - - def get_keys(data, fasta_mapper = nil) - # This global variable is necessary because we need to know how many items should be - # nil in the convert function. - $keys_length = 0 # rubocop:disable Style/GlobalVars - # This array keeps track of items that are certainly filled in for each type of annotation - non_empty_items = { 'ec' => nil, 'go' => nil, 'ipr' => nil } - - # First we look for items for both ec numbers, go terms and ipr codes that are fully filled in. - data.each do |row| - non_empty_items.each_key do |annotation_type| - non_empty_items[annotation_type] = row if row[annotation_type] && !row[annotation_type].empty? - end - end - - keys = fasta_mapper ? ['fasta_header'] : [] - keys += (data.first.keys - %w[ec go ipr]) - processed_keys = keys - - non_empty_items.each do |annotation_type, non_empty_item| - next unless non_empty_item - - keys += (non_empty_item.keys - processed_keys) - processed_keys += non_empty_item.keys - - idx = keys.index(annotation_type) - keys.delete_at(idx) - keys.insert(idx, *non_empty_item[annotation_type].first.keys.map { |el| %w[ec_number go_term ipr_code].include?(el) ? el : "#{annotation_type}_#{el}" }) - $keys_length = *non_empty_item[annotation_type].first.keys.length # rubocop:disable Style/GlobalVars - end - - keys - end - - # Returns the header row for the given data and fasta_mapper. This row - # contains all the keys of the first element of the data, preceded by - # 'fasta_header' if a fasta_mapper is given. - # - # @param [Array] data The data that we will use to extract the keys from. - # - # @param [Array>] fasta_mapper Optional mapping between input - # data and corresponding fasta header. The data is represented as a list - # containing tuples where the first element is the fasta header and second - # element is the input data If a fasta_mapper is given, the output will be - # preceded with 'fasta_header'. - # - # @return [String] The header row - def header(data, fasta_mapper = nil) - keys = get_keys(data, fasta_mapper) - - CSV.generate do |csv| - csv << keys.map(&:to_s) if keys.length.positive? - end - end - - def footer - '' - end - - # Converts the given input data to the CSV format. - # - # @param [Array] data The data we wish to convert - # - # @param [Boolean] Is this the first output batch? - # - # @return [String] The converted input data in the CSV format - def convert(data, _first) - keys = get_keys(data) - - CSV.generate do |csv| - data.each do |o| - row = {} - o.each do |k, v| - if %w[ec go ipr].include? k - if v && !v.empty? - v.first.each_key do |key| - row[key == 'protein_count' ? "#{k}_protein_count" : key] = (v.map { |el| el[key] }).join(' ').strip - end - else - row[k] = row.concat(Array.new($keys_length[0], nil)) # rubocop:disable Style/GlobalVars - end - else - row[k] = (v == '' ? nil : v) - end - end - csv << keys.map { |k| row[k] } - end - end - end - end - - class XMLFormatter < Formatter - # Monkey patch (do as to_xml, but saner) - - class ::Object - def to_xml(name = nil) - name ? %(<#{name}>#{self}) : to_s - end - end - - class ::Array - def to_xml(array_name = :array, _item_name = :item) - %(<#{array_name}>) + map { |n| n.to_xml(:item) }.join + "" - end - end - - class ::Hash - def to_xml(name = nil) - data = to_a.map { |k, v| v.to_xml(k) }.join - name ? "<#{name}>#{data}" : data - end - end - - register :xml - - # @return [String] The type of the current formatter: xml - def type - 'xml' - end - - def header(_data, _fasta_mapper = nil) - '' - end - - def footer - "\n" - end - - # Converts the given input data to the XML format. - # - # @param [Array] data The data we wish to convert - # - # @param [Boolean] Is this the first output batch? - # - # @return [String] The converted input data in the XML format - def convert(data, _first) - data.map { |row| "#{row.to_xml}" }.join - end - end - - class BlastFormatter < Formatter - register :blast - - # @return [String] The type of the current formatter: blast - def type - 'blast' - end - - def self.hidden? - true - end - - def header(_data, _fasta_mapper = nil) - '' - end - - def footer - '' - end - - # Converts the given input data to the Blast format. - # - # @param [Array] data The data we wish to convert - # - # @param [Boolean] Is this the first output batch? - # - # @return [String] The converted input data in the Blast format - def convert(data, _first) - data - .reject { |o| o['refseq_protein_ids'].empty? } - .map do |o| - "#{o['peptide']}\tref|#{o['refseq_protein_ids']}|\t100\t10\t0\t0\t0\t10\t0\t10\t1e-100\t100\n" - end - .join - end - end - - class HtmlFormatter < Formatter - register :html - - # @return [String] The type of the current formatter: html - def type - 'html' - end - - def self.hidden? - false - end - - def header(_data, _fasta_mapper = nil) - '' - end - - def footer - '' - end - - # Converts the given input data to an HTML page that contains the Unipept visualizations - # - # @param [Array] data The data we wish to convert - # - # @param [Boolean] Is this the first output batch? - # - # @return [String] The converted input data in the Blast format - def convert(data, _first) - data - end - end - - class UrlFormatter < Formatter - register :url - - # @return [String] The type of the current formatter: html - def type - 'url' - end - - def self.hidden? - false - end - - def header(_data, _fasta_mapper = nil) - '' - end - - def footer - '' - end - - # Converts the given input data to an HTML page that contains the Unipept visualizations - # - # @param [Array] data The data we wish to convert - # - # @param [Boolean] Is this the first output batch? - # - # @return [String] The converted input data in the Blast format - def convert(data, _first) - "#{data[0]['gist'].sub!('https://gist.github.com/', 'https://bl.ocks.org/')}\n" - end - end -end diff --git a/lib/output_writer.rb b/lib/output_writer.rb deleted file mode 100644 index 067b1972..00000000 --- a/lib/output_writer.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Unipept - class OutputWriter - attr_reader :output - - def initialize(file) - @output = file ? File.open(file, 'a') : $stdout - end - - def write_line(line) - @output.write line - end - end -end diff --git a/lib/retryable_typhoeus.rb b/lib/retryable_typhoeus.rb deleted file mode 100644 index b8aa8db6..00000000 --- a/lib/retryable_typhoeus.rb +++ /dev/null @@ -1,34 +0,0 @@ -# Retryable Typheous -# Inspiration: https://gist.github.com/kunalmodi/2939288 -# Patches the request and hydra to allow requests to get resend when they fail - -module RetryableTyphoeus - require 'typhoeus' - - include Typhoeus - - DEFAULT_RETRIES = 10 - - class Request < Typhoeus::Request - attr_accessor :retries - - def initialize(base_url, options = {}) - @retries = (options.delete(:retries) || DEFAULT_RETRIES) - - super - end - - def finish(response, bypass_memoization = nil) - if response.success? || @retries <= 0 - super - else - @retries -= 1 - if @hydra - @hydra.queue_front self - else - run - end - end - end - end -end diff --git a/lib/server_message.rb b/lib/server_message.rb deleted file mode 100644 index a0ad04fa..00000000 --- a/lib/server_message.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'typhoeus' - -require_relative 'configuration' - -module Unipept - class ServerMessage - attr_reader :message_url, :configuration - - def initialize(host) - @message_url = "#{host}/api/v2/messages.json" - @configuration = Unipept::Configuration.new - end - - # Checks if the server has a message and prints it if not empty. - # We will only check this once a day and won't print anything if the quiet - # option is set or if we output to a file. - def print - return unless $stdout.tty? - return if recently_fetched? - - resp = fetch_server_message - update_fetched - puts resp unless resp.empty? - end - - # Fetches a message from the server and returns it - def fetch_server_message - Typhoeus.get(@message_url, params: { version: Unipept::VERSION }).body.chomp - end - - # Returns true if the last check for a server message was less than a day - # ago. - def recently_fetched? - last_fetched = @configuration['last_fetch_date'] - !last_fetched.nil? && (last_fetched + (60 * 60 * 24)) > Time.now - end - - # Updates the last checked timestamp - def update_fetched - @configuration['last_fetch_date'] = Time.now - @configuration.save - end - end -end diff --git a/lib/version.rb b/lib/version.rb deleted file mode 100644 index 2986a649..00000000 --- a/lib/version.rb +++ /dev/null @@ -1,3 +0,0 @@ -module Unipept - VERSION = File.read(File.join(File.dirname(__FILE__), '..', 'VERSION')).chomp -end diff --git a/test/commands/test_peptfilter.rb b/test/commands/test_peptfilter.rb deleted file mode 100644 index b2e3282c..00000000 --- a/test/commands/test_peptfilter.rb +++ /dev/null @@ -1,180 +0,0 @@ -require_relative '../../lib/commands' - -module Unipept - class PeptfilterTestCase < Unipept::TestCase - def test_length_filter - # min length - assert(Commands::Peptfilter.filter_length('AALER', 4, 10)) - assert(Commands::Peptfilter.filter_length('AALER', 5, 10)) - assert(!Commands::Peptfilter.filter_length('AALER', 6, 10)) - - # max length - assert(!Commands::Peptfilter.filter_length('AALER', 1, 4)) - assert(Commands::Peptfilter.filter_length('AALER', 1, 5)) - assert(Commands::Peptfilter.filter_length('AALER', 1, 6)) - end - - def test_lacks_filter - assert(Commands::Peptfilter.filter_lacks('AALER', ''.chars.to_a)) - assert(Commands::Peptfilter.filter_lacks('AALER', 'BCD'.chars.to_a)) - assert(!Commands::Peptfilter.filter_lacks('AALER', 'A'.chars.to_a)) - assert(!Commands::Peptfilter.filter_lacks('AALER', 'AE'.chars.to_a)) - end - - def test_contains_filter - assert(Commands::Peptfilter.filter_contains('AALER', ''.chars.to_a)) - assert(Commands::Peptfilter.filter_contains('AALER', 'A'.chars.to_a)) - assert(Commands::Peptfilter.filter_contains('AALER', 'AE'.chars.to_a)) - assert(!Commands::Peptfilter.filter_contains('AALER', 'BCD'.chars.to_a)) - assert(!Commands::Peptfilter.filter_contains('AALER', 'AB'.chars.to_a)) - end - - def test_filter - assert(Commands::Peptfilter.filter('AALTER', 4, 10, 'BCD'.chars.to_a, 'AL'.chars.to_a)) - assert(!Commands::Peptfilter.filter('AALTER', 7, 10, 'BCD.chars.to_a', 'AL'.chars.to_a)) - assert(!Commands::Peptfilter.filter('AALTER', 4, 5, 'BCD'.chars.to_a, 'AL'.chars.to_a)) - assert(!Commands::Peptfilter.filter('AALTER', 4, 10, 'ABC'.chars.to_a, 'AL'.chars.to_a)) - assert(!Commands::Peptfilter.filter('AALTER', 4, 10, 'BCD'.chars.to_a, 'ALC'.chars.to_a)) - end - - def test_default_min_length_argument - out, _err = capture_io_with_input('A' * 6) do - Commands::Peptfilter.run(%w[]) - end - assert_equal('A' * 6, out.chomp) - - out, _err = capture_io_with_input('A' * 5) do - Commands::Peptfilter.run(%w[]) - end - assert_equal('A' * 5, out.chomp) - - out, _err = capture_io_with_input('A' * 4) do - Commands::Peptfilter.run(%w[]) - end - assert_equal('', out.chomp) - end - - def test_default_max_length_argument - out, _err = capture_io_with_input('A' * 49) do - Commands::Peptfilter.run(%w[]) - end - assert_equal('A' * 49, out.chomp) - - out, _err = capture_io_with_input('A' * 50) do - Commands::Peptfilter.run(%w[]) - end - assert_equal('A' * 50, out.chomp) - - out, _err = capture_io_with_input('A' * 51) do - Commands::Peptfilter.run(%w[]) - end - assert_equal('', out.chomp) - end - - def test_with_min_argument - out, _err = capture_io_with_input('A' * 6) do - Commands::Peptfilter.run(%w[--minlen 7]) - end - assert_equal('', out.chomp) - - out, _err = capture_io_with_input('A' * 4) do - Commands::Peptfilter.run(%w[--minlen 3]) - end - assert_equal('A' * 4, out.chomp) - end - - def test_with_max_argument - out, _err = capture_io_with_input('A' * 45) do - Commands::Peptfilter.run(%w[--maxlen 40]) - end - assert_equal('', out.chomp) - - out, _err = capture_io_with_input('A' * 55) do - Commands::Peptfilter.run(%w[--maxlen 60]) - end - assert_equal('A' * 55, out.chomp) - end - - def test_with_lacks_argument - out, _err = capture_io_with_input('A' * 10) do - Commands::Peptfilter.run(%w[--lacks B]) - end - assert_equal('A' * 10, out.chomp) - - out, _err = capture_io_with_input('A' * 10) do - Commands::Peptfilter.run(%w[-l B]) - end - assert_equal('A' * 10, out.chomp) - - out, _err = capture_io_with_input('A' * 10) do - Commands::Peptfilter.run(%w[--lacks A]) - end - assert_equal('', out.chomp) - - out, _err = capture_io_with_input('A' * 10) do - Commands::Peptfilter.run(%w[-l A]) - end - assert_equal('', out.chomp) - end - - def test_with_contains_argument - out, _err = capture_io_with_input('A' * 10) do - Commands::Peptfilter.run(%w[--contains A]) - end - assert_equal('A' * 10, out.chomp) - - out, _err = capture_io_with_input('A' * 10) do - Commands::Peptfilter.run(%w[-c A]) - end - assert_equal('A' * 10, out.chomp) - - out, _err = capture_io_with_input('A' * 10) do - Commands::Peptfilter.run(%w[--contains B]) - end - assert_equal('', out.chomp) - - out, _err = capture_io_with_input('A' * 10) do - Commands::Peptfilter.run(%w[-c B]) - end - assert_equal('', out.chomp) - end - - def test_fasta_input - out, _err = capture_io_with_input('>') do - Commands::Peptfilter.run(%w[]) - end - assert_equal('>', out.chomp) - - out, _err = capture_io_with_input(['>', 'A', 'AALTER', '>']) do - Commands::Peptfilter.run(%w[]) - end - assert_equal(">\nAALTER\n>", out.chomp) - end - - def test_no_input - out, err = capture_io_while do - assert_raises SystemExit do - Commands::Peptfilter.run(%w[some argument]) - end - end - assert_equal('', out.chomp) - assert_equal("error: peptfilter doesn't support input as arguments. Use standard input instead.", err.chomp) - end - - def test_normal_input - out, _err = capture_io_with_input(['A', 'A' * 11, 'AAAAB', 'BBBBB', 'CCCCC', 'CCCCCA']) do - Commands::Peptfilter.run(%w[--minlen 4 --maxlen 10 --lacks B --contains A]) - end - assert_equal('CCCCCA', out.chomp) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Peptfilter.run(%w[-h]) - end - end - assert(out.include?('show help for this command')) - end - end -end diff --git a/test/commands/test_prot2pept.rb b/test/commands/test_prot2pept.rb deleted file mode 100644 index 2baccf64..00000000 --- a/test/commands/test_prot2pept.rb +++ /dev/null @@ -1,87 +0,0 @@ -require_relative '../../lib/commands' - -module Unipept - class Prot2peptTestCase < Unipept::TestCase - def test_normal_input - out, _err = capture_io_with_input('AALTERAALTERPAALTER') do - Commands::Prot2pept.run(%w[]) - end - assert_equal("AALTER\nAALTERPAALTER", out.chomp) - - out, _err = capture_io_with_input('AALTERAAL*TERPAALTER') do - Commands::Prot2pept.run(%w[]) - end - assert_equal("AALTER\nAAL\nTERPAALTER", out.chomp) - - out, _err = capture_io_with_input('KRKPR') do - Commands::Prot2pept.run(%w[]) - end - assert_equal("K\nR\nKPR", out.chomp) - - out, _err = capture_io_with_input(%w[AALTERAALTERPAALTER AALTERAA]) do - Commands::Prot2pept.run(%w[]) - end - assert_equal("AALTER\nAALTERPAALTER\nAALTER\nAA", out.chomp) - end - - def test_fasta_input - out, _err = capture_io_with_input(">AKA\nAALTERAALTERPAALTER") do - Commands::Prot2pept.run(%w[]) - end - assert_equal(">AKA\nAALTER\nAALTERPAALTER", out.chomp) - - out, _err = capture_io_with_input(">AKA\nAAL\nT\nERAALTER\nP\nAALTER") do - Commands::Prot2pept.run(%w[]) - end - assert_equal(">AKA\nAALTER\nAALTERPAALTER", out.chomp) - - out, _err = capture_io_with_input(">AKA\nAAL\nT\n>\nERAALTER\nP\nAALTER") do - Commands::Prot2pept.run(%w[]) - end - assert_equal(">AKA\nAALT\n>\nER\nAALTERPAALTER", out.chomp) - end - - def test_default_pattern - default_out, _err = capture_io_with_input('AALTERAALTERPAALTER') do - Commands::Prot2pept.run(%w[]) - end - assert_equal("AALTER\nAALTERPAALTER", default_out.chomp) - - pattern_out, _err = capture_io_with_input('AALTERAALTERPAALTER') do - Commands::Prot2pept.run(['-p', '([KR])([^P])']) - end - assert_equal(default_out, pattern_out) - - pattern_out, _err = capture_io_with_input('AALTERAALTERPAALTER') do - Commands::Prot2pept.run(['--pattern', '([KR])([^P])']) - end - assert_equal(default_out, pattern_out) - end - - def test_pattern - out, _err = capture_io_with_input('AALTERAALTERPAALTER') do - Commands::Prot2pept.run(%w[]) - end - assert_equal("AALTER\nAALTERPAALTER", out.chomp) - - out, _err = capture_io_with_input('AALTERAALTERPAALTER') do - Commands::Prot2pept.run(%w(-p ([KR])([^A]))) - end - assert_equal("AALTERAALTER\nPAALTER", out.chomp) - - out, _err = capture_io_with_input('AALTERAALTERPAALTER') do - Commands::Prot2pept.run(%w(--pattern ([KR])([^A]))) - end - assert_equal("AALTERAALTER\nPAALTER", out.chomp) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Prot2pept.run(%w[-h]) - end - end - assert(out.include?('show help for this command')) - end - end -end diff --git a/test/commands/test_unipept.rb b/test/commands/test_unipept.rb deleted file mode 100644 index 0055a5a2..00000000 --- a/test/commands/test_unipept.rb +++ /dev/null @@ -1,37 +0,0 @@ -require_relative '../../lib/commands' - -module Unipept - class UnipeptTestCase < Unipept::TestCase - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[-h]) - end - end - assert(out.include?('show help for this command')) - - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[--help]) - end - end - assert(out.include?('show help for this command')) - end - - def test_no_valid_subcommand - _out, err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[]) - end - end - assert(err.include?('show help for this command')) - end - - def test_version - out, _err = capture_io_while do - Commands::Unipept.run(%w[-v]) - end - assert_equal(VERSION, out.chomp) - end - end -end diff --git a/test/commands/test_uniprot.rb b/test/commands/test_uniprot.rb deleted file mode 100644 index bf5b6ec6..00000000 --- a/test/commands/test_uniprot.rb +++ /dev/null @@ -1,136 +0,0 @@ -require_relative '../../lib/commands' - -module Unipept - class UniprotTestCase < Unipept::TestCase - def test_argument_input - out, _err = capture_io_while do - Commands::Uniprot.run(%w[Q6GZX3]) - end - assert_equal(1, out.split(/\n/).length) - - out, _err = capture_io_while do - Commands::Uniprot.run(%w[Q6GZX3 Q6GZX4]) - end - assert_equal(2, out.split(/\n/).length) - - out, _err = capture_io_while do - Commands::Uniprot.run(%w[-f fasta Q6GZX3 Q6GZX4]) - end - assert_equal(2, out.count('>')) - - out, _err = capture_io_while do - Commands::Uniprot.run(%w[--format fasta Q6GZX3 Q6GZX4]) - end - assert_equal(2, out.count('>')) - end - - def test_stdin_input - out, _err = capture_io_with_input('Q6GZX3') do - Commands::Uniprot.run(%w[]) - end - assert_equal(1, out.split(/\n/).length) - - out, _err = capture_io_with_input(%w[Q6GZX3 Q6GZX4]) do - Commands::Uniprot.run(%w[]) - end - assert_equal(2, out.split(/\n/).length) - - out, _err = capture_io_with_input(%w[Q6GZX3 Q6GZX4]) do - Commands::Uniprot.run(%w[-f fasta]) - end - assert_equal(2, out.count('>')) - - out, _err = capture_io_with_input(%w[Q6GZX3 Q6GZX4]) do - Commands::Uniprot.run(%w[--format fasta]) - end - assert_equal(2, out.count('>')) - end - - def test_argument_input_priority - out, _err = capture_io_with_input('Q6GZX3') do - Commands::Uniprot.run(%w[Q6GZX3 Q6GZX4]) - end - assert_equal(2, out.split(/\n/).length) - - out, _err = capture_io_with_input(%w[Q6GZX3 Q6GZX4]) do - Commands::Uniprot.run(%w[Q6GZX3]) - end - assert_equal(1, out.split(/\n/).length) - end - - def test_invalid_format - out, err = capture_io_while do - assert_raises SystemExit do - Commands::Uniprot.run(%w[--format xxx]) - end - end - assert_equal('', out) - assert(err.include?('xxx is not a valid output format')) - end - - def test_default_format - out_default, _err = capture_io_while do - Commands::Uniprot.run(%w[Q6GZX3]) - end - assert_equal(1, out_default.split(/\n/).length) - - out_sequence, _err = capture_io_while do - Commands::Uniprot.run(%w[-f sequence Q6GZX3]) - end - assert_equal(out_default, out_sequence) - - out_sequence, _err = capture_io_while do - Commands::Uniprot.run(%w[--format sequence Q6GZX3]) - end - assert_equal(out_default, out_sequence) - end - - def test_format_options - # fasta txt xml rdf gff sequence - out, err = capture_io_while do - Commands::Uniprot.run(%w[-f fasta Q6GZX3]) - end - assert(!out.empty?) - assert(err.empty?) - - out, err = capture_io_while do - Commands::Uniprot.run(%w[-f txt Q6GZX3]) - end - assert(!out.empty?) - assert(err.empty?) - - out, err = capture_io_while do - Commands::Uniprot.run(%w[-f xml Q6GZX3]) - end - assert(!out.empty?) - assert(err.empty?) - - out, err = capture_io_while do - Commands::Uniprot.run(%w[-f rdf Q6GZX3]) - end - assert(!out.empty?) - assert(err.empty?) - - out, err = capture_io_while do - Commands::Uniprot.run(%w[-f gff Q6GZX3]) - end - assert(!out.empty?) - assert(err.empty?) - - out, err = capture_io_while do - Commands::Uniprot.run(%w[-f sequence Q6GZX3]) - end - assert(!out.empty?) - assert(err.empty?) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Uniprot.run(%w[-h]) - end - end - assert(out.include?('show help for this command')) - end - end -end diff --git a/test/commands/unipept/test_api_runner.rb b/test/commands/unipept/test_api_runner.rb deleted file mode 100644 index 89908b28..00000000 --- a/test/commands/unipept/test_api_runner.rb +++ /dev/null @@ -1,451 +0,0 @@ -require_relative '../../../lib/commands/unipept/api_runner' - -module Unipept - # make methods public to test them - class Commands::ApiRunner - public :glob_to_regex, :handle_response, :error_file_path, :filter_result - end - - class UnipeptAPIRunnerTestCase < Unipept::TestCase - def test_init - runner = new_runner('test', { host: 'test_host' }, %w[a b c]) - assert_equal('test', runner.command.name) - assert_equal('test_host', runner.options[:host]) - assert_equal(%w[a b c], runner.arguments) - assert(!runner.configuration.nil?) - assert_equal('http://test_host/api/v2/test.json', runner.url) - assert(/Unipept CLI - unipept [0-9]*\.[0-9]*\.[0-9]*/.match runner.user_agent) - end - - def test_config_host - runner = new_runner('test', { host: 'http://param_host' }, %w[a b c]) - runner.options.delete(:host) - runner.configuration['host'] = 'http://config_host' - host = runner.host - assert_equal('http://config_host', host) - end - - def test_param_host - runner = new_runner('test', { host: 'http://param_host' }, %w[a b c]) - runner.configuration.delete('host') - host = runner.host - assert_equal('http://param_host', host) - end - - def test_no_host - runner = new_runner('test', { host: 'param_host' }, %w[a b c]) - runner.configuration.delete('host') - runner.options.delete(:host) - host = runner.host - assert_equal('http://api.unipept.ugent.be', host) - end - - def test_host_priority - runner = new_runner('test', { host: 'http://param_host' }, %w[a b c]) - runner.configuration['host'] = 'http://config_host' - host = runner.host - assert_equal('http://param_host', host) - end - - def test_http_host - runner = new_runner('test', { host: 'param_host' }, %w[a b c]) - host = runner.host - assert_equal('http://param_host', host) - end - - def test_https_host - runner = new_runner('test', { host: 'https://param_host' }, %w[a b c]) - host = runner.host - assert_equal('https://param_host', host) - end - - def test_input_iterator_args - runner = new_runner('test', { host: 'https://param_host' }, %w[a b c]) - output = [] - runner.input_iterator.each { |el| output << el.chomp } - assert_equal(%w[a b c], output) - end - - def test_input_iterator_file - File.write('input_file', %w[a b c].join("\n")) - runner = new_runner('test', host: 'https://param_host', input: 'input_file') - output = [] - runner.input_iterator.each { |el| output << el.chomp } - assert_equal(%w[a b c], output) - end - - def test_input_iterator_stdin - runner = new_runner('test', host: 'https://param_host') - output = [] - _out, _err = capture_io_with_input(%w[a b c]) do - runner.input_iterator.each { |el| output << el.chomp } - end - assert_equal(%w[a b c], output) - end - - def test_input_iterator_arguments_priority - File.write('input_file', %w[1 2 3].join("\n")) - runner = new_runner('test', { host: 'https://param_host', input: 'input_file' }, %w[a b c]) - output = [] - _out, _err = capture_io_with_input(%w[1 2 3]) do - runner.input_iterator.each { |el| output << el.chomp } - end - assert_equal(%w[a b c], output) - end - - def test_input_iterator_file_priority - File.write('input_file', %w[a b c].join("\n")) - runner = new_runner('test', host: 'https://param_host', input: 'input_file') - output = [] - _out, _err = capture_io_with_input(%w[1 2 3]) do - runner.input_iterator.each { |el| output << el.chomp } - end - assert_equal(%w[a b c], output) - end - - def test_required_fields - assert_equal([], new_runner.required_fields) - end - - def test_required_fields_configurable - r = new_runner - def r.required_fields - ['test'] - end - assert_equal(['test'], r.required_fields) - end - - def test_default_batch_size - assert_raises NotImplementedError do - new_runner.default_batch_size - end - end - - def test_batch_size - r = new_runner - def r.default_batch_size - 100 - end - assert_equal(100, r.batch_size) - end - - def test_argument_batch_size - runner = new_runner('test', host: 'http://param_host', batch: '123') - assert_equal(123, runner.batch_size) - end - - def test_number_of_parallel_requests - assert_equal(10, new_runner.concurrent_requests) - runner = new_runner('test', host: 'http://param_host', parallel: '123') - assert_equal(123, runner.concurrent_requests) - end - - def test_queue_size - assert_equal(200, new_runner.queue_size) - runner = new_runner('test', host: 'http://param_host', parallel: '100') - assert_equal(2000, runner.queue_size) - end - - def test_default_formatter - runner = new_runner - assert_equal('csv', runner.formatter.type) - end - - def test_param_formatter - runner = new_runner('test', host: 'http://param_host', format: 'json') - assert_equal('json', runner.formatter.type) - end - - def test_no_selected_fields - runner = new_runner - assert_equal([], runner.selected_fields) - end - - def test_required_fields_are_not_selected_with_empty_selection - runner = new_runner - def runner.required_fields - ['test'] - end - runner.required_fields - assert_equal([], runner.selected_fields) - end - - def test_required_fields_are_selected_for_fasta - runner = new_runner('test', host: 'http://param_host', select: 'field') - def runner.required_fields - ['test'] - end - runner.instance_variable_set(:@fasta, true) - assert_equal([/^field$/, /^test$/], runner.selected_fields) - end - - def test_required_fields_are_not_selected_if_not_fasta - runner = new_runner('test', host: 'http://param_host', select: 'field') - def runner.required_fields - ['test'] - end - runner.instance_variable_set(:@fasta, false) - assert_equal([/^field$/], runner.selected_fields) - end - - def test_single_selected_fields - runner = new_runner('test', host: 'http://param_host', select: 'field') - assert_equal([/^field$/], runner.selected_fields) - end - - def test_comma_selected_fields - runner = new_runner('test', host: 'http://param_host', select: 'field1,field2') - assert_equal([/^field1$/, /^field2$/], runner.selected_fields) - end - - def test_multiple_selected_fields - runner = new_runner('test', host: 'http://param_host', select: %w[field1 field2]) - assert_equal([/^field1$/, /^field2$/], runner.selected_fields) - end - - def test_combined_selected_fields - runner = new_runner('test', host: 'http://param_host', select: ['field1', 'field2,field3']) - assert_equal([/^field1$/, /^field2$/, /^field3$/], runner.selected_fields) - end - - def test_wildcard_selected_fields - runner = new_runner('test', host: 'http://param_host', select: 'field*') - assert_equal([/^field.*$/], runner.selected_fields) - end - - def test_basic_construct_request_body - runner = new_runner('test', host: 'http://param_host') - body = runner.construct_request_body('test') - assert_equal('test', body[:input]) - assert_equal(false, body[:equate_il]) - assert_equal(false, body[:extra]) - assert_equal(false, body[:names]) - end - - def test_equate_construct_request_body - runner = new_runner('test', host: 'http://param_host', equate: true) - body = runner.construct_request_body('test') - assert_equal('test', body[:input]) - assert_equal(true, body[:equate_il]) - assert_equal(false, body[:extra]) - assert_equal(false, body[:names]) - end - - def test_all_no_select_construct_request_body - runner = new_runner('test', host: 'http://param_host', all: true) - body = runner.construct_request_body('test') - assert_equal('test', body[:input]) - assert_equal(false, body[:equate_il]) - assert_equal(true, body[:extra]) - assert_equal(true, body[:names]) - end - - def test_all_names_select_construct_request_body - runner = new_runner('test', host: 'http://param_host', all: true, select: 'test,names') - body = runner.construct_request_body('test') - assert_equal('test', body[:input]) - assert_equal(false, body[:equate_il]) - assert_equal(true, body[:extra]) - assert_equal(true, body[:names]) - end - - def test_all_names_wildcard_select_construct_request_body - runner = new_runner('test', host: 'http://param_host', all: true, select: 'test,order*') - body = runner.construct_request_body('test') - assert_equal('test', body[:input]) - assert_equal(false, body[:equate_il]) - assert_equal(true, body[:extra]) - assert_equal(true, body[:names]) - end - - def test_all_no_names_select_construct_request_body - runner = new_runner('test', host: 'http://param_host', all: true, select: 'test') - body = runner.construct_request_body('test') - assert_equal('test', body[:input]) - assert_equal(false, body[:equate_il]) - assert_equal(true, body[:extra]) - assert_equal(false, body[:names]) - end - - def test_glob_to_regex - runner = new_runner - assert(/^simple$/, runner.glob_to_regex('simple')) - assert(/^.*simple.*$/, runner.glob_to_regex('*simple*')) - end - - def test_save_error - runner = new_runner - runner.stub(:error_file_path, 'errordir/error.log') do - _out, err = capture_io_while do - runner.save_error('error message') - end - assert(err.start_with?('API request failed! log can be found in')) - assert_equal('error message', File.foreach('errordir/error.log').next.chomp) - end - end - - def test_error_file_path - runner = new_runner - assert(runner.error_file_path.include?('/.unipept/')) - end - - def test_invalid_filter_result - runner = new_runner - assert_equal([], runner.filter_result('{"key":"value')) - end - - def test_array_wrap_filter_result - runner = new_runner - assert_equal([{ 'key' => 'value' }], runner.filter_result('{"key":"value"}')) - end - - def test_filter_filter_result - runner = new_runner('test', host: 'test', select: 'key1') - result = runner.filter_result('[{"key1":"value1","key2":"value1"},{"key1":"value2","key2":"value2"}]') - assert_equal([{ 'key1' => 'value1' }, { 'key1' => 'value2' }], result) - end - - def test_success_header_handle_response - runner = new_runner - response = new_response(success: true, response_body: '[{"key1":"value1","key2":"value1"},{"key1":"value2","key2":"value2"}]') - lambda = runner.handle_response(response, 0, nil) - assert(lambda.lambda?) - out, err = capture_io_while(&lambda) - lines = out.each_line - assert_equal('', err) - assert_equal('key1,key2', lines.next.chomp) - assert_equal('value1,value1', lines.next.chomp) - assert_equal('value2,value2', lines.next.chomp) - end - - def test_success_no_header_option_handle_response - runner = new_runner('test', { host: 'test', 'no-header': true }) - response = new_response(success: true, response_body: '[{"key1":"value1","key2":"value1"},{"key1":"value2","key2":"value2"}]') - lambda = runner.handle_response(response, 0, nil) - assert(lambda.lambda?) - out, err = capture_io_while(&lambda) - lines = out.each_line - assert_equal('', err) - assert_equal('value1,value1', lines.next.chomp) - assert_equal('value2,value2', lines.next.chomp) - end - - def test_success_no_header_handle_response - runner = new_runner - response = new_response(success: true, response_body: '[{"key1":"value1","key2":"value1"},{"key1":"value2","key2":"value2"}]') - lambda = runner.handle_response(response, 1, nil) - assert(lambda.lambda?) - out, err = capture_io_while(&lambda) - lines = out.each_line - assert_equal('', err) - assert_equal('value1,value1', lines.next.chomp) - assert_equal('value2,value2', lines.next.chomp) - end - - def test_time_out_handle_response - runner = new_runner - response = new_response(success: false, timed_out: true) - lambda = runner.handle_response(response, 0, nil) - assert(lambda.lambda?) - def runner.save_error(input) - warn(input) - end - out, err = capture_io_while(&lambda) - assert_equal('', out) - assert(err.chomp.start_with?('request timed out')) - end - - def test_code_0_handle_response - runner = new_runner - response = new_response(success: false, timed_out: false, code: 0) - lambda = runner.handle_response(response, 0, nil) - assert(lambda.lambda?) - def runner.save_error(input) - warn(input) - end - out, err = capture_io_while(&lambda) - assert_equal('', out) - assert(err.chomp.start_with?('could not get an http')) - end - - def test_failed_handle_response - runner = new_runner - response = new_response(success: false, timed_out: false, code: 10) - lambda = runner.handle_response(response, 0, nil) - assert(lambda.lambda?) - def runner.save_error(input) - warn(input) - end - out, err = capture_io_while(&lambda) - assert_equal('', out) - assert(err.chomp.start_with?('Got 10')) - end - - def test_run - runner = new_runner('taxonomy', host: 'http://api.unipept.ugent.be') - out, err = capture_io_while do - def runner.input_iterator - %w[0 1 2].each - end - - def runner.batch_size - 2 - end - runner.run - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('taxon_id')) - assert(lines.next.start_with?('1,root')) - assert(lines.next.start_with?('2,Bacteria')) - assert_raises(StopIteration) { lines.next } - end - - def new_runner(command_name = 'test', options = { host: 'http://param_host' }, arguments = []) - command = Cri::Command.define { name command_name } - Commands::ApiRunner.new(options, arguments, command) - end - - def new_response(values) - response = Class.new do - def initialize(values) - super() - @values = values - end - - def success? - @values[:success] - end - - def timed_out? - @values[:timed_out] - end - - def code - @values[:code] - end - - def response_body - @values[:response_body] - end - - def return_message - '' - end - - def request - o = Object.new - def o.options - '' - end - - def o.encoded_body - '' - end - o - end - end - response.new(values) - end - end -end diff --git a/test/commands/unipept/test_config.rb b/test/commands/unipept/test_config.rb deleted file mode 100644 index 348ea3bb..00000000 --- a/test/commands/unipept/test_config.rb +++ /dev/null @@ -1,64 +0,0 @@ -require_relative '../../../lib/commands' - -module Unipept - class UnipeptConfigTestCase < Unipept::TestCase - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[config -h]) - end - end - assert(out.include?('show help for this command')) - - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[config --help]) - end - end - assert(out.include?('show help for this command')) - end - - def test_no_args - _out, err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[config]) - end - end - assert(err.include?('show help for this command')) - end - - def test_too_many_args - _out, err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[config a b c]) - end - end - assert(err.include?('show help for this command')) - end - - def test_setting_config - value = Random.rand.to_s - config = Unipept::Configuration.new - config.delete('test') - config.save - out, _err = capture_io_while do - Commands::Unipept.run(['config', 'test', value]) - end - assert_equal("test was set to #{value}", out.chomp) - assert_equal(value, Unipept::Configuration.new['test']) - end - - def test_getting_config - value = Random.rand.to_s - config = Unipept::Configuration.new - config['test'] = value - config.save - out, _err = capture_io_while do - Commands::Unipept.run(%w[config test]) - end - config.delete('test') - config.save - assert_equal(value, out.chomp) - end - end -end diff --git a/test/commands/unipept/test_pept2ec.rb b/test/commands/unipept/test_pept2ec.rb deleted file mode 100644 index 0e0160fc..00000000 --- a/test/commands/unipept/test_pept2ec.rb +++ /dev/null @@ -1,140 +0,0 @@ -require_relative '../../../lib/commands' - -module Unipept - class UnipeptPept2ecTestCase < Unipept::TestCase - def test_default_batch_size - command = Cri::Command.define { name 'pept2ec' } - pept2ec = Commands::Pept2ec.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(1000, pept2ec.default_batch_size) - pept2ec.options[:all] = true - assert_equal(100, pept2ec.default_batch_size) - end - - def test_required_fields - command = Cri::Command.define { name 'pept2ec' } - pept2ec = Commands::Pept2ec.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(['peptide'], pept2ec.required_fields) - end - - def test_argument_batch_size - command = Cri::Command.define { name 'pept2ec' } - pept2ec = Commands::Pept2ec.new({ host: 'http://api.unipept.ugent.be', batch: '123' }, [], command) - assert_equal(123, pept2ec.batch_size) - end - - def test_batch_size - command = Cri::Command.define { name 'pept2ec' } - pept2ec = Commands::Pept2ec.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(1000, pept2ec.batch_size) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[pept2ec -h]) - end - end - assert(out.include?('show help for this command')) - - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[pept2ec --help]) - end - end - assert(out.include?('show help for this command')) - end - - def test_run - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2ec --host http://api.unipept.ugent.be AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('peptide,total_protein_count,ec_number,ec_protein_count')) - assert(lines.next.start_with?('AALTER,7,3.1.3.3 6.3.2.13,2 2')) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2ec --host http://api.unipept.ugent.be --batch 2 >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('fasta_header,peptide,total_protein_count,ec_number,ec_protein_count')) - assert(lines.next.start_with?('>test,AALTER,7,3.1.3.3 6.3.2.13,2 2')) - assert(lines.next.start_with?('>test,AALER,208,6.1.1.16 2.7.7.38,44 13')) - assert(lines.next.start_with?('>tost,AALTER,7,3.1.3.3 6.3.2.13,2 2')) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches_and_select - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2ec --host http://api.unipept.ugent.be --batch 2 --select ec_number >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('fasta_header,peptide,ec_number')) - assert(lines.next.start_with?('>test,AALTER,3.1.3.3 6.3.2.13')) - assert(lines.next.start_with?('>test,AALER,6.1.1.16 2.7.7.38')) - assert(lines.next.start_with?('>tost,AALTER,3.1.3.3 6.3.2.13')) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches_json - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2ec --host http://api.unipept.ugent.be --batch 2 --format json >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('[')) - assert(output.end_with?(']')) - assert(!output.include?('}{')) - assert(output.include?('fasta_header')) - end - - def test_run_with_fasta_multiple_batches_xml - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2ec --host http://api.unipept.ugent.be --batch 2 --format xml >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('')) - assert(output.end_with?('')) - assert(output.include?('')) - end - - def test_run_with_empty_peptide - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2ec --host http://api.unipept.ugent.be AKVYSKY]) - end - lines = out.each_line - assert_equal('', err) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_empty_and_existing_peptide - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2ec --host http://api.unipept.ugent.be AKVYSKY AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('peptide,total_protein_count,ec_number,ec_protein_count')) - assert(lines.next.start_with?('AALTER,7,3.1.3.3 6.3.2.13,2 2')) - assert_raises(StopIteration) { lines.next } - end - - def test_run_existing_peptide_no_ec_numbers - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2ec --host http://api.unipept.ugent.be MDGTEYIIVK]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('peptide,total_protein_count')) - assert(lines.next.start_with?('MDGTEYIIVK,35')) - assert_raises(StopIteration) { lines.next } - end - end -end diff --git a/test/commands/unipept/test_pept2funct.rb b/test/commands/unipept/test_pept2funct.rb deleted file mode 100644 index e8141bfb..00000000 --- a/test/commands/unipept/test_pept2funct.rb +++ /dev/null @@ -1,140 +0,0 @@ -require_relative '../../../lib/commands' - -module Unipept - class UnipeptPept2functTestCase < Unipept::TestCase - def test_default_batch_size - command = Cri::Command.define { name 'pept2funct' } - pept2funct = Commands::Pept2funct.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(1000, pept2funct.default_batch_size) - pept2funct.options[:all] = true - assert_equal(100, pept2funct.default_batch_size) - end - - def test_required_fields - command = Cri::Command.define { name 'pept2funct' } - pept2funct = Commands::Pept2funct.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(['peptide'], pept2funct.required_fields) - end - - def test_argument_batch_size - command = Cri::Command.define { name 'pept2funct' } - pept2funct = Commands::Pept2funct.new({ host: 'http://api.unipept.ugent.be', batch: '123' }, [], command) - assert_equal(123, pept2funct.batch_size) - end - - def test_batch_size - command = Cri::Command.define { name 'pept2funct' } - pept2funct = Commands::Pept2funct.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(1000, pept2funct.batch_size) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[pept2funct -h]) - end - end - assert(out.include?('show help for this command')) - - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[pept2funct --help]) - end - end - assert(out.include?('show help for this command')) - end - - def test_run - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2funct --host http://api.unipept.ugent.be AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert_equal('peptide,total_protein_count,ec_number,ec_protein_count,go_term,go_protein_count,ipr_code,ipr_protein_count', lines.next.rstrip) - assert_equal('AALTER,7,3.1.3.3 6.3.2.13,2 2,GO:0000287 GO:0005737,5 5,IPR013221,2', lines.next.rstrip) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2funct --host http://api.unipept.ugent.be --batch 2 >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert_equal('fasta_header,peptide,total_protein_count,ec_number,ec_protein_count,go_term,go_protein_count,ipr_code,ipr_protein_count', lines.next.rstrip) - assert_equal('>test,AALTER,7,3.1.3.3 6.3.2.13,2 2,GO:0000287 GO:0005737,5 5,IPR013221,2', lines.next.rstrip) - assert_equal('>test,AALER,208,6.1.1.16 2.7.7.38,44 13,GO:0005737 GO:0005524,106 75,IPR014729 IPR009080,48 45', lines.next.rstrip) - assert_equal('>tost,AALTER,7,3.1.3.3 6.3.2.13,2 2,GO:0000287 GO:0005737,5 5,IPR013221,2', lines.next.rstrip) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches_and_select - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2funct --host http://api.unipept.ugent.be --batch 2 --select go_term >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert_equal('fasta_header,peptide,go_term', lines.next.rstrip) - assert_equal('>test,AALTER,GO:0000287 GO:0005737', lines.next.rstrip) - assert_equal('>test,AALER,GO:0005737 GO:0005524', lines.next.rstrip) - assert_equal('>tost,AALTER,GO:0000287 GO:0005737', lines.next.rstrip) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches_json - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2funct --host http://api.unipept.ugent.be --batch 2 --format json >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('[')) - assert(output.end_with?(']')) - assert(!output.include?('}{')) - assert(output.include?('fasta_header')) - end - - def test_run_with_fasta_multiple_batches_xml - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2funct --host http://api.unipept.ugent.be --batch 2 --format xml >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('')) - assert(output.end_with?('')) - assert(output.include?('')) - end - - def test_run_with_empty_peptide - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2funct --host http://api.unipept.ugent.be AKVYSKY]) - end - lines = out.each_line - assert_equal('', err) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_empty_and_existing_peptide - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2funct --host http://api.unipept.ugent.be AKVYSKY AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('peptide,total_protein_count,ec_number,ec_protein_count,go_term,go_protein_count')) - assert_equal('AALTER,7,3.1.3.3 6.3.2.13,2 2,GO:0000287 GO:0005737,5 5,IPR013221,2', lines.next.rstrip) - assert_raises(StopIteration) { lines.next } - end - - def test_run_existing_peptide_no_go_terms - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2funct --host http://api.unipept.ugent.be AAEVALVGTEK]) - end - lines = out.each_line - assert_equal('', err) - assert('peptide,total_protein_count', lines.next.rstrip) - assert_equal('AAEVALVGTEK,0', lines.next.rstrip) - assert_raises(StopIteration) { lines.next } - end - end -end diff --git a/test/commands/unipept/test_pept2go.rb b/test/commands/unipept/test_pept2go.rb deleted file mode 100644 index 5f1f6ae3..00000000 --- a/test/commands/unipept/test_pept2go.rb +++ /dev/null @@ -1,140 +0,0 @@ -require_relative '../../../lib/commands' - -module Unipept - class UnipeptPept2goTestCase < Unipept::TestCase - def test_default_batch_size - command = Cri::Command.define { name 'pept2go' } - pept2go = Commands::Pept2go.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(1000, pept2go.default_batch_size) - pept2go.options[:all] = true - assert_equal(100, pept2go.default_batch_size) - end - - def test_required_fields - command = Cri::Command.define { name 'pept2go' } - pept2go = Commands::Pept2go.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(['peptide'], pept2go.required_fields) - end - - def test_argument_batch_size - command = Cri::Command.define { name 'pept2go' } - pept2go = Commands::Pept2go.new({ host: 'http://api.unipept.ugent.be', batch: '123' }, [], command) - assert_equal(123, pept2go.batch_size) - end - - def test_batch_size - command = Cri::Command.define { name 'pept2go' } - pept2go = Commands::Pept2go.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(1000, pept2go.batch_size) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[pept2go -h]) - end - end - assert(out.include?('show help for this command')) - - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[pept2go --help]) - end - end - assert(out.include?('show help for this command')) - end - - def test_run - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2go --host http://api.unipept.ugent.be AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('peptide,total_protein_count,go_term,go_protein_count')) - assert(lines.next.start_with?('AALTER,7,GO:0000287 GO:0005737 GO:0042803,5 5 1')) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2go --host http://api.unipept.ugent.be --batch 2 >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('fasta_header,peptide,total_protein_count,go_term,go_protein_count')) - assert(lines.next.start_with?('>test,AALTER,7,GO:0000287 GO:0005737 GO:0042803,5 5 1')) - assert(lines.next.start_with?('>test,AALER,208,GO:0005737 GO:0005524 GO:0008270,106 75 48')) - assert(lines.next.start_with?('>tost,AALTER,7,GO:0000287 GO:0005737 GO:0042803,5 5 1')) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches_and_select - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2go --host http://api.unipept.ugent.be --batch 2 --select go_term >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('fasta_header,peptide,go_term')) - assert(lines.next.start_with?('>test,AALTER,GO:0000287 GO:0005737 GO:0042803')) - assert(lines.next.start_with?('>test,AALER,GO:0005737 GO:0005524 GO:0008270')) - assert(lines.next.start_with?('>tost,AALTER,GO:0000287 GO:0005737 GO:0042803')) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches_json - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2go --host http://api.unipept.ugent.be --batch 2 --format json >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('[')) - assert(output.end_with?(']')) - assert(!output.include?('}{')) - assert(output.include?('fasta_header')) - end - - def test_run_with_fasta_multiple_batches_xml - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2go --host http://api.unipept.ugent.be --batch 2 --format xml >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('')) - assert(output.end_with?('')) - assert(output.include?('')) - end - - def test_run_with_empty_peptide - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2go --host http://api.unipept.ugent.be AKVYSKY]) - end - lines = out.each_line - assert_equal('', err) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_empty_and_existing_peptide - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2go --host http://api.unipept.ugent.be AKVYSKY AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('peptide,total_protein_count,go_term,go_protein_count')) - assert(lines.next.start_with?('AALTER,7,GO:0000287 GO:0005737 GO:0042803,5 5 1')) - assert_raises(StopIteration) { lines.next } - end - - def test_run_existing_peptide_no_go_terms - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2go --host http://api.unipept.ugent.be AAEVALVGTEK]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('peptide,total_protein_count')) - assert(lines.next.start_with?('AAEVALVGTEK,0')) - assert_raises(StopIteration) { lines.next } - end - end -end diff --git a/test/commands/unipept/test_pept2interpro.rb b/test/commands/unipept/test_pept2interpro.rb deleted file mode 100644 index d0bfac97..00000000 --- a/test/commands/unipept/test_pept2interpro.rb +++ /dev/null @@ -1,140 +0,0 @@ -require_relative '../../../lib/commands' - -module Unipept - class Unipeptpept2interproTestCase < Unipept::TestCase - def test_default_batch_size - command = Cri::Command.define { name 'pept2interpro' } - pept2interpro = Commands::Pept2interpro.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(1000, pept2interpro.default_batch_size) - pept2interpro.options[:all] = true - assert_equal(100, pept2interpro.default_batch_size) - end - - def test_required_fields - command = Cri::Command.define { name 'pept2interpro' } - pept2interpro = Commands::Pept2interpro.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(['peptide'], pept2interpro.required_fields) - end - - def test_argument_batch_size - command = Cri::Command.define { name 'pept2interpro' } - pept2interpro = Commands::Pept2interpro.new({ host: 'http://api.unipept.ugent.be', batch: '123' }, [], command) - assert_equal(123, pept2interpro.batch_size) - end - - def test_batch_size - command = Cri::Command.define { name 'pept2interpro' } - pept2interpro = Commands::Pept2interpro.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(1000, pept2interpro.batch_size) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[pept2interpro -h]) - end - end - assert(out.include?('show help for this command')) - - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[pept2interpro --help]) - end - end - assert(out.include?('show help for this command')) - end - - def test_run - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2interpro --host http://api.unipept.ugent.be AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert_equal('peptide,total_protein_count,ipr_code,ipr_protein_count', lines.next.rstrip) - assert_equal('AALTER,7,IPR013221 IPR036565 IPR023214,2 2 2', lines.next.rstrip) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2interpro --host http://api.unipept.ugent.be --batch 2 >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert_equal('fasta_header,peptide,total_protein_count,ipr_code,ipr_protein_count', lines.next.rstrip) - assert_equal('>test,AALTER,7,IPR013221 IPR036565 IPR023214,2 2 2', lines.next.rstrip) - assert_equal('>test,AALER,208,IPR014729 IPR009080 IPR015803,48 45 44', lines.next.rstrip) - assert_equal('>tost,AALTER,7,IPR013221 IPR036565 IPR023214,2 2 2', lines.next.rstrip) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches_and_select - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2interpro --host http://api.unipept.ugent.be --batch 2 --select ipr_code >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert_equal('fasta_header,peptide,ipr_code', lines.next.rstrip) - assert_equal('>test,AALTER,IPR013221 IPR036565 IPR023214', lines.next.rstrip) - assert_equal('>test,AALER,IPR014729 IPR009080 IPR015803', lines.next.rstrip) - assert_equal('>tost,AALTER,IPR013221 IPR036565 IPR023214', lines.next.rstrip) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches_json - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2interpro --host http://api.unipept.ugent.be --batch 2 --format json >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('[')) - assert(output.end_with?(']')) - assert(!output.include?('}{')) - assert(output.include?('fasta_header')) - end - - def test_run_with_fasta_multiple_batches_xml - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2interpro --host http://api.unipept.ugent.be --batch 2 --format xml >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('')) - assert(output.end_with?('')) - assert(output.include?('')) - end - - def test_run_with_empty_peptide - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2interpro --host http://api.unipept.ugent.be AKVYSKY]) - end - lines = out.each_line - assert_equal('', err) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_empty_and_existing_peptide - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2interpro --host http://api.unipept.ugent.be AKVYSKY AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert_equal('peptide,total_protein_count,ipr_code,ipr_protein_count', lines.next.rstrip) - assert_equal('AALTER,7,IPR013221 IPR036565 IPR023214,2 2 2', lines.next.rstrip) - assert_raises(StopIteration) { lines.next } - end - - def test_run_existing_peptide_no_ipr_codes - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2interpro --host http://api.unipept.ugent.be VAQFLL]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('peptide,total_protein_count')) - assert(lines.next.start_with?('VAQFLL,0')) - assert_raises(StopIteration) { lines.next } - end - end -end diff --git a/test/commands/unipept/test_pept2lca.rb b/test/commands/unipept/test_pept2lca.rb deleted file mode 100644 index 5d4abed1..00000000 --- a/test/commands/unipept/test_pept2lca.rb +++ /dev/null @@ -1,109 +0,0 @@ -require_relative '../../../lib/commands' - -module Unipept - class UnipeptPept2lcaTestCase < Unipept::TestCase - def test_default_batch_size - command = Cri::Command.define { name 'pept2lca' } - pept2lca = Commands::Pept2lca.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(1000, pept2lca.default_batch_size) - pept2lca.options[:all] = true - assert_equal(100, pept2lca.default_batch_size) - end - - def test_required_fields - command = Cri::Command.define { name 'pept2lca' } - pept2lca = Commands::Pept2lca.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(['peptide'], pept2lca.required_fields) - end - - def test_argument_batch_size - command = Cri::Command.define { name 'pept2lca' } - pept2lca = Commands::Pept2lca.new({ host: 'http://api.unipept.ugent.be', batch: '123' }, [], command) - assert_equal(123, pept2lca.batch_size) - end - - def test_batch_size - command = Cri::Command.define { name 'pept2lca' } - pept2lca = Commands::Pept2lca.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(1000, pept2lca.batch_size) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[pept2lca -h]) - end - end - assert(out.include?('show help for this command')) - - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[pept2lca --help]) - end - end - assert(out.include?('show help for this command')) - end - - def test_run - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2lca --host http://api.unipept.ugent.be AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('peptide,taxon_id')) - assert(lines.next.start_with?('AALTER,1,root,no rank')) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2lca --host http://api.unipept.ugent.be --batch 2 >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('fasta_header,peptide,taxon_id')) - assert(lines.next.start_with?('>test,AALTER,1,root,no rank')) - assert(lines.next.start_with?('>test,AALER,1,root,no rank')) - assert(lines.next.start_with?('>tost,AALTER,1,root,no rank')) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches_and_select - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2lca --host http://api.unipept.ugent.be --batch 2 --select taxon_id >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('fasta_header,peptide,taxon_id')) - assert(lines.next.start_with?('>test,AALTER,1')) - assert(lines.next.start_with?('>test,AALER,1')) - assert(lines.next.start_with?('>tost,AALTER,1')) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches_json - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2lca --host http://api.unipept.ugent.be --batch 2 --format json >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('[')) - assert(output.end_with?(']')) - assert(!output.include?('}{')) - assert(output.include?('fasta_header')) - end - - def test_run_with_fasta_multiple_batches_xml - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2lca --host http://api.unipept.ugent.be --batch 2 --format xml >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('')) - assert(output.end_with?('')) - assert(output.include?('')) - end - end -end diff --git a/test/commands/unipept/test_pept2prot.rb b/test/commands/unipept/test_pept2prot.rb deleted file mode 100644 index 26f287dd..00000000 --- a/test/commands/unipept/test_pept2prot.rb +++ /dev/null @@ -1,110 +0,0 @@ -require_relative '../../../lib/commands' - -module Unipept - class UnipeptPept2protTestCase < Unipept::TestCase - def test_default_batch_size - command = Cri::Command.define { name 'pept2prot' } - pept2prot = Commands::Pept2prot.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(10, pept2prot.default_batch_size) - pept2prot.options[:all] = true - assert_equal(5, pept2prot.default_batch_size) - end - - def test_required_fields - command = Cri::Command.define { name 'pept2prot' } - pept2prot = Commands::Pept2prot.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(['peptide'], pept2prot.required_fields) - end - - def test_meganize_options - command = Cri::Command.define { name 'pept2prot' } - pept2prot = Commands::Pept2prot.new({ meganize: true, host: 'http://api.unipept.ugent.be' }, [], command) - assert(pept2prot.options[:all]) - assert_equal(['peptide,refseq_protein_ids'], pept2prot.options[:select]) - assert_equal('blast', pept2prot.options[:format]) - end - - def test_meganize_options_overridecommand - command = Cri::Command.define { name 'pept2prot' } - pept2prot = Commands::Pept2prot.new({ meganize: true, format: 'xml', all: false, select: ['something'], host: 'http://api.unipept.ugent.be' }, [], command) - assert(pept2prot.options[:all]) - assert_equal(['peptide,refseq_protein_ids'], pept2prot.options[:select]) - assert_equal('blast', pept2prot.options[:format]) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[pept2prot -h]) - end - end - assert(out.include?('show help for this command')) - - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[pept2prot --help]) - end - end - assert(out.include?('show help for this command')) - end - - def test_run - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2prot --host http://api.unipept.ugent.be ENFVYIAK]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('peptide,uniprot_id,protein_name,taxon_id')) - assert(lines.next.start_with?('ENFVYIAK,')) - end - - def test_run_with_fasta_multiple_batches - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2prot --host http://api.unipept.ugent.be --batch 2 >test EGGAGSSTGQR ENFVYIAK >tost EGGAGSSTGQR]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('fasta_header,peptide,uniprot_id,protein_name,taxon_id')) - assert(lines.count { |line| line.start_with? '>test,EGGAGSSTGQR,' } >= 1) - assert(lines.count { |line| line.start_with? '>test,ENFVYIAK,' } >= 1) - assert(lines.count { |line| line.start_with? '>tost,EGGAGSSTGQR,' } >= 1) - end - - def test_run_with_fasta_multiple_batches_and_select - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2prot --host http://api.unipept.ugent.be --batch 2 --select uniprot_id >test EGGAGSSTGQR ENFVYIAK >tost EGGAGSSTGQR]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('fasta_header,peptide,uniprot_id')) - assert(lines.count { |line| line.start_with? '>test,EGGAGSSTGQR,' } >= 1) - assert(lines.count { |line| line.start_with? '>test,ENFVYIAK,' } >= 1) - assert(lines.count { |line| line.start_with? '>tost,EGGAGSSTGQR,' } >= 1) - end - - def test_run_with_fasta_multiple_batches_json - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2prot --host http://api.unipept.ugent.be --batch 2 --format json >test EGGAGSSTGQR ENFVYIAK >tost EGGAGSSTGQR]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('[')) - assert(output.end_with?(']')) - assert(!output.include?('}{')) - assert(output.include?('fasta_header')) - end - - def test_run_with_fasta_multiple_batches_xml - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2prot --host http://api.unipept.ugent.be --batch 2 --format xml >test EGGAGSSTGQR ENFVYIAK >tost EGGAGSSTGQR]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('')) - assert(output.end_with?('')) - assert(output.include?('')) - end - end -end diff --git a/test/commands/unipept/test_pept2taxa.rb b/test/commands/unipept/test_pept2taxa.rb deleted file mode 100644 index 7de9c0a2..00000000 --- a/test/commands/unipept/test_pept2taxa.rb +++ /dev/null @@ -1,92 +0,0 @@ -require_relative '../../../lib/commands' - -module Unipept - class UnipeptPept2taxaTestCase < Unipept::TestCase - def test_default_batch_size - command = Cri::Command.define { name 'pept2taxa' } - pept2taxa = Commands::Pept2taxa.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(5, pept2taxa.default_batch_size) - end - - def test_required_fields - command = Cri::Command.define { name 'pept2taxa' } - pept2taxa = Commands::Pept2taxa.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(['peptide'], pept2taxa.required_fields) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[pept2taxa -h]) - end - end - assert(out.include?('show help for this command')) - - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[pept2taxa --help]) - end - end - assert(out.include?('show help for this command')) - end - - def test_run - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2taxa --host http://api.unipept.ugent.be ENFVYIAK]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('peptide,taxon_id,taxon_name,taxon_rank')) - assert(lines.next.start_with?('ENFVYIAK,')) - end - - def test_run_with_fasta_multiple_batches - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2taxa --host http://api.unipept.ugent.be --batch 2 >test EGGAGSSTGQR ENFVYIAK >tost EGGAGSSTGQR]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('fasta_header,peptide,taxon_id,taxon_name,taxon_rank')) - assert(lines.count { |line| line.start_with? '>test,EGGAGSSTGQR,' } >= 1) - assert(lines.count { |line| line.start_with? '>test,ENFVYIAK,' } >= 1) - assert(lines.count { |line| line.start_with? '>tost,EGGAGSSTGQR,' } >= 1) - end - - def test_run_with_fasta_multiple_batches_and_select - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2taxa --host http://api.unipept.ugent.be --batch 2 --select taxon_id >test EGGAGSSTGQR ENFVYIAK >tost EGGAGSSTGQR]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('fasta_header,peptide,taxon_id')) - assert(lines.count { |line| line.start_with? '>test,EGGAGSSTGQR,' } >= 1) - assert(lines.count { |line| line.start_with? '>test,ENFVYIAK,' } >= 1) - assert(lines.count { |line| line.start_with? '>tost,EGGAGSSTGQR,' } >= 1) - end - - def test_run_with_fasta_multiple_batches_json - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2taxa --host http://api.unipept.ugent.be --batch 2 --format json >test EGGAGSSTGQR ENFVYIAK >tost EGGAGSSTGQR]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('[')) - assert(output.end_with?(']')) - assert(!output.include?('}{')) - assert(output.include?('fasta_header')) - end - - def test_run_with_fasta_multiple_batches_xml - out, err = capture_io_while do - Commands::Unipept.run(%w[pept2taxa --host http://api.unipept.ugent.be --batch 2 --format xml >test EGGAGSSTGQR ENFVYIAK >tost EGGAGSSTGQR]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('')) - assert(output.end_with?('')) - assert(output.include?('')) - end - end -end diff --git a/test/commands/unipept/test_peptinfo.rb b/test/commands/unipept/test_peptinfo.rb deleted file mode 100644 index 3adf6b85..00000000 --- a/test/commands/unipept/test_peptinfo.rb +++ /dev/null @@ -1,140 +0,0 @@ -require_relative '../../../lib/commands' - -module Unipept - class UnipeptPeptinfoTestCase < Unipept::TestCase - def test_default_batch_size - command = Cri::Command.define { name 'peptinfo' } - peptinfo = Commands::Peptinfo.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(1000, peptinfo.default_batch_size) - peptinfo.options[:all] = true - assert_equal(100, peptinfo.default_batch_size) - end - - def test_required_fields - command = Cri::Command.define { name 'peptinfo' } - peptinfo = Commands::Peptinfo.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(['peptide'], peptinfo.required_fields) - end - - def test_argument_batch_size - command = Cri::Command.define { name 'peptinfo' } - peptinfo = Commands::Peptinfo.new({ host: 'http://api.unipept.ugent.be', batch: '123' }, [], command) - assert_equal(123, peptinfo.batch_size) - end - - def test_batch_size - command = Cri::Command.define { name 'peptinfo' } - peptinfo = Commands::Peptinfo.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(1000, peptinfo.batch_size) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[peptinfo -h]) - end - end - assert(out.include?('show help for this command')) - - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[peptinfo --help]) - end - end - assert(out.include?('show help for this command')) - end - - def test_run - out, err = capture_io_while do - Commands::Unipept.run(%w[peptinfo --host http://api.unipept.ugent.be AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert_equal('peptide,total_protein_count,taxon_id,taxon_name,taxon_rank,ec_number,ec_protein_count,go_term,go_protein_count,ipr_code,ipr_protein_count', lines.next.rstrip) - assert_equal('AALTER,7,1,root,no rank,3.1.3.3,2,GO:0000287,5,IPR013221,2', lines.next.rstrip) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches - out, err = capture_io_while do - Commands::Unipept.run(%w[peptinfo --host http://api.unipept.ugent.be --batch 2 >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert_equal('fasta_header,peptide,total_protein_count,taxon_id,taxon_name,taxon_rank,ec_number,ec_protein_count,go_term,go_protein_count,ipr_code,ipr_protein_count', lines.next.rstrip) - assert_equal('>test,AALTER,7,1,root,no rank,3.1.3.3,2,GO:0000287,5,IPR013221,2', lines.next.rstrip) - assert_equal('>test,AALER,208,1,root,no rank,6.1.1.16,44,GO:0005737,106,IPR014729 IPR009080 IPR015803,48 45 44', lines.next.rstrip) - assert_equal('>tost,AALTER,7,1,root,no rank,3.1.3.3,2,GO:0000287,5,IPR013221,2', lines.next.rstrip) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches_and_select - out, err = capture_io_while do - Commands::Unipept.run(%w[peptinfo --host http://api.unipept.ugent.be --batch 2 --select go_term >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert_equal('fasta_header,peptide,go_term', lines.next.rstrip) - assert_equal('>test,AALTER,GO:0000287', lines.next.rstrip) - assert_equal('>test,AALER,GO:0005737', lines.next.rstrip) - assert_equal('>tost,AALTER,GO:0000287', lines.next.rstrip) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_fasta_multiple_batches_json - out, err = capture_io_while do - Commands::Unipept.run(%w[peptinfo --host http://api.unipept.ugent.be --batch 2 --format json >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('[')) - assert(output.end_with?(']')) - assert(!output.include?('}{')) - assert(output.include?('fasta_header')) - end - - def test_run_with_fasta_multiple_batches_xml - out, err = capture_io_while do - Commands::Unipept.run(%w[peptinfo --host http://api.unipept.ugent.be --batch 2 --format xml >test AALTER AALER >tost AALTER]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('')) - assert(output.end_with?('')) - assert(output.include?('')) - end - - def test_run_with_empty_peptide - out, err = capture_io_while do - Commands::Unipept.run(%w[peptinfo --host http://api.unipept.ugent.be AKVYSKY]) - end - lines = out.each_line - assert_equal('', err) - assert_raises(StopIteration) { lines.next } - end - - def test_run_with_empty_and_existing_peptide - out, err = capture_io_while do - Commands::Unipept.run(%w[peptinfo --host http://api.unipept.ugent.be AKVYSKY AALTER]) - end - lines = out.each_line - assert_equal('', err) - assert_equal('peptide,total_protein_count,taxon_id,taxon_name,taxon_rank,ec_number,ec_protein_count,go_term,go_protein_count,ipr_code,ipr_protein_count', lines.next.rstrip) - assert_equal('AALTER,7,1,root,no rank,3.1.3.3,2,GO:0000287,5,IPR013221,2', lines.next.rstrip) - assert_raises(StopIteration) { lines.next } - end - - def test_run_existing_peptide_no_go_terms - out, err = capture_io_while do - Commands::Unipept.run(%w[peptinfo --host http://api.unipept.ugent.be AAEVALVGTEK]) - end - lines = out.each_line - assert_equal('', err) - assert_equal('peptide,total_protein_count,taxon_id,taxon_name,taxon_rank', lines.next.rstrip) - assert_equal('AAEVALVGTEK,0,1,root,no rank', lines.next.rstrip) - assert_raises(StopIteration) { lines.next } - end - end -end diff --git a/test/commands/unipept/test_protinfo.rb b/test/commands/unipept/test_protinfo.rb deleted file mode 100644 index d9756f3e..00000000 --- a/test/commands/unipept/test_protinfo.rb +++ /dev/null @@ -1,45 +0,0 @@ -require_relative '../../../lib/commands' - -module Unipept - class UnipeptProtinfoTestCase < Unipept::TestCase - def test_default_batch_size - command = Cri::Command.define { name 'protinfo' } - protinfo = Commands::Protinfo.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(1000, protinfo.default_batch_size) - end - - def test_required_fields - command = Cri::Command.define { name 'protinfo' } - protinfo = Commands::Protinfo.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(['protein'], protinfo.required_fields) - end - - def test_argument_batch_size - command = Cri::Command.define { name 'protinfo' } - protinfo = Commands::Protinfo.new({ host: 'http://api.unipept.ugent.be', batch: '123' }, [], command) - assert_equal(123, protinfo.batch_size) - end - - def test_batch_size - command = Cri::Command.define { name 'protinfo' } - protinfo = Commands::Protinfo.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(1000, protinfo.batch_size) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[protinfo -h]) - end - end - assert(out.include?('show help for this command')) - - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[protinfo --help]) - end - end - assert(out.include?('show help for this command')) - end - end -end diff --git a/test/commands/unipept/test_taxa2lca.rb b/test/commands/unipept/test_taxa2lca.rb deleted file mode 100644 index 79e8a089..00000000 --- a/test/commands/unipept/test_taxa2lca.rb +++ /dev/null @@ -1,69 +0,0 @@ -require_relative '../../../lib/commands' - -module Unipept - class UnipeptTaxa2lcaTestCase < Unipept::TestCase - def test_default_batch_size - command = Cri::Command.define { name 'taxa2lca' } - taxa2lca = Commands::Taxa2lca.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_raises RuntimeError do - taxa2lca.default_batch_size - end - end - - def test_required_fields - command = Cri::Command.define { name 'taxa2lca' } - taxa2lca = Commands::Taxa2lca.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal([], taxa2lca.required_fields) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[taxa2lca -h]) - end - end - assert(out.include?('show help for this command')) - - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[taxa2lca --help]) - end - end - assert(out.include?('show help for this command')) - end - - def test_run - out, err = capture_io_while do - Commands::Unipept.run(%w[taxa2lca --host http://api.unipept.ugent.be 216816 1680]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('taxon_id,taxon_name,taxon_rank')) - assert(lines.next.start_with?('1678,Bifidobacterium,genus')) - end - - def test_run_xml - out, err = capture_io_while do - Commands::Unipept.run(%w[taxa2lca --host http://api.unipept.ugent.be --format xml 216816 1680]) - end - lines = out.each_line - output = lines.to_a.join.chomp - assert_equal('', err) - assert(output.start_with?('')) - assert(output.end_with?('')) - end - - def test_run_json - out, err = capture_io_while do - Commands::Unipept.run(%w[taxa2lca --host http://api.unipept.ugent.be --format json 216816 1680]) - end - lines = out.each_line - output = lines.to_a.join.chomp - assert_equal('', err) - assert(output.start_with?('[')) - assert(output.end_with?(']')) - assert(!output.include?('}{')) - assert(!output.include?('][')) - end - end -end diff --git a/test/commands/unipept/test_taxa2tree.rb b/test/commands/unipept/test_taxa2tree.rb deleted file mode 100644 index 8032e5ab..00000000 --- a/test/commands/unipept/test_taxa2tree.rb +++ /dev/null @@ -1,68 +0,0 @@ -require_relative '../../../lib/commands' - -module Unipept - class UnipeptTaxa2TreeTestCase < Unipept::TestCase - def test_required_fields - command = Cri::Command.define { name 'taxa2tree' } - taxa2tree = Commands::Taxa2Tree.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(['taxon_id'], taxa2tree.required_fields) - end - - def test_batch_size - command = Cri::Command.define { name 'taxa2tree' } - taxa2tree = Commands::Taxa2Tree.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(0, taxa2tree.batch_size) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[taxa2tree -h]) - end - end - assert(out.include?('show help for this command')) - - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[taxa2tre --help]) - end - end - assert(out.include?('show help for this command')) - end - end - - def test_run - out, err = capture_io_while do - Commands::Unipept.run(%w[taxa2tree --host http://api.unipept.ugent.be 78 57 89 28 67]) - end - lines = out.each_line - output = lines.to_a.join.chomp - assert_equal('', err) - - assert(output.start_with?('{')) - assert(output.end_with?('}')) - assert(output.include?('Bacteria')) - assert(output.include?('superkingdom')) - end - - def test_run_url - out, err = capture_io_while do - Commands::Unipept.run(%w[taxa2tree --host http://api.unipept.ugent.be --format url 78 57 89 28 67]) - end - lines = out.each_line - assert_equal('', err) - assert_equal('https://bl.ocks.org/8837824df7ef9831a9b4216f3fb547ee', lines.next.rstrip) - end - - def test_run_html - out, err = capture_io_while do - Commands::Unipept.run(%w[taxa2tree --host http://api.unipept.ugent.be --format html 78 57 89 28 67]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('')) - assert(output.end_with?('')) - assert(output.include?('')) - end -end diff --git a/test/commands/unipept/test_taxonomy.rb b/test/commands/unipept/test_taxonomy.rb deleted file mode 100644 index a278f07e..00000000 --- a/test/commands/unipept/test_taxonomy.rb +++ /dev/null @@ -1,92 +0,0 @@ -require_relative '../../../lib/commands' - -module Unipept - class UnipeptTaxonomyTestCase < Unipept::TestCase - def test_default_batch_size - command = Cri::Command.define { name 'taxonomy' } - taxonomy = Commands::Taxonomy.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(100, taxonomy.default_batch_size) - end - - def test_required_fields - command = Cri::Command.define { name 'taxonomy' } - taxonomy = Commands::Taxonomy.new({ host: 'http://api.unipept.ugent.be' }, [], command) - assert_equal(['taxon_id'], taxonomy.required_fields) - end - - def test_help - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[taxonomy -h]) - end - end - assert(out.include?('show help for this command')) - - out, _err = capture_io_while do - assert_raises SystemExit do - Commands::Unipept.run(%w[taxonomy --help]) - end - end - assert(out.include?('show help for this command')) - end - - def test_run - out, err = capture_io_while do - Commands::Unipept.run(%w[taxonomy --host http://api.unipept.ugent.be 1]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('taxon_id,taxon_name,taxon_rank')) - assert(lines.next.start_with?('1,root,no rank')) - end - - def test_run_with_fasta_multiple_batches - out, err = capture_io_while do - Commands::Unipept.run(%w[taxonomy --host http://api.unipept.ugent.be --batch 2 >test 1 216816 >tost 1]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('fasta_header,taxon_id,taxon_name,taxon_rank')) - assert(lines.count { |line| line.start_with? '>test,1,' } >= 1) - assert(lines.count { |line| line.start_with? '>test,216816,' } >= 1) - assert(lines.count { |line| line.start_with? '>tost,1,' } >= 1) - end - - def test_run_with_fasta_multiple_batches_and_select - out, err = capture_io_while do - Commands::Unipept.run(%w[taxonomy --host http://api.unipept.ugent.be --batch 2 --select taxon_name >test 1 216816 >tost 1]) - end - lines = out.each_line - assert_equal('', err) - assert(lines.next.start_with?('fasta_header,taxon_id,taxon_name')) - assert(lines.count { |line| line.start_with? '>test,1,' } >= 1) - assert(lines.count { |line| line.start_with? '>test,216816,' } >= 1) - assert(lines.count { |line| line.start_with? '>tost,1,' } >= 1) - end - - def test_run_with_fasta_multiple_batches_json - out, err = capture_io_while do - Commands::Unipept.run(%w[taxonomy --host http://api.unipept.ugent.be --batch 2 --format json >test 1 216816 >tost 1]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('[')) - assert(output.end_with?(']')) - assert(!output.include?('}{')) - assert(output.include?('fasta_header')) - end - - def test_run_with_fasta_multiple_batches_xml - out, err = capture_io_while do - Commands::Unipept.run(%w[taxonomy --host http://api.unipept.ugent.be --batch 2 --format xml >test 1 216816 >tost 1]) - end - lines = out.each_line - assert_equal('', err) - output = lines.to_a.join.chomp - assert(output.start_with?('')) - assert(output.end_with?('')) - assert(output.include?('')) - end - end -end diff --git a/test/helper.rb b/test/helper.rb deleted file mode 100644 index fd57fbde..00000000 --- a/test/helper.rb +++ /dev/null @@ -1,88 +0,0 @@ -require 'rubygems' -require 'bundler' - -require_relative 'support/api_stub' - -begin - Bundler.setup(:default, :development) -rescue Bundler::BundlerError => e - warn e.message - warn 'Run `bundle install` to install missing gems' - exit e.status_code -end - -require 'minitest' -require 'minitest/autorun' - -module Unipept - class TestCase < Minitest::Test - def setup - # Enter tmp - @tmp_dir = Dir.mktmpdir('unipept-test') - @orig_wd = FileUtils.pwd - FileUtils.cd(@tmp_dir) - - @orig_io = capture_io - - setup_api_stubs - end - - def teardown - uncapture_io(*@orig_io) - - # Exit tmp - FileUtils.cd(@orig_wd) - FileUtils.rm_rf(@tmp_dir) - end - - def capture_io_with_input(input) - capture_io_while do - input = input.join("\n") if input.is_a? Array - $stdin.write(input) - $stdin.rewind - yield - end - end - - def capture_io_while - orig_io = capture_io - yield - [$stdout.string, $stderr.string] - ensure - uncapture_io(*orig_io) - end - - def lines(string) - string.scan(/^.*\n/).map(&:chomp) - end - - private - - def capture_io - orig_stdout = $stdout - orig_stderr = $stderr - orig_stdin = $stdin - - $stdout = StringIO.new - $stderr = StringIO.new - $stdin = StringIO.new - - [orig_stdout, orig_stderr, orig_stdin] - end - - def uncapture_io(orig_stdout, orig_stderr, orig_stdin) - $stdout = orig_stdout - $stderr = orig_stderr - $stdin = orig_stdin - end - - # Setup the interceptors for API requests, that will reply with a predefined (static) set of data - def setup_api_stubs - api_stub = ApiStub.new - api_stub.setup_stubs - end - end -end - -# Unexpected system exit is unexpected -MiniTest::Unit::TestCase::PASSTHROUGH_EXCEPTIONS.delete(SystemExit) diff --git a/test/support/api_stub.rb b/test/support/api_stub.rb deleted file mode 100644 index 23a9a9f9..00000000 --- a/test/support/api_stub.rb +++ /dev/null @@ -1,60 +0,0 @@ -require 'json' - -# This class stubs the Unipept API that's being used during the tests. -class ApiStub - def setup_stubs - @base_url = 'http://unipept.ugent.be/' - - %w[pept2ec pept2go pept2interpro pept2funct pept2lca pept2prot pept2taxa peptinfo].each do |endpoint| - setup_endpoint endpoint - end - - setup_taxa2lca - setup_taxonomy - end - - def setup_endpoint(name) - items = JSON.parse(File.read(File.join(File.dirname(__FILE__), "resources/#{name}.json"))) - Typhoeus.stub("http://api.unipept.ugent.be/api/v2/#{name}.json").and_return do |req| - peptides = req.options[:body][:input] - - filtered = items.select { |item| peptides.include? item['peptide'] } - - Typhoeus::Response.new(code: 200, body: JSON.dump(filtered)) - end - end - - def setup_taxa2lca - Typhoeus.stub('http://api.unipept.ugent.be/api/v2/taxa2lca.json').and_return( - Typhoeus::Response.new(code: 200, body: '{ - "taxon_id": 1678, - "taxon_name": "Bifidobacterium", - "taxon_rank": "genus" - }') - ) - end - - def setup_taxonomy - items = JSON.parse(File.read(File.join(File.dirname(__FILE__), 'resources/taxonomy.json'))) - Typhoeus.stub('http://api.unipept.ugent.be/api/v2/taxonomy.json').and_return do |req| - taxa = req.options[:body][:input].map(&:to_i) - - filtered = items.select { |item| taxa.include? item['taxon_id'] } - - Typhoeus::Response.new(code: 200, body: JSON.dump(filtered)) - end - end - - # Expects to be called with taxa "78", "57", "89", "28" and "67" - def setup_taxa2tree - Typhoeus.stub('http://api.unipept.ugent.be/api/v2/taxa2tree.json').and_return do |_| - link = req.options[:body][:link] == 'true' - if link - Typhoeus::Response.new(code: 200, body: JSON.dump(gist: 'https://gist.github.com/8837824df7ef9831a9b4216f3fb547ee')) - else - result = JSON.parse(File.read(File.join(File.dirname(__FILE__), 'resources/taxa2tree.json'))) - Typhoeus::Response.new(code: 200, body: JSON.dump(result)) - end - end - end -end diff --git a/test/support/resources/pept2ec.json b/test/support/resources/pept2ec.json deleted file mode 100644 index 0335ecb4..00000000 --- a/test/support/resources/pept2ec.json +++ /dev/null @@ -1,55 +0,0 @@ -[ - { - "peptide": "AALTER", - "total_protein_count": 7, - "ec": [ - { - "ec_number": "3.1.3.3", - "protein_count": 2, - "name": "Phosphoserine phosphatase" - }, - { - "ec_number": "6.3.2.13", - "protein_count": 2, - "name": "UDP-N-acetylmuramoyl-L-alanyl-D-glutamate--2,6-diaminopimelate ligase" - } - ] - }, - { - "peptide": "AAITER", - "total_protein_count": 1, - "ec": [ - { - "ec_number": "3.6.4.12", - "protein_count": 1, - "name": "DNA helicase" - }, - { - "ec_number": "5.6.2.1", - "protein_count": 1, - "name": "DNA topoisomerase" - } - ] - }, - { - "peptide": "AALER", - "total_protein_count": 208, - "ec": [ - { - "ec_number": "6.1.1.16", - "protein_count": 44, - "name": "Cysteine--tRNA ligase" - }, - { - "ec_number": "2.7.7.38", - "protein_count": 13, - "name": "3-deoxy-manno-octulosonate cytidylyltransferase" - } - ] - }, - { - "peptide": "MDGTEYIIVK", - "total_protein_count": 35, - "ec": [] - } -] diff --git a/test/support/resources/pept2funct.json b/test/support/resources/pept2funct.json deleted file mode 100644 index 443c5a70..00000000 --- a/test/support/resources/pept2funct.json +++ /dev/null @@ -1,73 +0,0 @@ -[ - { - "peptide": "AALTER", - "total_protein_count": 7, - "ec": [ - { - "ec_number": "3.1.3.3", - "protein_count": 2 - }, - { - "ec_number": "6.3.2.13", - "protein_count": 2 - } - ], - "go": [ - { - "go_term": "GO:0000287", - "protein_count": 5 - }, - { - "go_term": "GO:0005737", - "protein_count": 5 - } - ], - "ipr": [ - { - "code": "IPR013221", - "protein_count": 2 - } - ] - }, - { - "peptide": "AALER", - "total_protein_count": 208, - "ec": [ - { - "ec_number": "6.1.1.16", - "protein_count": 44 - }, - { - "ec_number": "2.7.7.38", - "protein_count": 13 - } - ], - "go": [ - { - "go_term": "GO:0005737", - "protein_count": 106 - }, - { - "go_term": "GO:0005524", - "protein_count": 75 - } - ], - "ipr": [ - { - "code": "IPR014729", - "protein_count": 48 - }, - { - "code": "IPR009080", - "protein_count": 45 - } - ] - }, - { - "peptide": "AAEVALVGTEK", - "total_protein_count": 0, - "ec": [], - "go": [], - "ipr": [] - } -] diff --git a/test/support/resources/pept2go.json b/test/support/resources/pept2go.json deleted file mode 100644 index dba37a98..00000000 --- a/test/support/resources/pept2go.json +++ /dev/null @@ -1,43 +0,0 @@ -[ - { - "peptide": "AALTER", - "total_protein_count": 7, - "go": [ - { - "go_term": "GO:0000287", - "protein_count": 5 - }, - { - "go_term": "GO:0005737", - "protein_count": 5 - }, - { - "go_term": "GO:0042803", - "protein_count": 1 - } - ] - }, - { - "peptide": "AALER", - "total_protein_count": 208, - "go": [ - { - "go_term": "GO:0005737", - "protein_count": 106 - }, - { - "go_term": "GO:0005524", - "protein_count": 75 - }, - { - "go_term": "GO:0008270", - "protein_count": 48 - } - ] - }, - { - "peptide": "AAEVALVGTEK", - "total_protein_count": 0, - "go": [] - } -] diff --git a/test/support/resources/pept2interpro.json b/test/support/resources/pept2interpro.json deleted file mode 100644 index 9ec0a089..00000000 --- a/test/support/resources/pept2interpro.json +++ /dev/null @@ -1,43 +0,0 @@ -[ - { - "peptide": "AALTER", - "total_protein_count": 7, - "ipr": [ - { - "code": "IPR013221", - "protein_count": 2 - }, - { - "code": "IPR036565", - "protein_count": 2 - }, - { - "code": "IPR023214", - "protein_count": 2 - } - ] - }, - { - "peptide": "AALER", - "total_protein_count": 208, - "ipr": [ - { - "code": "IPR014729", - "protein_count": 48 - }, - { - "code": "IPR009080", - "protein_count": 45 - }, - { - "code": "IPR015803", - "protein_count": 44 - } - ] - }, - { - "peptide": "VAQFLL", - "total_protein_count": 0, - "ipr": [] - } -] diff --git a/test/support/resources/pept2lca.json b/test/support/resources/pept2lca.json deleted file mode 100644 index 1bb732f0..00000000 --- a/test/support/resources/pept2lca.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "peptide": "AALTER", - "taxon_id": 1, - "taxon_name": "root", - "taxon_rank": "no rank" - }, - { - "peptide": "AALER", - "taxon_id": 1, - "taxon_name": "root", - "taxon_rank": "no rank" - } -] diff --git a/test/support/resources/pept2prot.json b/test/support/resources/pept2prot.json deleted file mode 100644 index f59f85a8..00000000 --- a/test/support/resources/pept2prot.json +++ /dev/null @@ -1,422 +0,0 @@ -[ - { - "peptide": "ENFVYIAK", - "uniprot_id": "P42654", - "protein_name": "14-3-3-like protein B", - "taxon_id": 3906 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "Q96453", - "protein_name": "14-3-3-like protein D", - "taxon_id": 3847 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "C6TH93", - "protein_name": "Casparian strip membrane protein 4", - "taxon_id": 3847 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A2Z6PAC3", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 3900 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A5B7A3K5", - "protein_name": "Putative 14-3-3-like protein D isoform X1", - "taxon_id": 16924 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A396GNG6", - "protein_name": "Putative 14-3-3 protein", - "taxon_id": 3880 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A072VBW0", - "protein_name": "Putative 14-3-3 protein", - "taxon_id": 3880 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "I1LUM3", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 3847 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A1S3WXW9", - "protein_name": "14-3-3 protein 9-like isoform X1", - "taxon_id": 4097 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A2H5N535", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 55188 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A438E5J2", - "protein_name": "14-3-3-like protein D", - "taxon_id": 29760 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A445HT97", - "protein_name": "14-3-3-like protein D isoform A", - "taxon_id": 3848 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A5J5A607", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 561372 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A1S4A416", - "protein_name": "14-3-3 protein 9-like", - "taxon_id": 4097 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A1U7VM87", - "protein_name": "14-3-3 protein 9-like isoform X2", - "taxon_id": 4096 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A2N9J4Q3", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 28930 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A445IBP6", - "protein_name": "14-3-3-like protein D isoform A", - "taxon_id": 3848 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A445HTA3", - "protein_name": "14-3-3-like protein D isoform C", - "taxon_id": 3848 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "T2DN83", - "protein_name": "14-3-3-like protein D", - "taxon_id": 3885 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A445CI86", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 3818 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A444Y3Q8", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 3818 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A2K3PKW1", - "protein_name": "14-3-3-like protein d-like", - "taxon_id": 57577 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A200QJZ5", - "protein_name": "14-3-3 protein", - "taxon_id": 56857 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A5B6YWQ8", - "protein_name": "Putative 14-3-3-like protein D", - "taxon_id": 16924 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A5J5A796", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 561372 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "C6TM63", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 3847 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A1S3WXS1", - "protein_name": "14-3-3 protein 9-like isoform X2", - "taxon_id": 4097 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A1U7VVV5", - "protein_name": "14-3-3 protein 9-like isoform X1", - "taxon_id": 4096 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A2H3Y4D2", - "protein_name": "14-3-3-like protein D isoform X2", - "taxon_id": 42345 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A5B6YWR0", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 16924 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A067GE20", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 2711 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A5B6Z3E6", - "protein_name": "Putative 14-3-3-like protein D isoform X2", - "taxon_id": 16924 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A5J5A2G8", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 561372 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A445IC54", - "protein_name": "14-3-3-like protein D isoform C", - "taxon_id": 3848 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "E1U3Z1", - "protein_name": "14-3-3-like protein B", - "taxon_id": 3827 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "I1M3M0", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 3847 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A0R0HF31", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 3847 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A2H3ZH40", - "protein_name": "14-3-3-like protein D isoform X2", - "taxon_id": 42345 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A5B7A4U9", - "protein_name": "Putative 14-3-3 domain-containing protein", - "taxon_id": 16924 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "M0TAI1", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 214687 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A1S4CN65", - "protein_name": "14-3-3 protein 9-like isoform X2", - "taxon_id": 4097 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A2H5N3C1", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 55188 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A2H3ZHX0", - "protein_name": "14-3-3-like protein D isoform X1", - "taxon_id": 42345 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "M0TY03", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 214687 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A445CI73", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 3818 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A3S3NBY5", - "protein_name": "14-3-3-like protein D isoform X1", - "taxon_id": 337451 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "V4U9U4", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 85681 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A151SEM7", - "protein_name": "14-3-3-like protein D", - "taxon_id": 3821 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A445IBK9", - "protein_name": "14-3-3-like protein D isoform D", - "taxon_id": 3848 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A1S4CMF0", - "protein_name": "14-3-3 protein 9-like isoform X1", - "taxon_id": 4097 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A1J6KH36", - "protein_name": "14-3-3 protein 9", - "taxon_id": 49451 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A5B7A5K1", - "protein_name": "Putative 14-3-3-like protein D isoform X1", - "taxon_id": 16924 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "F6H2P0", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 29760 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A1J6IP66", - "protein_name": "14-3-3 protein 9", - "taxon_id": 49451 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A5B7A8T8", - "protein_name": "Putative 14-3-3-like protein D isoform X1", - "taxon_id": 16924 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A0R0H7R6", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 3847 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A067GDS1", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 2711 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A445BBW8", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 3818 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "V4W919", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 85681 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "G7LIR4", - "protein_name": "General regulatory factor 2", - "taxon_id": 3880 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A2H3Y3V0", - "protein_name": "14-3-3-like protein D isoform X1", - "taxon_id": 42345 - }, - { - "peptide": "ENFVYIAK", - "uniprot_id": "A0A5J4ZBP6", - "protein_name": "14_3_3 domain-containing protein", - "taxon_id": 561372 - }, - { - "peptide": "EGGAGSSTGQR", - "uniprot_id": "A0A370PI74", - "protein_name": "Uncharacterized protein", - "taxon_id": 1353007 - }, - { - "peptide": "EGGAGSSTGQR", - "uniprot_id": "G3Y2A8", - "protein_name": "Uncharacterized protein", - "taxon_id": 380704 - }, - { - "peptide": "EGGAGSSTGQR", - "uniprot_id": "A0A401KHS6", - "protein_name": "Uncharacterized protein", - "taxon_id": 105351 - }, - { - "peptide": "EGGAGSSTGQR", - "uniprot_id": "A0A3F3RJX9", - "protein_name": "Uncharacterized protein", - "taxon_id": 5061 - }, - { - "peptide": "EGGAGSSTGQR", - "uniprot_id": "A0A370CDF7", - "protein_name": "Uncharacterized protein", - "taxon_id": 1353008 - }, - { - "peptide": "EGGAGSSTGQR", - "uniprot_id": "A0A319AT72", - "protein_name": "Uncharacterized protein", - "taxon_id": 1450533 - }, - { - "peptide": "EGGAGSSTGQR", - "uniprot_id": "A2QCE4", - "protein_name": "Aspergillus niger contig An02c0090, genomic contig", - "taxon_id": 425011 - }, - { - "peptide": "EGGAGSSTGQR", - "uniprot_id": "A0A3F3PXY4", - "protein_name": "Uncharacterized protein", - "taxon_id": 1341132 - } -] diff --git a/test/support/resources/pept2taxa.json b/test/support/resources/pept2taxa.json deleted file mode 100644 index 0ec5cf57..00000000 --- a/test/support/resources/pept2taxa.json +++ /dev/null @@ -1,194 +0,0 @@ -[ - { - "peptide": "ENFVYIAK", - "taxon_id": 2711, - "taxon_name": "Citrus sinensis", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 3818, - "taxon_name": "Arachis hypogaea", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 3821, - "taxon_name": "Cajanus cajan", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 3827, - "taxon_name": "Cicer arietinum", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 3847, - "taxon_name": "Glycine max", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 3848, - "taxon_name": "Glycine soja", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 3880, - "taxon_name": "Medicago truncatula", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 3885, - "taxon_name": "Phaseolus vulgaris", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 3900, - "taxon_name": "Trifolium subterraneum", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 3906, - "taxon_name": "Vicia faba", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 4096, - "taxon_name": "Nicotiana sylvestris", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 4097, - "taxon_name": "Nicotiana tabacum", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 16924, - "taxon_name": "Davidia involucrata", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 28930, - "taxon_name": "Fagus sylvatica", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 29760, - "taxon_name": "Vitis vinifera", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 42345, - "taxon_name": "Phoenix dactylifera", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 49451, - "taxon_name": "Nicotiana attenuata", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 55188, - "taxon_name": "Citrus unshiu", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 56857, - "taxon_name": "Macleaya cordata", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 57577, - "taxon_name": "Trifolium pratense", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 85681, - "taxon_name": "Citrus clementina", - "taxon_rank": "species" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 214687, - "taxon_name": "Musa acuminata subsp. malaccensis", - "taxon_rank": "subspecies" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 337451, - "taxon_name": "Cinnamomum micranthum f. kanehirae", - "taxon_rank": "forma" - }, - { - "peptide": "ENFVYIAK", - "taxon_id": 561372, - "taxon_name": "Nyssa sinensis", - "taxon_rank": "species" - }, - { - "peptide": "EGGAGSSTGQR", - "taxon_id": 5061, - "taxon_name": "Aspergillus niger", - "taxon_rank": "species" - }, - { - "peptide": "EGGAGSSTGQR", - "taxon_id": 105351, - "taxon_name": "Aspergillus awamori", - "taxon_rank": "species" - }, - { - "peptide": "EGGAGSSTGQR", - "taxon_id": 380704, - "taxon_name": "Aspergillus niger ATCC 1015", - "taxon_rank": "no rank" - }, - { - "peptide": "EGGAGSSTGQR", - "taxon_id": 425011, - "taxon_name": "Aspergillus niger CBS 513.88", - "taxon_rank": "no rank" - }, - { - "peptide": "EGGAGSSTGQR", - "taxon_id": 1341132, - "taxon_name": "Aspergillus welwitschiae", - "taxon_rank": "species" - }, - { - "peptide": "EGGAGSSTGQR", - "taxon_id": 1353007, - "taxon_name": "Aspergillus phoenicis ATCC 13157", - "taxon_rank": "no rank" - }, - { - "peptide": "EGGAGSSTGQR", - "taxon_id": 1353008, - "taxon_name": "Aspergillus niger ATCC 13496", - "taxon_rank": "no rank" - }, - { - "peptide": "EGGAGSSTGQR", - "taxon_id": 1450533, - "taxon_name": "Aspergillus niger CBS 101883", - "taxon_rank": "no rank" - } -] diff --git a/test/support/resources/peptinfo.json b/test/support/resources/peptinfo.json deleted file mode 100644 index 2a176c9c..00000000 --- a/test/support/resources/peptinfo.json +++ /dev/null @@ -1,70 +0,0 @@ -[ - { - "peptide": "AALTER", - "total_protein_count": 7, - "ec": [ - { - "ec_number": "3.1.3.3", - "protein_count": 2 - } - ], - "go": [ - { - "go_term": "GO:0000287", - "protein_count": 5 - } - ], - "ipr": [ - { - "code": "IPR013221", - "protein_count": 2 - } - ], - "taxon_id": 1, - "taxon_name": "root", - "taxon_rank": "no rank" - }, - { - "peptide": "AALER", - "total_protein_count": 208, - "ec": [ - { - "ec_number": "6.1.1.16", - "protein_count": 44 - } - ], - "go": [ - { - "go_term": "GO:0005737", - "protein_count": 106 - } - ], - "ipr": [ - { - "code": "IPR014729", - "protein_count": 48 - }, - { - "code": "IPR009080", - "protein_count": 45 - }, - { - "code": "IPR015803", - "protein_count": 44 - } - ], - "taxon_id": 1, - "taxon_name": "root", - "taxon_rank": "no rank" - }, - { - "peptide": "AAEVALVGTEK", - "total_protein_count": 0, - "ec": [], - "go": [], - "ipr": [], - "taxon_id": 1, - "taxon_name": "root", - "taxon_rank": "no rank" - } -] diff --git a/test/support/resources/taxa2tree.json b/test/support/resources/taxa2tree.json deleted file mode 100644 index 86cff433..00000000 --- a/test/support/resources/taxa2tree.json +++ /dev/null @@ -1,194 +0,0 @@ -{ - "id": 1, - "name": "Organism", - "rank": "root", - "data": { - "count": 4, - "self_count": 0 - }, - "children": [ - { - "id": 2, - "name": "Bacteria", - "rank": "superkingdom", - "data": { - "count": 4, - "self_count": 0 - }, - "children": [ - { - "id": 28, - "name": "halophilic eubacterium", - "rank": "species", - "data": { - "count": 1, - "self_count": 1 - }, - "children": [] - }, - { - "id": 1224, - "name": "Proteobacteria", - "rank": "phylum", - "data": { - "count": 3, - "self_count": 0 - }, - "children": [ - { - "id": 68525, - "name": "delta/epsilon subdivisions", - "rank": "subphylum", - "data": { - "count": 1, - "self_count": 0 - }, - "children": [ - { - "id": 28221, - "name": "Deltaproteobacteria", - "rank": "class_", - "data": { - "count": 1, - "self_count": 0 - }, - "children": [ - { - "id": 29, - "name": "Myxococcales", - "rank": "order", - "data": { - "count": 1, - "self_count": 0 - }, - "children": [ - { - "id": 80812, - "name": "Sorangiineae", - "rank": "suborder", - "data": { - "count": 1, - "self_count": 0 - }, - "children": [ - { - "id": 49, - "name": "Polyangiaceae", - "rank": "family", - "data": { - "count": 1, - "self_count": 0 - }, - "children": [ - { - "id": 55, - "name": "Polyangium", - "rank": "genus", - "data": { - "count": 1, - "self_count": 1 - }, - "children": [] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "id": 28211, - "name": "Alphaproteobacteria", - "rank": "class_", - "data": { - "count": 1, - "self_count": 0 - }, - "children": [ - { - "id": 204458, - "name": "Caulobacterales", - "rank": "order", - "data": { - "count": 1, - "self_count": 0 - }, - "children": [ - { - "id": 76892, - "name": "Caulobacteraceae", - "rank": "family", - "data": { - "count": 1, - "self_count": 0 - }, - "children": [ - { - "id": 75, - "name": "Caulobacter", - "rank": "genus", - "data": { - "count": 1, - "self_count": 1 - }, - "children": [] - } - ] - } - ] - } - ] - }, - { - "id": 28216, - "name": "Betaproteobacteria", - "rank": "class_", - "data": { - "count": 1, - "self_count": 0 - }, - "children": [ - { - "id": 80840, - "name": "Burkholderiales", - "rank": "order", - "data": { - "count": 1, - "self_count": 0 - }, - "children": [ - { - "id": 88, - "name": "Leptothrix", - "rank": "genus", - "data": { - "count": 1, - "self_count": 0 - }, - "children": [ - { - "id": 89, - "name": "Leptothrix discophora", - "rank": "species", - "data": { - "count": 1, - "self_count": 1 - }, - "children": [] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] -} diff --git a/test/support/resources/taxonomy.json b/test/support/resources/taxonomy.json deleted file mode 100644 index 7ab84310..00000000 --- a/test/support/resources/taxonomy.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "taxon_id": 216816, - "taxon_name": "Bifidobacterium longum", - "taxon_rank": "species" - }, - { - "taxon_id": 1680, - "taxon_name": "Bifidobacterium adolescentis", - "taxon_rank": "species" - }, - { - "taxon_id": 1, - "taxon_name": "root", - "taxon_rank": "no rank" - }, - { - "taxon_id": 2, - "taxon_name": "Bacteria", - "taxon_rank": "superkingdom" - } -] diff --git a/test/test_batch_iterator.rb b/test/test_batch_iterator.rb deleted file mode 100644 index 5653a022..00000000 --- a/test/test_batch_iterator.rb +++ /dev/null @@ -1,87 +0,0 @@ -require_relative '../lib/batch_iterator' - -module Unipept - class BatchIteratorTestCase < Unipept::TestCase - def test_batch_size - iterator = BatchIterator.new(50) - assert_equal(50, iterator.batch_size) - end - - def test_fasta - iterator = BatchIterator.new(50) - assert(iterator.fasta?('> test')) - assert(!(iterator.fasta? '< test')) - assert(!(iterator.fasta? 'test')) - end - - def test_normal_iterator - iterator = BatchIterator.new(2) - data = %w[a b c d e] - out, _err = capture_io_while do - iterator.iterate(data.each) do |batch, batch_id, fasta_mapper| - assert_nil(fasta_mapper) - puts batch_id - puts batch.to_s - end - end - assert_equal(['0', '["a", "b"]', '1', '["c", "d"]', '2', '["e"]', ''].join("\n"), out) - end - - def test_fasta_iterator_single_header - iterator = BatchIterator.new(2) - data = %w[>h1 a b c d e] - mappings = [] - out, _err = capture_io_while do - iterator.iterate(data.each) do |batch, batch_id, fasta_mapper| - assert(!fasta_mapper.nil?) - mappings << fasta_mapper - puts batch_id - puts batch.to_s - end - end - assert_equal(['0', '["a"]', '1', '["b", "c"]', '2', '["d", "e"]', ''].join("\n"), out) - mappings.flatten!(1) - data.shift - data.each { |element| assert(mappings.include?(['>h1', element])) } - end - - def test_fasta_iterator_double_header_single_batch - iterator = BatchIterator.new(3) - data = %w[>h1 a >h2 b c d e] - mappings = [] - out, _err = capture_io_while do - iterator.iterate(data.each) do |batch, batch_id, fasta_mapper| - assert(!fasta_mapper.nil?) - mappings << fasta_mapper - puts batch_id - puts batch.to_s - end - end - assert_equal(['0', '["a"]', '1', '["b", "c", "d"]', '2', '["e"]', ''].join("\n"), out) - mappings.flatten!(1) - assert(mappings.include?(['>h1', 'a'])) - assert(mappings.include?(['>h2', 'b'])) - assert(mappings.include?(['>h2', 'c'])) - assert(mappings.include?(['>h2', 'd'])) - assert(mappings.include?(['>h2', 'e'])) - end - - def test_fasta_iterator_multiple_values - iterator = BatchIterator.new(4) - data = %w[>h1 a >h2 a a] - mappings = [] - out, _err = capture_io_while do - iterator.iterate(data.each) do |batch, batch_id, fasta_mapper| - assert(!fasta_mapper.nil?) - mappings << fasta_mapper - puts batch_id - puts batch.to_s - end - end - assert_equal(['0', '["a"]', '1', '["a"]', ''].join("\n"), out) - assert(mappings[0].include?(['>h1', 'a'])) - assert(mappings[0].include?(['>h2', 'a'])) - assert(mappings[1].include?(['>h2', 'a'])) - end - end -end diff --git a/test/test_batch_order.rb b/test/test_batch_order.rb deleted file mode 100644 index acbd4afd..00000000 --- a/test/test_batch_order.rb +++ /dev/null @@ -1,57 +0,0 @@ -require_relative '../lib/batch_order' - -module Unipept - class BatchOrderTestCase < Unipept::TestCase - def test_single_batch - order = BatchOrder.new - out, _err = capture_io_while do - run_batch(order, [0]) - end - assert_equal(['0', ''].join("\n"), out) - end - - def test_double_batch - order = BatchOrder.new - out, _err = capture_io_while do - run_batch(order, [0, 1]) - end - assert_equal(['0', '1', ''].join("\n"), out) - end - - def test_missing_batch - order = BatchOrder.new - out, _err = capture_io_while do - run_batch(order, [1, 2]) - end - assert_equal('', out) - end - - def test_out_order_batch - order = BatchOrder.new - out, _err = capture_io_while do - run_batch(order, [1, 0]) - end - assert_equal(['0', '1', ''].join("\n"), out) - end - - def test_gap_batch - order = BatchOrder.new - out, _err = capture_io_while do - run_batch(order, [1, 4, 0]) - end - assert_equal(['0', '1', ''].join("\n"), out) - out, _err = capture_io_while do - run_batch(order, [2, 3, 5]) - end - assert_equal(['2', '3', '4', '5', ''].join("\n"), out) - end - - def run_batch(order, list) - list.each do |i| - order.wait(i) do - puts i - end - end - end - end -end diff --git a/test/test_configuration.rb b/test/test_configuration.rb deleted file mode 100644 index 8030a08c..00000000 --- a/test/test_configuration.rb +++ /dev/null @@ -1,43 +0,0 @@ -require_relative '../lib/configuration' - -module Unipept - class ConfigurationTestCase < Unipept::TestCase - def test_load_without_file - config = Configuration.new('no_file') - assert_equal({}, config.config) - end - - def test_load_with_file - hash = { 'key' => 'value' } - File.write('new_file', hash.to_yaml) - config = Configuration.new('new_file') - assert_equal(hash, config.config) - end - - def test_save - file_name = 'no_file' - assert(!(File.exist? file_name)) - config = Configuration.new(file_name) - config.config['key'] = 'value' - config.save - assert((File.exist? file_name)) - other_config = Configuration.new(file_name) - assert_equal('value', other_config.config['key']) - end - - def test_assign - config = Configuration.new('no_file') - config['key'] = 'value' - assert_equal('value', config.config['key']) - assert_equal('value', config['key']) - end - - def test_delete - config = Configuration.new('no_file') - config['key'] = 'value' - assert_equal('value', config['key']) - config.delete('key') - assert_equal(nil, config['key']) - end - end -end diff --git a/test/test_formatters.rb b/test/test_formatters.rb deleted file mode 100644 index 344d6130..00000000 --- a/test/test_formatters.rb +++ /dev/null @@ -1,240 +0,0 @@ -require_relative '../lib/formatters' - -module Unipept - class FormattersTestCase < Unipept::TestCase - def test_available_formatters - formatters = Formatter.available - assert(formatters.include?('json')) - assert(formatters.include?('csv')) - assert(formatters.include?('xml')) - assert(!formatters.include?('blast')) - end - - def test_default_formatter - assert_equal('csv', Formatter.default) - end - - def test_formatter_registration - assert(!(Formatter.available.include? 'test')) - Formatter.register(:test) - assert(Formatter.available.include?('test')) - end - - def test_new_for_format - formatter = Formatter.new_for_format('json') - assert_equal('json', formatter.type) - - formatter = Formatter.new_for_format('xml') - assert_equal('xml', formatter.type) - - formatter = Formatter.new_for_format('csv') - assert_equal('csv', formatter.type) - - formatter = Formatter.new_for_format('blast') - assert_equal('blast', formatter.type) - - formatter = Formatter.new_for_format('blah') - assert_equal('csv', formatter.type) - end - - def test_group_by_first_key - array = [{ key1: 'v1', key2: 'v2' }, { key1: 'v1', key2: 'v3' }, { key1: 'v4', key2: 'v2' }] - grouped = formatter.group_by_first_key(array) - assert_equal({ 'v1' => [{ key1: 'v1', key2: 'v2' }, { key1: 'v1', key2: 'v3' }], 'v4' => [{ key1: 'v4', key2: 'v2' }] }, grouped) - end - - def test_integrate_fasta_headers - fasta = [['>test', '5']] - object = [TestObject.test_object, TestObject.test_object] - integrated = Array.new(2, { fasta_header: '>test' }.merge(TestObject.test_object)) - assert_equal(integrated, formatter.integrate_fasta_headers(object, fasta)) - end - - def formatter - Formatter.new - end - - def test_header - assert_raises NotImplementedError do - formatter.header(TestObject.test_object, nil) - end - end - - def test_footer - assert_raises NotImplementedError do - formatter.footer - end - end - - def test_type - assert_raises NotImplementedError do - formatter.type - end - end - - def test_format - f = formatter - def f.integrate_fasta_headers(_a, _b) - puts 'header' - end - - def f.convert(_a, _b) - 'body' - end - assert_equal('body', f.format(TestObject.test_object, nil, false)) - out, _err = capture_io_while do - assert_equal('body', f.format(TestObject.test_object, [], false)) - end - assert_equal('header', out.chomp) - end - - def test_convert - assert_raises NotImplementedError do - formatter.convert(TestObject.test_object, false) - end - end - end - - class JSONFormatterTestCase < Unipept::TestCase - def formatter - Formatter.new_for_format('json') - end - - def test_header - assert_equal('[', formatter.header(TestObject.test_object, nil)) - end - - def test_footer - assert_equal("]\n", formatter.footer) - end - - def test_type - assert_equal('json', formatter.type) - end - - def test_convert - assert_equal(TestObject.as_json, formatter.convert([TestObject.test_object], true)) - assert_equal(",#{TestObject.as_json}", formatter.convert([TestObject.test_object], false)) - end - - def test_format_with_fasta - fasta = [['>test', '5']] - output = formatter.format([TestObject.test_object], fasta, true) - json = '{"fasta_header":">test","integer":5,"string":"string","list":["a",2,false]}' - assert_equal(json, output) - end - end - - class BlastFormatterTestCase < Unipept::TestCase - def formatter - Formatter.new_for_format('blast') - end - - def test_header - assert_equal('', formatter.header(nil, nil)) - end - - def test_footer - assert_equal('', formatter.footer) - end - - def test_type - assert_equal('blast', formatter.type) - end - - def test_convert - object = [{ 'peptide' => 'MDGTEYIIVK', 'refseq_protein_ids' => 'WP_008705701.1' }, { 'peptide' => 'ISVAQGASK', 'refseq_protein_ids' => 'NP_003881.2 XP_011547112.1 XP_011547113.1' }, { 'peptide' => 'ISVAQGASK', 'refseq_protein_ids' => '' }] - output = "MDGTEYIIVK\tref|WP_008705701.1|\t100\t10\t0\t0\t0\t10\t0\t10\t1e-100\t100\nISVAQGASK\tref|NP_003881.2 XP_011547112.1 XP_011547113.1|\t100\t10\t0\t0\t0\t10\t0\t10\t1e-100\t100\n" - assert_equal(output, formatter.convert(object, true)) - assert_equal(output, formatter.convert(object, false)) - end - end - - class CSVFormatterTestCase < Unipept::TestCase - def formatter - Formatter.new_for_format('csv') - end - - def test_header - fasta = [['peptide', '>test']] - object = [TestObject.test_object, TestObject.test_object] - assert_equal(TestObject.as_csv_header, formatter.header(object)) - assert_equal("fasta_header,#{TestObject.as_csv_header}", formatter.header(object, fasta)) - end - - def test_footer - assert_equal('', formatter.footer) - end - - def test_type - assert_equal('csv', formatter.type) - end - - def test_convert - object = [TestObject.test_object, TestObject.test_object] - csv = [TestObject.as_csv, TestObject.as_csv, ''].join("\n") - assert_equal(csv, formatter.convert(object, true)) - assert_equal(csv, formatter.convert(object, false)) - end - - def test_format_with_fasta - fasta = [['>test', '5']] - object = [TestObject.test_object, TestObject.test_object] - csv = [">test,#{TestObject.as_csv}", ">test,#{TestObject.as_csv}", ''].join("\n") - assert_equal(csv, formatter.format(object, fasta, false)) - end - end - - class XMLFormatterTestCase < Unipept::TestCase - def formatter - Formatter.new_for_format('xml') - end - - def test_header - assert_equal('', formatter.header(TestObject.test_object)) - end - - def test_footer - assert_equal("\n", formatter.footer) - end - - def test_type - assert_equal('xml', formatter.type) - end - - def test_convert - xml = "#{TestObject.as_xml}" - assert_equal(xml, formatter.convert([TestObject.test_object], true)) - assert_equal(xml, formatter.convert([TestObject.test_object], false)) - end - - def test_format_with_fasta - fasta = [['>test', '5']] - output = formatter.format([TestObject.test_object], fasta, false) - xml = ">test#{TestObject.as_xml}" - assert_equal(xml, output) - end - end - - class TestObject - def self.test_object - JSON.parse('{"integer": 5, "string": "string", "list": ["a", 2, false]}') - end - - def self.as_json - '{"integer":5,"string":"string","list":["a",2,false]}' - end - - def self.as_xml - '5stringa2false' - end - - def self.as_csv - '5,string,"[""a"", 2, false]"' - end - - def self.as_csv_header - "integer,string,list\n" - end - end -end diff --git a/test/test_output_writer.rb b/test/test_output_writer.rb deleted file mode 100644 index 40f5fe21..00000000 --- a/test/test_output_writer.rb +++ /dev/null @@ -1,29 +0,0 @@ -require_relative '../lib/output_writer' - -module Unipept - class OutputWriterTestCase < Unipept::TestCase - def test_init - assert_equal($stdout, OutputWriter.new(nil).output) - assert_equal(File, OutputWriter.new('output.txt').output.class) - end - - def test_stdout_write_to_output - out, _err = capture_io_while do - writer = OutputWriter.new(nil) - writer.write_line('hello world') - writer.output.flush - end - assert_equal('hello world', out.chomp) - end - - def test_file_write_to_output - out, _err = capture_io_while do - writer = OutputWriter.new('output_file') - writer.write_line('hello world') - writer.output.flush - end - assert_equal('', out) - assert_equal('hello world', File.foreach('output_file').next.chomp) - end - end -end diff --git a/test/test_retryable_typhoeus.rb b/test/test_retryable_typhoeus.rb deleted file mode 100644 index b0bd9f55..00000000 --- a/test/test_retryable_typhoeus.rb +++ /dev/null @@ -1,108 +0,0 @@ -require_relative '../lib/retryable_typhoeus' - -module Unipept - class BatchIteratorTestCase < Unipept::TestCase - def test_request_retry_default_parameter - request = new_request - assert_equal(10, request.retries) - request.retries = 3 - assert_equal(3, request.retries) - end - - def test_request_retry_parameter - request = new_request(retries: 5) - assert_equal(5, request.retries) - end - - def test_succesful_requests_complete - hydra = new_hydra - - response = new_response - Typhoeus.stub('stubbed.com').and_return(response) - - request = new_request - - hydra.queue request - hydra.run - - assert_equal(10, request.retries) - assert_equal(true, response.success?) - end - - def test_failing_requests_retry_wo_hydra - response_fail = new_response(400) - response_success = new_response(200) - Typhoeus::Expectation.clear - Typhoeus.stub('stubbed.com').and_return([response_fail, response_success]) - - request = new_request - request.run - - assert_equal(9, request.retries) - end - - def test_failing_requests_retry_with_hydra - hydra = new_hydra - - response_fail = new_response(400) - response_success = new_response(200) - Typhoeus::Expectation.clear - Typhoeus.stub('stubbed.com').and_return([response_fail, response_success]) - - request = new_request - - hydra.queue request - hydra.run - - assert_equal(9, request.retries) - end - - def test_failing_requests_finally_complete_wo_hydra - response = new_response(400) - Typhoeus.stub('stubbed.com').and_return(response) - - request = new_request - request.run - - assert_equal(0, request.retries) - end - - def test_failing_requests_finally_complete_with_error - hydra = new_hydra - response = new_response(400) - Typhoeus.stub('stubbed.com').and_return(response) - - request = new_request - - hydra.queue request - hydra.run - - assert_equal(0, request.retries) - assert_equal(false, response.success?) - end - - def new_request(extra_opts = {}) - ::RetryableTyphoeus::Request.new( - 'stubbed.com', - request_options.merge(extra_opts) - ) - end - - def request_options - { - method: :post, - body: 'body', - accept_encoding: 'gzip', - headers: { 'User-Agent' => 'user-agent' } - } - end - - def new_hydra - Typhoeus::Hydra.new(max_concurrency: 10) - end - - def new_response(code = 200) - Typhoeus::Response.new(code: code, body: {}, return_code: :ok) - end - end -end diff --git a/test/test_server_message.rb b/test/test_server_message.rb deleted file mode 100644 index 8a04d222..00000000 --- a/test/test_server_message.rb +++ /dev/null @@ -1,92 +0,0 @@ -require_relative '../lib/server_message' - -module Unipept - class ServerMessageTestCase < Unipept::TestCase - def test_init - assert_equal('http://test_host/api/v2/messages.json', ServerMessage.new('http://test_host').message_url) - end - - def test_fetch_server_message - server = ServerMessage.new('http://api.unipept.ugent.be') - assert(!server.fetch_server_message.nil?) - end - - def test_print_server_message - server = ServerMessage.new('http://api.unipept.ugent.be') - server.stub(:recently_fetched?, false) do - server.stub(:fetch_server_message, 'message') do - out, _err = capture_io_while do - def $stdout.tty? - true - end - server.print - end - assert_equal('message', out.chomp) - end - end - end - - def test_empty_print_server_message - server = ServerMessage.new('http://api.unipept.ugent.be') - server.stub(:recently_fetched?, false) do - server.stub(:fetch_server_message, '') do - out, _err = capture_io_while do - def $stdout.tty? - true - end - server.print - end - assert_equal('', out) - end - end - end - - def test_recent_print_server_message - server = ServerMessage.new('http://api.unipept.ugent.be') - server.stub(:recently_fetched?, true) do - server.stub(:fetch_server_message, 'message') do - out, _err = capture_io_while do - def $stdout.tty? - true - end - server.print - end - assert_equal('', out) - end - end - end - - def test_no_tty_print_server_message - server = ServerMessage.new('http://api.unipept.ugent.be') - server.stub(:recently_fetched?, false) do - server.stub(:fetch_server_message, 'message') do - out, _err = capture_io_while do - def $stdout.tty? - false - end - server.print - end - assert_equal('', out) - end - end - end - - def test_never_recently_fetched - server = ServerMessage.new('http://api.unipept.ugent.be') - server.configuration.delete('last_fetch_date') - assert(!server.recently_fetched?) - end - - def test_old_recently_fetched - server = ServerMessage.new('http://api.unipept.ugent.be') - server.configuration['last_fetch_date'] = Time.now - (60 * 60 * 25) - assert(!server.recently_fetched?) - end - - def test_recently_recently_fetched - server = ServerMessage.new('http://api.unipept.ugent.be') - server.configuration['last_fetch_date'] = Time.now - (60 * 60 * 1) - assert(server.recently_fetched?) - end - end -end diff --git a/unipept.gemspec b/unipept.gemspec deleted file mode 100644 index a5a02e45..00000000 --- a/unipept.gemspec +++ /dev/null @@ -1,124 +0,0 @@ -# Generated by juwelier -# DO NOT EDIT THIS FILE DIRECTLY -# Instead, edit Juwelier::Tasks in rakefile, and run 'rake gemspec' -# -*- encoding: utf-8 -*- -# stub: unipept 3.1.0 ruby lib - -Gem::Specification.new do |s| - s.name = "unipept".freeze - s.version = "3.1.0" - - s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= - s.require_paths = ["lib".freeze] - s.authors = ["Bart Mesuere".freeze, "Pieter Verschaffelt".freeze, "Toon Willems".freeze, "Tom Naessens".freeze] - s.date = "2023-07-24" - s.description = " Command line interface to the Unipept (http://unipept.ugent.be) web services\n (pept2lca, taxa2lca, pept2taxa, pept2prot and taxonomy) and some utility\n commands for handling proteins using the command line.\n".freeze - s.email = "unipept@ugent.be".freeze - s.executables = ["unipept".freeze, "prot2pept".freeze, "peptfilter".freeze, "uniprot".freeze] - s.extra_rdoc_files = [ - "LICENSE.txt", - "README.md" - ] - s.files = [ - ".devcontainer/devcontainer.json", - ".document", - ".github/dependabot.yml", - ".github/workflows/ci.yml", - ".rakeTasks", - ".rubocop.yml", - ".ruby-version", - ".vscode/tasks.json", - "CITATION.cff", - "Gemfile", - "Gemfile.lock", - "LICENSE.txt", - "README.md", - "Rakefile", - "VERSION", - "bin/peptfilter", - "bin/prot2pept", - "bin/unipept", - "bin/uniprot", - "lib/batch_iterator.rb", - "lib/batch_order.rb", - "lib/commands.rb", - "lib/commands/peptfilter.rb", - "lib/commands/prot2pept.rb", - "lib/commands/unipept.rb", - "lib/commands/unipept/api_runner.rb", - "lib/commands/unipept/config.rb", - "lib/commands/unipept/pept2ec.rb", - "lib/commands/unipept/pept2funct.rb", - "lib/commands/unipept/pept2go.rb", - "lib/commands/unipept/pept2interpro.rb", - "lib/commands/unipept/pept2lca.rb", - "lib/commands/unipept/pept2prot.rb", - "lib/commands/unipept/pept2taxa.rb", - "lib/commands/unipept/peptinfo.rb", - "lib/commands/unipept/protinfo.rb", - "lib/commands/unipept/taxa2lca.rb", - "lib/commands/unipept/taxa2tree.rb", - "lib/commands/unipept/taxonomy.rb", - "lib/commands/uniprot.rb", - "lib/configuration.rb", - "lib/formatters.rb", - "lib/output_writer.rb", - "lib/retryable_typhoeus.rb", - "lib/server_message.rb", - "lib/version.rb", - "test.taxa", - "test/commands/test_peptfilter.rb", - "test/commands/test_prot2pept.rb", - "test/commands/test_unipept.rb", - "test/commands/test_uniprot.rb", - "test/commands/unipept/test_api_runner.rb", - "test/commands/unipept/test_config.rb", - "test/commands/unipept/test_pept2ec.rb", - "test/commands/unipept/test_pept2funct.rb", - "test/commands/unipept/test_pept2go.rb", - "test/commands/unipept/test_pept2interpro.rb", - "test/commands/unipept/test_pept2lca.rb", - "test/commands/unipept/test_pept2prot.rb", - "test/commands/unipept/test_pept2taxa.rb", - "test/commands/unipept/test_peptinfo.rb", - "test/commands/unipept/test_protinfo.rb", - "test/commands/unipept/test_taxa2lca.rb", - "test/commands/unipept/test_taxa2tree.rb", - "test/commands/unipept/test_taxonomy.rb", - "test/helper.rb", - "test/support/api_stub.rb", - "test/support/resources/pept2ec.json", - "test/support/resources/pept2funct.json", - "test/support/resources/pept2go.json", - "test/support/resources/pept2interpro.json", - "test/support/resources/pept2lca.json", - "test/support/resources/pept2prot.json", - "test/support/resources/pept2taxa.json", - "test/support/resources/peptinfo.json", - "test/support/resources/taxa2tree.json", - "test/support/resources/taxonomy.json", - "test/test_batch_iterator.rb", - "test/test_batch_order.rb", - "test/test_configuration.rb", - "test/test_formatters.rb", - "test/test_output_writer.rb", - "test/test_retryable_typhoeus.rb", - "test/test_server_message.rb", - "unipept.gemspec" - ] - s.homepage = "http://unipept.ugent.be".freeze - s.licenses = ["MIT".freeze] - s.required_ruby_version = Gem::Requirement.new(">= 2.6.0".freeze) - s.rubygems_version = "3.4.10".freeze - s.summary = "Command line interface to Unipept web services.".freeze - - s.specification_version = 4 - - s.add_runtime_dependency(%q.freeze, ["~> 2.15.10"]) - s.add_runtime_dependency(%q.freeze, ["~> 1.4.0"]) - s.add_runtime_dependency(%q.freeze, ["~> 5.1.0"]) - s.add_development_dependency(%q.freeze, ["~> 5.18"]) - s.add_development_dependency(%q.freeze, ["~> 13.0.1"]) - s.add_development_dependency(%q.freeze, ["~> 1.50"]) -end - From 52f0494d282880c8f78199a435c6eee1c4c8f7c6 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 17 Jun 2024 14:42:08 +0200 Subject: [PATCH 02/76] require node 22 to use fetch --- .nvmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..2bd5a0a9 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 From 02c2af5e94c6ede3e48be7cb8c045606d96cb3ad Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 17 Jun 2024 14:44:32 +0200 Subject: [PATCH 03/76] yarn init --- package.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 package.json diff --git a/package.json b/package.json new file mode 100644 index 00000000..5e12f37a --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "name": "unipept-cli", + "version": "4.0.0", + "description": "Command line interface to the Unipept web services", + "main": "index.js", + "repository": "git@github.com:unipept/unipept-cli.git", + "author": "Bart Mesuere ", + "license": "MIT", + "private": false +} From 49b56815cd848f4b558fa2f7601c3d93c04ddb9e Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 17 Jun 2024 16:07:08 +0200 Subject: [PATCH 04/76] get basic version working --- .gitignore | 2 ++ index.js | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 6 +++++- yarn.lock | 8 ++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 index.js create mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore index bd5f33a8..fb549fa7 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,5 @@ pkg # For rubinius: #*.rbc + +node_modules/** diff --git a/index.js b/index.js new file mode 100644 index 00000000..293bd307 --- /dev/null +++ b/index.js @@ -0,0 +1,53 @@ +import { Option, program } from 'commander'; +import { createInterface } from 'node:readline'; + +const VALID_FORMATS = ["fasta", "gff", "json", "rdf", "sequence", "xml"]; + +program + .version("1.0.0") + .summary("Command line interface to UniProt web services.") + .description(`Command line interface to UniProt web services. + +The uniprot command fetches UniProt entries from the UniProt web services. The command expects a list of UniProt Accession Numbers that are passed + +- as separate command line arguments +- to standard input + +The command will give priority to the first way UniProt Accession Numbers are passed, in the order as listed above. The standard input should have one UniProt Accession Number per line. + +The uniprot command yields just the protein sequences as a default, but can return several formats.`) + .argument("[accessions...]", "UniProt Accession Numbers") + .addOption(new Option("-f, --format ", `output format`).choices(VALID_FORMATS).default("sequence")); + +program.parse(process.argv); +const format = program.opts().format; +const accessions = program.args; + +if (accessions.length !== 0) { + accessions.forEach(processUniprotEntry); +} else { + for await (const line of createInterface({ input: process.stdin })) { + processUniprotEntry(line.trim()); + }; +} + +async function processUniprotEntry(accession) { + process.stdout.write(await getUniprotEntry(accession, format) + "\n"); +} + +async function getUniprotEntry(accession, format) { + if (format === "sequence") { + return (await getUniprotEntry(accession, "fasta")) + .split("\n") + .slice(1) + .join(""); + } else { + const r = await fetch(`https://rest.uniprot.org/uniprotkb/${accession}.${format}`); + if (r.ok) { + return r.text(); + } else { + process.stderr.write(`Error fetching ${accession}: ${r.status} ${r.statusText}\n`); + return ""; + } + } +} diff --git a/package.json b/package.json index 5e12f37a..e07702f5 100644 --- a/package.json +++ b/package.json @@ -6,5 +6,9 @@ "repository": "git@github.com:unipept/unipept-cli.git", "author": "Bart Mesuere ", "license": "MIT", - "private": false + "private": false, + "type": "module", + "dependencies": { + "commander": "^12.1.0" + } } diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..d082ea04 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +commander@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== From f1222746086b964b6749bdc0f6c0cf585e04268a Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 17 Jun 2024 16:18:10 +0200 Subject: [PATCH 05/76] move to bin folder --- index.js => bin/uniprot.js | 2 ++ package.json | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) rename index.js => bin/uniprot.js (98%) mode change 100644 => 100755 diff --git a/index.js b/bin/uniprot.js old mode 100644 new mode 100755 similarity index 98% rename from index.js rename to bin/uniprot.js index 293bd307..6918d6ad --- a/index.js +++ b/bin/uniprot.js @@ -1,3 +1,5 @@ +#!/usr/bin/env node + import { Option, program } from 'commander'; import { createInterface } from 'node:readline'; diff --git a/package.json b/package.json index e07702f5..fec1e02a 100644 --- a/package.json +++ b/package.json @@ -2,12 +2,14 @@ "name": "unipept-cli", "version": "4.0.0", "description": "Command line interface to the Unipept web services", - "main": "index.js", "repository": "git@github.com:unipept/unipept-cli.git", "author": "Bart Mesuere ", "license": "MIT", "private": false, "type": "module", + "bin": { + "uniprot": "./bin/uniprot.js" + }, "dependencies": { "commander": "^12.1.0" } From 691bda3f84a2b0fe80c65c1179e453103f7634f8 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 17 Jun 2024 17:06:15 +0200 Subject: [PATCH 06/76] move to typescript --- .gitignore | 1 + bin/{uniprot.js => uniprot.ts} | 4 +- package.json | 8 ++ tsconfig.json | 10 ++ yarn.lock | 188 +++++++++++++++++++++++++++++++++ 5 files changed, 209 insertions(+), 2 deletions(-) rename bin/{uniprot.js => uniprot.ts} (92%) create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index fb549fa7..bc79950e 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ pkg #*.rbc node_modules/** +dist/** diff --git a/bin/uniprot.js b/bin/uniprot.ts similarity index 92% rename from bin/uniprot.js rename to bin/uniprot.ts index 6918d6ad..8b1abfce 100755 --- a/bin/uniprot.js +++ b/bin/uniprot.ts @@ -33,11 +33,11 @@ if (accessions.length !== 0) { }; } -async function processUniprotEntry(accession) { +async function processUniprotEntry(accession: string) { process.stdout.write(await getUniprotEntry(accession, format) + "\n"); } -async function getUniprotEntry(accession, format) { +async function getUniprotEntry(accession: string, format: string): Promise { if (format === "sequence") { return (await getUniprotEntry(accession, "fasta")) .split("\n") diff --git a/package.json b/package.json index fec1e02a..94cac332 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,15 @@ "bin": { "uniprot": "./bin/uniprot.js" }, + "scripts": { + "build": "yarn run tsc" + }, "dependencies": { "commander": "^12.1.0" + }, + "devDependencies": { + "@types/node": "^20.14.2", + "tsx": "^4.15.6", + "typescript": "^5.4.5" } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..b5f214f7 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "include": [ + "bin/**/*" + ], + "compilerOptions": { + "outDir": "./dist", + "module": "NodeNext", + "strict": true + } +} diff --git a/yarn.lock b/yarn.lock index d082ea04..f6423ed5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,195 @@ # yarn lockfile v1 +"@esbuild/aix-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" + integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== + +"@esbuild/android-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" + integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + +"@esbuild/android-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" + integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== + +"@esbuild/android-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" + integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== + +"@esbuild/darwin-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" + integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + +"@esbuild/darwin-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" + integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== + +"@esbuild/freebsd-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" + integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== + +"@esbuild/freebsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" + integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== + +"@esbuild/linux-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" + integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + +"@esbuild/linux-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" + integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== + +"@esbuild/linux-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" + integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== + +"@esbuild/linux-loong64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" + integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== + +"@esbuild/linux-mips64el@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" + integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== + +"@esbuild/linux-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" + integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== + +"@esbuild/linux-riscv64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" + integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== + +"@esbuild/linux-s390x@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" + integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== + +"@esbuild/linux-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" + integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== + +"@esbuild/netbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" + integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== + +"@esbuild/openbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" + integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== + +"@esbuild/sunos-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" + integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== + +"@esbuild/win32-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" + integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== + +"@esbuild/win32-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" + integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== + +"@esbuild/win32-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" + integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== + +"@types/node@^20.14.2": + version "20.14.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.2.tgz#a5f4d2bcb4b6a87bffcaa717718c5a0f208f4a18" + integrity sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q== + dependencies: + undici-types "~5.26.4" + commander@^12.1.0: version "12.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + +esbuild@~0.21.4: + version "0.21.5" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" + integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== + optionalDependencies: + "@esbuild/aix-ppc64" "0.21.5" + "@esbuild/android-arm" "0.21.5" + "@esbuild/android-arm64" "0.21.5" + "@esbuild/android-x64" "0.21.5" + "@esbuild/darwin-arm64" "0.21.5" + "@esbuild/darwin-x64" "0.21.5" + "@esbuild/freebsd-arm64" "0.21.5" + "@esbuild/freebsd-x64" "0.21.5" + "@esbuild/linux-arm" "0.21.5" + "@esbuild/linux-arm64" "0.21.5" + "@esbuild/linux-ia32" "0.21.5" + "@esbuild/linux-loong64" "0.21.5" + "@esbuild/linux-mips64el" "0.21.5" + "@esbuild/linux-ppc64" "0.21.5" + "@esbuild/linux-riscv64" "0.21.5" + "@esbuild/linux-s390x" "0.21.5" + "@esbuild/linux-x64" "0.21.5" + "@esbuild/netbsd-x64" "0.21.5" + "@esbuild/openbsd-x64" "0.21.5" + "@esbuild/sunos-x64" "0.21.5" + "@esbuild/win32-arm64" "0.21.5" + "@esbuild/win32-ia32" "0.21.5" + "@esbuild/win32-x64" "0.21.5" + +fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +get-tsconfig@^4.7.5: + version "4.7.5" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.5.tgz#5e012498579e9a6947511ed0cd403272c7acbbaf" + integrity sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw== + dependencies: + resolve-pkg-maps "^1.0.0" + +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + +tsx@^4.15.6: + version "4.15.6" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.15.6.tgz#4522ed093f7fa54f031a7a999274e8b35dbf3165" + integrity sha512-is0VQQlfNZRHEuSSTKA6m4xw74IU4AizmuB6lAYLRt9XtuyeQnyJYexhNZOPCB59SqC4JzmSzPnHGBXxf3k0hA== + dependencies: + esbuild "~0.21.4" + get-tsconfig "^4.7.5" + optionalDependencies: + fsevents "~2.3.3" + +typescript@^5.4.5: + version "5.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== From bff18e997208985c272e4705145ee8d4b69f6360 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 17 Jun 2024 17:15:21 +0200 Subject: [PATCH 07/76] cleanup --- VERSION | 1 - bin/uniprot.ts | 30 +++++++++++++++++++++++------- tsconfig.json | 3 ++- 3 files changed, 25 insertions(+), 9 deletions(-) delete mode 100644 VERSION diff --git a/VERSION b/VERSION deleted file mode 100644 index fd2a0186..00000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -3.1.0 diff --git a/bin/uniprot.ts b/bin/uniprot.ts index 8b1abfce..bbe9aab0 100755 --- a/bin/uniprot.ts +++ b/bin/uniprot.ts @@ -2,13 +2,11 @@ import { Option, program } from 'commander'; import { createInterface } from 'node:readline'; +import { version } from '../package.json'; const VALID_FORMATS = ["fasta", "gff", "json", "rdf", "sequence", "xml"]; -program - .version("1.0.0") - .summary("Command line interface to UniProt web services.") - .description(`Command line interface to UniProt web services. +const description = `Command line interface to UniProt web services. The uniprot command fetches UniProt entries from the UniProt web services. The command expects a list of UniProt Accession Numbers that are passed @@ -17,7 +15,12 @@ The uniprot command fetches UniProt entries from the UniProt web services. The c The command will give priority to the first way UniProt Accession Numbers are passed, in the order as listed above. The standard input should have one UniProt Accession Number per line. -The uniprot command yields just the protein sequences as a default, but can return several formats.`) +The uniprot command yields just the protein sequences as a default, but can return several formats.`; + +program + .version(version) + .summary("Command line interface to UniProt web services.") + .description(description) .argument("[accessions...]", "UniProt Accession Numbers") .addOption(new Option("-f, --format ", `output format`).choices(VALID_FORMATS).default("sequence")); @@ -25,19 +28,32 @@ program.parse(process.argv); const format = program.opts().format; const accessions = program.args; -if (accessions.length !== 0) { +if (accessions.length !== 0) { // input from command line arguments accessions.forEach(processUniprotEntry); -} else { +} else { // input from standard input for await (const line of createInterface({ input: process.stdin })) { processUniprotEntry(line.trim()); }; } +/** + * Fetches a UniProt entry and writes it to standard output. + * + * @param accession UniProt Accession Number + */ async function processUniprotEntry(accession: string) { process.stdout.write(await getUniprotEntry(accession, format) + "\n"); } +/** + * Fetches a UniProt entry in the requested format. + * + * @param accession UniProt Accession Number + * @param format output format + * @returns UniProt entry in the requested format + */ async function getUniprotEntry(accession: string, format: string): Promise { + // The UniProt REST API does not support the "sequence" format, so fetch fasta and remove the header if (format === "sequence") { return (await getUniprotEntry(accession, "fasta")) .split("\n") diff --git a/tsconfig.json b/tsconfig.json index b5f214f7..e101d71c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "compilerOptions": { "outDir": "./dist", "module": "NodeNext", - "strict": true + "strict": true, + "resolveJsonModule": true } } From dcaee59b520974c0927ae821972040c9b803b747 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 17 Jun 2024 17:18:48 +0200 Subject: [PATCH 08/76] add uniprot run command --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 94cac332..74077291 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "uniprot": "./bin/uniprot.js" }, "scripts": { - "build": "yarn run tsc" + "build": "yarn run tsc", + "uniprot": "yarn run tsx bin/uniprot.ts" }, "dependencies": { "commander": "^12.1.0" From 71823bf2758cc4794d0c5d317890c0d850e56e75 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 18 Jun 2024 13:50:18 +0200 Subject: [PATCH 09/76] refactor --- bin/uniprot.ts | 71 ++------------------------------- lib/commands/base_command.ts | 31 +++++++++++++++ lib/commands/uniprot.ts | 77 ++++++++++++++++++++++++++++++++++++ tsconfig.json | 3 +- 4 files changed, 113 insertions(+), 69 deletions(-) create mode 100644 lib/commands/base_command.ts create mode 100644 lib/commands/uniprot.ts diff --git a/bin/uniprot.ts b/bin/uniprot.ts index bbe9aab0..7746fac9 100755 --- a/bin/uniprot.ts +++ b/bin/uniprot.ts @@ -1,71 +1,6 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; -import { createInterface } from 'node:readline'; -import { version } from '../package.json'; +import { Uniprot } from '../lib/commands/uniprot.js'; -const VALID_FORMATS = ["fasta", "gff", "json", "rdf", "sequence", "xml"]; - -const description = `Command line interface to UniProt web services. - -The uniprot command fetches UniProt entries from the UniProt web services. The command expects a list of UniProt Accession Numbers that are passed - -- as separate command line arguments -- to standard input - -The command will give priority to the first way UniProt Accession Numbers are passed, in the order as listed above. The standard input should have one UniProt Accession Number per line. - -The uniprot command yields just the protein sequences as a default, but can return several formats.`; - -program - .version(version) - .summary("Command line interface to UniProt web services.") - .description(description) - .argument("[accessions...]", "UniProt Accession Numbers") - .addOption(new Option("-f, --format ", `output format`).choices(VALID_FORMATS).default("sequence")); - -program.parse(process.argv); -const format = program.opts().format; -const accessions = program.args; - -if (accessions.length !== 0) { // input from command line arguments - accessions.forEach(processUniprotEntry); -} else { // input from standard input - for await (const line of createInterface({ input: process.stdin })) { - processUniprotEntry(line.trim()); - }; -} - -/** - * Fetches a UniProt entry and writes it to standard output. - * - * @param accession UniProt Accession Number - */ -async function processUniprotEntry(accession: string) { - process.stdout.write(await getUniprotEntry(accession, format) + "\n"); -} - -/** - * Fetches a UniProt entry in the requested format. - * - * @param accession UniProt Accession Number - * @param format output format - * @returns UniProt entry in the requested format - */ -async function getUniprotEntry(accession: string, format: string): Promise { - // The UniProt REST API does not support the "sequence" format, so fetch fasta and remove the header - if (format === "sequence") { - return (await getUniprotEntry(accession, "fasta")) - .split("\n") - .slice(1) - .join(""); - } else { - const r = await fetch(`https://rest.uniprot.org/uniprotkb/${accession}.${format}`); - if (r.ok) { - return r.text(); - } else { - process.stderr.write(`Error fetching ${accession}: ${r.status} ${r.statusText}\n`); - return ""; - } - } -} +const command = new Uniprot(); +command.run(); diff --git a/lib/commands/base_command.ts b/lib/commands/base_command.ts new file mode 100644 index 00000000..e9980643 --- /dev/null +++ b/lib/commands/base_command.ts @@ -0,0 +1,31 @@ +import { Command } from "commander"; +import { version } from '../../package.json'; + +export abstract class BaseCommand { + public program: Command; + + constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean }) { + this.program = this.create(options); + } + + abstract run(): void; + + create(options?: { exitOverride?: boolean, suppressOutput?: boolean }): Command { + const program = new Command(); + + // used for debugging + if (options?.exitOverride) { + program.exitOverride(); // don't exit on error + } + if (options?.suppressOutput) { + program.configureOutput({ + writeOut: () => { }, + writeErr: () => { } + }); + } + + program.version(version); + + return program; + } +} diff --git a/lib/commands/uniprot.ts b/lib/commands/uniprot.ts new file mode 100644 index 00000000..1e07a7e7 --- /dev/null +++ b/lib/commands/uniprot.ts @@ -0,0 +1,77 @@ +import { Option } from 'commander'; +import { createInterface } from 'node:readline'; +import { BaseCommand } from './base_command.js'; + +export class Uniprot extends BaseCommand { + readonly VALID_FORMATS = ["fasta", "gff", "json", "rdf", "sequence", "xml"]; + + readonly description = `Command line interface to UniProt web services. + +The uniprot command fetches UniProt entries from the UniProt web services. The command expects a list of UniProt Accession Numbers that are passed + +- as separate command line arguments +- to standard input + +The command will give priority to the first way UniProt Accession Numbers are passed, in the order as listed above. The standard input should have one UniProt Accession Number per line. + +The uniprot command yields just the protein sequences as a default, but can return several formats.`; + format = "sequence"; + + constructor() { + super(); + + this.program + .summary("Command line interface to UniProt web services.") + .description(this.description) + .argument("[accessions...]", "UniProt Accession Numbers") + .addOption(new Option("-f, --format ", `output format`).choices(this.VALID_FORMATS).default("sequence")); + } + + async run() { + this.program.parse(process.argv); + this.format = this.program.opts().format; + const accessions = this.program.args; + + if (accessions.length !== 0) { // input from command line arguments + accessions.forEach(this.processUniprotEntry); + } else { // input from standard input + for await (const line of createInterface({ input: process.stdin })) { + this.processUniprotEntry(line.trim()); + }; + } + } + + /** + * Fetches a UniProt entry and writes it to standard output. + * + * @param accession UniProt Accession Number + */ + async processUniprotEntry(accession: string) { + process.stdout.write(await Uniprot.getUniprotEntry(accession, this.format) + "\n"); + } + + /** + * Fetches a UniProt entry in the requested format. + * + * @param accession UniProt Accession Number + * @param format output format + * @returns UniProt entry in the requested format + */ + static async getUniprotEntry(accession: string, format: string): Promise { + // The UniProt REST API does not support the "sequence" format, so fetch fasta and remove the header + if (format === "sequence") { + return (await Uniprot.getUniprotEntry(accession, "fasta")) + .split("\n") + .slice(1) + .join(""); + } else { + const r = await fetch(`https://rest.uniprot.org/uniprotkb/${accession}.${format}`); + if (r.ok) { + return r.text(); + } else { + process.stderr.write(`Error fetching ${accession}: ${r.status} ${r.statusText}\n`); + return ""; + } + } + } +} diff --git a/tsconfig.json b/tsconfig.json index e101d71c..bfb2d08f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "include": [ - "bin/**/*" + "bin/**/*", + "lib/**/*", ], "compilerOptions": { "outDir": "./dist", From dc6c822adb02b0f5ba0bbaa31095edaff7826db4 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 18 Jun 2024 14:03:02 +0200 Subject: [PATCH 10/76] add eslint --- eslint.config.js | 11 + package.json | 9 +- yarn.lock | 809 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 828 insertions(+), 1 deletion(-) create mode 100644 eslint.config.js diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000..17c0f257 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,11 @@ +import globals from "globals"; +import pluginJs from "@eslint/js"; +import tseslint from "typescript-eslint"; + + +export default [ + { languageOptions: { globals: globals.node } }, + pluginJs.configs.recommended, + ...tseslint.configs.recommended, + { ignores: ["dist/"] } +]; diff --git a/package.json b/package.json index 74077291..fc3569b4 100644 --- a/package.json +++ b/package.json @@ -12,14 +12,21 @@ }, "scripts": { "build": "yarn run tsc", + "lint": "yarn run eslint", "uniprot": "yarn run tsx bin/uniprot.ts" }, "dependencies": { "commander": "^12.1.0" }, "devDependencies": { + "@eslint/js": "^9.5.0", "@types/node": "^20.14.2", + "@typescript-eslint/eslint-plugin": "^7.13.1", + "@typescript-eslint/parser": "^7.13.1", + "eslint": "9.x", + "globals": "^15.6.0", "tsx": "^4.15.6", - "typescript": "^5.4.5" + "typescript": "^5.4.5", + "typescript-eslint": "^7.13.1" } } diff --git a/yarn.lock b/yarn.lock index f6423ed5..8fa12631 100644 --- a/yarn.lock +++ b/yarn.lock @@ -117,6 +117,83 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": + version "4.10.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.1.tgz#361461e5cb3845d874e61731c11cfedd664d83a0" + integrity sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA== + +"@eslint/config-array@^0.16.0": + version "0.16.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.16.0.tgz#bb3364fc39ee84ec3a62abdc4b8d988d99dfd706" + integrity sha512-/jmuSd74i4Czf1XXn7wGRWZCuyaUZ330NH1Bek0Pplatt4Sy1S5haN21SCLLdbeKslQ+S0wEJ+++v5YibSi+Lg== + dependencies: + "@eslint/object-schema" "^2.1.4" + debug "^4.3.1" + minimatch "^3.0.5" + +"@eslint/eslintrc@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6" + integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@9.5.0", "@eslint/js@^9.5.0": + version "9.5.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.5.0.tgz#0e9c24a670b8a5c86bff97b40be13d8d8f238045" + integrity sha512-A7+AOT2ICkodvtsWnxZP4Xxk3NbZ3VMHd8oihydLRGrJgqqdEz1qSeEgXYyT/Cu8h1TWWsQRejIx48mtjZ5y1w== + +"@eslint/object-schema@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843" + integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/retry@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570" + integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@types/node@^20.14.2": version "20.14.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.2.tgz#a5f4d2bcb4b6a87bffcaa717718c5a0f208f4a18" @@ -124,11 +201,219 @@ dependencies: undici-types "~5.26.4" +"@typescript-eslint/eslint-plugin@7.13.1", "@typescript-eslint/eslint-plugin@^7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.1.tgz#cdc521c8bca38b55585cf30db787fb2abad3f9fd" + integrity sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "7.13.1" + "@typescript-eslint/type-utils" "7.13.1" + "@typescript-eslint/utils" "7.13.1" + "@typescript-eslint/visitor-keys" "7.13.1" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/parser@7.13.1", "@typescript-eslint/parser@^7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.13.1.tgz#fac57811b3e519185f7259bac312291f7b9c4e72" + integrity sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A== + dependencies: + "@typescript-eslint/scope-manager" "7.13.1" + "@typescript-eslint/types" "7.13.1" + "@typescript-eslint/typescript-estree" "7.13.1" + "@typescript-eslint/visitor-keys" "7.13.1" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz#c08041206904bf36f0e6997efdb0ca775e0c452e" + integrity sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg== + dependencies: + "@typescript-eslint/types" "7.13.1" + "@typescript-eslint/visitor-keys" "7.13.1" + +"@typescript-eslint/type-utils@7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.13.1.tgz#63bec3f1fb43cf0bc409cbdb88ef96d118ca8632" + integrity sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg== + dependencies: + "@typescript-eslint/typescript-estree" "7.13.1" + "@typescript-eslint/utils" "7.13.1" + debug "^4.3.4" + ts-api-utils "^1.3.0" + +"@typescript-eslint/types@7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.13.1.tgz#787db283bd0b58751094c90d5b58bbf5e9fc9bd8" + integrity sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw== + +"@typescript-eslint/typescript-estree@7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz#3412841b130e070db2f675e3d9b8cb1ae49e1c3f" + integrity sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw== + dependencies: + "@typescript-eslint/types" "7.13.1" + "@typescript-eslint/visitor-keys" "7.13.1" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/utils@7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.13.1.tgz#611083379caa0d3a2c09d126c65065a3e4337ba2" + integrity sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "7.13.1" + "@typescript-eslint/types" "7.13.1" + "@typescript-eslint/typescript-estree" "7.13.1" + +"@typescript-eslint/visitor-keys@7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz#9c229a795a919db61f2d7f2337ef584ac05fbe96" + integrity sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA== + dependencies: + "@typescript-eslint/types" "7.13.1" + eslint-visitor-keys "^3.4.3" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.12.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c" + integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.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" + +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-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== + +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== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.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" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +chalk@^4.0.0: + 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" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + commander@^12.1.0: version "12.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== +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== + +cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +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" + esbuild@~0.21.4: version "0.21.5" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" @@ -158,6 +443,170 @@ esbuild@~0.21.4: "@esbuild/win32-ia32" "0.21.5" "@esbuild/win32-x64" "0.21.5" +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@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.0.1.tgz#a9601e4b81a0b9171657c343fb13111688963cfc" + integrity sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb" + integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw== + +eslint@9.x: + version "9.5.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.5.0.tgz#11856034b94a9e1a02cfcc7e96a9f0956963cd2f" + integrity sha512-+NAOZFrW/jFTS3dASCGBxX1pkFD0/fsO+hfAkJ4TyYKwgsXZbqzrw+seCYFCcPCYXvnD67tAnglU7GQTz6kcVw== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/config-array" "^0.16.0" + "@eslint/eslintrc" "^3.1.0" + "@eslint/js" "9.5.0" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.3.0" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.0.1" + eslint-visitor-keys "^4.0.0" + espree "^10.0.1" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^10.0.1: + version "10.1.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.1.0.tgz#8788dae611574c0f070691f522e4116c5a11fc56" + integrity sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA== + dependencies: + acorn "^8.12.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.0.0" + +esquery@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +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.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + +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" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" @@ -170,11 +619,331 @@ get-tsconfig@^4.7.5: dependencies: resolve-pkg-maps "^1.0.0" +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globals@^15.6.0: + version "15.6.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-15.6.0.tgz#3872d3ab4427e1df4718efd3f7d2c721c503f65f" + integrity sha512-UzcJi88Hw//CurUIRa9Jxb0vgOCcuD/MNjwmXp633cyaRKkCWACkoqHCtfZv43b1kqXGg/fpOa8bwgacCeXsVg== + +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" + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +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== + +ignore@^5.2.0, ignore@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +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-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-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-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +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== + +micromatch@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +minimatch@^3.0.5, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.4: + version "9.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + +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== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +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-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-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +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== + +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-pkg-maps@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== +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== + +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" + +semver@^7.6.0: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +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== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +ts-api-utils@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + tsx@^4.15.6: version "4.15.6" resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.15.6.tgz#4522ed093f7fa54f031a7a999274e8b35dbf3165" @@ -185,6 +954,22 @@ tsx@^4.15.6: optionalDependencies: fsevents "~2.3.3" +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +typescript-eslint@^7.13.1: + version "7.13.1" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-7.13.1.tgz#8bbcc4b59b6bb0c457505ee17a356b1868c3fcd5" + integrity sha512-pvLEuRs8iS9s3Cnp/Wt//hpK8nKc8hVa3cLljHqzaJJQYP8oys8GUyIFqtlev+2lT/fqMPcyQko+HJ6iYK3nFA== + dependencies: + "@typescript-eslint/eslint-plugin" "7.13.1" + "@typescript-eslint/parser" "7.13.1" + "@typescript-eslint/utils" "7.13.1" + typescript@^5.4.5: version "5.4.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" @@ -194,3 +979,27 @@ undici-types@~5.26.4: version "5.26.5" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +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" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +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== From 5ba8ae294a790c7dd93259588dfea8c825112e50 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 18 Jun 2024 14:08:38 +0200 Subject: [PATCH 11/76] add github action --- .github/workflows/ci.yml | 36 +++++++++++++----------------------- package.json | 1 + 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 273acce5..ef58d4e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,32 +3,22 @@ name: CI on: [push] jobs: - test: - runs-on: ubuntu-latest - strategy: - matrix: - ruby: ["2.7", "3.0", "3.1", "3.2"] - fail-fast: false - name: Test Ruby ${{ matrix.ruby }} - steps: - - uses: actions/checkout@v1 - - name: Setup Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - bundler-cache: true - - name: Test - run: | - bundle exec rake test lint: name: Lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - name: Setup Ruby - uses: ruby/setup-ruby@v1 + - uses: actions/checkout@v4 + - name: Set up Node + uses: actions/setup-node@v4 with: - bundler-cache: true - - name: Test + node-version: 22.x + cache: yarn + - name: Install dependencies + run: | + yarn install + - name: Lint + run: | + yarn lint + - name: Type check run: | - bundle exec rake test_style + yarn typecheck diff --git a/package.json b/package.json index fc3569b4..dd419f31 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "scripts": { "build": "yarn run tsc", "lint": "yarn run eslint", + "typecheck": "yarn tsc --skipLibCheck --noEmit", "uniprot": "yarn run tsx bin/uniprot.ts" }, "dependencies": { From 5077b025513e597a4b7061b46f0834848b4fd184 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 18 Jun 2024 16:40:59 +0200 Subject: [PATCH 12/76] refactor --- lib/commands/base_command.ts | 13 ++++++++++++- lib/commands/uniprot.ts | 23 ++++++++++------------- tsconfig.json | 2 +- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/lib/commands/base_command.ts b/lib/commands/base_command.ts index e9980643..14a3b934 100644 --- a/lib/commands/base_command.ts +++ b/lib/commands/base_command.ts @@ -3,9 +3,11 @@ import { version } from '../../package.json'; export abstract class BaseCommand { public program: Command; + args: string[] | undefined; - constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean }) { + constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean, args?: string[] }) { this.program = this.create(options); + this.args = options?.args; } abstract run(): void; @@ -28,4 +30,13 @@ export abstract class BaseCommand { return program; } + + parseArguments() { + if (this.args) { + // custom arg parsing to be able to inject args for testing + this.program.parse(this.args, { from: "user" }); + } else { + this.program.parse(); + } + } } diff --git a/lib/commands/uniprot.ts b/lib/commands/uniprot.ts index 1e07a7e7..51c32629 100644 --- a/lib/commands/uniprot.ts +++ b/lib/commands/uniprot.ts @@ -15,10 +15,9 @@ The uniprot command fetches UniProt entries from the UniProt web services. The c The command will give priority to the first way UniProt Accession Numbers are passed, in the order as listed above. The standard input should have one UniProt Accession Number per line. The uniprot command yields just the protein sequences as a default, but can return several formats.`; - format = "sequence"; - constructor() { - super(); + constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean, args?: string[] }) { + super(options); this.program .summary("Command line interface to UniProt web services.") @@ -28,16 +27,14 @@ The uniprot command yields just the protein sequences as a default, but can retu } async run() { - this.program.parse(process.argv); - this.format = this.program.opts().format; + this.parseArguments(); + const format = this.program.opts().format; const accessions = this.program.args; - if (accessions.length !== 0) { // input from command line arguments - accessions.forEach(this.processUniprotEntry); - } else { // input from standard input - for await (const line of createInterface({ input: process.stdin })) { - this.processUniprotEntry(line.trim()); - }; + // alternatively, we can also wrap the array in a Readable stream with ReadableStream.from() + const input = accessions.length !== 0 ? accessions : createInterface({ input: process.stdin }); + for await (const line of input) { + await Uniprot.processUniprotEntry(line.trim(), format); } } @@ -46,8 +43,8 @@ The uniprot command yields just the protein sequences as a default, but can retu * * @param accession UniProt Accession Number */ - async processUniprotEntry(accession: string) { - process.stdout.write(await Uniprot.getUniprotEntry(accession, this.format) + "\n"); + static async processUniprotEntry(accession: string, format: string) { + process.stdout.write(await Uniprot.getUniprotEntry(accession, format) + "\n"); } /** diff --git a/tsconfig.json b/tsconfig.json index bfb2d08f..5c9bef88 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,6 @@ "outDir": "./dist", "module": "NodeNext", "strict": true, - "resolveJsonModule": true + "resolveJsonModule": true, } } From d6ed622a82c0eb9bcacba6a81e4d8d4f7bc2f513 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 18 Jun 2024 17:32:46 +0200 Subject: [PATCH 13/76] add tests --- jest.config.ts | 203 ++++ lib/commands/uniprot.ts | 4 +- package.json | 6 + tests/commands/uniprot.test.ts | 93 ++ yarn.lock | 2060 +++++++++++++++++++++++++++++++- 5 files changed, 2352 insertions(+), 14 deletions(-) create mode 100644 jest.config.ts create mode 100644 tests/commands/uniprot.test.ts diff --git a/jest.config.ts b/jest.config.ts new file mode 100644 index 00000000..9627c0f6 --- /dev/null +++ b/jest.config.ts @@ -0,0 +1,203 @@ +/** + * For a detailed explanation regarding each configuration property, visit: + * https://jestjs.io/docs/configuration + */ + +import type { Config } from 'jest'; + +const config: Config = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/private/var/folders/j3/38fskpy159v07np8syk3p_2m0000gn/T/jest_dx", + + // Automatically clear mock calls, instances, contexts and results before every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + // collectCoverage: false, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + // collectCoverageFrom: undefined, + + // The directory where Jest should output its coverage files + // coverageDirectory: undefined, + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // "/node_modules/" + // ], + + // Indicates which provider should be used to instrument code for coverage + coverageProvider: "v8", + + // A list of reporter names that Jest uses when writing coverage reports + // coverageReporters: [ + // "json", + // "text", + // "lcov", + // "clover" + // ], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, + + // A path to a custom dependency extractor + // dependencyExtractor: undefined, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // The default configuration for fake timers + // fakeTimers: { + // "enableGlobally": false + // }, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, + + // A set of global variables that need to be available in all test environments + // globals: {}, + + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", + + // An array of directory names to be searched recursively up from the requiring module's location + // moduleDirectories: [ + // "node_modules" + // ], + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "mjs", + // "cjs", + // "jsx", + // "ts", + // "tsx", + // "json", + // "node" + // ], + + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + moduleNameMapper: { + "(.+)\\.js": "$1", + }, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: undefined, + + // Run tests from one or more projects + // projects: undefined, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state before every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: undefined, + + // Automatically restore mock state and implementation before every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: undefined, + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + // testEnvironment: "jest-environment-node", + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + // testMatch: [ + // "**/__tests__/**/*.[jt]s?(x)", + // "**/?(*.)+(spec|test).[tj]s?(x)" + // ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jest-circus/runner", + + // A map from regular expressions to paths to transformers + transform: { + "^.+\\.tsx?$": ["ts-jest", { "useESM": true, "diagnostics": { "ignoreCodes": ["TS151001"] } }], + }, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // "/node_modules/", + // "\\.pnp\\.[^\\/]+$" + // ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: true, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; + +export default config; diff --git a/lib/commands/uniprot.ts b/lib/commands/uniprot.ts index 51c32629..11f5e57b 100644 --- a/lib/commands/uniprot.ts +++ b/lib/commands/uniprot.ts @@ -3,7 +3,7 @@ import { createInterface } from 'node:readline'; import { BaseCommand } from './base_command.js'; export class Uniprot extends BaseCommand { - readonly VALID_FORMATS = ["fasta", "gff", "json", "rdf", "sequence", "xml"]; + static readonly VALID_FORMATS = ["fasta", "gff", "json", "rdf", "sequence", "xml"]; readonly description = `Command line interface to UniProt web services. @@ -23,7 +23,7 @@ The uniprot command yields just the protein sequences as a default, but can retu .summary("Command line interface to UniProt web services.") .description(this.description) .argument("[accessions...]", "UniProt Accession Numbers") - .addOption(new Option("-f, --format ", `output format`).choices(this.VALID_FORMATS).default("sequence")); + .addOption(new Option("-f, --format ", `output format`).choices(Uniprot.VALID_FORMATS).default("sequence")); } async run() { diff --git a/package.json b/package.json index dd419f31..71171b87 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "scripts": { "build": "yarn run tsc", "lint": "yarn run eslint", + "test": "yarn run jest", "typecheck": "yarn tsc --skipLibCheck --noEmit", "uniprot": "yarn run tsx bin/uniprot.ts" }, @@ -21,11 +22,16 @@ }, "devDependencies": { "@eslint/js": "^9.5.0", + "@types/jest": "^29.5.12", "@types/node": "^20.14.2", "@typescript-eslint/eslint-plugin": "^7.13.1", "@typescript-eslint/parser": "^7.13.1", "eslint": "9.x", "globals": "^15.6.0", + "jest": "^29.7.0", + "mock-stdin": "^1.0.0", + "ts-jest": "^29.1.5", + "ts-node": "^10.9.2", "tsx": "^4.15.6", "typescript": "^5.4.5", "typescript-eslint": "^7.13.1" diff --git a/tests/commands/uniprot.test.ts b/tests/commands/uniprot.test.ts new file mode 100644 index 00000000..ca72837b --- /dev/null +++ b/tests/commands/uniprot.test.ts @@ -0,0 +1,93 @@ +import { Uniprot } from '../../lib/commands/uniprot'; +import * as mock from 'mock-stdin'; + +let output: string[]; +let error: string[]; +const writeSpy = jest + .spyOn(process.stdout, "write") + .mockImplementation((data: unknown) => { output.push(data as string); return true; }); +const errorSpy = jest + .spyOn(process.stderr, "write") + .mockImplementation((data: unknown) => { error.push(data as string); return true; }); + +beforeEach(() => { + output = []; + error = []; +}); + +test('test single argument', async () => { + const command = new Uniprot({ args: ["Q6GZX3"] }); + await command.run(); + + expect(writeSpy).toHaveBeenCalledTimes(1); + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.length).toBe(1); +}); + +test('test two arguments', async () => { + const command = new Uniprot({ args: ["Q6GZX3", "Q6GZX4"] }); + await command.run(); + + expect(writeSpy).toHaveBeenCalledTimes(2); + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.length).toBe(2); +}); + +test('test fasta output', async () => { + const command = new Uniprot({ args: ["--format", "fasta", "Q6GZX3", "Q6GZX4"] }); + await command.run(); + + expect(writeSpy).toHaveBeenCalledTimes(2); + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.length).toBe(2); + expect(output[0].startsWith(">")).toBe(true); +}); + +test('test single line stdin', async () => { + const stdin = mock.stdin(); + + const command = new Uniprot(); + const run = command.run(); + + stdin.send("Q6GZX3\n"); + stdin.end(); + + await run; + + expect(writeSpy).toHaveBeenCalledTimes(1); + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.length).toBe(1); +}); + +test('test double line stdin', async () => { + const stdin = mock.stdin(); + + const command = new Uniprot(); + const run = command.run(); + + stdin.send("Q6GZX3\n"); + stdin.send("Q6GZX4\n"); + stdin.end(); + + await run; + + expect(writeSpy).toHaveBeenCalledTimes(2); + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.length).toBe(2); +}); + +test('test on invalid id', async () => { + const command = new Uniprot({ args: ["Bart"] }); + await command.run(); + + expect(errorSpy).toHaveBeenCalledTimes(1); +}); + +test('test all valid formats', async () => { + for (const format of Uniprot.VALID_FORMATS) { + const command = new Uniprot({ args: ["--format", format, "Q6GZX3"] }); + await command.run(); + + expect(errorSpy).toHaveBeenCalledTimes(0); + } +}); diff --git a/yarn.lock b/yarn.lock index 8fa12631..bb0cd995 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,312 @@ # yarn lockfile v1 +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" + integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== + dependencies: + "@babel/highlight" "^7.24.7" + picocolors "^1.0.0" + +"@babel/compat-data@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.7.tgz#d23bbea508c3883ba8251fb4164982c36ea577ed" + integrity sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw== + +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.7.tgz#b676450141e0b52a3d43bc91da86aa608f950ac4" + integrity sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.24.7" + "@babel/helper-compilation-targets" "^7.24.7" + "@babel/helper-module-transforms" "^7.24.7" + "@babel/helpers" "^7.24.7" + "@babel/parser" "^7.24.7" + "@babel/template" "^7.24.7" + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.24.7", "@babel/generator@^7.7.2": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.7.tgz#1654d01de20ad66b4b4d99c135471bc654c55e6d" + integrity sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA== + dependencies: + "@babel/types" "^7.24.7" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + +"@babel/helper-compilation-targets@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz#4eb6c4a80d6ffeac25ab8cd9a21b5dfa48d503a9" + integrity sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg== + dependencies: + "@babel/compat-data" "^7.24.7" + "@babel/helper-validator-option" "^7.24.7" + browserslist "^4.22.2" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-environment-visitor@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz#4b31ba9551d1f90781ba83491dd59cf9b269f7d9" + integrity sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ== + dependencies: + "@babel/types" "^7.24.7" + +"@babel/helper-function-name@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz#75f1e1725742f39ac6584ee0b16d94513da38dd2" + integrity sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA== + dependencies: + "@babel/template" "^7.24.7" + "@babel/types" "^7.24.7" + +"@babel/helper-hoist-variables@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz#b4ede1cde2fd89436397f30dc9376ee06b0f25ee" + integrity sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ== + dependencies: + "@babel/types" "^7.24.7" + +"@babel/helper-module-imports@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz#f2f980392de5b84c3328fc71d38bd81bbb83042b" + integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + +"@babel/helper-module-transforms@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz#31b6c9a2930679498db65b685b1698bfd6c7daf8" + integrity sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ== + dependencies: + "@babel/helper-environment-visitor" "^7.24.7" + "@babel/helper-module-imports" "^7.24.7" + "@babel/helper-simple-access" "^7.24.7" + "@babel/helper-split-export-declaration" "^7.24.7" + "@babel/helper-validator-identifier" "^7.24.7" + +"@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.14.5", "@babel/helper-plugin-utils@^7.24.7", "@babel/helper-plugin-utils@^7.8.0": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz#98c84fe6fe3d0d3ae7bfc3a5e166a46844feb2a0" + integrity sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg== + +"@babel/helper-simple-access@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz#bcade8da3aec8ed16b9c4953b74e506b51b5edb3" + integrity sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + +"@babel/helper-split-export-declaration@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz#83949436890e07fa3d6873c61a96e3bbf692d856" + integrity sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA== + dependencies: + "@babel/types" "^7.24.7" + +"@babel/helper-string-parser@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz#4d2d0f14820ede3b9807ea5fc36dfc8cd7da07f2" + integrity sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg== + +"@babel/helper-validator-identifier@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" + integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== + +"@babel/helper-validator-option@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz#24c3bb77c7a425d1742eec8fb433b5a1b38e62f6" + integrity sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw== + +"@babel/helpers@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.7.tgz#aa2ccda29f62185acb5d42fb4a3a1b1082107416" + integrity sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg== + dependencies: + "@babel/template" "^7.24.7" + "@babel/types" "^7.24.7" + +"@babel/highlight@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" + integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== + dependencies: + "@babel/helper-validator-identifier" "^7.24.7" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.7.tgz#9a5226f92f0c5c8ead550b750f5608e766c8ce85" + integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw== + +"@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-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.8.3": + 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-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@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.7.2": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz#39a1fa4a7e3d3d7f34e2acc6be585b718d30e02d" + integrity sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + 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.8.3": + 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": + 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-top-level-await@^7.8.3": + 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.7.2": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz#58d458271b4d3b6bb27ee6ac9525acbb259bad1c" + integrity sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + +"@babel/template@^7.24.7", "@babel/template@^7.3.3": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.7.tgz#02efcee317d0609d2c07117cb70ef8fb17ab7315" + integrity sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/parser" "^7.24.7" + "@babel/types" "^7.24.7" + +"@babel/traverse@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.7.tgz#de2b900163fa741721ba382163fe46a936c40cf5" + integrity sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.24.7" + "@babel/helper-environment-visitor" "^7.24.7" + "@babel/helper-function-name" "^7.24.7" + "@babel/helper-hoist-variables" "^7.24.7" + "@babel/helper-split-export-declaration" "^7.24.7" + "@babel/parser" "^7.24.7" + "@babel/types" "^7.24.7" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.24.7", "@babel/types@^7.3.3": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.7.tgz#6027fe12bc1aa724cd32ab113fb7f1988f1f66f2" + integrity sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q== + dependencies: + "@babel/helper-string-parser" "^7.24.7" + "@babel/helper-validator-identifier" "^7.24.7" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + "@esbuild/aix-ppc64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" @@ -173,6 +479,254 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570" integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== + dependencies: + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== + dependencies: + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== + dependencies: + expect "^29.7.0" + jest-snapshot "^29.7.0" + +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== + dependencies: + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" + +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" + +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^6.0.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" + callsites "^3.0.0" + graceful-fs "^4.2.9" + +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== + dependencies: + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== + dependencies: + "@jest/test-result" "^29.7.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + slash "^3.0.0" + +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@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.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@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" @@ -194,6 +748,119 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sinonjs/commons@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/babel__core@^7.1.14": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.8" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.20.6" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" + integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== + dependencies: + "@babel/types" "^7.20.7" + +"@types/graceful-fs@^4.1.3": + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^29.5.12": + version "29.5.12" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544" + integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/node@*": + version "20.14.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.5.tgz#fe35e3022ebe58b8f201580eb24e1fcfc0f2487d" + integrity sha512-aoRR+fJkZT2l0aGOJhuA8frnCSoNX6W7U2mpNq63+BxBIj5BQFt8rHy627kijCmm63ijdSdwvGgpUsU6MBsZZA== + dependencies: + undici-types "~5.26.4" + "@types/node@^20.14.2": version "20.14.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.2.tgz#a5f4d2bcb4b6a87bffcaa717718c5a0f208f4a18" @@ -201,6 +868,23 @@ dependencies: undici-types "~5.26.4" +"@types/stack-utils@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== + +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^17.0.8": + version "17.0.32" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" + integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== + dependencies: + "@types/yargs-parser" "*" + "@typescript-eslint/eslint-plugin@7.13.1", "@typescript-eslint/eslint-plugin@^7.13.1": version "7.13.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.1.tgz#cdc521c8bca38b55585cf30db787fb2abad3f9fd" @@ -287,7 +971,14 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^8.12.0: +acorn-walk@^8.1.1: + version "8.3.3" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e" + integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw== + dependencies: + acorn "^8.11.0" + +acorn@^8.11.0, acorn@^8.12.0, acorn@^8.4.1: version "8.12.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c" integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== @@ -302,18 +993,57 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^4.1.0: +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@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +anymatch@^3.0.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^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" @@ -324,6 +1054,66 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== + dependencies: + "@jest/transform" "^29.7.0" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.6.3" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@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-top-level-await" "^7.8.3" + +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== + dependencies: + babel-plugin-jest-hoist "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -351,11 +1141,64 @@ braces@^3.0.3: dependencies: fill-range "^7.1.1" +browserslist@^4.22.2: + version "4.23.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.1.tgz#ce4af0534b3d37db5c1a4ca98b9080f985041e96" + integrity sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw== + dependencies: + caniuse-lite "^1.0.30001629" + electron-to-chromium "^1.4.796" + node-releases "^2.0.14" + update-browserslist-db "^1.0.16" + +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +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== + 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== +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001629: + version "1.0.30001636" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz#b15f52d2bdb95fad32c2f53c0b68032b85188a78" + integrity sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg== + +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.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -364,6 +1207,47 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +cjs-module-lexer@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c" + integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q== + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +collect-v8-coverage@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== + +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" @@ -371,6 +1255,11 @@ color-convert@^2.0.1: 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" @@ -386,7 +1275,30 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -cross-spawn@^7.0.2: +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^7.0.2, 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== @@ -395,18 +1307,43 @@ cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" -debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.5" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== dependencies: ms "2.1.2" +dedent@^1.0.0: + version "1.5.3" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" + integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -414,6 +1351,28 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +electron-to-chromium@^1.4.796: + version "1.4.805" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.805.tgz#1d526e384c20944a3c68f618f9774edc384c4733" + integrity sha512-8W4UJwX/w9T0QSzINJckTKG6CYpAUTqsaWcWIsdud3I1FYJcMgW9QqT1/4CBff/pP/TihWh13OmiyY8neto6vw== + +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + +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== + +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" + esbuild@~0.21.4: version "0.21.5" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" @@ -443,6 +1402,21 @@ esbuild@~0.21.4: "@esbuild/win32-ia32" "0.21.5" "@esbuild/win32-x64" "0.21.5" +escalade@^3.1.1, escalade@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +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@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + 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" @@ -515,6 +1489,11 @@ espree@^10.0.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^4.0.0" +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== + esquery@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" @@ -539,6 +1518,37 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +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" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expect@^29.0.0, expect@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + 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" @@ -555,7 +1565,7 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.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== @@ -572,6 +1582,13 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + dependencies: + bser "2.1.1" + file-entry-cache@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" @@ -586,6 +1603,14 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" +find-up@^4.0.0, find-up@^4.1.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" @@ -607,11 +1632,41 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== -fsevents@~2.3.3: +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, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +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-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +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== + get-tsconfig@^4.7.5: version "4.7.5" resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.5.tgz#5e012498579e9a6947511ed0cd403272c7acbbaf" @@ -633,6 +1688,23 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +glob@^7.1.3, glob@^7.1.4: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^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== + globals@^14.0.0: version "14.0.0" resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" @@ -655,16 +1727,43 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + graphemer@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== +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== +hasown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +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== + ignore@^5.2.0, ignore@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" @@ -678,16 +1777,59 @@ import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +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-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + 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-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -705,11 +1847,440 @@ is-path-inside@^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-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== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-instrument@^5.0.4: + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-instrument@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz#91655936cf7380e4e473383081e38478b69993b1" + integrity sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw== + dependencies: + "@babel/core" "^7.23.9" + "@babel/parser" "^7.23.9" + "@istanbuljs/schema" "^0.1.3" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + +istanbul-lib-report@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.1.3: + version "3.1.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== + dependencies: + execa "^5.0.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^1.0.0" + is-generator-fn "^2.0.0" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + pretty-format "^29.7.0" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== + dependencies: + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + chalk "^4.0.0" + create-jest "^29.7.0" + exit "^0.1.2" + import-local "^3.0.2" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + yargs "^17.3.1" + +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== + dependencies: + detect-newline "^3.0.0" + +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" + +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + jest-util "^29.7.0" + +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== + dependencies: + "@jest/types" "^29.6.3" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== + dependencies: + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-util "^29.7.0" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== + +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== + dependencies: + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" + +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-pnp-resolver "^1.2.2" + jest-util "^29.7.0" + jest-validate "^29.7.0" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== + dependencies: + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.7.0" + graceful-fs "^4.2.9" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + natural-compare "^1.4.0" + pretty-format "^29.7.0" + semver "^7.5.3" + +jest-util@^29.0.0, jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== + dependencies: + "@jest/types" "^29.6.3" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.6.3" + leven "^3.1.0" + pretty-format "^29.7.0" + +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== + dependencies: + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.7.0" + string-length "^4.0.1" + +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== + dependencies: + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" + import-local "^3.0.2" + jest-cli "^29.7.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" @@ -717,11 +2288,21 @@ js-yaml@^4.1.0: 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== + 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: + 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" @@ -732,6 +2313,11 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -739,6 +2325,16 @@ keyv@^4.5.4: dependencies: json-buffer "3.0.1" +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== + +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== + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -747,6 +2343,18 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +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== + +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" @@ -754,11 +2362,47 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.memoize@4.x: + 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.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + +make-error@1.x, make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + +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" @@ -772,7 +2416,12 @@ micromatch@^4.0.4: braces "^3.0.3" picomatch "^2.3.1" -minimatch@^3.0.5, minimatch@^3.1.2: +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== + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -786,6 +2435,11 @@ minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" +mock-stdin@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mock-stdin/-/mock-stdin-1.0.0.tgz#efcfaf4b18077e14541742fd758b9cae4e5365ea" + integrity sha512-tukRdb9Beu27t6dN+XztSRHq9J0B/CoAOySGzHfn8UTfmqipA5yNT/sDUEyYdAV3Hpka6Wx6kOMxuObdOex60Q== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -796,6 +2450,42 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +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== + +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" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +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" + optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -808,13 +2498,27 @@ optionator@^0.9.3: type-check "^0.4.0" word-wrap "^1.2.5" -p-limit@^3.0.2: +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, p-limit@^3.1.0: 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@^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" @@ -822,6 +2526,11 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +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== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -829,51 +2538,146 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-json@^5.2.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" + 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-key@^3.1.0: +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.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-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -picomatch@^2.3.1: +picocolors@^1.0.0, picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pirates@^4.0.4: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +pkg-dir@^4.2.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" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +prompts@^2.0.1: + 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" + punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +pure-rand@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== + 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== +react-is@^18.0.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + resolve-pkg-maps@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== + +resolve@^1.20.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -886,7 +2690,12 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -semver@^7.6.0: +semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.5.3, semver@^7.5.4, semver@^7.6.0: version "7.6.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== @@ -903,23 +2712,92 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +signal-exit@^3.0.3, signal-exit@^3.0.7: + 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== + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -strip-ansi@^6.0.1: +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +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== + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +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== +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" @@ -927,11 +2805,42 @@ supports-color@^7.1.0: 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== + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + +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 sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + 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" @@ -944,6 +2853,39 @@ ts-api-utils@^1.3.0: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== +ts-jest@^29.1.5: + version "29.1.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.5.tgz#d6c0471cc78bffa2cb4664a0a6741ef36cfe8f69" + integrity sha512-UuClSYxM7byvvYfyWdFI+/2UxMmwNyJb0NPkZPQE2hew3RurV7l7zURgOHAd/1I1ZdPpe3GUsXNXAcN8TFKSIg== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "4.x" + make-error "1.x" + semver "^7.5.3" + yargs-parser "^21.0.1" + +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + tsx@^4.15.6: version "4.15.6" resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.15.6.tgz#4522ed093f7fa54f031a7a999274e8b35dbf3165" @@ -961,6 +2903,16 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + typescript-eslint@^7.13.1: version "7.13.1" resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-7.13.1.tgz#8bbcc4b59b6bb0c457505ee17a356b1868c3fcd5" @@ -980,6 +2932,14 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +update-browserslist-db@^1.0.16: + version "1.0.16" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz#f6d489ed90fb2f07d67784eb3f53d7891f736356" + integrity sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ== + dependencies: + escalade "^3.1.2" + picocolors "^1.0.1" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -987,6 +2947,27 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +v8-to-istanbul@^9.0.1: + version "9.2.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" + integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^2.0.0" + +walker@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -999,6 +2980,61 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yargs-parser@^21.0.1, yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.3.1: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" From e09cc6d6796f6f2320c06724ae5ad57d2bb7ffb1 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 18 Jun 2024 17:33:58 +0200 Subject: [PATCH 14/76] add CI --- .github/workflows/ci.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef58d4e6..2cb4ced5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,3 +22,19 @@ jobs: - name: Type check run: | yarn typecheck + test: + name: Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: 22.x + cache: yarn + - name: Install dependencies + run: | + yarn install + - name: Test + run: | + yarn test From e8b6773dfc6f10a97ccf5299353d569bb46e75b2 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 18 Jun 2024 18:14:07 +0200 Subject: [PATCH 15/76] add a bit more comments --- lib/commands/base_command.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/commands/base_command.ts b/lib/commands/base_command.ts index 14a3b934..90897abe 100644 --- a/lib/commands/base_command.ts +++ b/lib/commands/base_command.ts @@ -1,6 +1,13 @@ import { Command } from "commander"; import { version } from '../../package.json'; +/** + * This is a base class which provides a common interface for all commands. + * This is mostly used for testing purposes. + * + * Commands implementing this class should override the run method and call parseArguments + * at the beginning of the run method. + */ export abstract class BaseCommand { public program: Command; args: string[] | undefined; @@ -12,6 +19,10 @@ export abstract class BaseCommand { abstract run(): void; + /** + * Create sets up the command line program. Implementing classes can add additional options. + * to this.program. + */ create(options?: { exitOverride?: boolean, suppressOutput?: boolean }): Command { const program = new Command(); @@ -20,6 +31,7 @@ export abstract class BaseCommand { program.exitOverride(); // don't exit on error } if (options?.suppressOutput) { + // don't write anything to the console program.configureOutput({ writeOut: () => { }, writeErr: () => { } @@ -31,6 +43,9 @@ export abstract class BaseCommand { return program; } + /** + * This allows us to pass a custom list of strings as arguments to the command during testing. + */ parseArguments() { if (this.args) { // custom arg parsing to be able to inject args for testing From 72f970ab4ac701ae24983788c1115f2e8105f843 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Wed, 19 Jun 2024 13:10:01 +0200 Subject: [PATCH 16/76] implement peptfilter --- bin/peptfilter.ts | 6 +++++ lib/commands/peptfilter.ts | 53 ++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ 3 files changed, 61 insertions(+) create mode 100755 bin/peptfilter.ts create mode 100644 lib/commands/peptfilter.ts diff --git a/bin/peptfilter.ts b/bin/peptfilter.ts new file mode 100755 index 00000000..007dfdba --- /dev/null +++ b/bin/peptfilter.ts @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +import { Peptfilter } from '../lib/commands/peptfilter.js'; + +const command = new Peptfilter(); +command.run(); diff --git a/lib/commands/peptfilter.ts b/lib/commands/peptfilter.ts new file mode 100644 index 00000000..997801f2 --- /dev/null +++ b/lib/commands/peptfilter.ts @@ -0,0 +1,53 @@ +import { Option } from 'commander'; +import { createInterface } from 'node:readline'; +import { BaseCommand } from './base_command.js'; + +export class Peptfilter extends BaseCommand { + + readonly description = `The peptfilter command filters a list of peptides according to specific criteria. The command expects a list of peptides that are passed to standard input. + +The input should have one peptide per line. FASTA headers are preserved in the output, so that peptides remain bundled.`; + + constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean, args?: string[] }) { + super(options); + + this.program + .summary("Filter peptides based on specific criteria.") + .description(this.description) + .option("--minlen ", "only retain peptides having at least this many amino acids", (d) => parseInt(d, 10), 5) + .option("--maxlen ", "only retain peptides having at most this many amino acids", (d) => parseInt(d, 10), 50) + .option("-l, --lacks ", "only retain peptides that lack all of the specified amino acids", (d) => d.split("")) + .option("-c, --contains ", "only retain peptides that contain all of the specified amino acids", (d) => d.split("")); + } + + async run() { + this.parseArguments(); + console.log(this.program.opts()) + const minLen = this.program.opts().minLen; + const maxlen = this.program.opts().maxLen; + const lacks = this.program.opts().lacks || []; + const contains = this.program.opts().contains || []; + + for await (const line of createInterface({ input: process.stdin })) { + if (line.startsWith(">")) { + process.stdout.write(line + "\n"); + continue; + } + if (Peptfilter.checkLength(line, minLen, maxlen) && Peptfilter.checkLacks(line, lacks) && Peptfilter.checkContains(line, contains)) { + process.stdout.write(line + "\n"); + } + } + } + + static checkLength(line: string, minLen: number, maxlen: number): boolean { + return line.length >= minLen && line.length <= maxlen; + } + + static checkLacks(line: string, lacks: string[]): boolean { + return lacks.every((aa: string) => !line.includes(aa)); + } + + static checkContains(line: string, contains: string[]): boolean { + return contains.every((aa: string) => line.includes(aa)); + } +} diff --git a/package.json b/package.json index 71171b87..7daaa389 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "private": false, "type": "module", "bin": { + "peptfilter": "./bin/peptfilter.js", "uniprot": "./bin/uniprot.js" }, "scripts": { @@ -15,6 +16,7 @@ "lint": "yarn run eslint", "test": "yarn run jest", "typecheck": "yarn tsc --skipLibCheck --noEmit", + "peptfilter": "yarn run tsx bin/peptfilter.ts", "uniprot": "yarn run tsx bin/uniprot.ts" }, "dependencies": { From f70a95c21176543fcf5c2f84a71009ce89895b83 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Wed, 19 Jun 2024 14:01:09 +0200 Subject: [PATCH 17/76] write tests --- lib/commands/peptfilter.ts | 6 +- tests/commands/peptfilter.test.ts | 98 +++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 tests/commands/peptfilter.test.ts diff --git a/lib/commands/peptfilter.ts b/lib/commands/peptfilter.ts index 997801f2..48bba41f 100644 --- a/lib/commands/peptfilter.ts +++ b/lib/commands/peptfilter.ts @@ -22,9 +22,8 @@ The input should have one peptide per line. FASTA headers are preserved in the o async run() { this.parseArguments(); - console.log(this.program.opts()) - const minLen = this.program.opts().minLen; - const maxlen = this.program.opts().maxLen; + const minLen = this.program.opts().minlen; + const maxlen = this.program.opts().maxlen; const lacks = this.program.opts().lacks || []; const contains = this.program.opts().contains || []; @@ -33,6 +32,7 @@ The input should have one peptide per line. FASTA headers are preserved in the o process.stdout.write(line + "\n"); continue; } + if (Peptfilter.checkLength(line, minLen, maxlen) && Peptfilter.checkLacks(line, lacks) && Peptfilter.checkContains(line, contains)) { process.stdout.write(line + "\n"); } diff --git a/tests/commands/peptfilter.test.ts b/tests/commands/peptfilter.test.ts new file mode 100644 index 00000000..07bb7f20 --- /dev/null +++ b/tests/commands/peptfilter.test.ts @@ -0,0 +1,98 @@ +import { Peptfilter } from '../../lib/commands/peptfilter'; +import * as mock from 'mock-stdin'; + +let output: string[]; +let error: string[]; +const writeSpy = jest + .spyOn(process.stdout, "write") + .mockImplementation((data: unknown) => { output.push(data as string); return true; }); +const errorSpy = jest + .spyOn(process.stderr, "write") + .mockImplementation((data: unknown) => { error.push(data as string); return true; }); + +beforeEach(() => { + output = []; + error = []; +}); + +test('test length filter', async () => { + // min length + expect(Peptfilter.checkLength('AALER', 4, 10)).toBe(true); + expect(Peptfilter.checkLength('AALER', 5, 10)).toBe(true); + expect(Peptfilter.checkLength('AALER', 6, 10)).toBe(false); + + // max length + expect(Peptfilter.checkLength('AALER', 1, 4)).toBe(false); + expect(Peptfilter.checkLength('AALER', 1, 5)).toBe(true); + expect(Peptfilter.checkLength('AALER', 1, 6)).toBe(true); +}); + +test('test lacks filter', async () => { + expect(Peptfilter.checkLacks('AALER', ''.split(""))).toBe(true); + expect(Peptfilter.checkLacks('AALER', 'BCD'.split(""))).toBe(true); + expect(Peptfilter.checkLacks('AALER', 'A'.split(""))).toBe(false); + expect(Peptfilter.checkLacks('AALER', 'AE'.split(""))).toBe(false); +}); + +test('test contains filter', async () => { + expect(Peptfilter.checkContains('AALER', ''.split(""))).toBe(true); + expect(Peptfilter.checkContains('AALER', 'A'.split(""))).toBe(true); + expect(Peptfilter.checkContains('AALER', 'AE'.split(""))).toBe(true); + expect(Peptfilter.checkContains('AALER', 'BCD'.split(""))).toBe(false); + expect(Peptfilter.checkContains('AALER', 'AB'.split(""))).toBe(false); +}); + +test('test default filter from stdin', async () => { + const stdin = mock.stdin(); + + const command = new Peptfilter(); + const run = command.run(); + + stdin.send("AAAA\n"); + stdin.send("AAAAA\n"); + stdin.end(); + + await run; + + expect(writeSpy).toHaveBeenCalledTimes(1); + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.length).toBe(1); +}); + +test('test if it passes fasta from stdin', async () => { + const stdin = mock.stdin(); + + const command = new Peptfilter(); + const run = command.run(); + + stdin.send(">AA\n"); + stdin.send("AAA\n"); + stdin.end(); + + await run; + + expect(writeSpy).toHaveBeenCalledTimes(1); + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output[0]).toBe(">AA\n"); +}); + +test('test complex example from stdin', async () => { + const stdin = mock.stdin(); + + const command = new Peptfilter({ args: ["--minlen", "4", "--maxlen", "10", "--lacks", "B", "--contains", "A"] }); + const run = command.run(); + + stdin.send("A\n"); + stdin.send("AAAAAAAAAAA\n"); + stdin.send("AAAAB\n"); + stdin.send("BBBBB\n"); + stdin.send("CCCCC\n"); + stdin.send("CCCCCA\n"); + stdin.end(); + + await run; + + expect(writeSpy).toHaveBeenCalledTimes(1); + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output[0]).toBe("CCCCCA\n"); +}); From a62ff53c3aa176a93bed63fee44bf0bd22d09c0e Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Thu, 20 Jun 2024 09:58:41 +0200 Subject: [PATCH 18/76] optimize performance --- lib/commands/base_command.ts | 7 ++++--- lib/commands/peptfilter.ts | 25 +++++++++++++++++++------ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/lib/commands/base_command.ts b/lib/commands/base_command.ts index 90897abe..35c7b795 100644 --- a/lib/commands/base_command.ts +++ b/lib/commands/base_command.ts @@ -1,5 +1,5 @@ import { Command } from "commander"; -import { version } from '../../package.json'; +import { readFileSync } from "fs"; /** * This is a base class which provides a common interface for all commands. @@ -11,8 +11,10 @@ import { version } from '../../package.json'; export abstract class BaseCommand { public program: Command; args: string[] | undefined; + version: string; constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean, args?: string[] }) { + this.version = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")).version; this.program = this.create(options); this.args = options?.args; } @@ -37,8 +39,7 @@ export abstract class BaseCommand { writeErr: () => { } }); } - - program.version(version); + program.version(this.version); return program; } diff --git a/lib/commands/peptfilter.ts b/lib/commands/peptfilter.ts index 48bba41f..87f28ff5 100644 --- a/lib/commands/peptfilter.ts +++ b/lib/commands/peptfilter.ts @@ -1,4 +1,3 @@ -import { Option } from 'commander'; import { createInterface } from 'node:readline'; import { BaseCommand } from './base_command.js'; @@ -20,6 +19,11 @@ The input should have one peptide per line. FASTA headers are preserved in the o .option("-c, --contains ", "only retain peptides that contain all of the specified amino acids", (d) => d.split("")); } + /** + * Performance note: this implementation takes 4 seconds to run on swissprot. It can be made faster by using line events instead of + * async iterators. This alternative implementation runs in 2.5 seconds. However, I decided that the async iterator implementation is + * both more readable and more in line with the implementation of the other commands. + */ async run() { this.parseArguments(); const minLen = this.program.opts().minlen; @@ -27,16 +31,25 @@ The input should have one peptide per line. FASTA headers are preserved in the o const lacks = this.program.opts().lacks || []; const contains = this.program.opts().contains || []; + let output = []; + let i = 0; + for await (const line of createInterface({ input: process.stdin })) { + i++; if (line.startsWith(">")) { - process.stdout.write(line + "\n"); - continue; + output.push(line); + } else if (Peptfilter.checkLength(line, minLen, maxlen) && Peptfilter.checkLacks(line, lacks) && Peptfilter.checkContains(line, contains)) { + output.push(line); } - - if (Peptfilter.checkLength(line, minLen, maxlen) && Peptfilter.checkLacks(line, lacks) && Peptfilter.checkContains(line, contains)) { - process.stdout.write(line + "\n"); + if (i % 1000 === 0) { + output.push(""); + process.stdout.write(output.join("\n")); + output = []; } } + + output.push(""); + process.stdout.write(output.join("\n")); } static checkLength(line: string, minLen: number, maxlen: number): boolean { From 72448de4008f09dda6bee6c3debee5c8c0b64eec Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Thu, 20 Jun 2024 10:21:03 +0200 Subject: [PATCH 19/76] fix tests --- jest.config.ts | 2 +- package.json | 2 +- tests/commands/peptfilter.test.ts | 8 ++++---- tests/commands/uniprot.test.ts | 1 + 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/jest.config.ts b/jest.config.ts index 9627c0f6..9b51f878 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -104,7 +104,7 @@ const config: Config = { // notifyMode: "failure-change", // A preset that is used as a base for Jest's configuration - // preset: undefined, + preset: 'ts-jest/presets/default-esm', // Run tests from one or more projects // projects: undefined, diff --git a/package.json b/package.json index 7daaa389..ddaa71bc 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "scripts": { "build": "yarn run tsc", "lint": "yarn run eslint", - "test": "yarn run jest", + "test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' yarn run jest", "typecheck": "yarn tsc --skipLibCheck --noEmit", "peptfilter": "yarn run tsx bin/peptfilter.ts", "uniprot": "yarn run tsx bin/uniprot.ts" diff --git a/tests/commands/peptfilter.test.ts b/tests/commands/peptfilter.test.ts index 07bb7f20..ad1cfff1 100644 --- a/tests/commands/peptfilter.test.ts +++ b/tests/commands/peptfilter.test.ts @@ -1,4 +1,5 @@ import { Peptfilter } from '../../lib/commands/peptfilter'; +import { jest } from '@jest/globals'; import * as mock from 'mock-stdin'; let output: string[]; @@ -54,9 +55,8 @@ test('test default filter from stdin', async () => { await run; - expect(writeSpy).toHaveBeenCalledTimes(1); expect(errorSpy).toHaveBeenCalledTimes(0); - expect(output.length).toBe(1); + expect(output.join("").trimEnd().split("\n").length).toBe(1); }); test('test if it passes fasta from stdin', async () => { @@ -71,8 +71,8 @@ test('test if it passes fasta from stdin', async () => { await run; - expect(writeSpy).toHaveBeenCalledTimes(1); expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.join("").trimEnd().split("\n").length).toBe(1); expect(output[0]).toBe(">AA\n"); }); @@ -92,7 +92,7 @@ test('test complex example from stdin', async () => { await run; - expect(writeSpy).toHaveBeenCalledTimes(1); expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.join("").trimEnd().split("\n").length).toBe(1); expect(output[0]).toBe("CCCCCA\n"); }); diff --git a/tests/commands/uniprot.test.ts b/tests/commands/uniprot.test.ts index ca72837b..59ba4f7f 100644 --- a/tests/commands/uniprot.test.ts +++ b/tests/commands/uniprot.test.ts @@ -1,4 +1,5 @@ import { Uniprot } from '../../lib/commands/uniprot'; +import { jest } from '@jest/globals'; import * as mock from 'mock-stdin'; let output: string[]; From f5b879ff5739a024421abe800c27bf04cdfbea07 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Thu, 20 Jun 2024 10:23:51 +0200 Subject: [PATCH 20/76] lint --- tests/commands/peptfilter.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/commands/peptfilter.test.ts b/tests/commands/peptfilter.test.ts index ad1cfff1..0318fde2 100644 --- a/tests/commands/peptfilter.test.ts +++ b/tests/commands/peptfilter.test.ts @@ -4,6 +4,7 @@ import * as mock from 'mock-stdin'; let output: string[]; let error: string[]; +// eslint-disable-next-line @typescript-eslint/no-unused-vars const writeSpy = jest .spyOn(process.stdout, "write") .mockImplementation((data: unknown) => { output.push(data as string); return true; }); From eb8fb5d48be135937e010541de0db4d331e845c6 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Thu, 20 Jun 2024 13:01:13 +0200 Subject: [PATCH 21/76] add comments --- lib/commands/peptfilter.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/commands/peptfilter.ts b/lib/commands/peptfilter.ts index 87f28ff5..f8435dc4 100644 --- a/lib/commands/peptfilter.ts +++ b/lib/commands/peptfilter.ts @@ -31,18 +31,19 @@ The input should have one peptide per line. FASTA headers are preserved in the o const lacks = this.program.opts().lacks || []; const contains = this.program.opts().contains || []; + // buffering output makes a big difference in performance let output = []; let i = 0; for await (const line of createInterface({ input: process.stdin })) { i++; - if (line.startsWith(">")) { + if (line.startsWith(">")) { // pass through FASTA headers output.push(line); } else if (Peptfilter.checkLength(line, minLen, maxlen) && Peptfilter.checkLacks(line, lacks) && Peptfilter.checkContains(line, contains)) { output.push(line); } if (i % 1000 === 0) { - output.push(""); + output.push(""); //add a newline at the end of the buffer without additional string copy process.stdout.write(output.join("\n")); output = []; } From aa5d358be428479e88564b3fa0c7614de366debc Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Fri, 21 Jun 2024 13:59:11 +0200 Subject: [PATCH 22/76] implement prot2pept --- bin/prot2pept.ts | 6 ++++ lib/commands/prot2pept.ts | 73 +++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ 3 files changed, 81 insertions(+) create mode 100755 bin/prot2pept.ts create mode 100644 lib/commands/prot2pept.ts diff --git a/bin/prot2pept.ts b/bin/prot2pept.ts new file mode 100755 index 00000000..d2d593be --- /dev/null +++ b/bin/prot2pept.ts @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +import { Prot2pept } from '../lib/commands/prot2pept.js'; + +const command = new Prot2pept(); +command.run(); diff --git a/lib/commands/prot2pept.ts b/lib/commands/prot2pept.ts new file mode 100644 index 00000000..ac7c8fb7 --- /dev/null +++ b/lib/commands/prot2pept.ts @@ -0,0 +1,73 @@ +import { createInterface } from 'node:readline'; +import { BaseCommand } from './base_command.js'; + +export class Prot2pept extends BaseCommand { + + readonly description = `The prot2pept command splits each protein sequence into a list of peptides according to a given cleavage-pattern. The command expects a list of protein sequences that are passed to standard input. + +The input should have either one protein sequence per line or contain a FASTA formatted list of protein sequences. FASTA headers are preserved in the output, so that peptides can be bundled per protein sequence. +`; + + constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean, args?: string[] }) { + super(options); + + this.program + .summary("Splits each protein sequence into a list of peptides.") + .description(this.description) + .option("-p, --pattern ", "specify cleavage-pattern (regex) as the pattern after which the next peptide will be cleaved. By default, it will create tryptic peptides.", "([KR])([^P])") + } + + /** + * Performance note: Just as with peptfilter, this implementation can be made faster by using line events instead of + * async iterators. + */ + async run() { + this.parseArguments(); + const pattern = new RegExp(this.program.opts().pattern, "g"); + + let fasta = false; + let protein = []; + + // buffering output makes a big difference in performance + let output = []; + let i = 0; + + for await (const line of createInterface({ input: process.stdin })) { + if (i === 0 && line.startsWith(">")) { + fasta = true; + } + + i++; + + if (fasta) { // if we're in fasta mode, a protein could be split over multiple lines + if (line.startsWith(">")) { // if we encounter a new header, process the previous protein and output the current header + if (protein.length > 0) { + output.push(Prot2pept.splitProtein(protein.join(""), pattern)); + } + output.push(line.trimEnd()); + protein = []; + } else { + protein.push(line.trimEnd()); + } + } else { // if we're not in fasta mode, each line is a protein sequence + output.push(Prot2pept.splitProtein(line.trimEnd(), pattern)); + } + + if (i % 1000 === 0) { + output.push(""); //add a newline at the end of the buffer without additional string copy + process.stdout.write(output.join("\n")); + output = []; + } + } + + if (fasta) { // if in fasta mode, process the last protein + output.push(Prot2pept.splitProtein(protein.join(""), pattern)); + } + output.push(""); + process.stdout.write(output.join("\n")); + } + + static splitProtein(line: string, pattern: RegExp): string { + return line.replaceAll(pattern, "$1\n$2").replaceAll(pattern, "$1\n$2").replaceAll("\n\n", "\n"); + } +} diff --git a/package.json b/package.json index ddaa71bc..f27f3c4c 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "type": "module", "bin": { "peptfilter": "./bin/peptfilter.js", + "prot2pept": "./bin/prot2pept.js", "uniprot": "./bin/uniprot.js" }, "scripts": { @@ -17,6 +18,7 @@ "test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' yarn run jest", "typecheck": "yarn tsc --skipLibCheck --noEmit", "peptfilter": "yarn run tsx bin/peptfilter.ts", + "prot2pept": "yarn run tsx bin/prot2pept.ts", "uniprot": "yarn run tsx bin/uniprot.ts" }, "dependencies": { From c24382a1d725103d8bc3615b6c1f8630b3efcbc9 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Fri, 21 Jun 2024 14:30:08 +0200 Subject: [PATCH 23/76] add tests --- lib/commands/prot2pept.ts | 9 ++- tests/commands/prot2pept.test.ts | 124 +++++++++++++++++++++++++++++++ tsconfig.json | 1 + 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 tests/commands/prot2pept.test.ts diff --git a/lib/commands/prot2pept.ts b/lib/commands/prot2pept.ts index ac7c8fb7..3b3a6b01 100644 --- a/lib/commands/prot2pept.ts +++ b/lib/commands/prot2pept.ts @@ -23,7 +23,14 @@ The input should have either one protein sequence per line or contain a FASTA fo */ async run() { this.parseArguments(); - const pattern = new RegExp(this.program.opts().pattern, "g"); + + let pattern; + try { + pattern = new RegExp(this.program.opts().pattern, "g"); + } catch (e) { + this.program.error(`Your pattern was invalid: ${(e as Error).message}`); + //process.exit(1); + } let fasta = false; let protein = []; diff --git a/tests/commands/prot2pept.test.ts b/tests/commands/prot2pept.test.ts new file mode 100644 index 00000000..ca01533a --- /dev/null +++ b/tests/commands/prot2pept.test.ts @@ -0,0 +1,124 @@ +import { Prot2pept } from '../../lib/commands/prot2pept'; +import { jest } from '@jest/globals'; +import * as mock from 'mock-stdin'; + +let output: string[]; +let error: string[]; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const writeSpy = jest + .spyOn(process.stdout, "write") + .mockImplementation((data: unknown) => { output.push(data as string); return true; }); +const errorSpy = jest + .spyOn(process.stderr, "write") + .mockImplementation((data: unknown) => { error.push(data as string); return true; }); + +beforeEach(() => { + output = []; + error = []; +}); + +test('test single line input 1', async () => { + const stdin = mock.stdin(); + + const command = new Prot2pept(); + const run = command.run(); + + stdin.send("AALTERAALTERPAALTER\n"); + stdin.end(); + + await run; + + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.join("").trimEnd()).toBe("AALTER\nAALTERPAALTER"); +}); + +test('test single line input 2', async () => { + const stdin = mock.stdin(); + + const command = new Prot2pept(); + const run = command.run(); + + stdin.send("KRKPR\n"); + stdin.end(); + + await run; + + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.join("").trimEnd()).toBe("K\nR\nKPR"); +}); + +test('test multi line input', async () => { + const stdin = mock.stdin(); + + const command = new Prot2pept(); + const run = command.run(); + + stdin.send("AALTERAALTERPAALTER\n"); + stdin.send("AALTERAA\n"); + stdin.end(); + + await run; + + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.join("").trimEnd()).toBe("AALTER\nAALTERPAALTER\nAALTER\nAA"); +}); + +test('test fasta input 1', async () => { + const stdin = mock.stdin(); + + const command = new Prot2pept(); + const run = command.run(); + + stdin.send(">AKA\nAALTERAALTERPAALTER\n"); + stdin.end(); + + await run; + + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.join("").trimEnd()).toBe(">AKA\nAALTER\nAALTERPAALTER"); +}); + +test('test fasta input 2', async () => { + const stdin = mock.stdin(); + + const command = new Prot2pept(); + const run = command.run(); + + stdin.send(">AKA\nAAL\nT\nERAALTER\nP\nAALTER\n"); + stdin.end(); + + await run; + + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.join("").trimEnd()).toBe(">AKA\nAALTER\nAALTERPAALTER"); +}); + +test('test fasta input 3', async () => { + const stdin = mock.stdin(); + + const command = new Prot2pept(); + const run = command.run(); + + stdin.send(">AKA\nAAL\nT\n>\nERAALTER\nP\nAALTER"); + stdin.end(); + + await run; + + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.join("").trimEnd()).toBe(">AKA\nAALT\n>\nER\nAALTERPAALTER"); +}); + +test('test custom pattern', async () => { + const stdin = mock.stdin(); + + const command = new Prot2pept({ args: ["--pattern", "([KR])([^A])"] }); + const run = command.run(); + + stdin.send("AALTERAALTERPAALTER\n"); + stdin.end(); + + await run; + + expect(errorSpy).toHaveBeenCalledTimes(0); + expect(output.join("").trimEnd()).toBe("AALTERAALTER\nPAALTER"); +}); diff --git a/tsconfig.json b/tsconfig.json index 5c9bef88..23047235 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,5 +8,6 @@ "module": "NodeNext", "strict": true, "resolveJsonModule": true, + "target": "esnext" } } From f0346d34aa9d099a5f0afde1f6fd1314fe3a1e15 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Fri, 21 Jun 2024 18:57:13 +0200 Subject: [PATCH 24/76] Update lib/commands/prot2pept.ts Co-authored-by: Pieter Verschaffelt --- lib/commands/prot2pept.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/commands/prot2pept.ts b/lib/commands/prot2pept.ts index 3b3a6b01..a64d862a 100644 --- a/lib/commands/prot2pept.ts +++ b/lib/commands/prot2pept.ts @@ -29,7 +29,6 @@ The input should have either one protein sequence per line or contain a FASTA fo pattern = new RegExp(this.program.opts().pattern, "g"); } catch (e) { this.program.error(`Your pattern was invalid: ${(e as Error).message}`); - //process.exit(1); } let fasta = false; From 9fef29aa78eca122276662fa8772240d8bd9a5bd Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 2 Jul 2024 14:45:32 +0200 Subject: [PATCH 25/76] implement basic version of the pept2lca command --- bin/unipept.ts | 6 +++ lib/commands/unipept.ts | 28 ++++++++++++ lib/commands/unipept/pept2lca.ts | 37 +++++++++++++++ lib/commands/unipept/unipept_subcommand.ts | 53 ++++++++++++++++++++++ package.json | 2 + tests/commands/unipept.test.ts | 0 6 files changed, 126 insertions(+) create mode 100755 bin/unipept.ts create mode 100644 lib/commands/unipept.ts create mode 100644 lib/commands/unipept/pept2lca.ts create mode 100644 lib/commands/unipept/unipept_subcommand.ts create mode 100644 tests/commands/unipept.test.ts diff --git a/bin/unipept.ts b/bin/unipept.ts new file mode 100755 index 00000000..bbb5e051 --- /dev/null +++ b/bin/unipept.ts @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +import { Unipept } from '../lib/commands/unipept.js'; + +const command = new Unipept(); +command.run(); diff --git a/lib/commands/unipept.ts b/lib/commands/unipept.ts new file mode 100644 index 00000000..553a5fc2 --- /dev/null +++ b/lib/commands/unipept.ts @@ -0,0 +1,28 @@ +import { BaseCommand } from './base_command.js'; +import { Pept2lca } from './unipept/pept2lca.js'; + +export class Unipept extends BaseCommand { + + readonly description = `The unipept subcommands are command line wrappers around the Unipept web services. + +Subcommands that start with pept expect a list of tryptic peptides as input. Subcommands that start with tax expect a list of NCBI Taxonomy Identifiers as input. Input is passed + +- as separate command line arguments +- in a text file that is passed as an argument to the -i option +- to standard input + +The command will give priority to the first way the input is passed, in the order as listed above. Text files and standard input should have one tryptic peptide or one NCBI Taxonomy Identifier per line.`; + + constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean, args?: string[] }) { + super(options); + + this.program + .summary("Command line interface to Unipept web services.") + .description(this.description) + .addCommand(new Pept2lca().command); + } + + async run() { + this.parseArguments(); + } +} diff --git a/lib/commands/unipept/pept2lca.ts b/lib/commands/unipept/pept2lca.ts new file mode 100644 index 00000000..561e73b4 --- /dev/null +++ b/lib/commands/unipept/pept2lca.ts @@ -0,0 +1,37 @@ +import { Option } from "commander"; +import { UnipeptSubcommand } from "./unipept_subcommand.js"; + +export class Pept2lca extends UnipeptSubcommand { + + readonly description = `For each tryptic peptide the unipept pept2lca command retrieves from Unipept the lowest common ancestor of the set of taxa from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The lowest common ancestor is based on the topology of the Unipept Taxonomy -- a cleaned up version of the NCBI Taxonomy -- and is itself a record from the NCBI Taxonomy. The command expects a list of tryptic peptides that are passed + +- as separate command line arguments +- in a text file that is passed as an argument to the -i option +- to standard input + +The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line.`; + + constructor() { + super("pept2lca"); + + this.command + .summary("Fetch taxonomic lowest common ancestor of UniProt entries that match tryptic peptides.") + .description(this.description) + .option("-e, --equate", "equate isoleucine (I) and leucine (L) when matching peptides") + .option("-a, --all", "report all information fields of NCBI Taxonomy records available in Unipept. Note that this may have a performance penalty.") + .addOption(new Option("-s --select ", "select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.")) + .argument("[peptides...]", "optionally, 1 or more peptides") + .action((args, options) => this.run(args, options)); + } + + async run(args: string[], options: object) { + super.run(args, options); + + for (const peptide of args) { + console.log(peptide); + const r = await fetch(this.url + "?input=" + peptide); + console.log(await r.json()); + } + + } +} diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts new file mode 100644 index 00000000..a97a6d19 --- /dev/null +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -0,0 +1,53 @@ +import { Command, Option } from "commander"; +import { readFileSync } from "fs"; + +export abstract class UnipeptSubcommand { + public command: Command; + static readonly VALID_FORMATS = ["blast", "csv", "json", "xml"]; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + options: any = {}; + name: string; + user_agent: string; + host = "https://api.unipept.ugent.be"; + url: string; + + constructor(name: string) { + this.name = name; + const version = JSON.parse(readFileSync(new URL("../../../package.json", import.meta.url), "utf8")).version; + this.user_agent = `unipept-cli/${version}`; + this.command = this.create(name); + } + + create(name: string): Command { + const command = new Command(name); + + command.option("-q, --quiet", "disable service messages"); + command.option("-i, -input ", "read input from file"); + command.option("-o, --output ", "write output to file"); + command.addOption(new Option("-f, --format ", "define the output format").choices(UnipeptSubcommand.VALID_FORMATS).default("json")); + command.option("--host ", "specify the server running the Unipept web service"); + + // internal options + command.addOption(new Option("--no-header", "disable the header in csv output").hideHelp()); + command.addOption(new Option("--batch ", "specify the batch size").hideHelp()); + + return command; + } + + run(args: string[], options: object): void { + this.options = options; + this.host = this.getHost(); + this.url = `${this.host}/api/v2/${this.name}.json`; + } + + getHost(): string { + const host = this.options.host || this.host; + + // add http:// if needed + if (host.startsWith("http://") || host.startsWith("https://")) { + return host; + } else { + return `http://${host}`; + } + } +} diff --git a/package.json b/package.json index f27f3c4c..3e69dd9e 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "bin": { "peptfilter": "./bin/peptfilter.js", "prot2pept": "./bin/prot2pept.js", + "unipept": "./bin/unipept.js", "uniprot": "./bin/uniprot.js" }, "scripts": { @@ -19,6 +20,7 @@ "typecheck": "yarn tsc --skipLibCheck --noEmit", "peptfilter": "yarn run tsx bin/peptfilter.ts", "prot2pept": "yarn run tsx bin/prot2pept.ts", + "unipept": "yarn run tsx bin/unipept.ts", "uniprot": "yarn run tsx bin/uniprot.ts" }, "dependencies": { diff --git a/tests/commands/unipept.test.ts b/tests/commands/unipept.test.ts new file mode 100644 index 00000000..e69de29b From bd3d031e8d582123f4a31c5138aae50653cb5f64 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 2 Jul 2024 14:52:54 +0200 Subject: [PATCH 26/76] use post and set headers --- lib/commands/unipept/pept2lca.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/commands/unipept/pept2lca.ts b/lib/commands/unipept/pept2lca.ts index 561e73b4..d9ea25db 100644 --- a/lib/commands/unipept/pept2lca.ts +++ b/lib/commands/unipept/pept2lca.ts @@ -28,8 +28,10 @@ The command will give priority to the first way tryptic peptides are passed, in super.run(args, options); for (const peptide of args) { - console.log(peptide); - const r = await fetch(this.url + "?input=" + peptide); + const r = await fetch(this.url + "?input=" + peptide, { + method: "POST", + body: JSON.stringify({ input: [peptide] }) + }); console.log(await r.json()); } From 33353b1f8378d2d68fdd095686215ea2b868ec04 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 2 Jul 2024 15:20:38 +0200 Subject: [PATCH 27/76] move processing loop to the super class --- lib/commands/unipept/pept2lca.ts | 11 +---------- lib/commands/unipept/unipept_subcommand.ts | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/commands/unipept/pept2lca.ts b/lib/commands/unipept/pept2lca.ts index d9ea25db..3c56257a 100644 --- a/lib/commands/unipept/pept2lca.ts +++ b/lib/commands/unipept/pept2lca.ts @@ -25,15 +25,6 @@ The command will give priority to the first way tryptic peptides are passed, in } async run(args: string[], options: object) { - super.run(args, options); - - for (const peptide of args) { - const r = await fetch(this.url + "?input=" + peptide, { - method: "POST", - body: JSON.stringify({ input: [peptide] }) - }); - console.log(await r.json()); - } - + await super.run(args, options); } } diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index a97a6d19..d0353431 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -9,7 +9,7 @@ export abstract class UnipeptSubcommand { name: string; user_agent: string; host = "https://api.unipept.ugent.be"; - url: string; + url?: string; constructor(name: string) { this.name = name; @@ -34,13 +34,25 @@ export abstract class UnipeptSubcommand { return command; } - run(args: string[], options: object): void { + async run(args: string[], options: object): Promise { this.options = options; this.host = this.getHost(); this.url = `${this.host}/api/v2/${this.name}.json`; + + for (const input of args) { + const r = await fetch(this.url, { + method: "POST", + body: new URLSearchParams({ "input": input }), + headers: { + "Accept-Encoding": "gzip", + "User-Agent": this.user_agent, + } + }); + console.log(await r.json()); + } } - getHost(): string { + private getHost(): string { const host = this.options.host || this.host; // add http:// if needed From c62a0b2ee9c55867b2393678320ed6bb497c2c73 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 2 Jul 2024 15:32:39 +0200 Subject: [PATCH 28/76] implement input batches --- lib/commands/unipept/pept2lca.ts | 4 +++ lib/commands/unipept/unipept_subcommand.ts | 33 ++++++++++++++++------ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/lib/commands/unipept/pept2lca.ts b/lib/commands/unipept/pept2lca.ts index 3c56257a..5fdc0239 100644 --- a/lib/commands/unipept/pept2lca.ts +++ b/lib/commands/unipept/pept2lca.ts @@ -24,6 +24,10 @@ The command will give priority to the first way tryptic peptides are passed, in .action((args, options) => this.run(args, options)); } + defaultBatchSize(): number { + return 100; + } + async run(args: string[], options: object) { await super.run(args, options); } diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index d0353431..e33490e1 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -39,19 +39,34 @@ export abstract class UnipeptSubcommand { this.host = this.getHost(); this.url = `${this.host}/api/v2/${this.name}.json`; + let slice = []; + for (const input of args) { - const r = await fetch(this.url, { - method: "POST", - body: new URLSearchParams({ "input": input }), - headers: { - "Accept-Encoding": "gzip", - "User-Agent": this.user_agent, - } - }); - console.log(await r.json()); + slice.push(input); + if (slice.length >= this.defaultBatchSize()) { + await this.processBatch(slice); + slice = []; + } } + await this.processBatch(slice); + + } + + async processBatch(slice: string[]): Promise { + const r = await fetch(this.url as string, { + method: "POST", + body: new URLSearchParams({ "input": JSON.stringify(slice) }), + headers: { + "Accept-Encoding": "gzip", + "User-Agent": this.user_agent, + } + }); + console.log(await r.json()); } + + abstract defaultBatchSize(): number; + private getHost(): string { const host = this.options.host || this.host; From 1b30a66588492784ae7bd5eff50627dfe6425f27 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 2 Jul 2024 15:46:54 +0200 Subject: [PATCH 29/76] use different input options if available --- lib/commands/unipept/unipept_subcommand.ts | 23 +++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index e33490e1..b03c5b40 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -1,5 +1,7 @@ import { Command, Option } from "commander"; -import { readFileSync } from "fs"; +import { createReadStream, readFileSync } from "fs"; +import { createInterface } from "node:readline"; +import { Interface } from "readline"; export abstract class UnipeptSubcommand { public command: Command; @@ -41,7 +43,7 @@ export abstract class UnipeptSubcommand { let slice = []; - for (const input of args) { + for await (const input of this.getInputIterator(args, options)) { slice.push(input); if (slice.length >= this.defaultBatchSize()) { await this.processBatch(slice); @@ -49,7 +51,6 @@ export abstract class UnipeptSubcommand { } } await this.processBatch(slice); - } async processBatch(slice: string[]): Promise { @@ -64,6 +65,22 @@ export abstract class UnipeptSubcommand { console.log(await r.json()); } + /** + * Returns an input iterator to use for the request. + * - if arguments are given, use arguments + * - if an input file is given, use the file + * - otherwise, use standard input + */ + getInputIterator(args: string[], options: { input?: string }): string[] | Interface { + if (args.length > 0) { + return args; + } else if (options.input) { + return createInterface({ input: createReadStream(options.input) }); + } else { + return createInterface({ input: process.stdin }) + } + } + abstract defaultBatchSize(): number; From d13561190a6a88e8409f5f125f102ac4948da7b1 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 2 Jul 2024 16:38:13 +0200 Subject: [PATCH 30/76] parse more parameters --- lib/commands/unipept/unipept_subcommand.ts | 34 +++++++++++++++------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index b03c5b40..873a24bc 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -18,13 +18,15 @@ export abstract class UnipeptSubcommand { const version = JSON.parse(readFileSync(new URL("../../../package.json", import.meta.url), "utf8")).version; this.user_agent = `unipept-cli/${version}`; this.command = this.create(name); + this.fasta = false; } + abstract defaultBatchSize(): number; create(name: string): Command { const command = new Command(name); command.option("-q, --quiet", "disable service messages"); - command.option("-i, -input ", "read input from file"); + command.option("-i, --input ", "read input from file"); command.option("-o, --output ", "write output to file"); command.addOption(new Option("-f, --format ", "define the output format").choices(UnipeptSubcommand.VALID_FORMATS).default("json")); command.option("--host ", "specify the server running the Unipept web service"); @@ -36,14 +38,14 @@ export abstract class UnipeptSubcommand { return command; } - async run(args: string[], options: object): Promise { + async run(args: string[], options: { input?: string }): Promise { this.options = options; this.host = this.getHost(); this.url = `${this.host}/api/v2/${this.name}.json`; let slice = []; - for await (const input of this.getInputIterator(args, options)) { + for await (const input of this.getInputIterator(args, options.input)) { slice.push(input); if (slice.length >= this.defaultBatchSize()) { await this.processBatch(slice); @@ -56,7 +58,7 @@ export abstract class UnipeptSubcommand { async processBatch(slice: string[]): Promise { const r = await fetch(this.url as string, { method: "POST", - body: new URLSearchParams({ "input": JSON.stringify(slice) }), + body: this.constructRequestBody(slice), headers: { "Accept-Encoding": "gzip", "User-Agent": this.user_agent, @@ -65,25 +67,37 @@ export abstract class UnipeptSubcommand { console.log(await r.json()); } + private constructRequestBody(slice: string[]): URLSearchParams { + const names = this.constructSelectedFields().length === 0 || this.constructSelectedFields().some(regex => regex.toString().includes("name") || regex.toString().includes(".*$")); + return new URLSearchParams({ + input: JSON.stringify(slice), + equate_il: this.options.equate, + extra: this.options.all, + names: this.options.all && names + }); + } + + // TODO: implement + private constructSelectedFields(): RegExp[] { + return []; + } + /** * Returns an input iterator to use for the request. * - if arguments are given, use arguments * - if an input file is given, use the file * - otherwise, use standard input */ - getInputIterator(args: string[], options: { input?: string }): string[] | Interface { + private getInputIterator(args: string[], input?: string): string[] | Interface { if (args.length > 0) { return args; - } else if (options.input) { - return createInterface({ input: createReadStream(options.input) }); + } else if (input) { + return createInterface({ input: createReadStream(input) }); } else { return createInterface({ input: process.stdin }) } } - - abstract defaultBatchSize(): number; - private getHost(): string { const host = this.options.host || this.host; From 5da999ccd66f37c187c3b9f09dc7a44f9f9ea6aa Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 2 Jul 2024 17:13:48 +0200 Subject: [PATCH 31/76] add a few utility methods --- lib/commands/unipept/pept2lca.ts | 8 ++--- lib/commands/unipept/unipept_subcommand.ts | 35 ++++++++++++++++++---- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/lib/commands/unipept/pept2lca.ts b/lib/commands/unipept/pept2lca.ts index 5fdc0239..97b8a871 100644 --- a/lib/commands/unipept/pept2lca.ts +++ b/lib/commands/unipept/pept2lca.ts @@ -24,11 +24,11 @@ The command will give priority to the first way tryptic peptides are passed, in .action((args, options) => this.run(args, options)); } - defaultBatchSize(): number { - return 100; + requiredFields(): string[] { + return ["peptide"]; } - async run(args: string[], options: object) { - await super.run(args, options); + defaultBatchSize(): number { + return 100; } } diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index 873a24bc..dca448ce 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -12,6 +12,8 @@ export abstract class UnipeptSubcommand { user_agent: string; host = "https://api.unipept.ugent.be"; url?: string; + selectedFields?: RegExp[]; + fasta: boolean; constructor(name: string) { this.name = name; @@ -22,6 +24,10 @@ export abstract class UnipeptSubcommand { } abstract defaultBatchSize(): number; + requiredFields(): string[] { + return []; + } + create(name: string): Command { const command = new Command(name); @@ -47,7 +53,7 @@ export abstract class UnipeptSubcommand { for await (const input of this.getInputIterator(args, options.input)) { slice.push(input); - if (slice.length >= this.defaultBatchSize()) { + if (slice.length >= this.batchSize) { await this.processBatch(slice); slice = []; } @@ -68,7 +74,7 @@ export abstract class UnipeptSubcommand { } private constructRequestBody(slice: string[]): URLSearchParams { - const names = this.constructSelectedFields().length === 0 || this.constructSelectedFields().some(regex => regex.toString().includes("name") || regex.toString().includes(".*$")); + const names = this.getSelectedFields().length === 0 || this.getSelectedFields().some(regex => regex.toString().includes("name") || regex.toString().includes(".*$")); return new URLSearchParams({ input: JSON.stringify(slice), equate_il: this.options.equate, @@ -77,9 +83,24 @@ export abstract class UnipeptSubcommand { }); } - // TODO: implement - private constructSelectedFields(): RegExp[] { - return []; + private getSelectedFields(): RegExp[] { + if (this.selectedFields) return this.getSelectedFields(); + + const fields = (this.options.fields as string[]).flatMap(f => f.split(",")); + if (this.fasta && fields.length > 0) { + fields.push(...this.requiredFields()); + } + this.selectedFields = fields.map(f => this.globToRegex(f)); + + return this.selectedFields; + } + + private get batchSize(): number { + if (this.options.batch) { + return +this.options.batch; + } else { + return this.defaultBatchSize(); + } } /** @@ -108,4 +129,8 @@ export abstract class UnipeptSubcommand { return `http://${host}`; } } + + private globToRegex(glob: string): RegExp { + return new RegExp(glob.replace(/\*/g, ".*")); + } } From bcd9535dea452a0b67b8bc7e3721220805dbfefb Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 8 Jul 2024 09:54:54 +0200 Subject: [PATCH 32/76] add basic csv formatter --- lib/commands/unipept/unipept_subcommand.ts | 20 +++++++++++++++++--- lib/formatters/csv_formatter.ts | 21 +++++++++++++++++++++ lib/formatters/formatter.ts | 19 +++++++++++++++++++ lib/formatters/formatter_factory.ts | 11 +++++++++++ package.json | 3 ++- yarn.lock | 5 +++++ 6 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 lib/formatters/csv_formatter.ts create mode 100644 lib/formatters/formatter.ts create mode 100644 lib/formatters/formatter_factory.ts diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index dca448ce..fc43d6b6 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -2,6 +2,8 @@ import { Command, Option } from "commander"; import { createReadStream, readFileSync } from "fs"; import { createInterface } from "node:readline"; import { Interface } from "readline"; +import { Formatter } from "../../formatters/formatter.js"; +import { FormatterFactory } from "../../formatters/formatter_factory.js"; export abstract class UnipeptSubcommand { public command: Command; @@ -14,6 +16,8 @@ export abstract class UnipeptSubcommand { url?: string; selectedFields?: RegExp[]; fasta: boolean; + formatter?: Formatter; + firstBatch = true; constructor(name: string) { this.name = name; @@ -48,6 +52,7 @@ export abstract class UnipeptSubcommand { this.options = options; this.host = this.getHost(); this.url = `${this.host}/api/v2/${this.name}.json`; + this.formatter = FormatterFactory.getFormatter(this.options.format); let slice = []; @@ -62,6 +67,8 @@ export abstract class UnipeptSubcommand { } async processBatch(slice: string[]): Promise { + if (!this.formatter) throw new Error("Formatter not set"); + const r = await fetch(this.url as string, { method: "POST", body: this.constructRequestBody(slice), @@ -70,7 +77,14 @@ export abstract class UnipeptSubcommand { "User-Agent": this.user_agent, } }); - console.log(await r.json()); + const result = await r.json(); + + if (this.firstBatch) { + this.firstBatch = false; + process.stdout.write(this.formatter.header(result, this.fasta)); + } + + process.stdout.write(this.formatter.format(result, this.fasta)); } private constructRequestBody(slice: string[]): URLSearchParams { @@ -84,9 +98,9 @@ export abstract class UnipeptSubcommand { } private getSelectedFields(): RegExp[] { - if (this.selectedFields) return this.getSelectedFields(); + if (this.selectedFields) return this.selectedFields; - const fields = (this.options.fields as string[]).flatMap(f => f.split(",")); + const fields = (this.options.select as string[])?.flatMap(f => f.split(",")) ?? []; if (this.fasta && fields.length > 0) { fields.push(...this.requiredFields()); } diff --git a/lib/formatters/csv_formatter.ts b/lib/formatters/csv_formatter.ts new file mode 100644 index 00000000..39b8657f --- /dev/null +++ b/lib/formatters/csv_formatter.ts @@ -0,0 +1,21 @@ +import { Formatter } from "./formatter.js"; +import { stringify } from "csv-stringify/sync"; + +export class CSVFormatter extends Formatter { + + header(sampleData: { [key: string]: string }[], fastaMapper?: boolean | undefined): string { + return stringify([this.getKeys(sampleData, fastaMapper)]); + } + + footer(): string { + return ""; + } + + convert(data: object[]): string { + return stringify(data); + } + + getKeys(data: { [key: string]: string }[], fastaMapper?: boolean | undefined): string[] { + return fastaMapper ? ["fasta_header", ...Object.keys(data[0])] : Object.keys(data[0]); + } +} diff --git a/lib/formatters/formatter.ts b/lib/formatters/formatter.ts new file mode 100644 index 00000000..4f1ffcca --- /dev/null +++ b/lib/formatters/formatter.ts @@ -0,0 +1,19 @@ +import { CSVFormatter } from "./csv_formatter.js"; + +export abstract class Formatter { + + abstract header(sampleData: object, fastaMapper?: boolean): string; + abstract footer(): string; + abstract convert(data: any, first?: boolean): string; + + format(data, fastaMapper?: boolean, first?: boolean): string { + if (fastaMapper) { + data = this.integrateFastaHeaders(data, fastaMapper); + } + return this.convert(data, first); + } + + integrateFastaHeaders(data: any, fastaMapper: boolean): any { + return data; + } +} diff --git a/lib/formatters/formatter_factory.ts b/lib/formatters/formatter_factory.ts new file mode 100644 index 00000000..b350df77 --- /dev/null +++ b/lib/formatters/formatter_factory.ts @@ -0,0 +1,11 @@ +import { CSVFormatter } from "./csv_formatter.js"; +import { Formatter } from "./formatter.js"; + +export class FormatterFactory { + static getFormatter(name: string): Formatter { + if (name === "csv") { + return new CSVFormatter(); + } + return new CSVFormatter(); + } +} diff --git a/package.json b/package.json index 3e69dd9e..fc1ff4c4 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ "uniprot": "yarn run tsx bin/uniprot.ts" }, "dependencies": { - "commander": "^12.1.0" + "commander": "^12.1.0", + "csv-stringify": "^6.5.0" }, "devDependencies": { "@eslint/js": "^9.5.0", diff --git a/yarn.lock b/yarn.lock index bb0cd995..90d703b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1307,6 +1307,11 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +csv-stringify@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-6.5.0.tgz#7b1491893c917e018a97de9bf9604e23b88647c2" + integrity sha512-edlXFVKcUx7r8Vx5zQucsuMg4wb/xT6qyz+Sr1vnLrdXqlLD1+UKyWNyZ9zn6mUW1ewmGxrpVwAcChGF0HQ/2Q== + debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.5" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" From 0ad8004e1fce2795fbe38f76cc61687200646ff9 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 8 Jul 2024 10:02:22 +0200 Subject: [PATCH 33/76] write to file if needed --- lib/commands/unipept/unipept_subcommand.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index fc43d6b6..78777153 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -1,5 +1,5 @@ import { Command, Option } from "commander"; -import { createReadStream, readFileSync } from "fs"; +import { createReadStream, createWriteStream, readFileSync } from "fs"; import { createInterface } from "node:readline"; import { Interface } from "readline"; import { Formatter } from "../../formatters/formatter.js"; @@ -14,10 +14,11 @@ export abstract class UnipeptSubcommand { user_agent: string; host = "https://api.unipept.ugent.be"; url?: string; - selectedFields?: RegExp[]; - fasta: boolean; formatter?: Formatter; + outputStream: NodeJS.WritableStream = process.stdout; firstBatch = true; + selectedFields?: RegExp[]; + fasta: boolean; constructor(name: string) { this.name = name; @@ -53,6 +54,9 @@ export abstract class UnipeptSubcommand { this.host = this.getHost(); this.url = `${this.host}/api/v2/${this.name}.json`; this.formatter = FormatterFactory.getFormatter(this.options.format); + if (this.options.output) { + this.outputStream = createWriteStream(this.options.output); + } let slice = []; @@ -81,10 +85,10 @@ export abstract class UnipeptSubcommand { if (this.firstBatch) { this.firstBatch = false; - process.stdout.write(this.formatter.header(result, this.fasta)); + this.outputStream.write(this.formatter.header(result, this.fasta)); } - process.stdout.write(this.formatter.format(result, this.fasta)); + this.outputStream.write(this.formatter.format(result, this.fasta)); } private constructRequestBody(slice: string[]): URLSearchParams { From 8f9334a2aca04f277e6222cbf321f6475d8f9f7c Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 8 Jul 2024 10:07:22 +0200 Subject: [PATCH 34/76] support no-header --- lib/commands/unipept/unipept_subcommand.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index 78777153..3ebecee6 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -83,7 +83,7 @@ export abstract class UnipeptSubcommand { }); const result = await r.json(); - if (this.firstBatch) { + if (this.firstBatch && this.options.header) { this.firstBatch = false; this.outputStream.write(this.formatter.header(result, this.fasta)); } From beb414c44b5f5d99528522d1b7660dd55c5d4b84 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 8 Jul 2024 11:00:57 +0200 Subject: [PATCH 35/76] fix lint --- lib/formatters/formatter.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/formatters/formatter.ts b/lib/formatters/formatter.ts index 4f1ffcca..39d4202d 100644 --- a/lib/formatters/formatter.ts +++ b/lib/formatters/formatter.ts @@ -1,19 +1,18 @@ -import { CSVFormatter } from "./csv_formatter.js"; - export abstract class Formatter { abstract header(sampleData: object, fastaMapper?: boolean): string; abstract footer(): string; - abstract convert(data: any, first?: boolean): string; + abstract convert(data: { [key: string]: string }[], first?: boolean): string; - format(data, fastaMapper?: boolean, first?: boolean): string { + format(data: { [key: string]: string }[], fastaMapper?: boolean, first?: boolean): string { if (fastaMapper) { data = this.integrateFastaHeaders(data, fastaMapper); } return this.convert(data, first); } - integrateFastaHeaders(data: any, fastaMapper: boolean): any { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + integrateFastaHeaders(data: { [key: string]: string }[], fastaMapper: boolean): { [key: string]: string }[] { return data; } } From 5c9e17f4d942bb6a41f70d5813c62f895e4bb8de Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 30 Jul 2024 10:48:56 +0200 Subject: [PATCH 36/76] add test for unipept --- tests/commands/unipept.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/commands/unipept.test.ts b/tests/commands/unipept.test.ts index e69de29b..c2b2e340 100644 --- a/tests/commands/unipept.test.ts +++ b/tests/commands/unipept.test.ts @@ -0,0 +1,7 @@ +import { Unipept } from '../../lib/commands/unipept'; + +test('test single argument', async () => { + const command = new Unipept(); + const commandNames = command.program.commands.map(c => c.name()); + expect(commandNames).toContain("pept2lca"); +}); From e0b691a7fb0817e7daa57fbbbc8e5b7b7843c21d Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 30 Jul 2024 14:57:33 +0200 Subject: [PATCH 37/76] add formatter tests --- lib/formatters/formatter.ts | 6 ++--- tests/commands/unipept.test.ts | 2 +- tests/formatters/csv_formatter.test.ts | 30 ++++++++++++++++++++++ tests/formatters/formatter_factory.test.ts | 11 ++++++++ tests/formatters/test_object.ts | 21 +++++++++++++++ 5 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 tests/formatters/csv_formatter.test.ts create mode 100644 tests/formatters/formatter_factory.test.ts create mode 100644 tests/formatters/test_object.ts diff --git a/lib/formatters/formatter.ts b/lib/formatters/formatter.ts index 39d4202d..1c8ecf02 100644 --- a/lib/formatters/formatter.ts +++ b/lib/formatters/formatter.ts @@ -2,9 +2,9 @@ export abstract class Formatter { abstract header(sampleData: object, fastaMapper?: boolean): string; abstract footer(): string; - abstract convert(data: { [key: string]: string }[], first?: boolean): string; + abstract convert(data: object[], first?: boolean): string; - format(data: { [key: string]: string }[], fastaMapper?: boolean, first?: boolean): string { + format(data: object[], fastaMapper?: boolean, first?: boolean): string { if (fastaMapper) { data = this.integrateFastaHeaders(data, fastaMapper); } @@ -12,7 +12,7 @@ export abstract class Formatter { } // eslint-disable-next-line @typescript-eslint/no-unused-vars - integrateFastaHeaders(data: { [key: string]: string }[], fastaMapper: boolean): { [key: string]: string }[] { + integrateFastaHeaders(data: object[], fastaMapper: boolean): object[] { return data; } } diff --git a/tests/commands/unipept.test.ts b/tests/commands/unipept.test.ts index c2b2e340..cdda14dd 100644 --- a/tests/commands/unipept.test.ts +++ b/tests/commands/unipept.test.ts @@ -1,6 +1,6 @@ import { Unipept } from '../../lib/commands/unipept'; -test('test single argument', async () => { +test('test if all commands are available', async () => { const command = new Unipept(); const commandNames = command.program.commands.map(c => c.name()); expect(commandNames).toContain("pept2lca"); diff --git a/tests/formatters/csv_formatter.test.ts b/tests/formatters/csv_formatter.test.ts new file mode 100644 index 00000000..847f086b --- /dev/null +++ b/tests/formatters/csv_formatter.test.ts @@ -0,0 +1,30 @@ +import { FormatterFactory } from "../../lib/formatters/formatter_factory"; +import { TestObject } from "./test_object"; + +const formatter = FormatterFactory.getFormatter("csv"); + +test('test header', () => { + const fasta = [["peptide", ">test"]]; + const object = [TestObject.testObject(), TestObject.testObject()]; + expect(formatter.header(object)).toBe(TestObject.asCsvHeader()); + //expect(formatter.header(object, fasta)).toBe(`fasta_header,${TestObject.asCsvHeader()}`); +}); + +test('test footer', () => { + expect(formatter.footer()).toBe(""); +}); + +test('test convert', () => { + const object = [TestObject.testObject(), TestObject.testObject()]; + const csv = [TestObject.asCsv(), TestObject.asCsv(), ""].join("\n"); + + expect(formatter.convert(object, true)).toBe(csv); + expect(formatter.convert(object, false)).toBe(csv); +}); + +test('test format with fasta', () => { + const fasta = [['>test', '5']]; + const object = [TestObject.testObject(), TestObject.testObject()]; + const csv = [`>test,${TestObject.asCsv()}`, TestObject.asCsv(), ""].join("\n"); + //expect(formatter.format(object, fasta, false)).toBe(csv); +}); diff --git a/tests/formatters/formatter_factory.test.ts b/tests/formatters/formatter_factory.test.ts new file mode 100644 index 00000000..05933522 --- /dev/null +++ b/tests/formatters/formatter_factory.test.ts @@ -0,0 +1,11 @@ +import { FormatterFactory } from "../../lib/formatters/formatter_factory"; + +test('test if default formatter is csv', async () => { + const formatter = FormatterFactory.getFormatter("foo"); + expect(formatter.constructor.name).toBe("CSVFormatter"); +}); + +test('test if csv formatter is csv', async () => { + const formatter = FormatterFactory.getFormatter("csv"); + expect(formatter.constructor.name).toBe("CSVFormatter"); +}); diff --git a/tests/formatters/test_object.ts b/tests/formatters/test_object.ts new file mode 100644 index 00000000..097ffc57 --- /dev/null +++ b/tests/formatters/test_object.ts @@ -0,0 +1,21 @@ +export class TestObject { + static testObject() { + return { "integer": 5, "string": "string", "list": ["a", 2, false] }; + } + + static asJson() { + return '{"integer":5,"string":"string","list":["a",2,false]}'; + } + + static asXml() { + return '5stringa2false'; + } + + static asCsv() { + return '5,string,"[""a"",2,false]"'; + } + + static asCsvHeader() { + return "integer,string,list\n"; + } +} From e1589da2519a9bd9b2b7c45408d6bce7c0022d70 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 30 Jul 2024 15:02:32 +0200 Subject: [PATCH 38/76] linter --- tests/formatters/csv_formatter.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/formatters/csv_formatter.test.ts b/tests/formatters/csv_formatter.test.ts index 847f086b..2b9a3b85 100644 --- a/tests/formatters/csv_formatter.test.ts +++ b/tests/formatters/csv_formatter.test.ts @@ -4,7 +4,7 @@ import { TestObject } from "./test_object"; const formatter = FormatterFactory.getFormatter("csv"); test('test header', () => { - const fasta = [["peptide", ">test"]]; + //const fasta = [["peptide", ">test"]]; const object = [TestObject.testObject(), TestObject.testObject()]; expect(formatter.header(object)).toBe(TestObject.asCsvHeader()); //expect(formatter.header(object, fasta)).toBe(`fasta_header,${TestObject.asCsvHeader()}`); @@ -23,8 +23,8 @@ test('test convert', () => { }); test('test format with fasta', () => { - const fasta = [['>test', '5']]; - const object = [TestObject.testObject(), TestObject.testObject()]; - const csv = [`>test,${TestObject.asCsv()}`, TestObject.asCsv(), ""].join("\n"); + //const fasta = [['>test', '5']]; + //const object = [TestObject.testObject(), TestObject.testObject()]; + //const csv = [`>test,${TestObject.asCsv()}`, TestObject.asCsv(), ""].join("\n"); //expect(formatter.format(object, fasta, false)).toBe(csv); }); From 819f1a61d5e23953146b120cd49c7881cdbdac5d Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 30 Jul 2024 15:14:28 +0200 Subject: [PATCH 39/76] refactor passing arguments programatically --- lib/commands/base_command.ts | 12 +++++------- lib/commands/peptfilter.ts | 6 +++--- lib/commands/prot2pept.ts | 6 +++--- lib/commands/unipept.ts | 6 +++--- lib/commands/uniprot.ts | 6 +++--- tests/commands/peptfilter.test.ts | 4 ++-- tests/commands/prot2pept.test.ts | 4 ++-- tests/commands/uniprot.test.ts | 20 ++++++++++---------- 8 files changed, 31 insertions(+), 33 deletions(-) diff --git a/lib/commands/base_command.ts b/lib/commands/base_command.ts index 35c7b795..efa87e6a 100644 --- a/lib/commands/base_command.ts +++ b/lib/commands/base_command.ts @@ -10,16 +10,14 @@ import { readFileSync } from "fs"; */ export abstract class BaseCommand { public program: Command; - args: string[] | undefined; version: string; - constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean, args?: string[] }) { + constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean }) { this.version = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")).version; this.program = this.create(options); - this.args = options?.args; } - abstract run(): void; + abstract run(args?: string[]): void; /** * Create sets up the command line program. Implementing classes can add additional options. @@ -47,10 +45,10 @@ export abstract class BaseCommand { /** * This allows us to pass a custom list of strings as arguments to the command during testing. */ - parseArguments() { - if (this.args) { + parseArguments(args?: string[]) { + if (args) { // custom arg parsing to be able to inject args for testing - this.program.parse(this.args, { from: "user" }); + this.program.parse(args, { from: "user" }); } else { this.program.parse(); } diff --git a/lib/commands/peptfilter.ts b/lib/commands/peptfilter.ts index f8435dc4..0bdb17ec 100644 --- a/lib/commands/peptfilter.ts +++ b/lib/commands/peptfilter.ts @@ -7,7 +7,7 @@ export class Peptfilter extends BaseCommand { The input should have one peptide per line. FASTA headers are preserved in the output, so that peptides remain bundled.`; - constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean, args?: string[] }) { + constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean }) { super(options); this.program @@ -24,8 +24,8 @@ The input should have one peptide per line. FASTA headers are preserved in the o * async iterators. This alternative implementation runs in 2.5 seconds. However, I decided that the async iterator implementation is * both more readable and more in line with the implementation of the other commands. */ - async run() { - this.parseArguments(); + async run(args?: string[]) { + this.parseArguments(args); const minLen = this.program.opts().minlen; const maxlen = this.program.opts().maxlen; const lacks = this.program.opts().lacks || []; diff --git a/lib/commands/prot2pept.ts b/lib/commands/prot2pept.ts index a64d862a..4c33ed7c 100644 --- a/lib/commands/prot2pept.ts +++ b/lib/commands/prot2pept.ts @@ -8,7 +8,7 @@ export class Prot2pept extends BaseCommand { The input should have either one protein sequence per line or contain a FASTA formatted list of protein sequences. FASTA headers are preserved in the output, so that peptides can be bundled per protein sequence. `; - constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean, args?: string[] }) { + constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean }) { super(options); this.program @@ -21,8 +21,8 @@ The input should have either one protein sequence per line or contain a FASTA fo * Performance note: Just as with peptfilter, this implementation can be made faster by using line events instead of * async iterators. */ - async run() { - this.parseArguments(); + async run(args?: string[]) { + this.parseArguments(args); let pattern; try { diff --git a/lib/commands/unipept.ts b/lib/commands/unipept.ts index 553a5fc2..24c1b29a 100644 --- a/lib/commands/unipept.ts +++ b/lib/commands/unipept.ts @@ -13,7 +13,7 @@ Subcommands that start with pept expect a list of tryptic peptides as input. Sub The command will give priority to the first way the input is passed, in the order as listed above. Text files and standard input should have one tryptic peptide or one NCBI Taxonomy Identifier per line.`; - constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean, args?: string[] }) { + constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean }) { super(options); this.program @@ -22,7 +22,7 @@ The command will give priority to the first way the input is passed, in the orde .addCommand(new Pept2lca().command); } - async run() { - this.parseArguments(); + async run(args?: string[]) { + this.parseArguments(args); } } diff --git a/lib/commands/uniprot.ts b/lib/commands/uniprot.ts index 11f5e57b..e0946251 100644 --- a/lib/commands/uniprot.ts +++ b/lib/commands/uniprot.ts @@ -16,7 +16,7 @@ The command will give priority to the first way UniProt Accession Numbers are pa The uniprot command yields just the protein sequences as a default, but can return several formats.`; - constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean, args?: string[] }) { + constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean }) { super(options); this.program @@ -26,8 +26,8 @@ The uniprot command yields just the protein sequences as a default, but can retu .addOption(new Option("-f, --format ", `output format`).choices(Uniprot.VALID_FORMATS).default("sequence")); } - async run() { - this.parseArguments(); + async run(args?: string[]) { + this.parseArguments(args); const format = this.program.opts().format; const accessions = this.program.args; diff --git a/tests/commands/peptfilter.test.ts b/tests/commands/peptfilter.test.ts index 0318fde2..0f13096b 100644 --- a/tests/commands/peptfilter.test.ts +++ b/tests/commands/peptfilter.test.ts @@ -80,8 +80,8 @@ test('test if it passes fasta from stdin', async () => { test('test complex example from stdin', async () => { const stdin = mock.stdin(); - const command = new Peptfilter({ args: ["--minlen", "4", "--maxlen", "10", "--lacks", "B", "--contains", "A"] }); - const run = command.run(); + const command = new Peptfilter(); + const run = command.run(["--minlen", "4", "--maxlen", "10", "--lacks", "B", "--contains", "A"]); stdin.send("A\n"); stdin.send("AAAAAAAAAAA\n"); diff --git a/tests/commands/prot2pept.test.ts b/tests/commands/prot2pept.test.ts index ca01533a..80d7e504 100644 --- a/tests/commands/prot2pept.test.ts +++ b/tests/commands/prot2pept.test.ts @@ -111,8 +111,8 @@ test('test fasta input 3', async () => { test('test custom pattern', async () => { const stdin = mock.stdin(); - const command = new Prot2pept({ args: ["--pattern", "([KR])([^A])"] }); - const run = command.run(); + const command = new Prot2pept(); + const run = command.run(["--pattern", "([KR])([^A])"]); stdin.send("AALTERAALTERPAALTER\n"); stdin.end(); diff --git a/tests/commands/uniprot.test.ts b/tests/commands/uniprot.test.ts index 59ba4f7f..27b7f304 100644 --- a/tests/commands/uniprot.test.ts +++ b/tests/commands/uniprot.test.ts @@ -17,8 +17,8 @@ beforeEach(() => { }); test('test single argument', async () => { - const command = new Uniprot({ args: ["Q6GZX3"] }); - await command.run(); + const command = new Uniprot(); + await command.run(["Q6GZX3"]); expect(writeSpy).toHaveBeenCalledTimes(1); expect(errorSpy).toHaveBeenCalledTimes(0); @@ -26,8 +26,8 @@ test('test single argument', async () => { }); test('test two arguments', async () => { - const command = new Uniprot({ args: ["Q6GZX3", "Q6GZX4"] }); - await command.run(); + const command = new Uniprot(); + await command.run(["Q6GZX3", "Q6GZX4"]); expect(writeSpy).toHaveBeenCalledTimes(2); expect(errorSpy).toHaveBeenCalledTimes(0); @@ -35,8 +35,8 @@ test('test two arguments', async () => { }); test('test fasta output', async () => { - const command = new Uniprot({ args: ["--format", "fasta", "Q6GZX3", "Q6GZX4"] }); - await command.run(); + const command = new Uniprot(); + await command.run(["--format", "fasta", "Q6GZX3", "Q6GZX4"]); expect(writeSpy).toHaveBeenCalledTimes(2); expect(errorSpy).toHaveBeenCalledTimes(0); @@ -78,16 +78,16 @@ test('test double line stdin', async () => { }); test('test on invalid id', async () => { - const command = new Uniprot({ args: ["Bart"] }); - await command.run(); + const command = new Uniprot(); + await command.run(["Bart"]); expect(errorSpy).toHaveBeenCalledTimes(1); }); test('test all valid formats', async () => { for (const format of Uniprot.VALID_FORMATS) { - const command = new Uniprot({ args: ["--format", format, "Q6GZX3"] }); - await command.run(); + const command = new Uniprot(); + await command.run(["--format", format, "Q6GZX3"]); expect(errorSpy).toHaveBeenCalledTimes(0); } From f7dcf0e5b2c0a8c51f1940b38f6acaad3f41bd00 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Wed, 31 Jul 2024 15:01:59 +0200 Subject: [PATCH 40/76] add basic command runner tests --- lib/commands/unipept/unipept_subcommand.ts | 2 +- .../unipept/unipept_subcommand.test.ts | 58 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 tests/commands/unipept/unipept_subcommand.test.ts diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index 3ebecee6..a9e6e593 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -149,6 +149,6 @@ export abstract class UnipeptSubcommand { } private globToRegex(glob: string): RegExp { - return new RegExp(glob.replace(/\*/g, ".*")); + return new RegExp(`^${glob.replace(/\*/g, ".*")}$`); } } diff --git a/tests/commands/unipept/unipept_subcommand.test.ts b/tests/commands/unipept/unipept_subcommand.test.ts new file mode 100644 index 00000000..de816a36 --- /dev/null +++ b/tests/commands/unipept/unipept_subcommand.test.ts @@ -0,0 +1,58 @@ +import { Interface } from 'readline'; +import { Pept2lca } from '../../../lib/commands/unipept/pept2lca'; + +test('test command setup', () => { + const command = new Pept2lca(); + expect(command.name).toBe("pept2lca"); + expect(command.user_agent).toMatch(/^unipept-cli/); + expect(command.command.name()).toBe("pept2lca"); +}); + +test('test correct host', () => { + const command = new Pept2lca(); + + expect(command.host).toBe("https://api.unipept.ugent.be"); + expect(command["getHost"]()).toBe("https://api.unipept.ugent.be"); + + command.options.host = "https://optionshost"; + expect(command["getHost"]()).toBe("https://optionshost"); + + command.options.host = "http://optionshost"; + expect(command["getHost"]()).toBe("http://optionshost"); + + command.options.host = "optionshost"; + expect(command["getHost"]()).toBe("http://optionshost"); +}); + +test('test correct inputIterator', async () => { + const command = new Pept2lca(); + + // should be stdin + let input = command["getInputIterator"]([]) as Interface; + expect(input).toBeInstanceOf(Interface); + input.close(); + + // should be a (non-existant) file and error + input = command["getInputIterator"]([], "filename") as Interface; + input.on("error", (e) => { + expect(e.toString()).toMatch(/no such file/); + }); + + // should be array + const inputArray = command["getInputIterator"](["A", "B"]); + expect(inputArray).toBeInstanceOf(Array); +}); + +test('test selected fields parsing', () => { + const command = new Pept2lca(); + + command.options.select = ["a,b,c"]; + expect(command["getSelectedFields"]()).toStrictEqual([/^a$/, /^b$/, /^c$/]); +}); + +test('test selected fields with wildcards', () => { + const command = new Pept2lca(); + + command.options.select = ["taxon*,name"]; + expect(command["getSelectedFields"]()).toStrictEqual([/^taxon.*$/, /^name$/]); +}); From 7da1e02b3beb03d236a7c56e20ce3b2f833583d4 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Thu, 1 Aug 2024 14:20:48 +0200 Subject: [PATCH 41/76] implement json formatter --- lib/commands/unipept/unipept_subcommand.ts | 5 +++-- lib/formatters/formatter_factory.ts | 3 +++ lib/formatters/json_formatter.ts | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 lib/formatters/json_formatter.ts diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index a9e6e593..09a62bde 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -84,11 +84,12 @@ export abstract class UnipeptSubcommand { const result = await r.json(); if (this.firstBatch && this.options.header) { - this.firstBatch = false; this.outputStream.write(this.formatter.header(result, this.fasta)); } - this.outputStream.write(this.formatter.format(result, this.fasta)); + this.outputStream.write(this.formatter.format(result, this.fasta, this.firstBatch)); + + if (this.firstBatch) this.firstBatch = false; } private constructRequestBody(slice: string[]): URLSearchParams { diff --git a/lib/formatters/formatter_factory.ts b/lib/formatters/formatter_factory.ts index b350df77..110b76ad 100644 --- a/lib/formatters/formatter_factory.ts +++ b/lib/formatters/formatter_factory.ts @@ -1,10 +1,13 @@ import { CSVFormatter } from "./csv_formatter.js"; import { Formatter } from "./formatter.js"; +import { JSONFormatter } from "./json_formatter.js"; export class FormatterFactory { static getFormatter(name: string): Formatter { if (name === "csv") { return new CSVFormatter(); + } else if (name === "json") { + return new JSONFormatter(); } return new CSVFormatter(); } diff --git a/lib/formatters/json_formatter.ts b/lib/formatters/json_formatter.ts new file mode 100644 index 00000000..0ab5436a --- /dev/null +++ b/lib/formatters/json_formatter.ts @@ -0,0 +1,18 @@ +import { Formatter } from "./formatter.js"; + +export class JSONFormatter extends Formatter { + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + header(sampleData: { [key: string]: string }[], fastaMapper?: boolean | undefined): string { + return "["; + } + + footer(): string { + return "]\n"; + } + + convert(data: object[], first: boolean): string { + const output = data.map(d => JSON.stringify(d)).join(","); + return first ? output : `,${output}`; + } +} From ec28bfe11354e23cc1ec16b757334d1f478f49d7 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Thu, 1 Aug 2024 14:28:26 +0200 Subject: [PATCH 42/76] add tests for json formatter --- tests/formatters/json_formatter.test.ts | 28 +++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/formatters/json_formatter.test.ts diff --git a/tests/formatters/json_formatter.test.ts b/tests/formatters/json_formatter.test.ts new file mode 100644 index 00000000..c4e80648 --- /dev/null +++ b/tests/formatters/json_formatter.test.ts @@ -0,0 +1,28 @@ +import { FormatterFactory } from "../../lib/formatters/formatter_factory"; +import { TestObject } from "./test_object"; + +const formatter = FormatterFactory.getFormatter("json"); + +test('test header', () => { + const object = [TestObject.testObject(), TestObject.testObject()]; + expect(formatter.header(object)).toBe("["); +}); + +test('test footer', () => { + expect(formatter.footer()).toBe("]\n"); +}); + +test('test convert', () => { + const object = [TestObject.testObject()]; + const json = TestObject.asJson(); + + expect(formatter.convert(object, true)).toBe(json); + expect(formatter.convert(object, false)).toBe(`,${json}`); +}); + +test('test format with fasta', () => { + //const fasta = [['>test', '5']]; + //const object = [TestObject.testObject()]; + //const json = '{"fasta_header":">test","integer":5,"string":"string","list":["a",2,false]}'; + //expect(formatter.format(object, fasta, true)).toBe(json); +}); From 0f915f5b92a2bdecdc405498f5d770d363d6b1cb Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Thu, 1 Aug 2024 14:46:29 +0200 Subject: [PATCH 43/76] add xml formatter --- eslint.config.js | 7 ++++++- lib/formatters/formatter_factory.ts | 5 ++++- lib/formatters/json_formatter.ts | 3 +-- lib/formatters/xml_formatter.ts | 17 +++++++++++++++++ package.json | 3 ++- yarn.lock | 5 +++++ 6 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 lib/formatters/xml_formatter.ts diff --git a/eslint.config.js b/eslint.config.js index 17c0f257..3f8a7e2e 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -7,5 +7,10 @@ export default [ { languageOptions: { globals: globals.node } }, pluginJs.configs.recommended, ...tseslint.configs.recommended, - { ignores: ["dist/"] } + { + rules: { + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], + }, + ignores: ["dist/"] + } ]; diff --git a/lib/formatters/formatter_factory.ts b/lib/formatters/formatter_factory.ts index 110b76ad..2a1f3cf5 100644 --- a/lib/formatters/formatter_factory.ts +++ b/lib/formatters/formatter_factory.ts @@ -1,6 +1,7 @@ -import { CSVFormatter } from "./csv_formatter.js"; import { Formatter } from "./formatter.js"; +import { CSVFormatter } from "./csv_formatter.js"; import { JSONFormatter } from "./json_formatter.js"; +import { XMLFormatter } from "./xml_formatter.js"; export class FormatterFactory { static getFormatter(name: string): Formatter { @@ -8,6 +9,8 @@ export class FormatterFactory { return new CSVFormatter(); } else if (name === "json") { return new JSONFormatter(); + } else if (name === "xml") { + return new XMLFormatter(); } return new CSVFormatter(); } diff --git a/lib/formatters/json_formatter.ts b/lib/formatters/json_formatter.ts index 0ab5436a..a6af5efe 100644 --- a/lib/formatters/json_formatter.ts +++ b/lib/formatters/json_formatter.ts @@ -2,8 +2,7 @@ import { Formatter } from "./formatter.js"; export class JSONFormatter extends Formatter { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - header(sampleData: { [key: string]: string }[], fastaMapper?: boolean | undefined): string { + header(_sampleData: { [key: string]: string }[], _fastaMapper?: boolean | undefined): string { return "["; } diff --git a/lib/formatters/xml_formatter.ts b/lib/formatters/xml_formatter.ts new file mode 100644 index 00000000..ebbd5a35 --- /dev/null +++ b/lib/formatters/xml_formatter.ts @@ -0,0 +1,17 @@ +import { Formatter } from "./formatter.js"; +import { toXML } from "to-xml"; + +export class XMLFormatter extends Formatter { + + header(_sampleData: { [key: string]: string }[], _fastaMapper?: boolean | undefined): string { + return ""; + } + + footer(): string { + return "\n"; + } + + convert(data: object[], _first: boolean): string { + return data.map(d => `${toXML(d)}`).join(""); + } +} diff --git a/package.json b/package.json index fc1ff4c4..24a343a8 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ }, "dependencies": { "commander": "^12.1.0", - "csv-stringify": "^6.5.0" + "csv-stringify": "^6.5.0", + "to-xml": "^0.1.11" }, "devDependencies": { "@eslint/js": "^9.5.0", diff --git a/yarn.lock b/yarn.lock index 90d703b4..c6c157f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2853,6 +2853,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +to-xml@^0.1.11: + version "0.1.11" + resolved "https://registry.yarnpkg.com/to-xml/-/to-xml-0.1.11.tgz#fae4dafe89889e5013c86e4ea65933dca97d985b" + integrity sha512-deRSQy7vONsawpyrPdhuV0Lh0yXtKQEhAnvfSv3JMahCq3PF0MZJB/BdV1jJANVbuMdnx96zhqD/X0zMMXx0Pw== + ts-api-utils@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" From 8044011173e94f6998852d98e1e69ff665184788 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Thu, 1 Aug 2024 16:03:13 +0200 Subject: [PATCH 44/76] add tests for xml formatter --- lib/formatters/to_xml.ts | 255 +++++++++++++++++++++++++ lib/formatters/xml_formatter.ts | 2 +- package.json | 3 +- tests/formatters/xml_formatter.test.ts | 28 +++ yarn.lock | 5 - 5 files changed, 285 insertions(+), 8 deletions(-) create mode 100644 lib/formatters/to_xml.ts create mode 100644 tests/formatters/xml_formatter.test.ts diff --git a/lib/formatters/to_xml.ts b/lib/formatters/to_xml.ts new file mode 100644 index 00000000..bcebc152 --- /dev/null +++ b/lib/formatters/to_xml.ts @@ -0,0 +1,255 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck + +// This file was taken from https://github.com/kawanet/to-xml and modified to have a specific output for arrays. + +/** + * The toXML() method converts a JavaScript value to an XML string. + * + * @function toXML + * @param value {Object} The value to convert to an XML string. + * @param [replacer] {Function} A function that alters the behavior + * of the stringification process. + * @param [space] {Number|String} A String or Number object that's + * used to insert white space into the output XML string for + * readability purposes. If this is a Number, it indicates the number + * of space characters to use as white space. + * If this is a String, the string is used as white space. + * @returns {String} + */ + +const TYPES = { + "boolean": fromString, + "number": fromString, + "object": fromObject, + "string": fromString +}; + +const ESCAPE = { + "\t": " ", + "\n": " ", + "\r": " ", + " ": " ", + "&": "&", + "<": "<", + ">": ">", + '"': """ +}; + +const ATTRIBUTE_KEY = "@"; +const CHILD_NODE_KEY = "#"; +const LF = "\n"; + +const isArray = Array.isArray || _isArray; + +const REPLACE = String.prototype.replace; + +function _toXML(value, replacer, space) { + const job = createJob(replacer, space); + fromAny(job, "", value); + return job.r; +} + +function createJob(replacer, space) { + const job = { + f: replacer, // replacer function + // s: "", // indent string + // i: 0, // indent string length + l: "", // current indent string + r: "" // result string + }; + + if (space) { + let str = ""; + + if (space > 0) { + for (let i = space; i; i--) { + str += " "; + } + } else { + str += space; // stringify + } + job.s = str; + + // indent string length + job.i = str.length; + } + + return job; +} + +function fromAny(job, key, value) { + // child node synonym + if (key === CHILD_NODE_KEY) key = ""; + + if (_isArray(value)) return fromArray(job, key, value); + + const replacer = job.f; + if (replacer) value = replacer(key, value); + + const f = TYPES[typeof value]; + if (f) f(job, key, value); +} + +function fromString(job, key, value) { + if (key === "?") { + // XML declaration + value = ""; + } else if (key === "!") { + // comment, CDATA section + value = ""; + } else { + value = escapeTextNode(value); + if (key) { + // text element without attributes + value = "<" + key + ">" + value + ""; + } + } + + if (key && job.i && job.r) { + job.r += LF + job.l; // indent + } + + job.r += value; +} + +function fromArray(job, key, value) { + if (key !== "item") { + fromObject(job, key, { item: value }); + } else { + Array.prototype.forEach.call(value, function (value) { + fromAny(job, key, value); + }); + } +} + +function fromObject(job, key, value) { + // empty tag + const hasTag = !!key; + const closeTag = (value === null); + if (closeTag) { + if (!hasTag) return; + value = {}; + } + + const keys = Object.keys(value); + const keyLength = keys.length; + const attrs = keys.filter(isAttribute); + const attrLength = attrs.length; + const hasIndent = job.i; + const curIndent = job.l; + let willIndent = hasTag && hasIndent; + let didIndent; + + // open tag + if (hasTag) { + if (hasIndent && job.r) { + job.r += LF + curIndent; + } + + job.r += '<' + key; + + // attributes + attrs.forEach(function (name) { + writeAttributes(job, name.substr(1), value[name]); + }); + + // empty element + const isEmpty = closeTag || (attrLength && keyLength === attrLength); + if (isEmpty) { + const firstChar = key[0]; + if (firstChar !== "!" && firstChar !== "?") { + job.r += "/"; + } + } + + job.r += '>'; + + if (isEmpty) return; + } + + keys.forEach(function (name) { + // skip attribute + if (isAttribute(name)) return; + + // indent when it has child node but not fragment + if (willIndent && ((name && name !== CHILD_NODE_KEY) || isArray(value[name]))) { + job.l += job.s; // increase indent level + willIndent = 0; + didIndent = 1; + } + + // child node or text node + fromAny(job, name, value[name]); + }); + + if (didIndent) { + // decrease indent level + job.l = job.l.substr(job.i); + + job.r += LF + job.l; + } + + // close tag + if (hasTag) { + job.r += ''; + } +} + +function writeAttributes(job, key, val) { + if (isArray(val)) { + val.forEach(function (child) { + writeAttributes(job, key, child); + }); + } else if (!key && "object" === typeof val) { + Object.keys(val).forEach(function (name) { + writeAttributes(job, name, val[name]); + }); + } else { + writeAttribute(job, key, val); + } +} + +function writeAttribute(job, key, val) { + const replacer = job.f; + if (replacer) val = replacer(ATTRIBUTE_KEY + key, val); + if ("undefined" === typeof val) return; + + // empty attribute name + if (!key) { + job.r += ' ' + val; + return; + } + + // attribute name + job.r += ' ' + key; + + // property attribute + if (val === null) return; + + job.r += '="' + escapeAttribute(val) + '"'; +} + +function isAttribute(name) { + return name && name[0] === ATTRIBUTE_KEY; +} + +function escapeTextNode(str) { + return REPLACE.call(str, /(^\s|[&<>]|\s$)/g, escapeRef); +} + +function escapeAttribute(str) { + return REPLACE.call(str, /([&"])/g, escapeRef); +} + +function escapeRef(str) { + return ESCAPE[str] || str; +} + +function _isArray(array) { + return array instanceof Array; +} + +export function toXML(value: Object, replacer?: Function, space?: Number | String): String { + return _toXML(value, replacer, space); +} diff --git a/lib/formatters/xml_formatter.ts b/lib/formatters/xml_formatter.ts index ebbd5a35..e30c54e5 100644 --- a/lib/formatters/xml_formatter.ts +++ b/lib/formatters/xml_formatter.ts @@ -1,5 +1,5 @@ import { Formatter } from "./formatter.js"; -import { toXML } from "to-xml"; +import { toXML } from "./to_xml.js"; export class XMLFormatter extends Formatter { diff --git a/package.json b/package.json index 24a343a8..fc1ff4c4 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,7 @@ }, "dependencies": { "commander": "^12.1.0", - "csv-stringify": "^6.5.0", - "to-xml": "^0.1.11" + "csv-stringify": "^6.5.0" }, "devDependencies": { "@eslint/js": "^9.5.0", diff --git a/tests/formatters/xml_formatter.test.ts b/tests/formatters/xml_formatter.test.ts new file mode 100644 index 00000000..c327d034 --- /dev/null +++ b/tests/formatters/xml_formatter.test.ts @@ -0,0 +1,28 @@ +import { FormatterFactory } from "../../lib/formatters/formatter_factory"; +import { TestObject } from "./test_object"; + +const formatter = FormatterFactory.getFormatter("xml"); + +test('test header', () => { + const object = [TestObject.testObject(), TestObject.testObject()]; + expect(formatter.header(object)).toBe(""); +}); + +test('test footer', () => { + expect(formatter.footer()).toBe("\n"); +}); + +test('test convert', () => { + const object = [TestObject.testObject()]; + const xml = `${TestObject.asXml()}`; + + expect(formatter.convert(object, true)).toBe(xml); + expect(formatter.convert(object, false)).toBe(xml); +}); + +test('test format with fasta', () => { + //const fasta = [['>test', '5']]; + //const object = [TestObject.testObject()]; + //const json = '{"fasta_header":">test","integer":5,"string":"string","list":["a",2,false]}'; + //expect(formatter.format(object, fasta, true)).toBe(json); +}); diff --git a/yarn.lock b/yarn.lock index c6c157f9..90d703b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2853,11 +2853,6 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -to-xml@^0.1.11: - version "0.1.11" - resolved "https://registry.yarnpkg.com/to-xml/-/to-xml-0.1.11.tgz#fae4dafe89889e5013c86e4ea65933dca97d985b" - integrity sha512-deRSQy7vONsawpyrPdhuV0Lh0yXtKQEhAnvfSv3JMahCq3PF0MZJB/BdV1jJANVbuMdnx96zhqD/X0zMMXx0Pw== - ts-api-utils@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" From ed97baba271f9e47ff753f7bb0626f2f2473a437 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Thu, 1 Aug 2024 16:05:44 +0200 Subject: [PATCH 45/76] fix linter --- lib/formatters/to_xml.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/formatters/to_xml.ts b/lib/formatters/to_xml.ts index bcebc152..c40593b6 100644 --- a/lib/formatters/to_xml.ts +++ b/lib/formatters/to_xml.ts @@ -250,6 +250,6 @@ function _isArray(array) { return array instanceof Array; } -export function toXML(value: Object, replacer?: Function, space?: Number | String): String { +export function toXML(value: object, replacer?: function, space?: number | string): string { return _toXML(value, replacer, space); } From 3b575a98ab2b114969736ee72ae5f35af9e6401f Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 5 Aug 2024 14:27:40 +0200 Subject: [PATCH 46/76] refactor existing code --- lib/commands/unipept/unipept_subcommand.ts | 39 ++++++++++++------- .../unipept/unipept_subcommand.test.ts | 17 ++++---- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index 09a62bde..e7758f5b 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -20,6 +20,9 @@ export abstract class UnipeptSubcommand { selectedFields?: RegExp[]; fasta: boolean; + // we must save this to be able to close it properly in tests + private streamInterface?: Interface; + constructor(name: string) { this.name = name; const version = JSON.parse(readFileSync(new URL("../../../package.json", import.meta.url), "utf8")).version; @@ -27,6 +30,7 @@ export abstract class UnipeptSubcommand { this.command = this.create(name); this.fasta = false; } + abstract defaultBatchSize(): number; requiredFields(): string[] { @@ -58,16 +62,10 @@ export abstract class UnipeptSubcommand { this.outputStream = createWriteStream(this.options.output); } - let slice = []; + const iterator = this.getInputIterator(args, options.input); + const firstLine = (await iterator.next()).value; - for await (const input of this.getInputIterator(args, options.input)) { - slice.push(input); - if (slice.length >= this.batchSize) { - await this.processBatch(slice); - slice = []; - } - } - await this.processBatch(slice); + await this.normalInputProcessor(firstLine, iterator); } async processBatch(slice: string[]): Promise { @@ -92,6 +90,19 @@ export abstract class UnipeptSubcommand { if (this.firstBatch) this.firstBatch = false; } + async normalInputProcessor(firstLine: string, iterator: IterableIterator | AsyncIterableIterator) { + let slice = [firstLine]; + + for await (const line of iterator) { + slice.push(line); + if (slice.length >= this.batchSize) { + await this.processBatch(slice); + slice = []; + } + } + await this.processBatch(slice); + } + private constructRequestBody(slice: string[]): URLSearchParams { const names = this.getSelectedFields().length === 0 || this.getSelectedFields().some(regex => regex.toString().includes("name") || regex.toString().includes(".*$")); return new URLSearchParams({ @@ -128,13 +139,15 @@ export abstract class UnipeptSubcommand { * - if an input file is given, use the file * - otherwise, use standard input */ - private getInputIterator(args: string[], input?: string): string[] | Interface { + private getInputIterator(args: string[], input?: string): IterableIterator | AsyncIterableIterator { if (args.length > 0) { - return args; + return args.values(); } else if (input) { - return createInterface({ input: createReadStream(input) }); + this.streamInterface = createInterface({ input: createReadStream(input) }); + return this.streamInterface[Symbol.asyncIterator](); } else { - return createInterface({ input: process.stdin }) + this.streamInterface = createInterface({ input: process.stdin }); + return this.streamInterface[Symbol.asyncIterator](); } } diff --git a/tests/commands/unipept/unipept_subcommand.test.ts b/tests/commands/unipept/unipept_subcommand.test.ts index de816a36..4dab997d 100644 --- a/tests/commands/unipept/unipept_subcommand.test.ts +++ b/tests/commands/unipept/unipept_subcommand.test.ts @@ -28,19 +28,18 @@ test('test correct inputIterator', async () => { const command = new Pept2lca(); // should be stdin - let input = command["getInputIterator"]([]) as Interface; - expect(input).toBeInstanceOf(Interface); - input.close(); + let input = command["getInputIterator"]([]) as AsyncIterableIterator; + expect(typeof input[Symbol.asyncIterator]).toBe("function"); + command['streamInterface']?.close(); // should be a (non-existant) file and error - input = command["getInputIterator"]([], "filename") as Interface; - input.on("error", (e) => { - expect(e.toString()).toMatch(/no such file/); - }); + input = command["getInputIterator"]([], "filename") as AsyncIterableIterator; + expect(typeof input[Symbol.asyncIterator]).toBe("function"); + await expect(async () => { await input.next() }).rejects.toThrow(/no such file/); // should be array - const inputArray = command["getInputIterator"](["A", "B"]); - expect(inputArray).toBeInstanceOf(Array); + const inputArray = command["getInputIterator"](["A", "B"]) as IterableIterator; + expect(typeof inputArray[Symbol.iterator]).toBe("function"); }); test('test selected fields parsing', () => { From 2435f1fc2df0a2245952753790fd9f8558805da8 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 5 Aug 2024 15:22:57 +0200 Subject: [PATCH 47/76] add fasta support --- lib/commands/unipept/unipept_subcommand.ts | 32 ++++++++++++++++--- lib/formatters/formatter.ts | 11 ++++--- lib/formatters/to_xml.ts | 4 +-- .../unipept/unipept_subcommand.test.ts | 1 - 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index e7758f5b..56f9e1fc 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -64,11 +64,15 @@ export abstract class UnipeptSubcommand { const iterator = this.getInputIterator(args, options.input); const firstLine = (await iterator.next()).value; - - await this.normalInputProcessor(firstLine, iterator); + if (firstLine.startsWith(">")) { + this.fasta = true; + await this.fastaInputProcessor(firstLine, iterator); + } else { + await this.normalInputProcessor(firstLine, iterator); + } } - async processBatch(slice: string[]): Promise { + async processBatch(slice: string[], fastaMapper?: { [key: string]: string }): Promise { if (!this.formatter) throw new Error("Formatter not set"); const r = await fetch(this.url as string, { @@ -85,7 +89,7 @@ export abstract class UnipeptSubcommand { this.outputStream.write(this.formatter.header(result, this.fasta)); } - this.outputStream.write(this.formatter.format(result, this.fasta, this.firstBatch)); + this.outputStream.write(this.formatter.format(result, fastaMapper, this.firstBatch)); if (this.firstBatch) this.firstBatch = false; } @@ -103,6 +107,26 @@ export abstract class UnipeptSubcommand { await this.processBatch(slice); } + async fastaInputProcessor(firstLine: string, iterator: IterableIterator | AsyncIterableIterator) { + let currentFastaHeader = firstLine; + let slice = []; + let fastaMapper: { [key: string]: string } = {}; + for await (const line of iterator) { + if (line.startsWith(">")) { + currentFastaHeader = line; + } else { + fastaMapper[line] = currentFastaHeader; + slice.push(line); + if (slice.length >= this.batchSize) { + await this.processBatch(slice, fastaMapper); + slice = []; + fastaMapper = {}; + } + } + } + await this.processBatch(slice, fastaMapper); + } + private constructRequestBody(slice: string[]): URLSearchParams { const names = this.getSelectedFields().length === 0 || this.getSelectedFields().some(regex => regex.toString().includes("name") || regex.toString().includes(".*$")); return new URLSearchParams({ diff --git a/lib/formatters/formatter.ts b/lib/formatters/formatter.ts index 1c8ecf02..5310ec19 100644 --- a/lib/formatters/formatter.ts +++ b/lib/formatters/formatter.ts @@ -4,15 +4,18 @@ export abstract class Formatter { abstract footer(): string; abstract convert(data: object[], first?: boolean): string; - format(data: object[], fastaMapper?: boolean, first?: boolean): string { + format(data: object[], fastaMapper?: { [key: string]: string }, first?: boolean): string { if (fastaMapper) { - data = this.integrateFastaHeaders(data, fastaMapper); + data = this.integrateFastaHeaders(data as { [key: string]: string }[], fastaMapper); } return this.convert(data, first); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - integrateFastaHeaders(data: object[], fastaMapper: boolean): object[] { + integrateFastaHeaders(data: { [key: string]: string }[], fastaMapper: { [key: string]: string }): object[] { + const key = Object.keys(data[0])[0]; + data.forEach((entry, i) => { + data[i] = Object.assign({ fastaHeader: fastaMapper[entry[key]] }, entry); + }); return data; } } diff --git a/lib/formatters/to_xml.ts b/lib/formatters/to_xml.ts index c40593b6..fb10ead4 100644 --- a/lib/formatters/to_xml.ts +++ b/lib/formatters/to_xml.ts @@ -250,6 +250,6 @@ function _isArray(array) { return array instanceof Array; } -export function toXML(value: object, replacer?: function, space?: number | string): string { - return _toXML(value, replacer, space); +export function toXML(value: object): string { + return _toXML(value); } diff --git a/tests/commands/unipept/unipept_subcommand.test.ts b/tests/commands/unipept/unipept_subcommand.test.ts index 4dab997d..1fde3605 100644 --- a/tests/commands/unipept/unipept_subcommand.test.ts +++ b/tests/commands/unipept/unipept_subcommand.test.ts @@ -1,4 +1,3 @@ -import { Interface } from 'readline'; import { Pept2lca } from '../../../lib/commands/unipept/pept2lca'; test('test command setup', () => { From b21872dbfe2062749c104f100647244501a42af8 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Mon, 5 Aug 2024 16:02:57 +0200 Subject: [PATCH 48/76] add tests --- lib/formatters/formatter.ts | 2 +- tests/formatters/csv_formatter.test.ts | 8 ++++---- tests/formatters/formatter.test.ts | 12 ++++++++++++ tests/formatters/json_formatter.test.ts | 8 ++++---- tests/formatters/xml_formatter.test.ts | 8 ++++---- 5 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 tests/formatters/formatter.test.ts diff --git a/lib/formatters/formatter.ts b/lib/formatters/formatter.ts index 5310ec19..d0ca6dca 100644 --- a/lib/formatters/formatter.ts +++ b/lib/formatters/formatter.ts @@ -14,7 +14,7 @@ export abstract class Formatter { integrateFastaHeaders(data: { [key: string]: string }[], fastaMapper: { [key: string]: string }): object[] { const key = Object.keys(data[0])[0]; data.forEach((entry, i) => { - data[i] = Object.assign({ fastaHeader: fastaMapper[entry[key]] }, entry); + data[i] = Object.assign({ fasta_header: fastaMapper[entry[key]] }, entry); }); return data; } diff --git a/tests/formatters/csv_formatter.test.ts b/tests/formatters/csv_formatter.test.ts index 2b9a3b85..2ea80c37 100644 --- a/tests/formatters/csv_formatter.test.ts +++ b/tests/formatters/csv_formatter.test.ts @@ -23,8 +23,8 @@ test('test convert', () => { }); test('test format with fasta', () => { - //const fasta = [['>test', '5']]; - //const object = [TestObject.testObject(), TestObject.testObject()]; - //const csv = [`>test,${TestObject.asCsv()}`, TestObject.asCsv(), ""].join("\n"); - //expect(formatter.format(object, fasta, false)).toBe(csv); + const fasta = { 5: ">test" }; + const object = [TestObject.testObject(), TestObject.testObject()]; + const csv = [`>test,${TestObject.asCsv()}`, `>test,${TestObject.asCsv()}`, ""].join("\n"); + expect(formatter.format(object, fasta, false)).toBe(csv); }); diff --git a/tests/formatters/formatter.test.ts b/tests/formatters/formatter.test.ts new file mode 100644 index 00000000..c041f31b --- /dev/null +++ b/tests/formatters/formatter.test.ts @@ -0,0 +1,12 @@ +import { FormatterFactory } from "../../lib/formatters/formatter_factory"; +import { TestObject } from "./test_object"; + +test('test integrate fasta headers', async () => { + const formatter = FormatterFactory.getFormatter("csv"); + const fasta = { 5: ">test" }; + const object = [TestObject.testObject(), TestObject.testObject()]; + const integrated = [Object.assign({ fasta_header: ">test" }, TestObject.testObject()), Object.assign({ fasta_header: ">test" }, TestObject.testObject())]; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + expect(formatter.integrateFastaHeaders(object, fasta)).toEqual(integrated); +}); diff --git a/tests/formatters/json_formatter.test.ts b/tests/formatters/json_formatter.test.ts index c4e80648..42e14293 100644 --- a/tests/formatters/json_formatter.test.ts +++ b/tests/formatters/json_formatter.test.ts @@ -21,8 +21,8 @@ test('test convert', () => { }); test('test format with fasta', () => { - //const fasta = [['>test', '5']]; - //const object = [TestObject.testObject()]; - //const json = '{"fasta_header":">test","integer":5,"string":"string","list":["a",2,false]}'; - //expect(formatter.format(object, fasta, true)).toBe(json); + const fasta = { 5: ">test" }; + const object = [TestObject.testObject()]; + const json = '{"fasta_header":">test","integer":5,"string":"string","list":["a",2,false]}'; + expect(formatter.format(object, fasta, true)).toBe(json); }); diff --git a/tests/formatters/xml_formatter.test.ts b/tests/formatters/xml_formatter.test.ts index c327d034..6994861b 100644 --- a/tests/formatters/xml_formatter.test.ts +++ b/tests/formatters/xml_formatter.test.ts @@ -21,8 +21,8 @@ test('test convert', () => { }); test('test format with fasta', () => { - //const fasta = [['>test', '5']]; - //const object = [TestObject.testObject()]; - //const json = '{"fasta_header":">test","integer":5,"string":"string","list":["a",2,false]}'; - //expect(formatter.format(object, fasta, true)).toBe(json); + const fasta = { 5: ">test" }; + const object = [TestObject.testObject()]; + const xml = `>test${TestObject.asXml()}`; + expect(formatter.format(object, fasta, true)).toBe(xml); }); From c3126fdbd4086a9d3c556b7f04799c001ebf766b Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 6 Aug 2024 11:00:47 +0200 Subject: [PATCH 49/76] implement pept2ec --- lib/commands/unipept.ts | 2 ++ lib/commands/unipept/pept2ec.ts | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 lib/commands/unipept/pept2ec.ts diff --git a/lib/commands/unipept.ts b/lib/commands/unipept.ts index 24c1b29a..c25d2bdd 100644 --- a/lib/commands/unipept.ts +++ b/lib/commands/unipept.ts @@ -1,4 +1,5 @@ import { BaseCommand } from './base_command.js'; +import { Pept2ec } from './unipept/pept2ec.js'; import { Pept2lca } from './unipept/pept2lca.js'; export class Unipept extends BaseCommand { @@ -19,6 +20,7 @@ The command will give priority to the first way the input is passed, in the orde this.program .summary("Command line interface to Unipept web services.") .description(this.description) + .addCommand(new Pept2ec().command) .addCommand(new Pept2lca().command); } diff --git a/lib/commands/unipept/pept2ec.ts b/lib/commands/unipept/pept2ec.ts new file mode 100644 index 00000000..437a311b --- /dev/null +++ b/lib/commands/unipept/pept2ec.ts @@ -0,0 +1,38 @@ +import { Option } from "commander"; +import { UnipeptSubcommand } from "./unipept_subcommand.js"; + +export class Pept2ec extends UnipeptSubcommand { + + readonly description = `For each tryptic peptide the unipept pept2ec command retrieves from Unipept the set of EC numbers from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The command expects a list of tryptic peptides that are passed + +- as separate command line arguments +- in a text file that is passed as an argument to the -i option +- to standard input + +The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line.`; + + constructor() { + super("pept2ec"); + + this.command + .summary("Fetch EC numbers of UniProt entries that match tryptic peptides.") + .description(this.description) + .option("-e, --equate", "equate isoleucine (I) and leucine (L) when matching peptides") + .option("-a, --all", "Also return the names of the EC numbers. Note that this may have a performance penalty.") + .addOption(new Option("-s --select ", "select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.")) + .argument("[peptides...]", "optionally, 1 or more peptides") + .action((args, options) => this.run(args, options)); + } + + requiredFields(): string[] { + return ["peptide"]; + } + + defaultBatchSize(): number { + if (this.options.all) { + return 100; + } else { + return 1000; + } + } +} From 931f810863bbfc61b06b69355214bfc882660ce1 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 6 Aug 2024 11:39:31 +0200 Subject: [PATCH 50/76] add pept2lca test --- lib/commands/unipept/unipept_subcommand.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index 56f9e1fc..c5dfc4b4 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -43,7 +43,7 @@ export abstract class UnipeptSubcommand { command.option("-q, --quiet", "disable service messages"); command.option("-i, --input ", "read input from file"); command.option("-o, --output ", "write output to file"); - command.addOption(new Option("-f, --format ", "define the output format").choices(UnipeptSubcommand.VALID_FORMATS).default("json")); + command.addOption(new Option("-f, --format ", "define the output format").choices(UnipeptSubcommand.VALID_FORMATS).default("csv")); command.option("--host ", "specify the server running the Unipept web service"); // internal options @@ -53,7 +53,7 @@ export abstract class UnipeptSubcommand { return command; } - async run(args: string[], options: { input?: string }): Promise { + async run(args: string[], options: { [key: string]: unknown }): Promise { this.options = options; this.host = this.getHost(); this.url = `${this.host}/api/v2/${this.name}.json`; @@ -62,7 +62,7 @@ export abstract class UnipeptSubcommand { this.outputStream = createWriteStream(this.options.output); } - const iterator = this.getInputIterator(args, options.input); + const iterator = this.getInputIterator(args, options.input as string); const firstLine = (await iterator.next()).value; if (firstLine.startsWith(">")) { this.fasta = true; From d5446279e231aa870c092fd0c71512fe87deb917 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 6 Aug 2024 12:23:14 +0200 Subject: [PATCH 51/76] add pept2ec test --- eslint.config.js | 1 + lib/formatters/csv_formatter.ts | 23 ++++++++++++++++++--- tests/commands/unipept/pept2ec.test.ts | 27 +++++++++++++++++++++++++ tests/commands/unipept/pept2lca.test.ts | 27 +++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 tests/commands/unipept/pept2ec.test.ts create mode 100644 tests/commands/unipept/pept2lca.test.ts diff --git a/eslint.config.js b/eslint.config.js index 3f8a7e2e..195212d1 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -10,6 +10,7 @@ export default [ { rules: { "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], + "@typescript-eslint/ban-ts-comment": "off", }, ignores: ["dist/"] } diff --git a/lib/formatters/csv_formatter.ts b/lib/formatters/csv_formatter.ts index 39b8657f..7cd36212 100644 --- a/lib/formatters/csv_formatter.ts +++ b/lib/formatters/csv_formatter.ts @@ -1,10 +1,11 @@ +import { get } from "http"; import { Formatter } from "./formatter.js"; import { stringify } from "csv-stringify/sync"; export class CSVFormatter extends Formatter { header(sampleData: { [key: string]: string }[], fastaMapper?: boolean | undefined): string { - return stringify([this.getKeys(sampleData, fastaMapper)]); + return stringify([this.getKeys(this.flatten(sampleData), fastaMapper)]); } footer(): string { @@ -12,10 +13,26 @@ export class CSVFormatter extends Formatter { } convert(data: object[]): string { - return stringify(data); + return stringify(this.flatten(data as { [key: string]: unknown }[])); } - getKeys(data: { [key: string]: string }[], fastaMapper?: boolean | undefined): string[] { + getKeys(data: { [key: string]: unknown }[], fastaMapper?: boolean | undefined): string[] { return fastaMapper ? ["fasta_header", ...Object.keys(data[0])] : Object.keys(data[0]); } + + flatten(data: { [key: string]: unknown }[]): { [key: string]: unknown }[] { + if (this.getKeys(data).includes("ec")) { + // @ts-ignore + const keys = Object.keys(data[0].ec[0]); + data.forEach(row => { + keys.forEach(key => { + const newKey = key.startsWith("ec") ? key : `ec_${key}`; + // @ts-ignore + row[newKey] = row.ec.map(e => e[key]).join(" "); + }); + delete row.ec; + }); + } + return data; + } } diff --git a/tests/commands/unipept/pept2ec.test.ts b/tests/commands/unipept/pept2ec.test.ts new file mode 100644 index 00000000..ee789bed --- /dev/null +++ b/tests/commands/unipept/pept2ec.test.ts @@ -0,0 +1,27 @@ +import { jest } from '@jest/globals'; +import { Pept2ec } from "../../../lib/commands/unipept/pept2ec"; + +let output: string[]; +const writeSpy = jest + .spyOn(process.stdout, "write") + .mockImplementation((data: unknown) => { output.push(data as string); return true; }); + +beforeEach(() => { + output = []; +}); + +test('test with default args', async () => { + const command = new Pept2ec(); + await command.run(["AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("peptide,total_protein_count,ec_number,ec_protein_count")).toBeTruthy(); + expect(output[1].startsWith("AALTER,3310,2.3.2.27 3.1.3.3")).toBeTruthy(); + expect(output.length).toBe(2); +}); + +test('test with fasta', async () => { + const command = new Pept2ec(); + await command.run([">test", "AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("fasta_header,peptide,total_protein_count,ec_number,ec_protein_count")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,3310,2.3.2.27 3.1.3.3")).toBeTruthy(); + expect(output.length).toBe(2); +}); diff --git a/tests/commands/unipept/pept2lca.test.ts b/tests/commands/unipept/pept2lca.test.ts new file mode 100644 index 00000000..0b18c6ac --- /dev/null +++ b/tests/commands/unipept/pept2lca.test.ts @@ -0,0 +1,27 @@ +import { jest } from '@jest/globals'; +import { Pept2lca } from "../../../lib/commands/unipept/pept2lca"; + +let output: string[]; +const writeSpy = jest + .spyOn(process.stdout, "write") + .mockImplementation((data: unknown) => { output.push(data as string); return true; }); + +beforeEach(() => { + output = []; +}); + +test('test with default args', async () => { + const command = new Pept2lca(); + await command.run(["AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("peptide,taxon_id")).toBeTruthy(); + expect(output[1].startsWith("AALTER,1,root,no rank")).toBeTruthy(); + expect(output.length).toBe(2); +}); + +test('test with fasta', async () => { + const command = new Pept2lca(); + await command.run([">test", "AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("fasta_header,peptide,taxon_id")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,1,root,no rank")).toBeTruthy(); + expect(output.length).toBe(2); +}); From d7ed283d5071ea839e7d4a3902764d9e22bb7fa3 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 6 Aug 2024 12:25:48 +0200 Subject: [PATCH 52/76] fix linter --- lib/formatters/csv_formatter.ts | 1 - lib/formatters/to_xml.ts | 1 - tests/commands/unipept/pept2ec.test.ts | 2 +- tests/commands/unipept/pept2lca.test.ts | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/formatters/csv_formatter.ts b/lib/formatters/csv_formatter.ts index 7cd36212..3b661207 100644 --- a/lib/formatters/csv_formatter.ts +++ b/lib/formatters/csv_formatter.ts @@ -1,4 +1,3 @@ -import { get } from "http"; import { Formatter } from "./formatter.js"; import { stringify } from "csv-stringify/sync"; diff --git a/lib/formatters/to_xml.ts b/lib/formatters/to_xml.ts index fb10ead4..c806e4f2 100644 --- a/lib/formatters/to_xml.ts +++ b/lib/formatters/to_xml.ts @@ -1,4 +1,3 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck // This file was taken from https://github.com/kawanet/to-xml and modified to have a specific output for arrays. diff --git a/tests/commands/unipept/pept2ec.test.ts b/tests/commands/unipept/pept2ec.test.ts index ee789bed..87283aa7 100644 --- a/tests/commands/unipept/pept2ec.test.ts +++ b/tests/commands/unipept/pept2ec.test.ts @@ -2,7 +2,7 @@ import { jest } from '@jest/globals'; import { Pept2ec } from "../../../lib/commands/unipept/pept2ec"; let output: string[]; -const writeSpy = jest +jest .spyOn(process.stdout, "write") .mockImplementation((data: unknown) => { output.push(data as string); return true; }); diff --git a/tests/commands/unipept/pept2lca.test.ts b/tests/commands/unipept/pept2lca.test.ts index 0b18c6ac..881d6fa5 100644 --- a/tests/commands/unipept/pept2lca.test.ts +++ b/tests/commands/unipept/pept2lca.test.ts @@ -2,7 +2,7 @@ import { jest } from '@jest/globals'; import { Pept2lca } from "../../../lib/commands/unipept/pept2lca"; let output: string[]; -const writeSpy = jest +jest .spyOn(process.stdout, "write") .mockImplementation((data: unknown) => { output.push(data as string); return true; }); From 42fe0bf8f90ea610234818b7b688085e5f098e06 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 6 Aug 2024 13:53:32 +0200 Subject: [PATCH 53/76] add pept2funct --- lib/commands/unipept.ts | 2 ++ lib/commands/unipept/pept2funct.ts | 38 +++++++++++++++++++++++ lib/formatters/csv_formatter.ts | 24 +++++++------- tests/commands/unipept/pept2funct.test.ts | 27 ++++++++++++++++ 4 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 lib/commands/unipept/pept2funct.ts create mode 100644 tests/commands/unipept/pept2funct.test.ts diff --git a/lib/commands/unipept.ts b/lib/commands/unipept.ts index c25d2bdd..525b6321 100644 --- a/lib/commands/unipept.ts +++ b/lib/commands/unipept.ts @@ -1,5 +1,6 @@ import { BaseCommand } from './base_command.js'; import { Pept2ec } from './unipept/pept2ec.js'; +import { Pept2funct } from './unipept/pept2funct.js'; import { Pept2lca } from './unipept/pept2lca.js'; export class Unipept extends BaseCommand { @@ -21,6 +22,7 @@ The command will give priority to the first way the input is passed, in the orde .summary("Command line interface to Unipept web services.") .description(this.description) .addCommand(new Pept2ec().command) + .addCommand(new Pept2funct().command) .addCommand(new Pept2lca().command); } diff --git a/lib/commands/unipept/pept2funct.ts b/lib/commands/unipept/pept2funct.ts new file mode 100644 index 00000000..ae11ff57 --- /dev/null +++ b/lib/commands/unipept/pept2funct.ts @@ -0,0 +1,38 @@ +import { Option } from "commander"; +import { UnipeptSubcommand } from "./unipept_subcommand.js"; + +export class Pept2funct extends UnipeptSubcommand { + + readonly description = `For each tryptic peptide the unipept pept2funct command retrieves from Unipept the set of EC numbers and GO terms from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The command expects a list of tryptic peptides that are passed + +- as separate command line arguments +- in a text file that is passed as an argument to the -i option +- to standard input + +The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line.`; + + constructor() { + super("pept2funct"); + + this.command + .summary("Fetch EC numbers, GO terms and InterPro codes of UniProt entries that match tryptic peptides.") + .description(this.description) + .option("-e, --equate", "equate isoleucine (I) and leucine (L) when matching peptides") + .option("-a, --all", "Also return the names of the EC numbers, GO terms and InterPro codes. Note that this may have a performance penalty.") + .addOption(new Option("-s --select ", "select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.")) + .argument("[peptides...]", "optionally, 1 or more peptides") + .action((args, options) => this.run(args, options)); + } + + requiredFields(): string[] { + return ["peptide"]; + } + + defaultBatchSize(): number { + if (this.options.all) { + return 100; + } else { + return 1000; + } + } +} diff --git a/lib/formatters/csv_formatter.ts b/lib/formatters/csv_formatter.ts index 3b661207..822c593e 100644 --- a/lib/formatters/csv_formatter.ts +++ b/lib/formatters/csv_formatter.ts @@ -20,18 +20,20 @@ export class CSVFormatter extends Formatter { } flatten(data: { [key: string]: unknown }[]): { [key: string]: unknown }[] { - if (this.getKeys(data).includes("ec")) { - // @ts-ignore - const keys = Object.keys(data[0].ec[0]); - data.forEach(row => { - keys.forEach(key => { - const newKey = key.startsWith("ec") ? key : `ec_${key}`; - // @ts-ignore - row[newKey] = row.ec.map(e => e[key]).join(" "); + const prefixes = ["ec", "go", "ipr"]; + prefixes.forEach(prefix => { + if (this.getKeys(data).includes(prefix)) {// @ts-ignore + const keys = Object.keys(data[0][prefix][0]); + data.forEach(row => { + keys.forEach(key => { + const newKey = key.startsWith(prefix) ? key : `${prefix}_${key}`; + // @ts-ignore + row[newKey] = row[prefix].map(e => e[key]).join(" "); + }); + delete row[prefix]; }); - delete row.ec; - }); - } + } + }); return data; } } diff --git a/tests/commands/unipept/pept2funct.test.ts b/tests/commands/unipept/pept2funct.test.ts new file mode 100644 index 00000000..97d21b7e --- /dev/null +++ b/tests/commands/unipept/pept2funct.test.ts @@ -0,0 +1,27 @@ +import { jest } from '@jest/globals'; +import { Pept2funct } from "../../../lib/commands/unipept/pept2funct"; + +let output: string[]; +jest + .spyOn(process.stdout, "write") + .mockImplementation((data: unknown) => { output.push(data as string); return true; }); + +beforeEach(() => { + output = []; +}); + +test('test with default args', async () => { + const command = new Pept2funct(); + await command.run(["AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("peptide,total_protein_count,ec_number,ec_protein_count,go_term,go_protein_count,ipr_code,ipr_protein_count")).toBeTruthy(); + expect(output[1].startsWith("AALTER,3310")).toBeTruthy(); + expect(output.length).toBe(2); +}); + +test('test with fasta', async () => { + const command = new Pept2funct(); + await command.run([">test", "AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("fasta_header,peptide,total_protein_count,ec_number,ec_protein_count,go_term,go_protein_count,ipr_code,ipr_protein_count")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,3310")).toBeTruthy(); + expect(output.length).toBe(2); +}); From d7fffcd300241d9d89c5b62af5f0f597ec142f78 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 6 Aug 2024 13:57:16 +0200 Subject: [PATCH 54/76] add pept2go --- lib/commands/unipept.ts | 2 ++ lib/commands/unipept/pept2go.ts | 38 ++++++++++++++++++++++++++ tests/commands/unipept/pept2go.test.ts | 27 ++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 lib/commands/unipept/pept2go.ts create mode 100644 tests/commands/unipept/pept2go.test.ts diff --git a/lib/commands/unipept.ts b/lib/commands/unipept.ts index 525b6321..b70718f6 100644 --- a/lib/commands/unipept.ts +++ b/lib/commands/unipept.ts @@ -1,6 +1,7 @@ import { BaseCommand } from './base_command.js'; import { Pept2ec } from './unipept/pept2ec.js'; import { Pept2funct } from './unipept/pept2funct.js'; +import { Pept2go } from './unipept/pept2go.js'; import { Pept2lca } from './unipept/pept2lca.js'; export class Unipept extends BaseCommand { @@ -23,6 +24,7 @@ The command will give priority to the first way the input is passed, in the orde .description(this.description) .addCommand(new Pept2ec().command) .addCommand(new Pept2funct().command) + .addCommand(new Pept2go().command) .addCommand(new Pept2lca().command); } diff --git a/lib/commands/unipept/pept2go.ts b/lib/commands/unipept/pept2go.ts new file mode 100644 index 00000000..8f91128d --- /dev/null +++ b/lib/commands/unipept/pept2go.ts @@ -0,0 +1,38 @@ +import { Option } from "commander"; +import { UnipeptSubcommand } from "./unipept_subcommand.js"; + +export class Pept2go extends UnipeptSubcommand { + + readonly description = `For each tryptic peptide the unipept pept2ec command retrieves from Unipept the set of GO terms from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The command expects a list of tryptic peptides that are passed + +- as separate command line arguments +- in a text file that is passed as an argument to the -i option +- to standard input + +The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line.`; + + constructor() { + super("pept2go"); + + this.command + .summary("Fetch GO terms of UniProt entries that match tryptic peptides.") + .description(this.description) + .option("-e, --equate", "equate isoleucine (I) and leucine (L) when matching peptides") + .option("-a, --all", "Also return the names of the GO terms. Note that this may have a performance penalty.") + .addOption(new Option("-s --select ", "select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.")) + .argument("[peptides...]", "optionally, 1 or more peptides") + .action((args, options) => this.run(args, options)); + } + + requiredFields(): string[] { + return ["peptide"]; + } + + defaultBatchSize(): number { + if (this.options.all) { + return 100; + } else { + return 1000; + } + } +} diff --git a/tests/commands/unipept/pept2go.test.ts b/tests/commands/unipept/pept2go.test.ts new file mode 100644 index 00000000..08775db1 --- /dev/null +++ b/tests/commands/unipept/pept2go.test.ts @@ -0,0 +1,27 @@ +import { jest } from '@jest/globals'; +import { Pept2go } from "../../../lib/commands/unipept/pept2go"; + +let output: string[]; +jest + .spyOn(process.stdout, "write") + .mockImplementation((data: unknown) => { output.push(data as string); return true; }); + +beforeEach(() => { + output = []; +}); + +test('test with default args', async () => { + const command = new Pept2go(); + await command.run(["AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("peptide,total_protein_count,go_term,go_protein_count")).toBeTruthy(); + expect(output[1].startsWith("AALTER,3310,GO:0003677")).toBeTruthy(); + expect(output.length).toBe(2); +}); + +test('test with fasta', async () => { + const command = new Pept2go(); + await command.run([">test", "AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("fasta_header,peptide,total_protein_count,go_term,go_protein_count")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,3310,GO:0003677")).toBeTruthy(); + expect(output.length).toBe(2); +}); From 07d13683e0dad3e76d6dfbb1b5e307e45fb3d499 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 6 Aug 2024 14:01:15 +0200 Subject: [PATCH 55/76] add pept2interpro --- lib/commands/unipept.ts | 2 ++ lib/commands/unipept/pept2go.ts | 2 +- lib/commands/unipept/pept2interpro.ts | 38 ++++++++++++++++++++ tests/commands/unipept/pept2interpro.test.ts | 27 ++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 lib/commands/unipept/pept2interpro.ts create mode 100644 tests/commands/unipept/pept2interpro.test.ts diff --git a/lib/commands/unipept.ts b/lib/commands/unipept.ts index b70718f6..3c299050 100644 --- a/lib/commands/unipept.ts +++ b/lib/commands/unipept.ts @@ -2,6 +2,7 @@ import { BaseCommand } from './base_command.js'; import { Pept2ec } from './unipept/pept2ec.js'; import { Pept2funct } from './unipept/pept2funct.js'; import { Pept2go } from './unipept/pept2go.js'; +import { Pept2interpro } from './unipept/pept2interpro.js'; import { Pept2lca } from './unipept/pept2lca.js'; export class Unipept extends BaseCommand { @@ -25,6 +26,7 @@ The command will give priority to the first way the input is passed, in the orde .addCommand(new Pept2ec().command) .addCommand(new Pept2funct().command) .addCommand(new Pept2go().command) + .addCommand(new Pept2interpro().command) .addCommand(new Pept2lca().command); } diff --git a/lib/commands/unipept/pept2go.ts b/lib/commands/unipept/pept2go.ts index 8f91128d..c14fcf15 100644 --- a/lib/commands/unipept/pept2go.ts +++ b/lib/commands/unipept/pept2go.ts @@ -3,7 +3,7 @@ import { UnipeptSubcommand } from "./unipept_subcommand.js"; export class Pept2go extends UnipeptSubcommand { - readonly description = `For each tryptic peptide the unipept pept2ec command retrieves from Unipept the set of GO terms from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The command expects a list of tryptic peptides that are passed + readonly description = `For each tryptic peptide the unipept pept2go command retrieves from Unipept the set of GO terms from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The command expects a list of tryptic peptides that are passed - as separate command line arguments - in a text file that is passed as an argument to the -i option diff --git a/lib/commands/unipept/pept2interpro.ts b/lib/commands/unipept/pept2interpro.ts new file mode 100644 index 00000000..f13a03d0 --- /dev/null +++ b/lib/commands/unipept/pept2interpro.ts @@ -0,0 +1,38 @@ +import { Option } from "commander"; +import { UnipeptSubcommand } from "./unipept_subcommand.js"; + +export class Pept2interpro extends UnipeptSubcommand { + + readonly description = `For each tryptic peptide the unipept pept2interpro command retrieves from Unipept the set of InterPro entries from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The command expects a list of tryptic peptides that are passed + +- as separate command line arguments +- in a text file that is passed as an argument to the -i option +- to standard input + +The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line.`; + + constructor() { + super("pept2interpro"); + + this.command + .summary("Fetch GO terms of UniProt entries that match tryptic peptides.") + .description(this.description) + .option("-e, --equate", "equate isoleucine (I) and leucine (L) when matching peptides") + .option("-a, --all", "Also return the names of the InterPro entries. Note that this may have a performance penalty.") + .addOption(new Option("-s --select ", "select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.")) + .argument("[peptides...]", "optionally, 1 or more peptides") + .action((args, options) => this.run(args, options)); + } + + requiredFields(): string[] { + return ["peptide"]; + } + + defaultBatchSize(): number { + if (this.options.all) { + return 100; + } else { + return 1000; + } + } +} diff --git a/tests/commands/unipept/pept2interpro.test.ts b/tests/commands/unipept/pept2interpro.test.ts new file mode 100644 index 00000000..5523e7fb --- /dev/null +++ b/tests/commands/unipept/pept2interpro.test.ts @@ -0,0 +1,27 @@ +import { jest } from '@jest/globals'; +import { Pept2interpro } from "../../../lib/commands/unipept/pept2interpro"; + +let output: string[]; +jest + .spyOn(process.stdout, "write") + .mockImplementation((data: unknown) => { output.push(data as string); return true; }); + +beforeEach(() => { + output = []; +}); + +test('test with default args', async () => { + const command = new Pept2interpro(); + await command.run(["AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("peptide,total_protein_count,ipr_code,ipr_protein_count")).toBeTruthy(); + expect(output[1].startsWith("AALTER,3310,IPR003613")).toBeTruthy(); + expect(output.length).toBe(2); +}); + +test('test with fasta', async () => { + const command = new Pept2interpro(); + await command.run([">test", "AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("fasta_header,peptide,total_protein_count,ipr_code,ipr_protein_count")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,3310,IPR003613")).toBeTruthy(); + expect(output.length).toBe(2); +}); From bb4ed7561b7a6c94ba6254319d836f574c04e2b9 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 6 Aug 2024 14:13:48 +0200 Subject: [PATCH 56/76] add pept2prot --- lib/commands/unipept.ts | 4 ++- lib/commands/unipept/pept2prot.ts | 38 ++++++++++++++++++++++++ tests/commands/unipept/pept2prot.test.ts | 27 +++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 lib/commands/unipept/pept2prot.ts create mode 100644 tests/commands/unipept/pept2prot.test.ts diff --git a/lib/commands/unipept.ts b/lib/commands/unipept.ts index 3c299050..deaf4ef1 100644 --- a/lib/commands/unipept.ts +++ b/lib/commands/unipept.ts @@ -4,6 +4,7 @@ import { Pept2funct } from './unipept/pept2funct.js'; import { Pept2go } from './unipept/pept2go.js'; import { Pept2interpro } from './unipept/pept2interpro.js'; import { Pept2lca } from './unipept/pept2lca.js'; +import { Pept2prot } from './unipept/pept2prot.js'; export class Unipept extends BaseCommand { @@ -27,7 +28,8 @@ The command will give priority to the first way the input is passed, in the orde .addCommand(new Pept2funct().command) .addCommand(new Pept2go().command) .addCommand(new Pept2interpro().command) - .addCommand(new Pept2lca().command); + .addCommand(new Pept2lca().command) + .addCommand(new Pept2prot().command); } async run(args?: string[]) { diff --git a/lib/commands/unipept/pept2prot.ts b/lib/commands/unipept/pept2prot.ts new file mode 100644 index 00000000..90f55e36 --- /dev/null +++ b/lib/commands/unipept/pept2prot.ts @@ -0,0 +1,38 @@ +import { Option } from "commander"; +import { UnipeptSubcommand } from "./unipept_subcommand.js"; + +export class Pept2prot extends UnipeptSubcommand { + + readonly description = `For each tryptic peptide the unipept pept2prot command retrieves from Unipept all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The command expects a list of tryptic peptides that are passed + +- as separate command line arguments +- in a text file that is passed as an argument to the -i option +- to standard input + +The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line.`; + + constructor() { + super("pept2prot"); + + this.command + .summary("Fetch UniProt entries that match tryptic peptides.") + .description(this.description) + .option("-e, --equate", "equate isoleucine (I) and leucine (L) when matching peptides") + .option("-a, --all", "Also return the names of the EC numbers. Note that this may have a performance penalty.") + .addOption(new Option("-s --select ", "select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.")) + .argument("[peptides...]", "optionally, 1 or more peptides") + .action((args, options) => this.run(args, options)); + } + + requiredFields(): string[] { + return ["peptide"]; + } + + defaultBatchSize(): number { + if (this.options.all) { + return 5; + } else { + return 10; + } + } +} diff --git a/tests/commands/unipept/pept2prot.test.ts b/tests/commands/unipept/pept2prot.test.ts new file mode 100644 index 00000000..768ff50e --- /dev/null +++ b/tests/commands/unipept/pept2prot.test.ts @@ -0,0 +1,27 @@ +import { jest } from '@jest/globals'; +import { Pept2prot } from "../../../lib/commands/unipept/pept2prot"; + +let output: string[]; +jest + .spyOn(process.stdout, "write") + .mockImplementation((data: unknown) => { output.push(data as string); return true; }); + +beforeEach(() => { + output = []; +}); + +test('test with default args', async () => { + const command = new Pept2prot(); + await command.run(["AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("peptide,uniprot_id,protein_name,taxon_id,protein")).toBeTruthy(); + expect(output[1].startsWith("AALTER,P78330")).toBeTruthy(); + expect(output.length).toBe(2); +}); + +test('test with fasta', async () => { + const command = new Pept2prot(); + await command.run([">test", "AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("fasta_header,peptide,uniprot_id,protein_name,taxon_id,protein")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,P78330")).toBeTruthy(); + expect(output.length).toBe(2); +}); From 362789f1ac23e9e22df8f834ce8c0f6820faeef0 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 6 Aug 2024 14:28:44 +0200 Subject: [PATCH 57/76] add pept2taxa --- lib/commands/unipept.ts | 4 ++- lib/commands/unipept/pept2interpro.ts | 2 +- lib/commands/unipept/pept2prot.ts | 2 +- lib/commands/unipept/pept2taxa.ts | 34 ++++++++++++++++++++++ lib/commands/unipept/unipept_subcommand.ts | 6 ++++ tests/commands/unipept/pept2taxa.test.ts | 27 +++++++++++++++++ 6 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 lib/commands/unipept/pept2taxa.ts create mode 100644 tests/commands/unipept/pept2taxa.test.ts diff --git a/lib/commands/unipept.ts b/lib/commands/unipept.ts index deaf4ef1..39ba2faa 100644 --- a/lib/commands/unipept.ts +++ b/lib/commands/unipept.ts @@ -5,6 +5,7 @@ import { Pept2go } from './unipept/pept2go.js'; import { Pept2interpro } from './unipept/pept2interpro.js'; import { Pept2lca } from './unipept/pept2lca.js'; import { Pept2prot } from './unipept/pept2prot.js'; +import { Pept2taxa } from './unipept/pept2taxa.js'; export class Unipept extends BaseCommand { @@ -29,7 +30,8 @@ The command will give priority to the first way the input is passed, in the orde .addCommand(new Pept2go().command) .addCommand(new Pept2interpro().command) .addCommand(new Pept2lca().command) - .addCommand(new Pept2prot().command); + .addCommand(new Pept2prot().command) + .addCommand(new Pept2taxa().command); } async run(args?: string[]) { diff --git a/lib/commands/unipept/pept2interpro.ts b/lib/commands/unipept/pept2interpro.ts index f13a03d0..55b9e766 100644 --- a/lib/commands/unipept/pept2interpro.ts +++ b/lib/commands/unipept/pept2interpro.ts @@ -15,7 +15,7 @@ The command will give priority to the first way tryptic peptides are passed, in super("pept2interpro"); this.command - .summary("Fetch GO terms of UniProt entries that match tryptic peptides.") + .summary("Fetch InterPro entries of UniProt entries that match tryptic peptides.") .description(this.description) .option("-e, --equate", "equate isoleucine (I) and leucine (L) when matching peptides") .option("-a, --all", "Also return the names of the InterPro entries. Note that this may have a performance penalty.") diff --git a/lib/commands/unipept/pept2prot.ts b/lib/commands/unipept/pept2prot.ts index 90f55e36..02f880d4 100644 --- a/lib/commands/unipept/pept2prot.ts +++ b/lib/commands/unipept/pept2prot.ts @@ -18,7 +18,7 @@ The command will give priority to the first way tryptic peptides are passed, in .summary("Fetch UniProt entries that match tryptic peptides.") .description(this.description) .option("-e, --equate", "equate isoleucine (I) and leucine (L) when matching peptides") - .option("-a, --all", "Also return the names of the EC numbers. Note that this may have a performance penalty.") + .option("-a, --all", "report all information fields of UniProt entries available in Unipept. Note that this may have a performance penalty.") .addOption(new Option("-s --select ", "select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.")) .argument("[peptides...]", "optionally, 1 or more peptides") .action((args, options) => this.run(args, options)); diff --git a/lib/commands/unipept/pept2taxa.ts b/lib/commands/unipept/pept2taxa.ts new file mode 100644 index 00000000..19e3adfd --- /dev/null +++ b/lib/commands/unipept/pept2taxa.ts @@ -0,0 +1,34 @@ +import { Option } from "commander"; +import { UnipeptSubcommand } from "./unipept_subcommand.js"; + +export class Pept2taxa extends UnipeptSubcommand { + + readonly description = `For each tryptic peptide the unipept pept2taxa command retrieves from Unipept the set of taxa from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The command expects a list of tryptic peptides that are passed + +- as separate command line arguments +- in a text file that is passed as an argument to the -i option +- to standard input + +The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line.`; + + constructor() { + super("pept2taxa"); + + this.command + .summary("Fetch taxa of UniProt entries that match tryptic peptides.") + .description(this.description) + .option("-e, --equate", "equate isoleucine (I) and leucine (L) when matching peptides") + .option("-a, --all", "report all information fields of NCBI Taxonomy records available in Unipept. Note that this may have a performance penalty.") + .addOption(new Option("-s --select ", "select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.")) + .argument("[peptides...]", "optionally, 1 or more peptides") + .action((args, options) => this.run(args, options)); + } + + requiredFields(): string[] { + return ["peptide"]; + } + + defaultBatchSize(): number { + return 5; + } +} diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index c5dfc4b4..e1aba774 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -60,6 +60,12 @@ export abstract class UnipeptSubcommand { this.formatter = FormatterFactory.getFormatter(this.options.format); if (this.options.output) { this.outputStream = createWriteStream(this.options.output); + } else { + process.stdout.on("error", (err) => { + if (err.code === "EPIPE") { + process.exit(0); + } + }) } const iterator = this.getInputIterator(args, options.input as string); diff --git a/tests/commands/unipept/pept2taxa.test.ts b/tests/commands/unipept/pept2taxa.test.ts new file mode 100644 index 00000000..cbfb72c7 --- /dev/null +++ b/tests/commands/unipept/pept2taxa.test.ts @@ -0,0 +1,27 @@ +import { jest } from '@jest/globals'; +import { Pept2taxa } from "../../../lib/commands/unipept/pept2taxa"; + +let output: string[]; +jest + .spyOn(process.stdout, "write") + .mockImplementation((data: unknown) => { output.push(data as string); return true; }); + +beforeEach(() => { + output = []; +}); + +test('test with default args', async () => { + const command = new Pept2taxa(); + await command.run(["AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("peptide,taxon_id,taxon_name,taxon_rank")).toBeTruthy(); + expect(output[1].startsWith("AALTER,41,Stigmatella aurantiaca,species")).toBeTruthy(); + expect(output.length).toBe(2); +}); + +test('test with fasta', async () => { + const command = new Pept2taxa(); + await command.run([">test", "AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("fasta_header,peptide,taxon_id,taxon_name,taxon_rank")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,41,Stigmatella aurantiaca,species")).toBeTruthy(); + expect(output.length).toBe(2); +}); From 49d282875e5d5cfc8a1544b2b25b0f24af4365d5 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 6 Aug 2024 14:34:22 +0200 Subject: [PATCH 58/76] add peptinfo --- lib/commands/unipept.ts | 4 ++- lib/commands/unipept/peptinfo.ts | 38 +++++++++++++++++++++++++ tests/commands/unipept/peptinfo.test.ts | 27 ++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 lib/commands/unipept/peptinfo.ts create mode 100644 tests/commands/unipept/peptinfo.test.ts diff --git a/lib/commands/unipept.ts b/lib/commands/unipept.ts index 39ba2faa..098f15cd 100644 --- a/lib/commands/unipept.ts +++ b/lib/commands/unipept.ts @@ -6,6 +6,7 @@ import { Pept2interpro } from './unipept/pept2interpro.js'; import { Pept2lca } from './unipept/pept2lca.js'; import { Pept2prot } from './unipept/pept2prot.js'; import { Pept2taxa } from './unipept/pept2taxa.js'; +import { Peptinfo } from './unipept/peptinfo.js'; export class Unipept extends BaseCommand { @@ -31,7 +32,8 @@ The command will give priority to the first way the input is passed, in the orde .addCommand(new Pept2interpro().command) .addCommand(new Pept2lca().command) .addCommand(new Pept2prot().command) - .addCommand(new Pept2taxa().command); + .addCommand(new Pept2taxa().command) + .addCommand(new Peptinfo().command); } async run(args?: string[]) { diff --git a/lib/commands/unipept/peptinfo.ts b/lib/commands/unipept/peptinfo.ts new file mode 100644 index 00000000..62cd9cd0 --- /dev/null +++ b/lib/commands/unipept/peptinfo.ts @@ -0,0 +1,38 @@ +import { Option } from "commander"; +import { UnipeptSubcommand } from "./unipept_subcommand.js"; + +export class Peptinfo extends UnipeptSubcommand { + + readonly description = `For each tryptic peptide the unipept peptinfo command retrieves from Unipept the functional information and the lowest common ancestor of the set of taxa from all UniProt entries whose protein sequence contains an exact matches to the tryptic peptide. The lowest common ancestor is based on the topology of the Unipept Taxonomy -- a cleaned up version of the NCBI Taxonomy -- and is itself a record from the NCBI Taxonomy. The command expects a list of tryptic peptides that are passed + +- as separate command line arguments +- in a text file that is passed as an argument to the -i option +- to standard input + +The command will give priority to the first way tryptic peptides are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line.`; + + constructor() { + super("peptinfo"); + + this.command + .summary("Fetch functional information and the taxonomic lowest common ancestor of UniProt entries that match tryptic peptides.") + .description(this.description) + .option("-e, --equate", "equate isoleucine (I) and leucine (L) when matching peptides") + .option("-a, --all", "report the names of the functional annotations and all information fields of NCBI Taxonomy records available in Unipept. Note that this may have a performance penalty.") + .addOption(new Option("-s --select ", "select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.")) + .argument("[peptides...]", "optionally, 1 or more peptides") + .action((args, options) => this.run(args, options)); + } + + requiredFields(): string[] { + return ["peptide"]; + } + + defaultBatchSize(): number { + if (this.options.all) { + return 100; + } else { + return 1000; + } + } +} diff --git a/tests/commands/unipept/peptinfo.test.ts b/tests/commands/unipept/peptinfo.test.ts new file mode 100644 index 00000000..1e4aa65d --- /dev/null +++ b/tests/commands/unipept/peptinfo.test.ts @@ -0,0 +1,27 @@ +import { jest } from '@jest/globals'; +import { Peptinfo } from "../../../lib/commands/unipept/peptinfo"; + +let output: string[]; +jest + .spyOn(process.stdout, "write") + .mockImplementation((data: unknown) => { output.push(data as string); return true; }); + +beforeEach(() => { + output = []; +}); + +test('test with default args', async () => { + const command = new Peptinfo(); + await command.run(["AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("peptide,total_protein_count,taxon_id,taxon_name,taxon_rank,ec_number,ec_protein_count,go_term,go_protein_count,ipr_code,ipr_protein_count")).toBeTruthy(); + expect(output[1].startsWith("AALTER,3310,1,root")).toBeTruthy(); + expect(output.length).toBe(2); +}); + +test('test with fasta', async () => { + const command = new Peptinfo(); + await command.run([">test", "AALTER"], { header: true, format: "csv" }); + expect(output[0].startsWith("fasta_header,peptide,total_protein_count,taxon_id,taxon_name,taxon_rank,ec_number,ec_protein_count,go_term,go_protein_count,ipr_code,ipr_protein_count")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,3310,1,root")).toBeTruthy(); + expect(output.length).toBe(2); +}); From ce55c09eacada8b7f8e6a0ea3263b06c3db4bbfe Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 6 Aug 2024 14:41:25 +0200 Subject: [PATCH 59/76] add protinfo --- lib/commands/unipept.ts | 4 +++- lib/commands/unipept/protinfo.ts | 32 +++++++++++++++++++++++++ tests/commands/unipept/protinfo.test.ts | 27 +++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 lib/commands/unipept/protinfo.ts create mode 100644 tests/commands/unipept/protinfo.test.ts diff --git a/lib/commands/unipept.ts b/lib/commands/unipept.ts index 098f15cd..3ba62182 100644 --- a/lib/commands/unipept.ts +++ b/lib/commands/unipept.ts @@ -7,6 +7,7 @@ import { Pept2lca } from './unipept/pept2lca.js'; import { Pept2prot } from './unipept/pept2prot.js'; import { Pept2taxa } from './unipept/pept2taxa.js'; import { Peptinfo } from './unipept/peptinfo.js'; +import { Protinfo } from './unipept/protinfo.js'; export class Unipept extends BaseCommand { @@ -33,7 +34,8 @@ The command will give priority to the first way the input is passed, in the orde .addCommand(new Pept2lca().command) .addCommand(new Pept2prot().command) .addCommand(new Pept2taxa().command) - .addCommand(new Peptinfo().command); + .addCommand(new Peptinfo().command) + .addCommand(new Protinfo().command); } async run(args?: string[]) { diff --git a/lib/commands/unipept/protinfo.ts b/lib/commands/unipept/protinfo.ts new file mode 100644 index 00000000..22b0ca62 --- /dev/null +++ b/lib/commands/unipept/protinfo.ts @@ -0,0 +1,32 @@ +import { Option } from "commander"; +import { UnipeptSubcommand } from "./unipept_subcommand.js"; + +export class Protinfo extends UnipeptSubcommand { + + readonly description = `For each UniProt id the unipept protinfo command retrieves from Unipept the functional information and the NCBI id. The command expects a list of UniProt ids that are passed + +- as separate command line arguments +- in a text file that is passed as an argument to the -i option +- to standard input + +The command will give priority to the first way protein id's are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line.`; + + constructor() { + super("protinfo"); + + this.command + .summary("Fetch functional and taxonomic information of UniProt ids") + .description(this.description) + .addOption(new Option("-s --select ", "select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.")) + .argument("[proteins...]", "optionally, 1 or more UniProt ids") + .action((args, options) => this.run(args, options)); + } + + requiredFields(): string[] { + return ["protein"]; + } + + defaultBatchSize(): number { + return 1000; + } +} diff --git a/tests/commands/unipept/protinfo.test.ts b/tests/commands/unipept/protinfo.test.ts new file mode 100644 index 00000000..561804f8 --- /dev/null +++ b/tests/commands/unipept/protinfo.test.ts @@ -0,0 +1,27 @@ +import { jest } from '@jest/globals'; +import { Protinfo } from "../../../lib/commands/unipept/protinfo"; + +let output: string[]; +jest + .spyOn(process.stdout, "write") + .mockImplementation((data: unknown) => { output.push(data as string); return true; }); + +beforeEach(() => { + output = []; +}); + +test('test with default args', async () => { + const command = new Protinfo(); + await command.run(["P78330"], { header: true, format: "csv" }); + expect(output[0].startsWith("protein,taxon_id,taxon_name,taxon_rank,ec_number,go_term,ipr_code")).toBeTruthy(); + expect(output[1].startsWith("P78330,9606,Homo sapiens")).toBeTruthy(); + expect(output.length).toBe(2); +}); + +test('test with fasta', async () => { + const command = new Protinfo(); + await command.run([">test", "P78330"], { header: true, format: "csv" }); + expect(output[0].startsWith("fasta_header,protein,taxon_id,taxon_name,taxon_rank,ec_number,go_term,ipr_code")).toBeTruthy(); + expect(output[1].startsWith(">test,P78330,9606,Homo sapiens")).toBeTruthy(); + expect(output.length).toBe(2); +}); From a58a0d39a30c7678e3ffc283b8255677018b8d4f Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 6 Aug 2024 15:04:53 +0200 Subject: [PATCH 60/76] add taxa2lca --- lib/commands/unipept.ts | 4 ++- lib/commands/unipept/taxa2lca.ts | 29 ++++++++++++++++++++++ lib/commands/unipept/unipept_subcommand.ts | 12 ++++++++- lib/formatters/csv_formatter.ts | 3 +++ lib/formatters/formatter.ts | 3 +++ tests/commands/unipept/taxa2lca.test.ts | 19 ++++++++++++++ 6 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 lib/commands/unipept/taxa2lca.ts create mode 100644 tests/commands/unipept/taxa2lca.test.ts diff --git a/lib/commands/unipept.ts b/lib/commands/unipept.ts index 3ba62182..7f4ac200 100644 --- a/lib/commands/unipept.ts +++ b/lib/commands/unipept.ts @@ -8,6 +8,7 @@ import { Pept2prot } from './unipept/pept2prot.js'; import { Pept2taxa } from './unipept/pept2taxa.js'; import { Peptinfo } from './unipept/peptinfo.js'; import { Protinfo } from './unipept/protinfo.js'; +import { Taxa2lca } from './unipept/taxa2lca.js'; export class Unipept extends BaseCommand { @@ -35,7 +36,8 @@ The command will give priority to the first way the input is passed, in the orde .addCommand(new Pept2prot().command) .addCommand(new Pept2taxa().command) .addCommand(new Peptinfo().command) - .addCommand(new Protinfo().command); + .addCommand(new Protinfo().command) + .addCommand(new Taxa2lca().command); } async run(args?: string[]) { diff --git a/lib/commands/unipept/taxa2lca.ts b/lib/commands/unipept/taxa2lca.ts new file mode 100644 index 00000000..573487d2 --- /dev/null +++ b/lib/commands/unipept/taxa2lca.ts @@ -0,0 +1,29 @@ +import { Option } from "commander"; +import { UnipeptSubcommand } from "./unipept_subcommand.js"; + +export class Taxa2lca extends UnipeptSubcommand { + + readonly description = `The unipept taxa2lca command computes the lowest common ancestor of a given list of NCBI Taxonomy Identifiers. The lowest common ancestor is based on the topology of the Unipept Taxonomy -- a cleaned up version of the NCBI Taxonomy -- and is itself a record from the NCBI Taxonomy. The command expects a list of NCBI Taxonomy Identifiers that are passed + +- as separate command line arguments +- in a text file that is passed as an argument to the -i option +- to standard input + +The command will give priority to the first way NCBI Taxonomy Identifiers are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line.`; + + constructor() { + super("taxa2lca"); + + this.command + .summary("Compute taxonomic lowest common ancestor for given list of taxa.") + .description(this.description) + .option("-a, --all", "report all information fields of NCBI Taxonomy records available in Unipept. Note that this may have a performance penalty.") + .addOption(new Option("-s --select ", "select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.")) + .argument("[proteins...]", "optionally, 1 or more UniProt ids") + .action((args, options) => this.run(args, options)); + } + + defaultBatchSize(): number { + throw new Error("Batch size not needed for this command."); + } +} diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index e1aba774..dcd18fbe 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -70,7 +70,9 @@ export abstract class UnipeptSubcommand { const iterator = this.getInputIterator(args, options.input as string); const firstLine = (await iterator.next()).value; - if (firstLine.startsWith(">")) { + if (this.command.name() === "taxa2lca") { + await this.simpleInputProcessor(firstLine, iterator); + } else if (firstLine.startsWith(">")) { this.fasta = true; await this.fastaInputProcessor(firstLine, iterator); } else { @@ -133,6 +135,14 @@ export abstract class UnipeptSubcommand { await this.processBatch(slice, fastaMapper); } + async simpleInputProcessor(firstLine: string, iterator: IterableIterator | AsyncIterableIterator) { + const slice = [firstLine]; + for await (const line of iterator) { + slice.push(line); + } + await this.processBatch(slice); + } + private constructRequestBody(slice: string[]): URLSearchParams { const names = this.getSelectedFields().length === 0 || this.getSelectedFields().some(regex => regex.toString().includes("name") || regex.toString().includes(".*$")); return new URLSearchParams({ diff --git a/lib/formatters/csv_formatter.ts b/lib/formatters/csv_formatter.ts index 822c593e..e674d2ca 100644 --- a/lib/formatters/csv_formatter.ts +++ b/lib/formatters/csv_formatter.ts @@ -16,6 +16,9 @@ export class CSVFormatter extends Formatter { } getKeys(data: { [key: string]: unknown }[], fastaMapper?: boolean | undefined): string[] { + if (!Array.isArray(data)) { + data = [data]; + } return fastaMapper ? ["fasta_header", ...Object.keys(data[0])] : Object.keys(data[0]); } diff --git a/lib/formatters/formatter.ts b/lib/formatters/formatter.ts index d0ca6dca..10d3cefc 100644 --- a/lib/formatters/formatter.ts +++ b/lib/formatters/formatter.ts @@ -8,6 +8,9 @@ export abstract class Formatter { if (fastaMapper) { data = this.integrateFastaHeaders(data as { [key: string]: string }[], fastaMapper); } + if (!Array.isArray(data)) { + data = [data]; + } return this.convert(data, first); } diff --git a/tests/commands/unipept/taxa2lca.test.ts b/tests/commands/unipept/taxa2lca.test.ts new file mode 100644 index 00000000..1f5c6351 --- /dev/null +++ b/tests/commands/unipept/taxa2lca.test.ts @@ -0,0 +1,19 @@ +import { jest } from '@jest/globals'; +import { Taxa2lca } from "../../../lib/commands/unipept/taxa2lca"; + +let output: string[]; +jest + .spyOn(process.stdout, "write") + .mockImplementation((data: unknown) => { output.push(data as string); return true; }); + +beforeEach(() => { + output = []; +}); + +test('test with default args', async () => { + const command = new Taxa2lca(); + await command.run(["216816", "1680"], { header: true, format: "csv" }); + expect(output[0].startsWith("taxon_id,taxon_name,taxon_rank")).toBeTruthy(); + expect(output[1].startsWith("1678,Bifidobacterium,genus")).toBeTruthy(); + expect(output.length).toBe(2); +}); From 2d98583dd46e1cca1dc81551088c991ccac29c1a Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 6 Aug 2024 15:35:11 +0200 Subject: [PATCH 61/76] add taxonomy command --- lib/commands/unipept.ts | 4 ++- lib/commands/unipept/taxa2lca.ts | 2 +- lib/commands/unipept/taxonomy.ts | 33 +++++++++++++++++++++++++ tests/commands/unipept/taxonomy.test.ts | 27 ++++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 lib/commands/unipept/taxonomy.ts create mode 100644 tests/commands/unipept/taxonomy.test.ts diff --git a/lib/commands/unipept.ts b/lib/commands/unipept.ts index 7f4ac200..0585138f 100644 --- a/lib/commands/unipept.ts +++ b/lib/commands/unipept.ts @@ -9,6 +9,7 @@ import { Pept2taxa } from './unipept/pept2taxa.js'; import { Peptinfo } from './unipept/peptinfo.js'; import { Protinfo } from './unipept/protinfo.js'; import { Taxa2lca } from './unipept/taxa2lca.js'; +import { Taxonomy } from './unipept/taxonomy.js'; export class Unipept extends BaseCommand { @@ -37,7 +38,8 @@ The command will give priority to the first way the input is passed, in the orde .addCommand(new Pept2taxa().command) .addCommand(new Peptinfo().command) .addCommand(new Protinfo().command) - .addCommand(new Taxa2lca().command); + .addCommand(new Taxa2lca().command) + .addCommand(new Taxonomy().command); } async run(args?: string[]) { diff --git a/lib/commands/unipept/taxa2lca.ts b/lib/commands/unipept/taxa2lca.ts index 573487d2..5b9a5e6c 100644 --- a/lib/commands/unipept/taxa2lca.ts +++ b/lib/commands/unipept/taxa2lca.ts @@ -19,7 +19,7 @@ The command will give priority to the first way NCBI Taxonomy Identifiers are pa .description(this.description) .option("-a, --all", "report all information fields of NCBI Taxonomy records available in Unipept. Note that this may have a performance penalty.") .addOption(new Option("-s --select ", "select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.")) - .argument("[proteins...]", "optionally, 1 or more UniProt ids") + .argument("[taxonids...]", "optionally, 1 or more taxon ids") .action((args, options) => this.run(args, options)); } diff --git a/lib/commands/unipept/taxonomy.ts b/lib/commands/unipept/taxonomy.ts new file mode 100644 index 00000000..ae8426a1 --- /dev/null +++ b/lib/commands/unipept/taxonomy.ts @@ -0,0 +1,33 @@ +import { Option } from "commander"; +import { UnipeptSubcommand } from "./unipept_subcommand.js"; + +export class Taxonomy extends UnipeptSubcommand { + + readonly description = `The unipept taxonomy command yields information from the Unipept Taxonomy records for a given list of NCBI Taxonomy Identifiers. The Unipept Taxonomy is a cleaned up version of the NCBI Taxonomy, and its records are also records of the NCBI Taxonomy. The command expects a list of NCBI Taxonomy Identifiers that are passed + +- as separate command line arguments +- in a text file that is passed as an argument to the -i option +- to standard input + +The command will give priority to the first way taxon id's are passed, in the order as listed above. Text files and standard input should have one taxon id per line.`; + + constructor() { + super("taxonomy"); + + this.command + .summary("Fetch taxonomic information from Unipept Taxonomy.") + .description(this.description) + .option("-a, --all", "report all information fields of NCBI Taxonomy records available in Unipept. Note that this may have a performance penalty.") + .addOption(new Option("-s --select ", "select the information fields to return. Selected fields are passed as a comma separated list of field names. Multiple -s (or --select) options may be used.")) + .argument("[peptides...]", "optionally, 1 or more peptides") + .action((args, options) => this.run(args, options)); + } + + requiredFields(): string[] { + return ["taxon_id"]; + } + + defaultBatchSize(): number { + return 100; + } +} diff --git a/tests/commands/unipept/taxonomy.test.ts b/tests/commands/unipept/taxonomy.test.ts new file mode 100644 index 00000000..afaec21e --- /dev/null +++ b/tests/commands/unipept/taxonomy.test.ts @@ -0,0 +1,27 @@ +import { jest } from '@jest/globals'; +import { Taxonomy } from "../../../lib/commands/unipept/taxonomy"; + +let output: string[]; +jest + .spyOn(process.stdout, "write") + .mockImplementation((data: unknown) => { output.push(data as string); return true; }); + +beforeEach(() => { + output = []; +}); + +test('test with default args', async () => { + const command = new Taxonomy(); + await command.run(["216816"], { header: true, format: "csv" }); + expect(output[0].startsWith("taxon_id,taxon_name,taxon_rank")).toBeTruthy(); + expect(output[1].startsWith("216816,Bifidobacterium longum,species")).toBeTruthy(); + expect(output.length).toBe(2); +}); + +test('test with fasta', async () => { + const command = new Taxonomy(); + await command.run([">test", "216816"], { header: true, format: "csv" }); + expect(output[0].startsWith("fasta_header,taxon_id,taxon_name,taxon_rank")).toBeTruthy(); + expect(output[1].startsWith(">test,216816,Bifidobacterium longum,species")).toBeTruthy(); + expect(output.length).toBe(2); +}); From e7c2365c560a6cf91d0a2f7cbfc0eacd32c81ab6 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Tue, 6 Aug 2024 15:37:40 +0200 Subject: [PATCH 62/76] fix strings --- lib/commands/unipept/protinfo.ts | 2 +- lib/commands/unipept/taxa2lca.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/commands/unipept/protinfo.ts b/lib/commands/unipept/protinfo.ts index 22b0ca62..df8f6f8c 100644 --- a/lib/commands/unipept/protinfo.ts +++ b/lib/commands/unipept/protinfo.ts @@ -9,7 +9,7 @@ export class Protinfo extends UnipeptSubcommand { - in a text file that is passed as an argument to the -i option - to standard input -The command will give priority to the first way protein id's are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line.`; +The command will give priority to the first way protein id's are passed, in the order as listed above. Text files and standard input should have one protein id per line.`; constructor() { super("protinfo"); diff --git a/lib/commands/unipept/taxa2lca.ts b/lib/commands/unipept/taxa2lca.ts index 5b9a5e6c..0dccba06 100644 --- a/lib/commands/unipept/taxa2lca.ts +++ b/lib/commands/unipept/taxa2lca.ts @@ -9,7 +9,7 @@ export class Taxa2lca extends UnipeptSubcommand { - in a text file that is passed as an argument to the -i option - to standard input -The command will give priority to the first way NCBI Taxonomy Identifiers are passed, in the order as listed above. Text files and standard input should have one tryptic peptide per line.`; +The command will give priority to the first way NCBI Taxonomy Identifiers are passed, in the order as listed above. Text files and standard input should have one taxon id per line.`; constructor() { super("taxa2lca"); From 83a281bc6281a4c748b8185e478b0aeac074e989 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Wed, 7 Aug 2024 13:28:30 +0200 Subject: [PATCH 63/76] filter results --- lib/commands/unipept/unipept_subcommand.ts | 18 +++++++++++++++++- lib/formatters/csv_formatter.ts | 3 --- lib/formatters/formatter.ts | 3 --- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index dcd18fbe..dbb40b5e 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -91,7 +91,7 @@ export abstract class UnipeptSubcommand { "User-Agent": this.user_agent, } }); - const result = await r.json(); + const result = this.filterResult(await r.json()); if (this.firstBatch && this.options.header) { this.outputStream.write(this.formatter.header(result, this.fasta)); @@ -102,6 +102,22 @@ export abstract class UnipeptSubcommand { if (this.firstBatch) this.firstBatch = false; } + filterResult(result: unknown): object[] { + if (!Array.isArray(result)) { + result = [result]; + } + if (this.getSelectedFields().length > 0) { + (result as { [key: string]: string }[]).forEach(entry => { + for (const key of Object.keys(entry)) { + if (!this.getSelectedFields().some(regex => regex.test(key))) { + delete entry[key]; + } + } + }); + } + return result as object[]; + } + async normalInputProcessor(firstLine: string, iterator: IterableIterator | AsyncIterableIterator) { let slice = [firstLine]; diff --git a/lib/formatters/csv_formatter.ts b/lib/formatters/csv_formatter.ts index e674d2ca..822c593e 100644 --- a/lib/formatters/csv_formatter.ts +++ b/lib/formatters/csv_formatter.ts @@ -16,9 +16,6 @@ export class CSVFormatter extends Formatter { } getKeys(data: { [key: string]: unknown }[], fastaMapper?: boolean | undefined): string[] { - if (!Array.isArray(data)) { - data = [data]; - } return fastaMapper ? ["fasta_header", ...Object.keys(data[0])] : Object.keys(data[0]); } diff --git a/lib/formatters/formatter.ts b/lib/formatters/formatter.ts index 10d3cefc..d0ca6dca 100644 --- a/lib/formatters/formatter.ts +++ b/lib/formatters/formatter.ts @@ -8,9 +8,6 @@ export abstract class Formatter { if (fastaMapper) { data = this.integrateFastaHeaders(data as { [key: string]: string }[], fastaMapper); } - if (!Array.isArray(data)) { - data = [data]; - } return this.convert(data, first); } From 5906ef096027a60edb81058e8ae49c279bd1c200 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Wed, 7 Aug 2024 13:34:35 +0200 Subject: [PATCH 64/76] always flatten the results for csv so select fields works --- lib/commands/unipept/unipept_subcommand.ts | 4 ++++ lib/formatters/csv_formatter.ts | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index dbb40b5e..40565283 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -4,6 +4,7 @@ import { createInterface } from "node:readline"; import { Interface } from "readline"; import { Formatter } from "../../formatters/formatter.js"; import { FormatterFactory } from "../../formatters/formatter_factory.js"; +import { CSVFormatter } from "../../formatters/csv_formatter.js"; export abstract class UnipeptSubcommand { public command: Command; @@ -106,6 +107,9 @@ export abstract class UnipeptSubcommand { if (!Array.isArray(result)) { result = [result]; } + if (this.formatter && this.formatter instanceof CSVFormatter) { + result = this.formatter.flatten(result as { [key: string]: unknown }[]); + } if (this.getSelectedFields().length > 0) { (result as { [key: string]: string }[]).forEach(entry => { for (const key of Object.keys(entry)) { diff --git a/lib/formatters/csv_formatter.ts b/lib/formatters/csv_formatter.ts index 822c593e..c2713353 100644 --- a/lib/formatters/csv_formatter.ts +++ b/lib/formatters/csv_formatter.ts @@ -4,7 +4,7 @@ import { stringify } from "csv-stringify/sync"; export class CSVFormatter extends Formatter { header(sampleData: { [key: string]: string }[], fastaMapper?: boolean | undefined): string { - return stringify([this.getKeys(this.flatten(sampleData), fastaMapper)]); + return stringify([this.getKeys(sampleData, fastaMapper)]); } footer(): string { @@ -12,7 +12,7 @@ export class CSVFormatter extends Formatter { } convert(data: object[]): string { - return stringify(this.flatten(data as { [key: string]: unknown }[])); + return stringify(data); } getKeys(data: { [key: string]: unknown }[], fastaMapper?: boolean | undefined): string[] { From 3c54ee19c81973b0d8bb848e26aa82e2a8084937 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Wed, 7 Aug 2024 13:42:13 +0200 Subject: [PATCH 65/76] don't crash on empty response --- lib/commands/unipept/unipept_subcommand.ts | 5 ++++- tests/formatters/csv_formatter.test.ts | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index 40565283..bd62346d 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -92,7 +92,10 @@ export abstract class UnipeptSubcommand { "User-Agent": this.user_agent, } }); - const result = this.filterResult(await r.json()); + + let result = await r.json(); + if (Array.isArray(result) && result.length === 0) return; + result = this.filterResult(result); if (this.firstBatch && this.options.header) { this.outputStream.write(this.formatter.header(result, this.fasta)); diff --git a/tests/formatters/csv_formatter.test.ts b/tests/formatters/csv_formatter.test.ts index 2ea80c37..cc8d010d 100644 --- a/tests/formatters/csv_formatter.test.ts +++ b/tests/formatters/csv_formatter.test.ts @@ -4,10 +4,8 @@ import { TestObject } from "./test_object"; const formatter = FormatterFactory.getFormatter("csv"); test('test header', () => { - //const fasta = [["peptide", ">test"]]; const object = [TestObject.testObject(), TestObject.testObject()]; expect(formatter.header(object)).toBe(TestObject.asCsvHeader()); - //expect(formatter.header(object, fasta)).toBe(`fasta_header,${TestObject.asCsvHeader()}`); }); test('test footer', () => { From 193284cc5aab4d7e2160919d5486b3df67b26060 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Wed, 7 Aug 2024 13:43:14 +0200 Subject: [PATCH 66/76] don't crash if the server doesn't return valid json --- lib/commands/unipept/unipept_subcommand.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index bd62346d..46be7ffc 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -93,7 +93,12 @@ export abstract class UnipeptSubcommand { } }); - let result = await r.json(); + let result; + try { + result = await r.json(); + } catch (e) { + result = []; + } if (Array.isArray(result) && result.length === 0) return; result = this.filterResult(result); From 495d461f8a7c238ab2afe5dffd44af915701cf29 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Wed, 7 Aug 2024 14:24:22 +0200 Subject: [PATCH 67/76] retry requests and log errors --- lib/commands/unipept/unipept_subcommand.ts | 59 +++++++++++++++++++--- 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index 46be7ffc..9ef198a5 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -5,6 +5,9 @@ import { Interface } from "readline"; import { Formatter } from "../../formatters/formatter.js"; import { FormatterFactory } from "../../formatters/formatter_factory.js"; import { CSVFormatter } from "../../formatters/csv_formatter.js"; +import path from "path"; +import os from "os"; +import { appendFile, mkdir, writeFile } from "fs/promises"; export abstract class UnipeptSubcommand { public command: Command; @@ -84,14 +87,20 @@ export abstract class UnipeptSubcommand { async processBatch(slice: string[], fastaMapper?: { [key: string]: string }): Promise { if (!this.formatter) throw new Error("Formatter not set"); - const r = await fetch(this.url as string, { - method: "POST", - body: this.constructRequestBody(slice), - headers: { - "Accept-Encoding": "gzip", - "User-Agent": this.user_agent, - } - }); + let r; + try { + r = await this.fetchWithRetry(this.url as string, { + method: "POST", + body: this.constructRequestBody(slice), + headers: { + "Accept-Encoding": "gzip", + "User-Agent": this.user_agent, + } + }); + } catch (e) { + await this.saveError(e as string); + return; + } let result; try { @@ -171,6 +180,35 @@ export abstract class UnipeptSubcommand { await this.processBatch(slice); } + async saveError(message: string) { + const errorPath = this.errorFilePath(); + mkdir(path.dirname(errorPath), { recursive: true }); + await appendFile(errorPath, `${message}\n`); + console.error(`API request failed! log can be found in ${errorPath}`); + } + + fetchWithRetry(url: string, options: RequestInit, retries = 5): Promise { + return fetch(url, options) + .then(response => { + if (response.ok) { + return response; + } else { + return Promise.reject(`${response.status} ${response.statusText}`); + } + }) + .catch(async error => { + if (retries > 0) { + // retry with delay + // console.error("retrying"); + const delay = 5000 * Math.random(); + await new Promise(resolve => setTimeout(resolve, delay)); + return this.fetchWithRetry(url, options, retries - 1); + } else { + return Promise.reject(`Failed to fetch data from the Unipept API: ${error}`); + } + }); + } + private constructRequestBody(slice: string[]): URLSearchParams { const names = this.getSelectedFields().length === 0 || this.getSelectedFields().some(regex => regex.toString().includes("name") || regex.toString().includes(".*$")); return new URLSearchParams({ @@ -201,6 +239,11 @@ export abstract class UnipeptSubcommand { } } + private errorFilePath(): string { + const timestamp = new Date().toISOString().split('T')[0]; + return path.join(os.homedir(), '.unipept', `unipept-${timestamp}.log`); + } + /** * Returns an input iterator to use for the request. * - if arguments are given, use arguments From 37a23a2735a81bc02747c5f9cc7d12191e461334 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Wed, 7 Aug 2024 14:26:05 +0200 Subject: [PATCH 68/76] linter --- lib/commands/unipept/unipept_subcommand.ts | 2 +- tests/formatters/formatter.test.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index 9ef198a5..517ef65a 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -7,7 +7,7 @@ import { FormatterFactory } from "../../formatters/formatter_factory.js"; import { CSVFormatter } from "../../formatters/csv_formatter.js"; import path from "path"; import os from "os"; -import { appendFile, mkdir, writeFile } from "fs/promises"; +import { appendFile, mkdir } from "fs/promises"; export abstract class UnipeptSubcommand { public command: Command; diff --git a/tests/formatters/formatter.test.ts b/tests/formatters/formatter.test.ts index c041f31b..9db35348 100644 --- a/tests/formatters/formatter.test.ts +++ b/tests/formatters/formatter.test.ts @@ -6,7 +6,6 @@ test('test integrate fasta headers', async () => { const fasta = { 5: ">test" }; const object = [TestObject.testObject(), TestObject.testObject()]; const integrated = [Object.assign({ fasta_header: ">test" }, TestObject.testObject()), Object.assign({ fasta_header: ">test" }, TestObject.testObject())]; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore expect(formatter.integrateFastaHeaders(object, fasta)).toEqual(integrated); }); From 0579bec14dd7cc9b8a34a3059ff68e485bf5d305 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Wed, 7 Aug 2024 14:41:46 +0200 Subject: [PATCH 69/76] add comments --- lib/commands/unipept/unipept_subcommand.ts | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index 517ef65a..47f6d210 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -62,9 +62,12 @@ export abstract class UnipeptSubcommand { this.host = this.getHost(); this.url = `${this.host}/api/v2/${this.name}.json`; this.formatter = FormatterFactory.getFormatter(this.options.format); + if (this.options.output) { this.outputStream = createWriteStream(this.options.output); } else { + // if we write to stdout, we need to handle the EPIPE error + // this happens when the output is piped to another command that stops reading process.stdout.on("error", (err) => { if (err.code === "EPIPE") { process.exit(0); @@ -75,6 +78,7 @@ export abstract class UnipeptSubcommand { const iterator = this.getInputIterator(args, options.input as string); const firstLine = (await iterator.next()).value; if (this.command.name() === "taxa2lca") { + // this subcommand is an exception where the entire input is read before processing await this.simpleInputProcessor(firstLine, iterator); } else if (firstLine.startsWith(">")) { this.fasta = true; @@ -120,6 +124,9 @@ export abstract class UnipeptSubcommand { if (this.firstBatch) this.firstBatch = false; } + /** + * Filter the result based on the selected fields + */ filterResult(result: unknown): object[] { if (!Array.isArray(result)) { result = [result]; @@ -139,6 +146,9 @@ export abstract class UnipeptSubcommand { return result as object[]; } + /** + * Reads batchSize lines from the input and processes them + */ async normalInputProcessor(firstLine: string, iterator: IterableIterator | AsyncIterableIterator) { let slice = [firstLine]; @@ -152,6 +162,10 @@ export abstract class UnipeptSubcommand { await this.processBatch(slice); } + /** + * Reads batchSize lines from the input and processes them, + * but takes into account the fasta headers. + */ async fastaInputProcessor(firstLine: string, iterator: IterableIterator | AsyncIterableIterator) { let currentFastaHeader = firstLine; let slice = []; @@ -172,6 +186,9 @@ export abstract class UnipeptSubcommand { await this.processBatch(slice, fastaMapper); } + /** + * Reads the entire input and processes it in one go + */ async simpleInputProcessor(firstLine: string, iterator: IterableIterator | AsyncIterableIterator) { const slice = [firstLine]; for await (const line of iterator) { @@ -180,6 +197,9 @@ export abstract class UnipeptSubcommand { await this.processBatch(slice); } + /** + * Appends the error message to the log file of today and prints it to the console + */ async saveError(message: string) { const errorPath = this.errorFilePath(); mkdir(path.dirname(errorPath), { recursive: true }); @@ -187,6 +207,11 @@ export abstract class UnipeptSubcommand { console.error(`API request failed! log can be found in ${errorPath}`); } + /** + * Uses fetch to get data from the Unipept API. + * Has a retry mechanism that retries the request up to 5 times with a delay of 0-5 seconds. + * In addition, handles failed requests by returning a rejected promise. + */ fetchWithRetry(url: string, options: RequestInit, retries = 5): Promise { return fetch(url, options) .then(response => { From e66da2354c4dedd8728932f5b12ee40e46fb4866 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Wed, 7 Aug 2024 16:30:00 +0200 Subject: [PATCH 70/76] prepare to publish on npm --- README.md | 21 +- package.json | 14 +- yarn.lock | 1533 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 1538 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 702999cb..453fe29f 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,33 @@ # unipept-cli -[![Gem Version](https://badge.fury.io/rb/unipept.svg)](http://badge.fury.io/rb/unipept) +![NPM Version](https://img.shields.io/npm/v/unipept-cli) Unipept-cli offers a command line interface to the [Unipept](http://unipept.ugent.be) web service. Documentation about the web service can be found at [http://unipept.ugent.be/apidocs](http://unipept.ugent.be/apidocs), documentation about the command line tools at [http://unipept.ugent.be/clidocs](http://unipept.ugent.be/clidocs). ## Installation -To use the Unipept CLI, Ruby version 2.7 or higher needs to be installed. You can check this by running `ruby -v` on the commandline: +To use the Unipept CLI, node 22 or higher needs to be installed. You can check this by running `node -v` on the commandline: ``` -$ ruby -v -ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [arm64-darwin21] +$ node -v +v22.3.0 ``` -More information on installing Ruby can be found at https://www.ruby-lang.org/en/installation/ +More information on installing Ruby can be found at https://nodejs.org/en/download/package-manager -The Unipept CLI is available as a _gem_. This means it can easily be installed with the following command: +The Unipept CLI is available as an npm package. This means it can easily be installed with the following command: ```bash -$ gem install unipept -Successfully installed unipept-1.0.1 -Parsing documentation for unipept-1.0.1 -Done installing documentation for unipept after 0 seconds -1 gem installed +$ npm install -g unipept-cli +added 3 packages in 986ms ``` After successful installation, the unipept command should be available: ```bash $ unipept -v -1.0.1 +4.0.0 ``` The help can be accessed by running `unipept -h`. diff --git a/package.json b/package.json index fc1ff4c4..befa17c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unipept-cli", - "version": "4.0.0", + "version": "4.0.0-1", "description": "Command line interface to the Unipept web services", "repository": "git@github.com:unipept/unipept-cli.git", "author": "Bart Mesuere ", @@ -8,10 +8,10 @@ "private": false, "type": "module", "bin": { - "peptfilter": "./bin/peptfilter.js", - "prot2pept": "./bin/prot2pept.js", - "unipept": "./bin/unipept.js", - "uniprot": "./bin/uniprot.js" + "peptfilter": "./dist/bin/peptfilter.js", + "prot2pept": "./dist/bin/prot2pept.js", + "unipept": "./dist/bin/unipept.js", + "uniprot": "./dist/bin/uniprot.js" }, "scripts": { "build": "yarn run tsc", @@ -23,6 +23,9 @@ "unipept": "yarn run tsx bin/unipept.ts", "uniprot": "yarn run tsx bin/uniprot.ts" }, + "files": [ + "dist" + ], "dependencies": { "commander": "^12.1.0", "csv-stringify": "^6.5.0" @@ -37,6 +40,7 @@ "globals": "^15.6.0", "jest": "^29.7.0", "mock-stdin": "^1.0.0", + "np": "^10.0.7", "ts-jest": "^29.1.5", "ts-node": "^10.9.2", "tsx": "^4.15.6", diff --git a/yarn.lock b/yarn.lock index 90d703b4..8b228b2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,7 +10,7 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.24.7": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== @@ -479,6 +479,11 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570" integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== +"@inquirer/figures@^1.0.3": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.5.tgz#57f9a996d64d3e3345d2a3ca04d36912e94f8790" + integrity sha512-79hP/VWdZ2UVc9bFGJnoQ/lQMpL74mGgzSYX1xUqCVk7/v73vJCMw1VuyWN1jGkZ9B3z7THAbySqGbCNefcjfA== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -748,6 +753,34 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@pnpm/config.env-replace@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz#ab29da53df41e8948a00f2433f085f54de8b3a4c" + integrity sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w== + +"@pnpm/network.ca-file@^1.0.1": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz#2ab05e09c1af0cdf2fcf5035bea1484e222f7983" + integrity sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA== + dependencies: + graceful-fs "4.2.10" + +"@pnpm/npm-conf@^2.1.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@pnpm/npm-conf/-/npm-conf-2.3.0.tgz#c687bd4eecd16347e197db3bfe7e28b12aa80bfd" + integrity sha512-DqrO+oXGR7HCuicNy6quk6ALJSDDPKI7RZz1bP5im8mSL8J2e+9w26LdkjuAfpAjOutYUJVbnXnx4IbTQeIgfw== + dependencies: + "@pnpm/config.env-replace" "^1.1.0" + "@pnpm/network.ca-file" "^1.0.1" + config-chain "^1.1.11" + +"@samverschueren/stream-to-observable@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz#a21117b19ee9be70c379ec1877537ef2e1c63301" + integrity sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ== + dependencies: + any-observable "^0.3.0" + "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -868,6 +901,11 @@ dependencies: undici-types "~5.26.4" +"@types/normalize-package-data@^2.4.3": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" + integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== + "@types/stack-utils@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" @@ -983,6 +1021,14 @@ acorn@^8.11.0, acorn@^8.12.0, acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c" integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== +aggregate-error@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-4.0.1.tgz#25091fe1573b9e0be892aeda15c7c66a545f758e" + integrity sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w== + dependencies: + clean-stack "^4.0.0" + indent-string "^5.0.0" + ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -993,18 +1039,62 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-escapes@^4.2.1: +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-escapes@^3.0.0, ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" +ansi-escapes@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-5.0.0.tgz#b6a0caf0eef0c41af190e9a749e0c00ec04bb2a6" + integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA== + dependencies: + type-fest "^1.0.2" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== + +ansi-regex@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== + +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + 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@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -1024,6 +1114,16 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +ansi-styles@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +any-observable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" + integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== + anymatch@^3.0.3: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -1054,6 +1154,14 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +atomically@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/atomically/-/atomically-2.0.3.tgz#27e47bbe39994d324918491ba7c0edb7783e56cb" + integrity sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw== + dependencies: + stubborn-fs "^1.2.5" + when-exit "^2.1.1" + babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" @@ -1119,6 +1227,34 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +boxen@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-8.0.1.tgz#7e9fcbb45e11a2d7e6daa8fdcebfc3242fc19fe3" + integrity sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw== + dependencies: + ansi-align "^3.0.1" + camelcase "^8.0.0" + chalk "^5.3.0" + cli-boxes "^3.0.0" + string-width "^7.2.0" + type-fest "^4.21.0" + widest-line "^5.0.0" + wrap-ansi "^9.0.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1170,6 +1306,21 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1185,12 +1336,35 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== +camelcase@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-8.0.0.tgz#c0d36d418753fb6ad9c5e0437579745c1c14a534" + integrity sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA== + caniuse-lite@^1.0.30001629: version "1.0.30001636" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz#b15f52d2bdb95fad32c2f53c0b68032b85188a78" integrity sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg== -chalk@^2.4.2: +chalk-template@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/chalk-template/-/chalk-template-1.1.0.tgz#ffc55db6dd745e9394b85327c8ac8466edb7a7b1" + integrity sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg== + dependencies: + chalk "^5.2.0" + +chalk@^1.0.0, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.4.1, 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== @@ -1199,7 +1373,7 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0: +chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1207,11 +1381,21 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.2.0, chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + ci-info@^3.2.0: version "3.9.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" @@ -1222,6 +1406,60 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c" integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q== +clean-stack@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-4.2.0.tgz#c464e4cde4ac789f4e0735c5d75beb49d7b30b31" + integrity sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg== + dependencies: + escape-string-regexp "5.0.0" + +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-cursor@^2.0.0, cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== + dependencies: + restore-cursor "^2.0.0" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + +cli-truncate@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" + integrity sha512-f4r4yJnbT++qUPI9NR4XLDLq41gQ+uqnPItWG0F5ZkehuNiTTa3EY0S4AqTSUOeJ7/zU41oWPQSNkW5BqPL9bg== + dependencies: + slice-ansi "0.0.4" + string-width "^1.0.1" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +cli-width@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5" + integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== + cliui@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" @@ -1231,11 +1469,21 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA== + collect-v8-coverage@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" @@ -1275,11 +1523,39 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +config-chain@^1.1.11: + version "1.1.13" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +configstore@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-7.0.0.tgz#4461561fc51cb40e5ee1161230bc0337e069cc6b" + integrity sha512-yk7/5PN5im4qwz0WFZW3PXnzHgPu9mX29Y8uZ3aefe2lBPC1FYttWZRcaW9fKkT0pBCJyuQ2HfbmPVaODi9jcQ== + dependencies: + atomically "^2.0.3" + dot-prop "^9.0.0" + graceful-fs "^4.2.11" + xdg-basedir "^5.1.0" + convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +cosmiconfig@^8.3.6: + version "8.3.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== + dependencies: + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + path-type "^4.0.0" + create-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" @@ -1312,6 +1588,11 @@ csv-stringify@^6.5.0: resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-6.5.0.tgz#7b1491893c917e018a97de9bf9604e23b88647c2" integrity sha512-edlXFVKcUx7r8Vx5zQucsuMg4wb/xT6qyz+Sr1vnLrdXqlLD1+UKyWNyZ9zn6mUW1ewmGxrpVwAcChGF0HQ/2Q== +date-fns@^1.27.2: + version "1.30.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" + integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== + debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.5" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" @@ -1324,6 +1605,11 @@ dedent@^1.0.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== +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== + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -1334,6 +1620,45 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + +defaults@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== + dependencies: + clone "^1.0.2" + +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + +del@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/del/-/del-7.1.0.tgz#0de0044d556b649ff05387f1fa7c885e155fd1b6" + integrity sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg== + dependencies: + globby "^13.1.2" + graceful-fs "^4.2.10" + is-glob "^4.0.3" + is-path-cwd "^3.0.0" + is-path-inside "^4.0.0" + p-map "^5.5.0" + rimraf "^3.0.2" + slash "^4.0.0" + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -1356,16 +1681,33 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +dot-prop@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-9.0.0.tgz#bae5982fe6dc6b8fddb92efef4f2ddff26779e92" + integrity sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ== + dependencies: + type-fest "^4.18.2" + electron-to-chromium@^1.4.796: version "1.4.805" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.805.tgz#1d526e384c20944a3c68f618f9774edc384c4733" integrity sha512-8W4UJwX/w9T0QSzINJckTKG6CYpAUTqsaWcWIsdud3I1FYJcMgW9QqT1/4CBff/pP/TihWh13OmiyY8neto6vw== +elegant-spinner@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" + integrity sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ== + emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== +emoji-regex@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" + integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -1412,7 +1754,17 @@ escalade@^3.1.1, escalade@^3.1.2: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== -escape-string-regexp@^1.0.5: +escape-goat@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-4.0.0.tgz#9424820331b510b0666b98f7873fe11ac4aa8081" + integrity sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg== + +escape-string-regexp@5.0.0, escape-string-regexp@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + +escape-string-regexp@^1.0.2, 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== @@ -1538,6 +1890,26 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +execa@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^8.0.1" + human-signals "^5.0.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^4.1.0" + strip-final-newline "^3.0.0" + +exit-hook@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-4.0.0.tgz#c1e16ebd03d3166f837b1502dac755bb5c460d58" + integrity sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ== + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -1554,12 +1926,21 @@ expect@^29.0.0, expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" +external-editor@^3.0.3, external-editor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + 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.9: +fast-glob@^3.2.9, fast-glob@^3.3.0: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -1594,6 +1975,28 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +figures@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + integrity sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ== + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA== + dependencies: + escape-string-regexp "^1.0.5" + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" @@ -1608,6 +2011,11 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" +find-up-simple@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-up-simple/-/find-up-simple-1.0.0.tgz#21d035fde9fdbd56c8f4d2f63f32fd93a1cfc368" + integrity sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw== + find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -1662,6 +2070,11 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-east-asian-width@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz#5e6ebd9baee6fb8b7b6bd505221065f0cd91f64e" + integrity sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA== + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -1672,6 +2085,11 @@ get-stream@^6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + get-tsconfig@^4.7.5: version "4.7.5" resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.5.tgz#5e012498579e9a6947511ed0cd403272c7acbbaf" @@ -1679,6 +2097,11 @@ get-tsconfig@^4.7.5: dependencies: resolve-pkg-maps "^1.0.0" +github-url-from-git@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/github-url-from-git/-/github-url-from-git-1.5.0.tgz#f985fedcc0a9aa579dc88d7aff068d55cc6251a0" + integrity sha512-WWOec4aRI7YAykQ9+BHmzjyNlkfJFG8QLXnDTsLz/kZefq7qkzdfo4p6fkYYMIq1aj+gZcQs/1HQhQh3DPPxlQ== + glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -1705,6 +2128,13 @@ glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +global-directory@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/global-directory/-/global-directory-4.0.1.tgz#4d7ac7cfd2cb73f304c53b8810891748df5e361e" + integrity sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q== + dependencies: + ini "4.1.1" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -1732,7 +2162,23 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -graceful-fs@^4.2.9: +globby@^13.1.2: + version "13.2.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" + integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.3.0" + ignore "^5.2.4" + merge2 "^1.4.1" + slash "^4.0.0" + +graceful-fs@4.2.10: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +graceful-fs@^4.2.10, graceful-fs@^4.2.11, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -1742,6 +2188,13 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== + dependencies: + ansi-regex "^2.0.0" + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -1759,6 +2212,13 @@ hasown@^2.0.0: dependencies: function-bind "^1.1.2" +hosted-git-info@^7.0.0, hosted-git-info@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-7.0.2.tgz#9b751acac097757667f30114607ef7b661ff4f17" + integrity sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w== + dependencies: + lru-cache "^10.0.1" + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -1769,12 +2229,36 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -ignore@^5.2.0, ignore@^5.3.1: +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + +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" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore-walk@^6.0.4: + version "6.0.5" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.5.tgz#ef8d61eab7da169078723d1f82833b36e200b0dd" + integrity sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A== + dependencies: + minimatch "^9.0.0" + +ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== -import-fresh@^3.2.1: +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== @@ -1782,6 +2266,11 @@ import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" +import-lazy@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" + integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== + import-local@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" @@ -1790,11 +2279,34 @@ import-local@^3.0.2: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" +import-local@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + integrity sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ== + +indent-string@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-5.0.0.tgz#4fd2980fccaf8622d14c64d694f4cf33c81951a5" + integrity sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg== + +index-to-position@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/index-to-position/-/index-to-position-0.1.2.tgz#e11bfe995ca4d8eddb1ec43274488f3c201a7f09" + integrity sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -1803,11 +2315,86 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.1.tgz#d95b3d843b1e906e56d6747d5447904ff50ce7a1" + integrity sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g== + +ini@^1.3.4, 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== + +inquirer-autosubmit-prompt@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/inquirer-autosubmit-prompt/-/inquirer-autosubmit-prompt-0.2.0.tgz#a10f952af4f7bac9c43010e3e9e0891d7e8d15a1" + integrity sha512-mzNrusCk5L6kSzlN0Ioddn8yzrhYNLli+Sn2ZxMuLechMYAzakiFCIULxsxlQb5YKzthLGfrFACcWoAvM7p04Q== + dependencies: + chalk "^2.4.1" + inquirer "^6.2.1" + rxjs "^6.3.3" + +inquirer@^6.2.1: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +inquirer@^7.0.0: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +inquirer@^9.2.15: + version "9.3.6" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-9.3.6.tgz#670f1e9408743c3ed23df576f94fe5369f353055" + integrity sha512-riK/iQB2ctwkpWYgjjWIRv3MBLt2gzb2Sj0JNQNbyTXgyXsLWcDPJ5WS5ZDTCx7BRFnJsARtYh+58fjP5M2Y0Q== + dependencies: + "@inquirer/figures" "^1.0.3" + ansi-escapes "^4.3.2" + cli-width "^4.1.0" + external-editor "^3.1.0" + mute-stream "1.0.0" + ora "^5.4.1" + run-async "^3.0.0" + rxjs "^7.8.1" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wrap-ansi "^6.2.0" + yoctocolors-cjs "^2.1.2" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -1820,11 +2407,28 @@ is-core-module@^2.13.0: dependencies: hasown "^2.0.0" +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + 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@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw== + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== + 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" @@ -1842,26 +2446,127 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: dependencies: is-extglob "^2.1.1" +is-in-ci@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-in-ci/-/is-in-ci-1.0.0.tgz#9a86bbda7e42c6129902e0574c54b018fbb6ab88" + integrity sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg== + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-installed-globally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-1.0.0.tgz#08952c43758c33d815692392f7f8437b9e436d5a" + integrity sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ== + dependencies: + global-directory "^4.0.1" + is-path-inside "^4.0.0" + +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + +is-interactive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" + integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== + +is-npm@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-6.0.0.tgz#b59e75e8915543ca5d881ecff864077cba095261" + integrity sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ== + 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-observable@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e" + integrity sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA== + dependencies: + symbol-observable "^1.1.0" + +is-path-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-3.0.0.tgz#889b41e55c8588b1eb2a96a61d05740a674521c7" + integrity sha512-kyiNFFLU0Ampr6SDZitD/DwUo4Zs1nSdnygUBqsu3LooL00Qvb5j+UnvApUn/TTj1J3OuE6BTdQ5rudKmU2ZaA== + is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-path-inside@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-4.0.0.tgz#805aeb62c47c1b12fc3fd13bfb3ed1e7430071db" + integrity sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA== + +is-promise@^2.1.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + +is-scoped@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-scoped/-/is-scoped-3.0.0.tgz#2f9fc6e37c17d432d8e38d3c749aab8c76d1bdd8" + integrity sha512-ezxLUq30kiTvP0w/5n9tj4qTOKlrA07Oty1hwTQ+lcqw11x6uc8sp7VRb2OVGRzKfCHZ2A22T5Zsau/Q2Akb0g== + dependencies: + scoped-regex "^3.0.0" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== + 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-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-unicode-supported@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" + integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== + +is-url-superb@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/is-url-superb/-/is-url-superb-6.1.0.tgz#182f0d92b482412afeadfba8e6ea2c76680e3631" + integrity sha512-LXdhGlYqUPdvEyIhWPEEwYYK3yrUiPcBjmFGlZNv1u5GtIL5qQRf7ddDyPNAvsMFqdzS923FROpTQU97tLe3JQ== + +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +issue-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/issue-regex/-/issue-regex-4.1.0.tgz#e2039123748a48e6711eed7a9eb392f2c17c9341" + integrity sha512-X3HBmm7+Th+l4/kMtqwcHHgELD0Lfl0Ina6S3+grr+mKmTxsrM84NAO1UuRPIxIbGLIl3TCEu45S1kdu21HYbQ== + istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" @@ -2335,6 +3040,18 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +ky@^1.2.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/ky/-/ky-1.5.0.tgz#fa2c9c11c175a6d0072e572216207a4edc895d10" + integrity sha512-bkQo+UqryW6Zmo/DsixYZE4Z9t2mzvNMhceyIhuMuInb3knm5Q+GNGMKveydJAj+Z6piN1SwI6eR/V0G+Z0BtA== + +latest-version@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-9.0.0.tgz#e91ed216e7a4badc6f73b66c65adb46c58ec6ba1" + integrity sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA== + dependencies: + package-json "^10.0.0" + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -2353,6 +3070,60 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +listr-input@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/listr-input/-/listr-input-0.2.1.tgz#ce735c34530683580388fdf9462ecfebd3b66126" + integrity sha512-oa8iVG870qJq+OuuMK3DjGqFcwsK1SDu+kULp9kEq09TY231aideIZenr3lFOQdASpAr6asuyJBbX62/a3IIhg== + dependencies: + inquirer "^7.0.0" + inquirer-autosubmit-prompt "^0.2.0" + rxjs "^6.5.3" + through "^2.3.8" + +listr-silent-renderer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" + integrity sha512-L26cIFm7/oZeSNVhWB6faeorXhMg4HNlb/dS/7jHhr708jxlXrtrBWo4YUxZQkc6dGoxEAe6J/D3juTRBUzjtA== + +listr-update-renderer@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2" + integrity sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA== + dependencies: + chalk "^1.1.3" + cli-truncate "^0.2.1" + elegant-spinner "^1.0.1" + figures "^1.7.0" + indent-string "^3.0.0" + log-symbols "^1.0.2" + log-update "^2.3.0" + strip-ansi "^3.0.1" + +listr-verbose-renderer@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db" + integrity sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw== + dependencies: + chalk "^2.4.1" + cli-cursor "^2.1.0" + date-fns "^1.27.2" + figures "^2.0.0" + +listr@^0.14.3: + version "0.14.3" + resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" + integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA== + dependencies: + "@samverschueren/stream-to-observable" "^0.3.0" + is-observable "^1.1.0" + is-promise "^2.1.0" + is-stream "^1.1.0" + listr-silent-renderer "^1.1.1" + listr-update-renderer "^0.5.0" + listr-verbose-renderer "^0.5.0" + p-map "^2.0.0" + rxjs "^6.3.3" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -2377,6 +3148,53 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.zip@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" + integrity sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg== + +lodash@^4.17.12, lodash@^4.17.19: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + integrity sha512-mmPrW0Fh2fxOzdBbFv4g1m6pR72haFLPJ2G5SJEELf1y+iaQrDG6cWCPjy54RHYbZAt7X+ls690Kw62AdWXBzQ== + dependencies: + chalk "^1.0.0" + +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log-symbols@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-6.0.0.tgz#bb95e5f05322651cac30c0feb6404f9f2a8a9439" + integrity sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw== + dependencies: + chalk "^5.3.0" + is-unicode-supported "^1.3.0" + +log-update@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" + integrity sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg== + dependencies: + ansi-escapes "^3.0.0" + cli-cursor "^2.0.0" + wrap-ansi "^3.0.1" + +lru-cache@^10.0.1: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -2403,6 +3221,11 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +meow@^13.2.0: + version "13.2.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-13.2.0.tgz#6b7d63f913f984063b3cc261b6e8800c4cd3474f" + integrity sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -2421,11 +3244,26 @@ micromatch@^4.0.4: braces "^3.0.3" picomatch "^2.3.1" +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + 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-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +mimic-function@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" + integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -2433,6 +3271,13 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^9.0.0: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimatch@^9.0.4: version "9.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" @@ -2440,6 +3285,11 @@ minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" +minimist@^1.2.0: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + mock-stdin@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/mock-stdin/-/mock-stdin-1.0.0.tgz#efcfaf4b18077e14541742fd758b9cae4e5365ea" @@ -2450,11 +3300,33 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +mute-stream@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" + integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +new-github-release-url@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/new-github-release-url/-/new-github-release-url-2.0.0.tgz#335189b91f52bbb9569042a7485900a205a0500b" + integrity sha512-NHDDGYudnvRutt/VhKFlX26IotXe1w0cmkDm6JGquh5bz/bDTw0LufSmH/GxTjEdpHEO+bVKFTwdrcGa/9XlKQ== + dependencies: + type-fest "^2.5.1" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -2465,11 +3337,77 @@ node-releases@^2.0.14: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== +normalize-package-data@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-6.0.2.tgz#a7bc22167fe24025412bcff0a9651eb768b03506" + integrity sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g== + dependencies: + hosted-git-info "^7.0.0" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" + 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== +np@^10.0.7: + version "10.0.7" + resolved "https://registry.yarnpkg.com/np/-/np-10.0.7.tgz#6f0dc3c7440c8ac95d55a2fa5d488c3d0848b7a5" + integrity sha512-vIPKQwOYKpQU40PU5x/vLfN2haj8ObxMvR1QGt7EZnBPWdm4WEbHdumYAnMV7AeR9kACsMqcqAP37sAo5cW5jA== + dependencies: + chalk "^5.3.0" + chalk-template "^1.1.0" + cosmiconfig "^8.3.6" + del "^7.1.0" + escape-goat "^4.0.0" + escape-string-regexp "^5.0.0" + execa "^8.0.1" + exit-hook "^4.0.0" + github-url-from-git "^1.5.0" + hosted-git-info "^7.0.1" + ignore-walk "^6.0.4" + import-local "^3.1.0" + inquirer "^9.2.15" + is-installed-globally "^1.0.0" + is-interactive "^2.0.0" + is-scoped "^3.0.0" + issue-regex "^4.1.0" + listr "^0.14.3" + listr-input "^0.2.1" + log-symbols "^6.0.0" + meow "^13.2.0" + new-github-release-url "^2.0.0" + npm-name "^8.0.0" + onetime "^7.0.0" + open "^10.0.4" + p-memoize "^7.1.1" + p-timeout "^6.1.2" + path-exists "^5.0.0" + pkg-dir "^8.0.0" + read-package-up "^11.0.0" + read-pkg "^9.0.1" + rxjs "^7.8.1" + semver "^7.6.0" + symbol-observable "^4.0.0" + terminal-link "^3.0.0" + update-notifier "^7.0.0" + +npm-name@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/npm-name/-/npm-name-8.0.0.tgz#05aeda9748706ffad66d9ecec8188b99d6e3908b" + integrity sha512-DIuCGcKYYhASAZW6Xh/tiaGMko8IHOHe0n3zOA7SzTi0Yvy00x8L7sa5yNiZ75Ny58O/KeRtNouy8Ut6gPbKiw== + dependencies: + is-scoped "^3.0.0" + is-url-superb "^6.1.0" + ky "^1.2.0" + lodash.zip "^4.2.0" + org-regex "^1.0.0" + p-map "^7.0.1" + registry-auth-token "^5.0.2" + registry-url "^6.0.1" + validate-npm-package-name "^5.0.0" + 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" @@ -2477,6 +3415,23 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +npm-run-path@^5.1.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" + integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== + dependencies: + path-key "^4.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -2484,13 +3439,44 @@ once@^1.3.0: dependencies: wrappy "1" -onetime@^5.1.2: +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ== + dependencies: + mimic-fn "^1.0.0" + +onetime@^5.1.0, 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" +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +onetime@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" + integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== + dependencies: + mimic-function "^5.0.0" + +open@^10.0.4: + version "10.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-10.1.0.tgz#a7795e6e5d519abe4286d9937bb24b51122598e1" + integrity sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw== + dependencies: + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^3.1.0" + optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -2503,6 +3489,31 @@ optionator@^0.9.3: type-check "^0.4.0" word-wrap "^1.2.5" +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +org-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/org-regex/-/org-regex-1.0.0.tgz#67ebb9ab3cb124fea5841289d60b59434f041a59" + integrity sha512-7bqkxkEJwzJQUAlyYniqEZ3Ilzjh0yoa62c7gL6Ijxj5bEpPL+8IE1Z0PFj0ywjjXQcdrwR51g9MIcLezR0hKQ== + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -2531,11 +3542,51 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-map@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-5.5.0.tgz#054ca8ca778dfa4cf3f8db6638ccb5b937266715" + integrity sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg== + dependencies: + aggregate-error "^4.0.0" + +p-map@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-7.0.2.tgz#7c5119fada4755660f70199a66aa3fe2f85a1fe8" + integrity sha512-z4cYYMMdKHzw4O5UkWJImbZynVIo0lSGTXc7bzB1e/rrDqkgGUNysK/o4bTr+0+xKvvLoTyGqYC4Fgljy9qe1Q== + +p-memoize@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/p-memoize/-/p-memoize-7.1.1.tgz#53b1d0e6007288f7261cfa11a7603b84c9261bfa" + integrity sha512-DZ/bONJILHkQ721hSr/E9wMz5Am/OTJ9P6LhLFo2Tu+jL8044tgc9LwHO8g4PiaYePnlVVRAJcKmgy8J9MVFrA== + dependencies: + mimic-fn "^4.0.0" + type-fest "^3.0.0" + +p-timeout@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-6.1.2.tgz#22b8d8a78abf5e103030211c5fc6dee1166a6aa5" + integrity sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ== + 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@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-10.0.1.tgz#e49ee07b8de63b638e7f1b5bb353733e428fe7d7" + integrity sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg== + dependencies: + ky "^1.2.0" + registry-auth-token "^5.0.2" + registry-url "^6.0.1" + semver "^7.6.0" + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -2553,11 +3604,25 @@ parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-json@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-8.1.0.tgz#91cdc7728004e955af9cb734de5684733b24a717" + integrity sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA== + dependencies: + "@babel/code-frame" "^7.22.13" + index-to-position "^0.1.2" + type-fest "^4.7.1" + 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-exists@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" + integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== + 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" @@ -2568,6 +3633,11 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -2600,6 +3670,13 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkg-dir@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-8.0.0.tgz#8f3de8ba83d46b72a05c80bfd4e579f060fa91e2" + integrity sha512-4peoBq4Wks0riS0z8741NVv+/8IiTvqnZAr8QGgtdifrtpdXbNw/FxRS1l6NFqm4EMzuS0EDqNNx4XGaz8cuyQ== + dependencies: + find-up-simple "^1.0.0" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -2622,11 +3699,23 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== + punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +pupa@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-3.1.0.tgz#f15610274376bbcc70c9a3aa8b505ea23f41c579" + integrity sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug== + dependencies: + escape-goat "^4.0.0" + pure-rand@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" @@ -2637,11 +3726,64 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +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-is@^18.0.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== +read-package-up@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/read-package-up/-/read-package-up-11.0.0.tgz#71fb879fdaac0e16891e6e666df22de24a48d5ba" + integrity sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ== + dependencies: + find-up-simple "^1.0.0" + read-pkg "^9.0.0" + type-fest "^4.6.0" + +read-pkg@^9.0.0, read-pkg@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-9.0.1.tgz#b1b81fb15104f5dbb121b6bbdee9bbc9739f569b" + integrity sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA== + dependencies: + "@types/normalize-package-data" "^2.4.3" + normalize-package-data "^6.0.0" + parse-json "^8.0.0" + type-fest "^4.6.0" + unicorn-magic "^0.1.0" + +readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +registry-auth-token@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-5.0.2.tgz#8b026cc507c8552ebbe06724136267e63302f756" + integrity sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ== + dependencies: + "@pnpm/npm-conf" "^2.1.0" + +registry-url@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-6.0.1.tgz#056d9343680f2f64400032b1e199faa692286c58" + integrity sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q== + dependencies: + rc "1.2.8" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -2683,11 +3825,49 @@ resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q== + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-applescript@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" + integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== + +run-async@^2.2.0, run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +run-async@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-3.0.0.tgz#42a432f6d76c689522058984384df28be379daad" + integrity sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q== + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -2695,11 +3875,52 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.3, rxjs@^6.6.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +rxjs@^7.8.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.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== + +scoped-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/scoped-regex/-/scoped-regex-3.0.0.tgz#cd7ede7d942f2ae90da53272102ff2d73129c46f" + integrity sha512-yEsN6TuxZhZ1Tl9iB81frTNS292m0I/IG7+w8lTvfcJQP2x3vnpOoevjBoE3Np5A6KnZM2+RtVenihj9t6NiYg== + +semver-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-4.0.0.tgz#3afcf5ed6d62259f5c72d0d5d50dffbdc9680df5" + integrity sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA== + dependencies: + semver "^7.3.5" + semver@^6.3.0, semver@^6.3.1: 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.5, semver@^7.6.3: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + semver@^7.5.3, semver@^7.5.4, semver@^7.6.0: version "7.6.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" @@ -2717,11 +3938,16 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: 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== +signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -2732,6 +3958,16 @@ slash@^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== + +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + integrity sha512-up04hB2hR92PgjpyU3y/eg91yIBILyjVY26NvvciY3EVVPjybkMszMpXQ9QAkcS3I5rtJBDLoTxxg+qvW8c7rw== + source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -2745,6 +3981,32 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.18" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz#22aa922dcf2f2885a6494a261f2d8b75345d0326" + integrity sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -2765,6 +4027,23 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw== + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.1.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -2774,6 +4053,43 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^7.0.0, string-width@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" + integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + +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" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.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" @@ -2781,6 +4097,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" @@ -2791,11 +4114,31 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + 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== + +stubborn-fs@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/stubborn-fs/-/stubborn-fs-1.2.5.tgz#e5e244223166921ddf66ed5e062b6b3bf285bfd2" + integrity sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g== + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -2803,7 +4146,7 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.1.0: +supports-color@^7.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== @@ -2817,11 +4160,37 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0" +supports-hyperlinks@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" + integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.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== +symbol-observable@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + +symbol-observable@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" + integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== + +terminal-link@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-3.0.0.tgz#91c82a66b52fc1684123297ce384429faf72ac5c" + integrity sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg== + dependencies: + ansi-escapes "^5.0.0" + supports-hyperlinks "^2.2.0" + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -2836,6 +4205,18 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +through@^2.3.6, through@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -2891,6 +4272,16 @@ ts-node@^10.9.2: v8-compile-cache-lib "^3.0.1" yn "3.1.1" +tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.1.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + tsx@^4.15.6: version "4.15.6" resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.15.6.tgz#4522ed093f7fa54f031a7a999274e8b35dbf3165" @@ -2918,6 +4309,26 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@^1.0.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== + +type-fest@^2.5.1: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + +type-fest@^3.0.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" + integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== + +type-fest@^4.18.2, type-fest@^4.21.0, type-fest@^4.6.0, type-fest@^4.7.1: + version "4.23.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.23.0.tgz#8196561a6b835175473be744f3e41e2dece1496b" + integrity sha512-ZiBujro2ohr5+Z/hZWHESLz3g08BBdrdLMieYFULJO+tWc437sn8kQsWLJoZErY8alNhxre9K4p3GURAG11n+w== + typescript-eslint@^7.13.1: version "7.13.1" resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-7.13.1.tgz#8bbcc4b59b6bb0c457505ee17a356b1868c3fcd5" @@ -2937,6 +4348,11 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +unicorn-magic@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" + integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== + update-browserslist-db@^1.0.16: version "1.0.16" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz#f6d489ed90fb2f07d67784eb3f53d7891f736356" @@ -2945,6 +4361,24 @@ update-browserslist-db@^1.0.16: escalade "^3.1.2" picocolors "^1.0.1" +update-notifier@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-7.2.0.tgz#ec5bd0cb87a56afbd5fddf83dd6621b0d1f3738b" + integrity sha512-GoBCFKIbF88latQyk8HpHUoJHqZUzYSPI6BySAjs5TWd/TCTMynAsIfGfJ6Ep2DAx6O5YExYGPs3Hdnt2TWdzQ== + dependencies: + boxen "^8.0.0" + chalk "^5.3.0" + configstore "^7.0.0" + import-lazy "^4.0.0" + is-in-ci "^1.0.0" + is-installed-globally "^1.0.0" + is-npm "^6.0.0" + latest-version "^9.0.0" + pupa "^3.1.0" + semver "^7.6.3" + semver-diff "^4.0.0" + xdg-basedir "^5.1.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -2952,6 +4386,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -2966,6 +4405,19 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^2.0.0" +validate-npm-package-license@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +validate-npm-package-name@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz#a316573e9b49f3ccd90dbb6eb52b3f06c6d604e8" + integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ== + walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" @@ -2973,6 +4425,18 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + +when-exit@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/when-exit/-/when-exit-2.1.3.tgz#5831cdbed8ad4984645da98c4a00d4ee3a3757e7" + integrity sha512-uVieSTccFIr/SFQdFWN/fFaQYmV37OKtuaGphMAzi4DmmUlrvRBJW5WSLkHyjNQY/ePJMz3LoiX9R3yy1Su6Hw== + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -2980,11 +4444,35 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +widest-line@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-5.0.0.tgz#b74826a1e480783345f0cd9061b49753c9da70d0" + integrity sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA== + dependencies: + string-width "^7.0.0" + word-wrap@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== +wrap-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba" + integrity sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ== + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -2994,6 +4482,15 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.0.tgz#1a3dc8b70d85eeb8398ddfb1e4a02cd186e58b3e" + integrity sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q== + dependencies: + ansi-styles "^6.2.1" + string-width "^7.0.0" + strip-ansi "^7.1.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -3007,6 +4504,11 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" +xdg-basedir@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-5.1.0.tgz#1efba19425e73be1bc6f2a6ceb52a3d2c884c0c9" + integrity sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" @@ -3044,3 +4546,8 @@ 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== + +yoctocolors-cjs@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz#f4b905a840a37506813a7acaa28febe97767a242" + integrity sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA== From 2472da20fc803dfde13fc31aa83edd8c12367fec Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Wed, 7 Aug 2024 16:39:31 +0200 Subject: [PATCH 71/76] fix relative file paths --- lib/commands/base_command.ts | 6 +++++- lib/commands/unipept/unipept_subcommand.ts | 6 +++++- package.json | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/commands/base_command.ts b/lib/commands/base_command.ts index efa87e6a..c0480c17 100644 --- a/lib/commands/base_command.ts +++ b/lib/commands/base_command.ts @@ -13,7 +13,11 @@ export abstract class BaseCommand { version: string; constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean }) { - this.version = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")).version; + let p = ""; + if (import.meta.url.includes("/dist/")) { + p = "../"; + } + this.version = JSON.parse(readFileSync(new URL(p + "../../package.json", import.meta.url), "utf8")).version; this.program = this.create(options); } diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index 47f6d210..f2d73324 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -29,7 +29,11 @@ export abstract class UnipeptSubcommand { constructor(name: string) { this.name = name; - const version = JSON.parse(readFileSync(new URL("../../../package.json", import.meta.url), "utf8")).version; + let p = ""; + if (import.meta.url.includes("/dist/")) { + p = "../"; + } + const version = JSON.parse(readFileSync(new URL(p + "../../../package.json", import.meta.url), "utf8")).version; this.user_agent = `unipept-cli/${version}`; this.command = this.create(name); this.fasta = false; diff --git a/package.json b/package.json index befa17c3..e985f632 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unipept-cli", - "version": "4.0.0-1", + "version": "4.0.0-2", "description": "Command line interface to the Unipept web services", "repository": "git@github.com:unipept/unipept-cli.git", "author": "Bart Mesuere ", From 5e851642a6b38a5a850b520c66c7b6975bc79716 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Wed, 27 Nov 2024 15:15:05 +0100 Subject: [PATCH 72/76] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 453fe29f..52afbd71 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ $ node -v v22.3.0 ``` -More information on installing Ruby can be found at https://nodejs.org/en/download/package-manager +More information on installing Node can be found at https://nodejs.org/en/download/package-manager The Unipept CLI is available as an npm package. This means it can easily be installed with the following command: From 8aa8ab8ed5922a947ecdad2d1b4f74148603835f Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Wed, 27 Nov 2024 15:21:12 +0100 Subject: [PATCH 73/76] minor tweaks --- README.md | 2 +- lib/commands/unipept/unipept_subcommand.ts | 7 ++++--- package.json | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 453fe29f..2a084057 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ added 3 packages in 986ms After successful installation, the unipept command should be available: ```bash -$ unipept -v +$ unipept --version 4.0.0 ``` diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index f2d73324..0a503047 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -101,6 +101,7 @@ export abstract class UnipeptSubcommand { method: "POST", body: this.constructRequestBody(slice), headers: { + "Content-Type": "application/json", "Accept-Encoding": "gzip", "User-Agent": this.user_agent, } @@ -238,10 +239,10 @@ export abstract class UnipeptSubcommand { }); } - private constructRequestBody(slice: string[]): URLSearchParams { + private constructRequestBody(slice: string[]): string { const names = this.getSelectedFields().length === 0 || this.getSelectedFields().some(regex => regex.toString().includes("name") || regex.toString().includes(".*$")); - return new URLSearchParams({ - input: JSON.stringify(slice), + return JSON.stringify({ + input: slice, equate_il: this.options.equate, extra: this.options.all, names: this.options.all && names diff --git a/package.json b/package.json index e985f632..07ecf12d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unipept-cli", - "version": "4.0.0-2", + "version": "4.0.0-4", "description": "Command line interface to the Unipept web services", "repository": "git@github.com:unipept/unipept-cli.git", "author": "Bart Mesuere ", From 7c8d1a332cd81dfae486c55c6db5491c382c0758 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Thu, 28 Nov 2024 10:34:19 +0100 Subject: [PATCH 74/76] more flexible test matching --- tests/commands/unipept/pept2ec.test.ts | 6 ++++-- tests/commands/unipept/pept2funct.test.ts | 4 ++-- tests/commands/unipept/pept2go.test.ts | 6 ++++-- tests/commands/unipept/pept2interpro.test.ts | 6 ++++-- tests/commands/unipept/pept2prot.test.ts | 4 ++-- tests/commands/unipept/pept2taxa.test.ts | 4 ++-- tests/commands/unipept/peptinfo.test.ts | 6 ++++-- 7 files changed, 22 insertions(+), 14 deletions(-) diff --git a/tests/commands/unipept/pept2ec.test.ts b/tests/commands/unipept/pept2ec.test.ts index 87283aa7..62aacb98 100644 --- a/tests/commands/unipept/pept2ec.test.ts +++ b/tests/commands/unipept/pept2ec.test.ts @@ -14,7 +14,8 @@ test('test with default args', async () => { const command = new Pept2ec(); await command.run(["AALTER"], { header: true, format: "csv" }); expect(output[0].startsWith("peptide,total_protein_count,ec_number,ec_protein_count")).toBeTruthy(); - expect(output[1].startsWith("AALTER,3310,2.3.2.27 3.1.3.3")).toBeTruthy(); + expect(output[1].startsWith("AALTER,")).toBeTruthy(); + expect(output[1].includes("2.3.2.27")).toBeTruthy(); expect(output.length).toBe(2); }); @@ -22,6 +23,7 @@ test('test with fasta', async () => { const command = new Pept2ec(); await command.run([">test", "AALTER"], { header: true, format: "csv" }); expect(output[0].startsWith("fasta_header,peptide,total_protein_count,ec_number,ec_protein_count")).toBeTruthy(); - expect(output[1].startsWith(">test,AALTER,3310,2.3.2.27 3.1.3.3")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,")).toBeTruthy(); + expect(output[1].includes("2.3.2.27")).toBeTruthy(); expect(output.length).toBe(2); }); diff --git a/tests/commands/unipept/pept2funct.test.ts b/tests/commands/unipept/pept2funct.test.ts index 97d21b7e..47af20c3 100644 --- a/tests/commands/unipept/pept2funct.test.ts +++ b/tests/commands/unipept/pept2funct.test.ts @@ -14,7 +14,7 @@ test('test with default args', async () => { const command = new Pept2funct(); await command.run(["AALTER"], { header: true, format: "csv" }); expect(output[0].startsWith("peptide,total_protein_count,ec_number,ec_protein_count,go_term,go_protein_count,ipr_code,ipr_protein_count")).toBeTruthy(); - expect(output[1].startsWith("AALTER,3310")).toBeTruthy(); + expect(output[1].startsWith("AALTER,")).toBeTruthy(); expect(output.length).toBe(2); }); @@ -22,6 +22,6 @@ test('test with fasta', async () => { const command = new Pept2funct(); await command.run([">test", "AALTER"], { header: true, format: "csv" }); expect(output[0].startsWith("fasta_header,peptide,total_protein_count,ec_number,ec_protein_count,go_term,go_protein_count,ipr_code,ipr_protein_count")).toBeTruthy(); - expect(output[1].startsWith(">test,AALTER,3310")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,")).toBeTruthy(); expect(output.length).toBe(2); }); diff --git a/tests/commands/unipept/pept2go.test.ts b/tests/commands/unipept/pept2go.test.ts index 08775db1..222e245a 100644 --- a/tests/commands/unipept/pept2go.test.ts +++ b/tests/commands/unipept/pept2go.test.ts @@ -14,7 +14,8 @@ test('test with default args', async () => { const command = new Pept2go(); await command.run(["AALTER"], { header: true, format: "csv" }); expect(output[0].startsWith("peptide,total_protein_count,go_term,go_protein_count")).toBeTruthy(); - expect(output[1].startsWith("AALTER,3310,GO:0003677")).toBeTruthy(); + expect(output[1].startsWith("AALTER,")).toBeTruthy(); + expect(output[1].includes("GO:0003677")).toBeTruthy(); expect(output.length).toBe(2); }); @@ -22,6 +23,7 @@ test('test with fasta', async () => { const command = new Pept2go(); await command.run([">test", "AALTER"], { header: true, format: "csv" }); expect(output[0].startsWith("fasta_header,peptide,total_protein_count,go_term,go_protein_count")).toBeTruthy(); - expect(output[1].startsWith(">test,AALTER,3310,GO:0003677")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,")).toBeTruthy(); + expect(output[1].includes("GO:0003677")).toBeTruthy(); expect(output.length).toBe(2); }); diff --git a/tests/commands/unipept/pept2interpro.test.ts b/tests/commands/unipept/pept2interpro.test.ts index 5523e7fb..a3c66283 100644 --- a/tests/commands/unipept/pept2interpro.test.ts +++ b/tests/commands/unipept/pept2interpro.test.ts @@ -14,7 +14,8 @@ test('test with default args', async () => { const command = new Pept2interpro(); await command.run(["AALTER"], { header: true, format: "csv" }); expect(output[0].startsWith("peptide,total_protein_count,ipr_code,ipr_protein_count")).toBeTruthy(); - expect(output[1].startsWith("AALTER,3310,IPR003613")).toBeTruthy(); + expect(output[1].startsWith("AALTER,")).toBeTruthy(); + expect(output[1].includes("IPR003613")).toBeTruthy(); expect(output.length).toBe(2); }); @@ -22,6 +23,7 @@ test('test with fasta', async () => { const command = new Pept2interpro(); await command.run([">test", "AALTER"], { header: true, format: "csv" }); expect(output[0].startsWith("fasta_header,peptide,total_protein_count,ipr_code,ipr_protein_count")).toBeTruthy(); - expect(output[1].startsWith(">test,AALTER,3310,IPR003613")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,")).toBeTruthy(); + expect(output[1].includes("IPR003613")).toBeTruthy(); expect(output.length).toBe(2); }); diff --git a/tests/commands/unipept/pept2prot.test.ts b/tests/commands/unipept/pept2prot.test.ts index 768ff50e..3258d4a1 100644 --- a/tests/commands/unipept/pept2prot.test.ts +++ b/tests/commands/unipept/pept2prot.test.ts @@ -14,7 +14,7 @@ test('test with default args', async () => { const command = new Pept2prot(); await command.run(["AALTER"], { header: true, format: "csv" }); expect(output[0].startsWith("peptide,uniprot_id,protein_name,taxon_id,protein")).toBeTruthy(); - expect(output[1].startsWith("AALTER,P78330")).toBeTruthy(); + expect(output[1].startsWith("AALTER,")).toBeTruthy(); expect(output.length).toBe(2); }); @@ -22,6 +22,6 @@ test('test with fasta', async () => { const command = new Pept2prot(); await command.run([">test", "AALTER"], { header: true, format: "csv" }); expect(output[0].startsWith("fasta_header,peptide,uniprot_id,protein_name,taxon_id,protein")).toBeTruthy(); - expect(output[1].startsWith(">test,AALTER,P78330")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,")).toBeTruthy(); expect(output.length).toBe(2); }); diff --git a/tests/commands/unipept/pept2taxa.test.ts b/tests/commands/unipept/pept2taxa.test.ts index cbfb72c7..99e710b5 100644 --- a/tests/commands/unipept/pept2taxa.test.ts +++ b/tests/commands/unipept/pept2taxa.test.ts @@ -14,7 +14,7 @@ test('test with default args', async () => { const command = new Pept2taxa(); await command.run(["AALTER"], { header: true, format: "csv" }); expect(output[0].startsWith("peptide,taxon_id,taxon_name,taxon_rank")).toBeTruthy(); - expect(output[1].startsWith("AALTER,41,Stigmatella aurantiaca,species")).toBeTruthy(); + expect(output[1].startsWith("AALTER,")).toBeTruthy(); expect(output.length).toBe(2); }); @@ -22,6 +22,6 @@ test('test with fasta', async () => { const command = new Pept2taxa(); await command.run([">test", "AALTER"], { header: true, format: "csv" }); expect(output[0].startsWith("fasta_header,peptide,taxon_id,taxon_name,taxon_rank")).toBeTruthy(); - expect(output[1].startsWith(">test,AALTER,41,Stigmatella aurantiaca,species")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,")).toBeTruthy(); expect(output.length).toBe(2); }); diff --git a/tests/commands/unipept/peptinfo.test.ts b/tests/commands/unipept/peptinfo.test.ts index 1e4aa65d..d88ba08e 100644 --- a/tests/commands/unipept/peptinfo.test.ts +++ b/tests/commands/unipept/peptinfo.test.ts @@ -14,7 +14,8 @@ test('test with default args', async () => { const command = new Peptinfo(); await command.run(["AALTER"], { header: true, format: "csv" }); expect(output[0].startsWith("peptide,total_protein_count,taxon_id,taxon_name,taxon_rank,ec_number,ec_protein_count,go_term,go_protein_count,ipr_code,ipr_protein_count")).toBeTruthy(); - expect(output[1].startsWith("AALTER,3310,1,root")).toBeTruthy(); + expect(output[1].startsWith("AALTER,")).toBeTruthy(); + expect(output[1].includes(",1,root,")).toBeTruthy(); expect(output.length).toBe(2); }); @@ -22,6 +23,7 @@ test('test with fasta', async () => { const command = new Peptinfo(); await command.run([">test", "AALTER"], { header: true, format: "csv" }); expect(output[0].startsWith("fasta_header,peptide,total_protein_count,taxon_id,taxon_name,taxon_rank,ec_number,ec_protein_count,go_term,go_protein_count,ipr_code,ipr_protein_count")).toBeTruthy(); - expect(output[1].startsWith(">test,AALTER,3310,1,root")).toBeTruthy(); + expect(output[1].startsWith(">test,AALTER,")).toBeTruthy(); + expect(output[1].includes(",1,root")).toBeTruthy(); expect(output.length).toBe(2); }); From 5f14a81e966017ada59aae66f2ffaa4619419972 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Thu, 28 Nov 2024 11:37:31 +0100 Subject: [PATCH 75/76] use integevers as input for taxa2lca and taxonomy --- lib/commands/unipept/taxa2lca.ts | 2 ++ lib/commands/unipept/taxonomy.ts | 2 ++ lib/commands/unipept/unipept_subcommand.ts | 7 ++++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/commands/unipept/taxa2lca.ts b/lib/commands/unipept/taxa2lca.ts index 0dccba06..a52c4625 100644 --- a/lib/commands/unipept/taxa2lca.ts +++ b/lib/commands/unipept/taxa2lca.ts @@ -11,6 +11,8 @@ export class Taxa2lca extends UnipeptSubcommand { The command will give priority to the first way NCBI Taxonomy Identifiers are passed, in the order as listed above. Text files and standard input should have one taxon id per line.`; + castInput = true; + constructor() { super("taxa2lca"); diff --git a/lib/commands/unipept/taxonomy.ts b/lib/commands/unipept/taxonomy.ts index ae8426a1..0798a2fd 100644 --- a/lib/commands/unipept/taxonomy.ts +++ b/lib/commands/unipept/taxonomy.ts @@ -11,6 +11,8 @@ export class Taxonomy extends UnipeptSubcommand { The command will give priority to the first way taxon id's are passed, in the order as listed above. Text files and standard input should have one taxon id per line.`; + castInput = true; + constructor() { super("taxonomy"); diff --git a/lib/commands/unipept/unipept_subcommand.ts b/lib/commands/unipept/unipept_subcommand.ts index 0a503047..cb30f2dd 100644 --- a/lib/commands/unipept/unipept_subcommand.ts +++ b/lib/commands/unipept/unipept_subcommand.ts @@ -23,6 +23,7 @@ export abstract class UnipeptSubcommand { firstBatch = true; selectedFields?: RegExp[]; fasta: boolean; + castInput = false; // we must save this to be able to close it properly in tests private streamInterface?: Interface; @@ -241,8 +242,12 @@ export abstract class UnipeptSubcommand { private constructRequestBody(slice: string[]): string { const names = this.getSelectedFields().length === 0 || this.getSelectedFields().some(regex => regex.toString().includes("name") || regex.toString().includes(".*$")); + let input: string[] | number[] = slice; + if (this.castInput) { + input = slice.map(s => parseInt(s, 10)); + } return JSON.stringify({ - input: slice, + input: input, equate_il: this.options.equate, extra: this.options.all, names: this.options.all && names From 1096d2ef193ea5b227d12cecc61d092df11212c2 Mon Sep 17 00:00:00 2001 From: Bart Mesuere Date: Thu, 28 Nov 2024 11:46:22 +0100 Subject: [PATCH 76/76] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 07ecf12d..9c7c5329 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unipept-cli", - "version": "4.0.0-4", + "version": "4.0.1", "description": "Command line interface to the Unipept web services", "repository": "git@github.com:unipept/unipept-cli.git", "author": "Bart Mesuere ",