├── .gitignore ├── .travis.yml ├── lib ├── constants.js ├── pollUntil.js ├── statusCheck.js ├── retrieve.js ├── submit.js └── schemas.js ├── package.json ├── blastjson-to-afa.js ├── README.md ├── index.js ├── CODE_OF_CONDUCT.md └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.swp 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "7" 4 | -------------------------------------------------------------------------------- /lib/constants.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const HOSTNAME = exports.HOSTNAME = 'blast.ncbi.nlm.nih.gov' 4 | const PATH = exports.PATH = '/Blast.cgi' 5 | 6 | const BASE_URL = exports.BASE_URL = 'https://' + HOSTNAME + PATH 7 | -------------------------------------------------------------------------------- /lib/pollUntil.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const statusCheck = require('./statusCheck.js') 4 | 5 | module.exports = (RID) => new Promise((resolve, reject) => { 6 | const INTERVAL = 30 * 1000 7 | let startTime = Date.now() 8 | 9 | console.log(`Beginning status checks for RID: ${RID}`) 10 | 11 | const cb = () => 12 | statusCheck(RID) 13 | .then(({ message, done }) => { 14 | const elapsed = Date.now() - startTime 15 | process.stdout.write(`\r[${elapsed}s] Status message: `, message) 16 | 17 | if (done) { 18 | clearInterval(poller) 19 | return resolve(RID) 20 | } 21 | }) 22 | .catch((err) => { 23 | clearInterval(poller) 24 | return reject(err) 25 | }) 26 | 27 | const poller = setInterval(cb, INTERVAL) 28 | }) 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bionode-blast", 3 | "version": "0.0.1", 4 | "description": "NodeJS API for convenient and sanitated usage of BLAST Common URL API", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/bionode/bionode-blast.git" 12 | }, 13 | "keywords": [ 14 | "bioinformatics", 15 | "blast" 16 | ], 17 | "main": "lib/index.js", 18 | "scripts": { 19 | "test": "standard && dependency-check . && mocha" 20 | }, 21 | "author": "Julian Mazzitelli ", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/bionode/bionode-blast/issues" 25 | }, 26 | "homepage": "https://github.com/bionode/bionode-blast#readme", 27 | "dependencies": { 28 | "concat-stream": "^1.6.0", 29 | "jsonschema": "^1.1.1", 30 | "jszip": "^3.1.3", 31 | "split2": "^2.1.1", 32 | "streamifier": "^0.1.1" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /blastjson-to-afa.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | 5 | const inFile = process.argv[2] 6 | const outFile = process.argv[3] 7 | 8 | const results = JSON.parse(fs.readFileSync(inFile, 'utf-8')) 9 | 10 | const alignments = results.BlastOutput2.report.results.search.hits 11 | 12 | const getOutStream = () => outFile === '-' ? process.stdout : fs.createWriteStream(outFile) 13 | 14 | const outStream = getOutStream() 15 | 16 | for (let i = 0; i < alignments.length; i++) { 17 | const alignment = alignments[i] 18 | 19 | for (let j = 0; j < alignment.description.length; j++) { 20 | outStream.write('>' + alignment.description[j].id + '\n') 21 | } 22 | 23 | for (let j = 0; j < alignment.hsps.length; j++) { 24 | outStream.write(alignment.hsps[j].qseq + '\n') 25 | outStream.write(alignment.hsps[j].midline + '\n') 26 | outStream.write(alignment.hsps[j].hseq + '\n') 27 | outStream.write('\n\n') 28 | } 29 | 30 | outStream.write('\n\n=====================\n\n') 31 | } 32 | 33 | if (outFile !== '-') { 34 | outStream.end() 35 | console.log('Finished') 36 | } 37 | -------------------------------------------------------------------------------- /lib/statusCheck.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const https = require('https') 4 | const querystring = require('querystring') 5 | const split = require('split2') 6 | 7 | const { BASE_URL } = require('./constants.js') 8 | 9 | module.exports = (RID) => new Promise((resolve, reject) => { 10 | // TODO verify params against schema 11 | const url = BASE_URL + '?' + querystring.stringify({ 12 | CMD: 'Get', 13 | RID, 14 | FORMAT_OBJECT: 'SearchInfo' 15 | }) 16 | 17 | https.get(url, (res) => { 18 | res.setEncoding('utf8') 19 | 20 | let hits = false 21 | 22 | res.pipe(split()).on('data', function (line) { 23 | if (line.match(/\s+ThereAreHits=yes/)) { 24 | hits = true 25 | } 26 | 27 | if (line.match(/\s+Status=WAITING/)) { 28 | resolve({ message: 'Waiting', done: false }) 29 | } else if (line.match(/\s+Status=FAILED/)) { 30 | reject(new Error('Search failed')) 31 | } else if (line.match(/\s+Status=UNKOWN/)) { 32 | reject(new Error('RID Expired')) 33 | } else if (line.match(/\s+Status=READY/)) { 34 | // TODO do not assume there are hits 35 | resolve({ message: 'Ready', done: true }) 36 | } 37 | }) 38 | }).on('error', reject) 39 | }) 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bionode-blast 2 | 3 | NodeJS API wrapping the NCBI BLAST [Common URL API](https://ncbi.github.io/blast-cloud/dev/api.html). 4 | WIP. 5 | 6 | Goals are to 7 | - provide a (typed) JS API that makes it simpler to work NCBI BLAST 8 | - probably Flow since it makes clean JS after flow-remove-types 9 | - question: how to support Flow and TS type libraries/definitions? 10 | - interporates well with other bionode modules: streams compatible (e.g. object stream of matches) 11 | - CLI API 12 | - express REST server wrapping JS API to provide a truly REST interface 13 | - once finalized, illustrate interoperability with webapp/Electron/BioJS visualization modules 14 | - e.g. Electron BLAST app that automatically stores results locally 15 | 16 | TODO 17 | - [ ] Only allow some params conditonal on other params 18 | - e.g. description only allowed when report is Text/HTML 19 | - [ ] Param validation 20 | - [ ] Param description 21 | - [ ] Param defaults 22 | - [ ] Param defaults conditional on other params 23 | - e.g. if program is blastp or blastn, change word size 24 | - [ ] Configurable param defaults 25 | - [ ] Response schema 26 | - [ ] Typed params 27 | - [ ] Typed response 28 | - [ ] RAML/Swagger spec REST API, local wrapper server 29 | -------------------------------------------------------------------------------- /lib/retrieve.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const https = require('https') 4 | const querystring = require('querystring') 5 | 6 | const jszip = require('jszip') 7 | const concat = require('concat-stream') 8 | const streamifier = require('streamifier') 9 | 10 | const { BASE_URL } = require('./constants.js') 11 | 12 | const get = (url) => new Promise((resolve, reject) => https.get(url, resolve).on('error', reject)) 13 | 14 | const getZip = (url) => new Promise((resolve, reject) => 15 | get(url) 16 | .then((res) => res.pipe(concat(resolve))) 17 | .catch(reject) 18 | ) 19 | 20 | const zipToJSON = (RID) => (zipBuffer) => jszip.loadAsync(zipBuffer) 21 | .then(zip => zip.file(`${RID}_1.json`).async('nodebuffer')) 22 | 23 | // Resolves to a stream 24 | module.exports = (RID, params = {}) => new Promise((resolve, reject) => { 25 | params = Object.assign({}, { 26 | FORMAT_TYPE: 'Text' // JSON2 27 | }, params) 28 | 29 | // TODO validate params against schema 30 | 31 | const url = BASE_URL + '?' + querystring.stringify(Object.assign({}, { 32 | CMD: 'Get', 33 | RID 34 | }, params)) 35 | 36 | if (params.FORMAT_TYPE === 'JSON2') { 37 | getZip(url) 38 | .then(zipToJSON(RID)) 39 | .then((results) => resolve(streamifier.createReadStream(results))) 40 | .catch(reject) 41 | } else if (params.FORMAT_TYPE === 'Text') { 42 | get(url).then(resolve).catch(reject) 43 | } 44 | }) 45 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | 5 | // const submit = require('./lib/submit.js') 6 | // const pollUntil = require('./lib/pollUntil.js') 7 | const retrieve = require('./lib/retrieve.js') 8 | 9 | const RID = process.argv[2] 10 | const outfile = process.argv[3] 11 | 12 | const getOutstream = () => (outfile === '-' || outfile === undefined) ? process.stdout : fs.createWriteStream(outfile) 13 | 14 | // submit({ 15 | // QUERY: '>tr|F4IWH1|F4IWH1_ARATH Uncharacterized protein OS=Arabidopsis thaliana GN=At3g27350 PE=4 SV=1\nMGESAVLVHSYSFAAPITRNDSHEENTIHALSQSISFGKFMTENLEWGKWSSFSHKKYVE EAEKYSRPGSVAQKKAFFEAHYKRIAEAKKAATEEQPSVTPAEVLLHTLETQPPPPPPPL VLKYGEEGRERNSFQIDDHDVTDELENVMFGGDYVKEEEEKKVEEELLKEDWSVGEKEKQ HRKSVTKNRPVFRLSLEKTIPPKSLDEISLTEKRSERPMTQVEEKPVHRQRFGLLSCFIS NAKTQDQNQSRNKRKTEKKKQFLCLCLKPKTIREWHRLCSIHTSLEKSPSAIGISFFCLV LFESLLSVLLCNERTTPVS', 16 | // DATABASE: 'nr', 17 | // PROGRAM: 'blastp', 18 | // FORMAT_TYPE: 'Text', 19 | // EXPECT: 1e-20, 20 | // GAPCOSTS: '11 1', 21 | // MATRIX: 'BLOSUM62', 22 | // HITLIST_SIZE: 250, 23 | // DESCRIPTIONS: 250, 24 | // ALIGNMENTS: 50, 25 | // WORD_SIZE: 6, 26 | // COMPOSITION_BASED_STATISTICS: 2 27 | // }) 28 | // .then(({ RID }) => pollUntil(RID)) 29 | // .then((RID) => console.log(`RID ${RID} is ready for fetching alignments`)) 30 | // .catch(console.error) 31 | 32 | retrieve(RID, { FORMAT_TYPE: 'JSON2' }) 33 | .then((res) => res.pipe(getOutstream())) 34 | .catch(console.error) 35 | -------------------------------------------------------------------------------- /lib/submit.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const split = require('split2') 4 | const { validate } = require('jsonschema') 5 | 6 | const https = require('https') 7 | const querystring = require('querystring') 8 | 9 | const { HOSTNAME, PATH } = require('./constants.js') 10 | const { putSchema } = require('./schemas.js') 11 | 12 | module.exports = (params) => new Promise((resolve, reject) => { 13 | try { 14 | const validation = validate(params, putSchema) 15 | 16 | if (validation.errors.length !== 0) { 17 | validation.errors.forEach(console.error) 18 | return reject(validation.errors) 19 | } 20 | } catch (e) { 21 | return reject(e) 22 | } 23 | 24 | const postData = querystring.stringify(Object.assign({}, params, { CMD: 'Put' })) 25 | 26 | const options = { 27 | hostname: HOSTNAME, 28 | path: PATH, 29 | method: 'POST', 30 | headers: { 31 | 'Content-Type': 'application/x-www-form-urlencoded', 32 | 'Content-Length': Buffer.byteLength(postData) 33 | } 34 | } 35 | 36 | const req = https.request(options, (res) => { 37 | res.setEncoding('utf8') 38 | 39 | /* We are looking for: 40 | 45 | */ 46 | 47 | let RID 48 | let RTOE 49 | 50 | res.pipe(split()) 51 | .on('data', function (line) { 52 | const RIDMatch = line.match(/^ {4}RID = (.+)$/) 53 | if (RIDMatch) { 54 | RID = RIDMatch[1] 55 | } 56 | 57 | const RTOEMatch = line.match(/^ {4}RTOE = (.+)$/) 58 | if (RTOEMatch) { 59 | RTOE = RTOEMatch[1] 60 | } 61 | }) 62 | 63 | res.on('end', () => resolve({ RID, RTOE })) 64 | }) 65 | 66 | req.on('error', reject) 67 | 68 | req.write(postData) 69 | req.end() 70 | }) 71 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at mail@bionode.io. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /lib/schemas.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const common = { 4 | definitions: { 5 | RID: { 6 | type: 'string', 7 | description: 'Request ID' 8 | }, 9 | ALIGNMENTS: { 10 | type: 'integer', 11 | minimum: 1, 12 | description: 'Number of alignments to print (for Text,HTML)' 13 | }, 14 | HITLIST_SIZE: { 15 | type: 'integer', 16 | minimum: 1, 17 | description: 'Number of database sequences to keep' 18 | }, 19 | DESCRIPTIONS: { 20 | type: 'integer', 21 | minimum: 1, 22 | description: 'Number of descriptions to print (for Text,HTML)' 23 | }, 24 | FORMAT_TYPE: { 25 | type: 'string', 26 | enum: [ 'HTML', 'Text', 'XML', 'XML2', 'JSON2', 'Tabular' ], 27 | description: 'Report type' 28 | }, 29 | NCBI_GI: { 30 | type: 'string', 31 | enum: [ 'T', 'F' ], 32 | description: 'Show NCBI GIs in report' 33 | } 34 | } 35 | } 36 | 37 | // For CMD=Delete 38 | exports.deleteSchema = Object.assign({}, common, { 39 | type: 'object', 40 | properties: { 41 | RID: { '$ref': '#/definitions/RID' } 42 | }, 43 | required: [ 'RID' ] 44 | }) 45 | 46 | // For CMD=Get 47 | exports.getSchema = Object.assign({}, common, { 48 | type: 'object', 49 | properties: { 50 | RID: { '$ref': '#/definitions/RID' }, 51 | FORMAT_TYPE: { '$ref': '#/definitions/FORMAT_TYPE' }, 52 | HITLIST_SIZE: { '$ref': '#/definitions/HITLIST_SIZE' }, 53 | DESCRIPTIONS: { '$ref': '#/definitions/DESCRIPTIONS' }, 54 | ALIGNMENTS: { '$ref': '#/definitions/ALIGNMENTS' }, 55 | NCBI_GI: { '$ref': '#/definitions/NCBI_GI' }, 56 | FORMAT_OBJECT: { 57 | type: 'string', 58 | enum: [ 'SearchInfo', 'Alignment' ], 59 | description: 'SearchInfo (status check) or Alignment (report formatting)' 60 | } 61 | }, 62 | required: [ 'RID' ] 63 | }) 64 | 65 | // For CMD=Put 66 | exports.putSchema = Object.assign({}, common, { 67 | type: 'object', 68 | properties: { 69 | QUERY: { 70 | type: 'string', 71 | description: 'Accession, GI, or FASTA' 72 | }, 73 | DATABASE: { 74 | type: 'string', 75 | enum: [ 'nr', 'refseq_protein', 'landmark', 'swissprot', 'pat', 'pdb', 'env_nr', 'tsa_nr' ], 76 | description: 'Search database' 77 | }, 78 | PROGRAM: { 79 | type: 'string', 80 | enum: [ 'blastn', 'blastp', 'blastx', 'tblastn', 'tblastx', 'megablast' ], 81 | description: 'Program' 82 | }, 83 | FILTER: { 84 | type: 'string', 85 | enum: [ 'F', 'T', 'L', 'mT', 'mL' ], 86 | description: 'Low complexity filtering' 87 | }, 88 | FORMAT_TYPE: { '$ref': '#/definitions/FORMAT_TYPE' }, 89 | EXPECT: { 90 | type: 'number', 91 | minimum: 0, 92 | description: 'Expect value' 93 | }, 94 | NUCL_REWARD: { 95 | type: 'integer', 96 | minimum: 0, 97 | description: 'Reward for matching bases (blastn and megablast)' 98 | }, 99 | NUCL_PENALTY: { 100 | type: 'integer', 101 | minimum: 0, 102 | description: 'Cost for mismatched bases (blastn and megablast)' 103 | }, 104 | GAPCOSTS: { 105 | type: 'string', 106 | description: 'Gap existence and extension costs; Pair of positive integers separate by a space, e.g. "11 1"' 107 | }, 108 | MATRIX: { 109 | type: 'string', 110 | enum: [ 'PAM30', 'PAM70', 'PAM250', 'BLOSUM45', 'BLOSUM50', 'BLOSUM62', 'BLOSUM80', 'BLOSUM90' ] 111 | }, 112 | HITLIST_SIZE: { '$ref': '#/definitions/HITLIST_SIZE' }, 113 | DESCRIPTIONS: { '$ref': '#/definitions/DESCRIPTIONS' }, 114 | ALIGNMENTS: { '$ref': '#/definitions/ALIGNMENTS' }, 115 | NCBI_GI: { '$ref': '#/definitions/NCBI_GI' }, 116 | THRESHOLD: { 117 | type: 'integer', 118 | minimum: 0, 119 | description: 'Neighboring score for initial words' 120 | }, 121 | WORD_SIZE: { 122 | type: 'integer', 123 | minimum: 0, 124 | description: 'Size of word for initial matches' 125 | }, 126 | COMPOSITION_BASED_STATISTICS: { 127 | type: 'integer', 128 | enum: [ 0, 1, 2, 3 ], 129 | description: ` 130 | 0: No adjustment; 131 | 1: Composition-based statistics; 132 | 2: Conditional compositional score matrix adjustment; 133 | 3: Universal compositional score matrix adjustment; 134 | See comp_based_stats in BLAST manual 135 | ` 136 | }, 137 | NUM_THREADS: { 138 | type: 'integer', 139 | minimum: 1, 140 | description: 'Supported only on the cloud' 141 | } 142 | }, 143 | required: [ 'QUERY', 'DATABASE', 'PROGRAM' ] 144 | }) 145 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | buffer-shims@^1.0.0: 6 | version "1.0.0" 7 | resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" 8 | 9 | concat-stream@^1.6.0: 10 | version "1.6.0" 11 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" 12 | dependencies: 13 | inherits "^2.0.3" 14 | readable-stream "^2.2.2" 15 | typedarray "^0.0.6" 16 | 17 | core-js@~2.3.0: 18 | version "2.3.0" 19 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65" 20 | 21 | core-util-is@~1.0.0: 22 | version "1.0.2" 23 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 24 | 25 | es6-promise@~3.0.2: 26 | version "3.0.2" 27 | resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6" 28 | 29 | immediate@~3.0.5: 30 | version "3.0.6" 31 | resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" 32 | 33 | inherits@^2.0.3, inherits@~2.0.1: 34 | version "2.0.3" 35 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 36 | 37 | isarray@~1.0.0: 38 | version "1.0.0" 39 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 40 | 41 | jsonschema@^1.1.1: 42 | version "1.1.1" 43 | resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.1.1.tgz#3cede8e3e411d377872eefbc9fdf26383cbc3ed9" 44 | 45 | jszip@^3.1.3: 46 | version "3.1.3" 47 | resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.1.3.tgz#8a920403b2b1651c0fc126be90192d9080957c37" 48 | dependencies: 49 | core-js "~2.3.0" 50 | es6-promise "~3.0.2" 51 | lie "~3.1.0" 52 | pako "~1.0.2" 53 | readable-stream "~2.0.6" 54 | 55 | lie@~3.1.0: 56 | version "3.1.1" 57 | resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" 58 | dependencies: 59 | immediate "~3.0.5" 60 | 61 | pako@~1.0.2: 62 | version "1.0.4" 63 | resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.4.tgz#412cc97c3b7ff06dc6c2557fd4f03d06f5e708d4" 64 | 65 | process-nextick-args@~1.0.6: 66 | version "1.0.7" 67 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" 68 | 69 | readable-stream@^2.1.5, readable-stream@^2.2.2: 70 | version "2.2.2" 71 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e" 72 | dependencies: 73 | buffer-shims "^1.0.0" 74 | core-util-is "~1.0.0" 75 | inherits "~2.0.1" 76 | isarray "~1.0.0" 77 | process-nextick-args "~1.0.6" 78 | string_decoder "~0.10.x" 79 | util-deprecate "~1.0.1" 80 | 81 | readable-stream@~2.0.6: 82 | version "2.0.6" 83 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" 84 | dependencies: 85 | core-util-is "~1.0.0" 86 | inherits "~2.0.1" 87 | isarray "~1.0.0" 88 | process-nextick-args "~1.0.6" 89 | string_decoder "~0.10.x" 90 | util-deprecate "~1.0.1" 91 | 92 | split2@^2.1.1: 93 | version "2.1.1" 94 | resolved "https://registry.yarnpkg.com/split2/-/split2-2.1.1.tgz#7a1f551e176a90ecd3345f7246a0cfe175ef4fd0" 95 | dependencies: 96 | through2 "^2.0.2" 97 | 98 | streamifier@^0.1.1: 99 | version "0.1.1" 100 | resolved "https://registry.yarnpkg.com/streamifier/-/streamifier-0.1.1.tgz#97e98d8fa4d105d62a2691d1dc07e820db8dfc4f" 101 | 102 | string_decoder@~0.10.x: 103 | version "0.10.31" 104 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" 105 | 106 | through2@^2.0.2: 107 | version "2.0.3" 108 | resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" 109 | dependencies: 110 | readable-stream "^2.1.5" 111 | xtend "~4.0.1" 112 | 113 | typedarray@^0.0.6: 114 | version "0.0.6" 115 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 116 | 117 | util-deprecate@~1.0.1: 118 | version "1.0.2" 119 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 120 | 121 | xtend@~4.0.1: 122 | version "4.0.1" 123 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" 124 | --------------------------------------------------------------------------------