├── .gitignore ├── index.js ├── .travis.yml ├── .gitattributes ├── bin └── nomics-platform.js ├── .eslintrc.json ├── test.js ├── lib ├── cli.js └── audit │ ├── index.js │ ├── au.js │ └── test │ └── audit.js ├── README.md ├── package.json ├── doc ├── simple-ticker-integration.md └── cryptocurrency-api-exchange-integration.md ├── example └── index.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | CLI: require('./lib/cli') 3 | } 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | - "10" 5 | - "8" 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=eol=lf 3 | -------------------------------------------------------------------------------- /bin/nomics-platform.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../').CLI(process.argv.slice(2)) 3 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "es6": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "parserOptions": { 8 | "ecmaVersion": 2018 9 | }, 10 | "rules": { 11 | "valid-typeof": "off" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const audit = require('./lib/audit') 2 | const Server = require('./example'); 3 | 4 | (async function test() { 5 | const port = process.env.PORT || '3000' 6 | const instance = Server().listen(port) 7 | await audit([`http://localhost:${port}`]) 8 | instance.close() 9 | })() 10 | -------------------------------------------------------------------------------- /lib/cli.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: "off" */ 2 | 3 | var audit = require('./audit') 4 | 5 | module.exports = function (argv) { 6 | if (argv.length < 1) { 7 | usage() 8 | process.exit(1) 9 | } 10 | 11 | var command = argv[0] 12 | var options = argv.slice(1) 13 | 14 | switch (command) { 15 | case 'audit': return audit(options) 16 | default: 17 | usage() 18 | process.exit(1) 19 | } 20 | } 21 | 22 | function usage() { 23 | console.log([ 24 | 'Usage: nomics-platform [command] [flags]', 25 | 'Commands:', 26 | '\taudit url' 27 | ].join('\n\t')) 28 | } 29 | -------------------------------------------------------------------------------- /lib/audit/index.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: "off" */ 2 | 3 | const teenytest = require('teenytest') 4 | 5 | module.exports = async function (options) { 6 | if (options.length < 1) { 7 | console.log('audit requires one argument: a url to and endpoint to audit.') 8 | process.exit(1) 9 | } 10 | try { 11 | await auditURL(options[0]) 12 | } catch (err) { 13 | console.log(err) 14 | process.exit(1) 15 | } 16 | } 17 | 18 | function auditURL(u) { 19 | return new Promise((resolve, reject) => { 20 | process.chdir(__dirname) 21 | process.env.NOMICS_PLATFORM_URL_ROOT = u 22 | teenytest('test/*.js', { plugins: ['teenytest-promise'], asyncTimeout: 30000 }, (err, passing) => { 23 | if (err || !passing) { 24 | reject(new Error('Audit Failed')) 25 | } else { 26 | resolve() 27 | } 28 | }) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nomics Platform 2 | 3 | [![Build Status](https://travis-ci.org/nomics-crypto/nomics-platform.svg?branch=master)](https://travis-ci.org/nomics-crypto/nomics-platform) 4 | 5 | This repository contains Nomics Integration Specifications as well as an auditing tool to verify compliance with the specification. 6 | 7 | ## Auditing 8 | 9 | To audit your endpoint, install Node.js and run: 10 | 11 | ``` 12 | npx nomics-platform@latest audit https://path-to-your-api-root 13 | ``` 14 | 15 | If data functionality needs to be audited prior to all metadata being available, you can use the `NOMICS_PLATFORM_RELAX` 16 | environment variable to temporarily relax requirements for description length, logo URL, and location. This flag will 17 | also skip checks for markets `type` and `active` flags. 18 | 19 | ```bash 20 | NOMICS_PLATFORM_RELAX=1 npx nomics-platform@latest audit https://path-to-your-api-root 21 | ``` 22 | 23 | ## Nomics Integration Specifications 24 | 25 | - [Simple Ticker Integration Specification](doc/simple-ticker-integration.md) 26 | - [Cryptocurrency API Exchange Integration Specification](doc/cryptocurrency-api-exchange-integration.md) 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nomics-platform", 3 | "version": "1.0.1", 4 | "description": "Nomics Platform Toolkit", 5 | "keywords": [ 6 | "cryptocurrency", 7 | "cryptoasset", 8 | "api", 9 | "nomics", 10 | "bitcoin", 11 | "ethereum" 12 | ], 13 | "files": [ 14 | "index.js", 15 | "lib/**/*.js", 16 | "bin/**/*.js" 17 | ], 18 | "bin": { 19 | "nomics-platform": "bin/nomics-platform.js" 20 | }, 21 | "main": "index.js", 22 | "author": "Nick Gauthier ", 23 | "license": "MIT", 24 | "repository": { 25 | "url": "https://github.com/nomics-crypto/nomics-platform-node", 26 | "type": "git" 27 | }, 28 | "scripts": { 29 | "cli": "node bin/nomics-platform.js", 30 | "lint": "eslint **/*.js", 31 | "test": "node test.js" 32 | }, 33 | "devDependencies": { 34 | "eslint": "^5.0.1", 35 | "eslint-config-standard": "^12.0.0", 36 | "eslint-plugin-import": "^2.13.0", 37 | "eslint-plugin-node": "^8.0.0", 38 | "eslint-plugin-promise": "^4.0.1", 39 | "eslint-plugin-standard": "^4.0.0", 40 | "express": "^4.16.3" 41 | }, 42 | "dependencies": { 43 | "axios": "^0.21.1", 44 | "teenytest": "^5.1.1", 45 | "teenytest-promise": "^1.0.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /doc/simple-ticker-integration.md: -------------------------------------------------------------------------------- 1 | # Nomics Simple Ticker Exchange Integration Specification 2 | 3 | This document describes the simplest method for integrating your exchange with Nomics. Note that implementing the simple ticker integration will result in a D Transparency Grade. If you are interested in a higher transparency grade please see [Cryptocurrency API Exchange Integration Specification](cryptocurrency-api-exchange-integration.md). 4 | 5 | ## Ticker Specification 6 | 7 | Your ticker can be at any valid URL that responds to an HTTP GET request and returns a JSON response. 8 | 9 | **Example:** https://example.com/api/nomics/ticker 10 | 11 | ### Response 12 | 13 | The response must be a JSON array of objects, with each object representing a market on your exchange. The object must contain all of the following fields: 14 | 15 | **Required:** 16 | 17 | - `market`: **String**. A unique identifier for this market. 18 | - `base`: **String**. The symbol of the base currency of the market. 19 | - `quote`: **String**. The symbol of the quote currency of the market. 20 | - `price_quote`: **String**. The current price of the base currency of the market given in the units of the quote currency. 21 | - `volume_base`: **String**. The total volume over the past 24 hours given in the units of the base currency. 22 | 23 | **Example:** 24 | 25 | ```json 26 | [ 27 | { 28 | "market": "BTC-USDT", 29 | "base": "BTC", 30 | "quote": "USDT", 31 | "price_quote": "20936.64", 32 | "volume_base": "365410.92" 33 | }, 34 | { 35 | "market": "ETH-EUR", 36 | "base": "ETH", 37 | "quote": "EUR", 38 | "price_quote": "1207.88", 39 | "volume_base": "197310.22" 40 | }, 41 | { 42 | "market": "ETH-BTC", 43 | "base": "ETH", 44 | "quote": "BTC", 45 | "price_quote": "0.058510", 46 | "volume_base": "98213.80" 47 | } 48 | ] 49 | ``` 50 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | 3 | const MINUTE = 1000 * 60 4 | const HOUR = MINUTE * 60 5 | const DAY = HOUR * 24 6 | 7 | function Server() { 8 | const app = express() 9 | 10 | app.get('/', status) 11 | app.get('/info', info) 12 | app.get('/markets', markets) 13 | app.get('/trades', trades) 14 | app.get('/trades-by-timestamp', tradesByTimestamp) 15 | app.get('/trades/snapshot', tradesSnapshot) 16 | app.get('/orders/snapshot', ordersSnapshot) 17 | app.get('/candles', candles) 18 | app.get('/ticker', ticker) 19 | 20 | return app 21 | } 22 | 23 | function status(_, res) { 24 | res.send('OK') 25 | } 26 | 27 | function info(_, res) { 28 | res.send({ 29 | description: 30 | 'Example Exchange is an example of an exchange integration for Nomics.com. Example Exchange is an example of an exchange integration for Nomics.com. Example Exchange is an example of an exchange integration for Nomics.com. Example Exchange is an example of an exchange integration for Nomics.com. Example Exchange is an example of an exchange integration for Nomics.com. Example Exchange is an example of an exchange integration for Nomics.com. Example Exchange is an example of an exchange integration for Nomics.com. Example Exchange is an example of an exchange integration for Nomics.com. Example Exchange is an example of an exchange integration for Nomics.com. Example Exchange is an example of an exchange integration for Nomics.com. Example Exchange is an example of an exchange integration for Nomics.com. Example Exchange is an example of an exchange integration for Nomics.com. Example Exchange is an example of an exchange integration for Nomics.com. Example Exchange is an example of an exchange integration for Nomics.com.', 31 | name: 'Example', 32 | location: 'Example', 33 | logo: 'https://nomics.com/logo.png', 34 | twitter: 'nomicsfinance', 35 | website: 'https://nomics.com', 36 | version: "1.0", 37 | capability: { 38 | markets: true, 39 | trades: true, 40 | tradesByTimestamp: true, 41 | tradesSnapshot: true, 42 | tradesSocket: false, 43 | orders: false, 44 | ordersSocket: false, 45 | ordersSnapshot: true, 46 | candles: true, 47 | ticker: true 48 | } 49 | }) 50 | } 51 | 52 | function markets(_, res) { 53 | res.send([ 54 | { 55 | id: 'btc-usd', 56 | type: 'derivative', 57 | base: 'BTC', 58 | quote: 'USD', 59 | subtypes: ['perpetual', 'future'], 60 | active: true, 61 | }, 62 | ]); 63 | } 64 | 65 | const allTrades = [ 66 | { 67 | id: '1', 68 | timestamp: '2006-01-02T15:04:05.999Z', 69 | price: '100.00', 70 | amount: '10.00', 71 | order: '1', 72 | type: 'market', 73 | side: 'buy', 74 | raw: [1, 1136214245, 100.0, 10.0, '1', 'm', 'b'] 75 | }, 76 | { 77 | id: '2', 78 | timestamp: '2006-01-02T15:14:05.999Z', 79 | price: '98.00', 80 | amount: '1.00', 81 | order: '3', 82 | type: 'market', 83 | side: 'sell', 84 | raw: [2, 1136214255, 98.0, 1.0, '3', 'm', 's'] 85 | }, 86 | { 87 | id: '3', 88 | timestamp: '2006-01-02T15:24:05.999Z', 89 | price: '101.37', 90 | amount: '3.50', 91 | order: '5', 92 | type: 'limit', 93 | side: 'buy', 94 | raw: [3, 1136214265, 101.37, 3.5, '5', 'l', 'b'] 95 | } 96 | ] 97 | 98 | function trades(req, res) { 99 | if (req.query.market !== 'btc-usd') { 100 | res.status(404).send({ error: 'unknown market' }) 101 | return 102 | } 103 | let since = parseInt(req.query.since) 104 | if (isNaN(since)) { 105 | since = 0 106 | } 107 | res.send(allTrades.filter(t => parseInt(t.id) > since)) 108 | } 109 | 110 | function tradesSnapshot(req, res) { 111 | if (req.query.market !== 'btc-usd') { 112 | res.status(404).send({ error: 'unknown market' }) 113 | return 114 | } 115 | 116 | res.send(allTrades) 117 | } 118 | 119 | function tradesByTimestamp(req, res) { 120 | if (req.query.market !== 'btc-usd') { 121 | res.status(404).send({ error: 'unknown market' }) 122 | return 123 | } 124 | let since 125 | if (req.query.since) { 126 | since = new Date(req.query.since) 127 | } else { 128 | since = new Date(0) 129 | } 130 | res.send(allTrades.filter(t => new Date(t.timestamp).getTime() > since.getTime())) 131 | } 132 | 133 | function ordersSnapshot(req, res) { 134 | if (req.query.market !== 'btc-usd') { 135 | res.status(404).send({ error: 'unknown market' }) 136 | return 137 | } 138 | res.send({ 139 | bids: [[5000.0, 1.0], [4900.0, 10.0]], 140 | asks: [[5100.0, 5.0], [5150.0, 10.0]], 141 | timestamp: new Date() 142 | }) 143 | } 144 | 145 | function candles(req, res) { 146 | const interval = req.query.interval 147 | 148 | if (req.query.market !== 'btc-usd') { 149 | res.status(404).send({ error: 'unknown market' }) 150 | return 151 | } 152 | 153 | if (!['1d', '1h', '1m'].includes(interval)) { 154 | res.status(404).send({ error: 'unknown interval' }) 155 | return 156 | } 157 | 158 | let count 159 | let dInterval 160 | let result = [] 161 | 162 | if (interval === '1d') { 163 | count = 7 164 | dInterval = DAY 165 | } else if (interval === '1h') { 166 | count = 24 167 | dInterval = HOUR 168 | } else { 169 | count = 60 170 | dInterval = MINUTE 171 | } 172 | 173 | for (let i = 0; i < count; i++) { 174 | let now = Date.now() 175 | let timestamp = new Date(Math.floor((now - dInterval) / dInterval) * dInterval) 176 | 177 | result[i] = { 178 | timestamp, 179 | open: '4002.8', 180 | high: '4119.98', 181 | low: '3741.95', 182 | close: '4102.8', 183 | volume: '19040.84' 184 | } 185 | } 186 | 187 | res.send(result.sort((a, b) => (a.timestamp < b.timestamp ? -1 : 1))) 188 | } 189 | 190 | function ticker(req, res) { 191 | if (req.query.market !== 'btc-usd') { 192 | res.status(404).send({ error: 'unknown market' }) 193 | return 194 | } 195 | 196 | const trade = allTrades[allTrades.length - 1] 197 | res.send({ 198 | close: trade.price, 199 | volume: trade.amount, 200 | timestamp: trade.timestamp, 201 | raw: trade 202 | }) 203 | } 204 | 205 | if (require.main === module) { 206 | const instance = Server().listen(process.env.PORT || '3000') 207 | process.on('SIGINT', () => instance.close()) 208 | process.on('SIGTERM', () => instance.close()) 209 | } 210 | 211 | module.exports = Server 212 | -------------------------------------------------------------------------------- /lib/audit/au.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | const url = require('url') 3 | 4 | const au = { 5 | // Error is an error that shows the url when available instead of a stack trace 6 | Error: class { 7 | constructor(message, url = null, status = null) { 8 | this.name = 'AuditError' 9 | this.message = message 10 | this.stack = url 11 | this.status = status 12 | } 13 | 14 | toString() { 15 | return this.message 16 | } 17 | }, 18 | 19 | // get performs a get request and returns the data (or throws an error on failure). It also sets 20 | // `this.url` to the full URL fetched so that assertions can use it for error messages. 21 | get: async function (path) { 22 | this.url = process.env.NOMICS_PLATFORM_URL_ROOT + path 23 | try { 24 | return (await axios.get(this.url)).data 25 | } catch (err) { 26 | throw new au.Error(err.toString(), this.url, err.response.status) 27 | } 28 | }, 29 | 30 | // assert is like node's assert but it throws an audit error with a url instead of a stack trace 31 | assert: function (b, msg, url = this.url) { 32 | if (!b) { 33 | throw new au.Error(msg, url) 34 | } 35 | }, 36 | 37 | // assertProperty asserts that the given object `o` contains the given key `p`. `opts` can be provided with key 38 | // `required` to indicate that it shouldn't fail if the property isn't present. It returns true if the property 39 | // was present and false if it was not found. It can be used in coordination with other property assertions to 40 | // only validate a property when present, e.g.: 41 | // if (assertProperty(obj, "hello", {required: false})) { 42 | // assertStringProperty(obj, "hello") 43 | // } 44 | // This says "hello must be a string if present" 45 | assertProperty: function (o, p, opts = {}) { 46 | opts = Object.assign({ required: true }, opts) 47 | if (!o.hasOwnProperty(p)) { 48 | if (!opts.required) { 49 | return false 50 | } 51 | throw new au.Error(`Expected '${p}' key: ${JSON.stringify(o)}`, this.url) 52 | } 53 | return true 54 | }, 55 | 56 | // assertPropertyType asserts that property `p` on object `o` is of type `t` 57 | assertPropertyType: function (o, p, t) { 58 | let type = typeof o[p] 59 | if (t === 'array' && Array.isArray(o[p])) { 60 | type = 'array' 61 | } 62 | au.assert(type === t, `Expected '${p}' to be a ${t} but was ${typeof o[p]}: ${JSON.stringify(o)}`) 63 | }, 64 | 65 | // assertPropertyNotEmpty asserts that property `p` of object `o` has positive length 66 | assertPropertyNotEmpty: function (o, p) { 67 | au.assert(o[p].length > 0, `Expected '${p}' not to be empty: ${JSON.stringify(o)}`) 68 | }, 69 | 70 | // assertStringProperty asserts that property `p` of object `o` is a non empty string if required 71 | assertStringProperty: function (o, p, opts = {}) { 72 | if (this.assertProperty(o, p, opts)) { 73 | this.assertPropertyType(o, p, 'string') 74 | this.assertPropertyNotEmpty(o, p) 75 | } 76 | }, 77 | 78 | // assertBooleanProperty asserts that property `p` of object `o` is a boolean if required 79 | assertBooleanProperty: function (o, p, opts = {}) { 80 | if (this.assertProperty(o, p, opts)) { 81 | this.assertPropertyType(o, p, 'boolean') 82 | } 83 | }, 84 | 85 | // assertURLProperty asserts that property `p` of object `o` is a valid URL 86 | assertURLProperty: function (o, p, opts = {}) { 87 | opts = Object.assign({ required: true }, opts) 88 | if (this.assertProperty(o, p, opts)) { 89 | this.assertPropertyType(o, p, 'string') 90 | 91 | if (opts.https) { 92 | au.assert( 93 | new url.URL(o[p]).protocol === 'https:', 94 | `Expected '${p}' to be a valid HTTPS URL: ${JSON.stringify(o)}` 95 | ) 96 | } else { 97 | au.assert(new url.URL(o[p]).protocol, `Expected '${p}' to be a valid URL: ${JSON.stringify(o)}`) 98 | } 99 | } 100 | }, 101 | 102 | // assertTimestampProperty asserts that property `p` of object `o` is a valid RFC3339 UTC timestamp 103 | assertTimestampProperty: function (o, p, opts = {}) { 104 | opts = Object.assign({ required: true }, opts) 105 | if (this.assertProperty(o, p, opts)) { 106 | this.assertPropertyType(o, p, 'string') 107 | let valid = false 108 | let validUTC 109 | let err = '' 110 | const d = new Date(o[p]) 111 | try { 112 | valid = new Date(d.toISOString()).getTime() === d.getTime() 113 | validUTC = valid && o[p].slice(-1) === 'Z' 114 | } catch (e) { 115 | err = e.toString() + ' ' 116 | } 117 | au.assert(valid, `Expected '${p}' to be a valid RFC3339 Timestamp: ${err}${JSON.stringify(o)}`) 118 | au.assert(validUTC, `Expected '${p}' to be a valid UTC Timestamp ending in 'Z': ${err}${JSON.stringify(o)}`) 119 | } 120 | }, 121 | 122 | // assertNumericStringProperty asserts that property `p` of object `o` is a string that is parseable to a number 123 | assertNumericStringProperty: function (o, p, opts = {}) { 124 | opts = Object.assign({ required: true }, opts) 125 | if (this.assertProperty(o, p, opts)) { 126 | au.assertStringProperty(o, p, opts) 127 | const n = parseFloat(o[p]) 128 | au.assert(typeof n === 'number' && !isNaN(n), `Expected '${p}' to be a numeric string: ${JSON.stringify(o)}`) 129 | } 130 | }, 131 | 132 | // assertPropertyInSet asserts that property `p` of object `o` is a value in the array `s` 133 | assertPropertyInSet: function (o, p, s, opts = {}) { 134 | opts = Object.assign({ required: true }, opts) 135 | if (au.assertProperty(o, p, opts)) { 136 | if (o[p] instanceof Array) { 137 | au.assert( 138 | o[p].every(val => s.includes(val)), 139 | `Expected ${JSON.stringify( 140 | o[p] 141 | )} to only contain values from ${JSON.stringify(s)}: ${JSON.stringify( 142 | o 143 | )}` 144 | ); 145 | } else { 146 | au.assert(s.indexOf(o[p]) > -1, `Expected '${p}' to be one of '${JSON.stringify(s)}': ${JSON.stringify(o)}`) 147 | } 148 | } 149 | }, 150 | 151 | // assertArrayProperty asserts that property `p` of object `o` is a non empty array if required 152 | assertArrayProperty: function (o, p, opts = {}) { 153 | if (this.assertProperty(o, p, opts)) { 154 | this.assertPropertyType(o, p, 'array') 155 | this.assertPropertyNotEmpty(o, p) 156 | } 157 | } 158 | } 159 | 160 | module.exports = au 161 | -------------------------------------------------------------------------------- /doc/cryptocurrency-api-exchange-integration.md: -------------------------------------------------------------------------------- 1 | # Exchange Integration Specification 2 | 3 | The following section describes the API that an exchange must implement in order to integrate with the Nomics platform. From the root of your API, you must implement the following endpoints. 4 | 5 | ## What do I need to implement? 6 | 7 | There are many endpoints in this spec, and not all of them are required. They are marked with one of the following: 8 | 9 | - Required: This endpoint **must** be implemented in order for Nomics to integrate. 10 | - Preferred: This endpoint is the simplest and provides the highest quality data to Nomics. 11 | - Optional: While not required, this endpoint adds extra information or reduces load or latency. 12 | - Discouraged: This endpoint is present for maximum compatibility, but Preferred endpoints should be implemented whenever possible. 13 | 14 | ## How can I test my integration? 15 | 16 | To test your integration prior to submission, run the following auditing tool 17 | (requires NodeJS and NPM to be installed): 18 | 19 | 20 | ```bash 21 | npx nomics-platform@latest audit https://path-to-your-api-root 22 | ``` 23 | 24 | If data functionality needs to be audited prior to all metadata being available, 25 | you can use the `NOMICS_PLATFORM_RELAX` environment variable to temporarily relax 26 | requirements for description length, logo URL, and location. This flag will also 27 | skip checks for markets `type` and `active` flags. 28 | 29 | ```bash 30 | NOMICS_PLATFORM_RELAX=1 npx nomics-platform@latest audit https://path-to-your-api-root 31 | ``` 32 | 33 | ## How do I submit my integration? 34 | 35 | Fill out the [Nomics Exchange Listing Form](https://nomics.typeform.com/to/v1mprM) in its entirety. 36 | 37 | ## `/info` - Exchange Information - **Required** 38 | 39 | The `/info` endpoint returns information about the exchange as a whole, and is used by Nomics to display information about your exchange to users. 40 | 41 | ### Parameters 42 | 43 | None 44 | 45 | ### Response 46 | 47 | JSON object containing the following properties: 48 | 49 | **Required**: 50 | 51 | - `name`: The name of the exchange 52 | - `description`: An exchange description of at least 1000 characters in plain text (no html) 53 | - `location`: The primary country the exchange operates from 54 | - `logo`: A HTTPS URL to your exchange's logo. It should be an SVG with a square aspect ratio or a 500x500 PNG 55 | - `website`: A URL to your exchange 56 | - `twitter`: Twitter username to your exchange (without @) 57 | - `version`: The Nomics Platform Specification version 58 | - `capability`: An object describing the endpoints this integration implements. If not provided, false is assumed for all capabilities. It a capability is ommitted, it is assumed false. 59 | - `markets`: boolean indicating markets endpoint is implemented 60 | - `trades`: boolean indicating trades endpoint is implemented 61 | - `tradesByTimestamp`: boolean indicating trades by timestamp endpoint is implemented 62 | - `tradesSocket`: boolean indicating trades socket endpoint is implemented 63 | - `orders`: boolean indicating orders endpoint is implemented 64 | - `ordersSocket`: boolean indicating orders socket endpoint is implemented 65 | - `ordersSnapshot`: boolean indicating orders snapshot endpoint is implemented 66 | - `candles`: boolean indicating candles endpoint is implemented 67 | 68 | Example: 69 | 70 | ```json 71 | { 72 | "name": "Exchange Name", 73 | "description": "An exchange description of at least 1000 characters in plain text (no html)", 74 | "location": "Country Name", 75 | "logo": "https://example.com/exchange-logo.png", 76 | "website": "https://example.com", 77 | "twitter": "example", 78 | "version": "1.0", 79 | "capability": { 80 | "markets": true, 81 | "trades": true, 82 | "ordersSnapshot": true, 83 | "candles": false, 84 | "ticker": false 85 | } 86 | } 87 | ``` 88 | 89 | ## `/markets` - Available Markets - **Required** 90 | 91 | The `/markets` endpoint returns a list of all available markets on your exchange and is used to query other endpoints on your API. 92 | 93 | ### Parameters 94 | 95 | None 96 | 97 | ### Response 98 | 99 | JSON array of objects (one for each market) containing the following properties: 100 | 101 | **Required**: 102 | 103 | - `id`: Your exchange's unique ID of the market as a string. 104 | - `type`: The type of the market: 105 | - `spot`: If the asset actually being traded is for immediate delivery. This is the most common type of cryptocurrency market. 106 | - `derivative`: If the market represents trading activity on any kind of contract or underlying asset. Examples of a derivative market are futures, options, and perpetual markets. 107 | - `index`: If the market represents the price of an index directly from its methodology, and it has no order book, or trading activity. This should only be used to price the underlying index and not for markets on that index. Volume for indexes should always be `1`. 108 | - `base`: The base asset of the market 109 | - `quote`: The quote asset of the market 110 | 111 | _Optional_: 112 | 113 | - `active`: Boolean representing if the market is currently active. Defaults to `true`. 114 | - `subtypes`: An array representing additional context based on the market's `type`. Multiple subtypes are allowed. 115 | - `type`: `spot`: No subtypes at this time 116 | - `type`: `derivative`: 117 | - `perpetual`: If the market is a perpetual futures market regardless of underlying assets 118 | - `future`: If the market is a futures market regardless of underlying assets 119 | - `option`: If the market represents an option regardless of underlying assets 120 | - `type`: `index`: No subtypes at this time 121 | - `settlement`: The settlement asset of the market. Used for derivative markets where the settlement currency may or may not differ from the base or quote currencies. 122 | - `underlying`: The underlying asset of the market upon which a derivative’s price is based. Used for derivative markets and is typically an index. 123 | - `market_url`: The full exchange URL for the market 124 | - `description`: A description of the market 125 | 126 | Example: 127 | 128 | ```json 129 | [ 130 | { 131 | "id": "ETH_BTC", 132 | "type": "spot", 133 | "base": "ETH", 134 | "quote": "BTC" 135 | }, 136 | { 137 | "id": "BTC_USDT", 138 | "type": "derivative", 139 | "base": "BTC", 140 | "quote": "USDT", 141 | "active": true, 142 | "subtypes": ["perpetual", "future"], 143 | "settlement": "USDT", 144 | "market_url": "https://www.binance.com/en/futures/BTCUSDT", 145 | "description": "Binance perpetual futures market for BTC quoted in USDT" 146 | }, 147 | { 148 | "id": "in_xrpxbt", 149 | "type": "index", 150 | "base": "XRP", 151 | "quote": "XBT", 152 | "active": true, 153 | "market_url": "https://www.cfbenchmarks.com/indices/XRP/XBT/RTI/seconds" 154 | } 155 | ] 156 | ``` 157 | 158 | ## `/trades` - Historical Executed Trades - **Required for A+ Verified Exchanges** 159 | 160 | The `/trades` endpoint returns executed trades historically for a given market (provided via parameters). It allows Nomics to ingest all trades from your exchange for all time. 161 | 162 | ### Parameters 163 | 164 | - `market` **Required** Your exchange's market ID from the `/markets` endpoint 165 | - `since` A trade ID from a previous `/trades` response. If none is provided, the oldest trades should be returned 166 | 167 | ### Response 168 | 169 | JSON array of trade object for the given market after (and not including) the trade ID provided, with the following properties: 170 | 171 | **Required**: 172 | 173 | - `id`: A string ID for the trade that is unique within the scope of the market 174 | - `timestamp`: Timestamp of the trade in RFC3339 in UTC 175 | - `price`: The price for one unit of the base currency expressed in the quote currency as a string that is parseable to a positive number. 176 | 177 | One of the following are **required**: 178 | 179 | - `amount`: The amount of the **base** currency that was traded as a string that is parseable to a positive number. Only one of `amount` and `amount_quote` are required but both are encouraged. 180 | - `amount_quote`: The amount of the **quote** currency that was traded as a string that is parseable to a positive number. Only one of `amount` and `amount_quote` are required but both are encouraged. 181 | 182 | _Optional_: 183 | 184 | - `order` The ID of the order that was executed to produce this trade 185 | - `type` The type of order that resulted in the trade: [`market`, `limit`] 186 | - `side` The direction of the trade [`buy`, `sell`] 187 | - `raw` The raw data of the trade as represented by the exchange. This can be any JSON encodable data. 188 | 189 | Example: 190 | 191 | ```json 192 | [ 193 | { 194 | "id": "123456789", 195 | "timestamp": "2006-01-02T15:04:05.999Z", 196 | "price": "123.45678", 197 | "amount": "48.75", 198 | "amount_quote": "0.02051282051", 199 | "order": "8afe76fabe8befa", 200 | "type": "market", 201 | "side": "buy", 202 | "raw": [123456789, 1136214245, 123.45678, 48.75, "8afe76fabe8befa", "m"] 203 | } 204 | ] 205 | ``` 206 | 207 | Notes: 208 | 209 | - The number of trades returned is up to the exchange's implementation. 210 | - Returning an empty array signifies there are no newer trades than the given `since` ID. 211 | 212 | ## `/trades-by-timestamp` - Historical Executed Trades Paged by Timestamp - (Discouraged) 213 | 214 | **If you implement `/trades` you do not need to implement `/trades-by-timestamp`.** 215 | 216 | The `/trades-by-timestamp` endpoint is nearly identical to the `/trades` endpoint. The core difference is that the `since` parameter is an RFC3339 timestamp instead of an ID. Otherwise, the parameters and response are the same. 217 | 218 | This endpoint is provided to maximum compatibility with exchanges that can't paginate trades based on ID. It is inferior to paging by ID because in extremely high volume instances there may be more trades executed at the same timestamp than fit on a single page, causing a gap in trade data. If possible, `/trades` should be used instead. 219 | 220 | ### Parameters 221 | 222 | - `market` **Required** Your exchange's market ID from the `/markets` endpoint 223 | - `since` A timestamp from a previous `/trades-by-timestamp` response in RFC3339 format. If none is provided, the oldest trades should be returned 224 | 225 | ### Response 226 | 227 | Same as `/trades`. 228 | 229 | ## `/orders/snapshot` - Current Order Book Snapshot - **Required for A+ Verified Exchanges** 230 | 231 | The `/orders/snapshot` endpoint returns the current order book for a given market. It allows Nomics to get a simple snapshot of open orders. 232 | 233 | ### Parameters 234 | 235 | - `market` **Required** Your exchange's market ID from the `/markets` endpoint 236 | 237 | ### Response 238 | 239 | JSON object of all bids and asks that are currently open for the provided market, with the following properties: 240 | 241 | **Required**: 242 | 243 | - `bids`: a list of all open bid orders 244 | - `asks`: as list of all open ask orders 245 | - `timestamp`: the timestamp this snapshot was created in RFC3339 in UTC 246 | 247 | Each order is a tuple with the following entries: 248 | 249 | **Required**: 250 | 251 | - `price`: the price for one unit of the base currency expressed in the quote currency as a JSON number or numeric string 252 | - `amount`: the amount of the base currency available at this price point as a JSON number or numeric string 253 | 254 | Example: 255 | 256 | ```json 257 | { 258 | "bids": [[8123.45678, 10.0]], 259 | "asks": [[8120.0, 5.0]], 260 | "timestamp": "2006-01-02T15:04:05.999Z" 261 | } 262 | ``` 263 | 264 | Bids **must be sorted in descending order** and asks **must be sorted in ascending order**. This means the first bid is the best bid and the first ask is the best ask. 265 | 266 | When returning orders, perform as little aggregation as possible (ideally none) and include as many orders as possible (ideally all). 267 | 268 | ## `/candles` - Candles - **Discouraged** 269 | 270 | **If you implement `/trades` you do not need to implement `/candles`.** 271 | 272 | The `/candles` endpoint returns open, high, low, close, and volume data for a given market in 24 hour, 1 hour, and 1 minute periods. It allows Nomics to get at least a 24 hour picture of a market, as well as a high level historical view when available. Implementing this endpoint **requires `1d`, `1h`, and `1m` candle intervals**. 273 | 274 | **We highly recommend implementing the `/trades` endpoint instead of the `/candles` endpoint.** The `/candles` endpoint should be used as a last resort if implementing `/trades` is not possible. 275 | 276 | ### Parameters 277 | 278 | - `market` **Required** Your exchange's market ID from the `/markets` endpoint 279 | - `interval` **Required** The interval of the OHLCV candles. Valid values are `1d`, `1h`, and `1m`. 280 | 281 | ### Response 282 | 283 | JSON array of OHLCV Candles for the given market and interval. If daily candles are available, as many as possible should be returned (preferably to inception). Otherwise, candles should be returned fixed 24 hour, 1 hour, or 1 minute intervals. Timestamps should be aligned to candle size. IE: Midnight UTC (`2018-01-01T:00:00:00.000Z`) for `1d`, to the hour (`2018-01-01T03:00:00.000Z`) for `1h`, and to the minute (`2018-01-01T03:03:00.000Z`) for `1m`. Candles should be sorted by timestamp ascending. Candles have the following properties: 284 | 285 | **Required**: 286 | 287 | - `timestamp`: timestamp of the start of the candle in RFC3339 aligned to candle size in UTC 288 | - `close`: close price of the asset in the quote currency as a string parseable to a positive number 289 | - `open`: open price of the asset in the quote currency as a string parseable to a positive number 290 | - `high`: highest price of the asset in the quote currency as a string parseable to a positive number 291 | - `low`: lowest price of the asset in the quote currency as a string parseable to a positive number 292 | 293 | One of the following are **required**: 294 | 295 | - `volume`: volume of the asset in the **base** currency as a string parseable to a positive number. Only one of `volume` and `volume_quote` are required but both are encouraged. 296 | - `volume_quote`: volume of the asset in the **quote** currency as a string parseable to a positive number. Only one of `volume` and `volume_quote` are required but both are encouraged. 297 | 298 | Candles are expected to include a minimum number of records for a given interval and to include the "last candle" within the given timeframe: 299 | 300 | - `1d`: 7 candles with last candle occuring within a rolling 48 hours 301 | - `1h`: 24 candles with last candle occuring within a rolling 2 hours 302 | - `1m`: 60 candles with last candle occurring within a rolling 10 minutes 303 | 304 | ## `/ticker` - Ticker - **Discouraged** 305 | 306 | **If you implement `/trades` you do not need to implement `/ticker`.** 307 | 308 | The `/ticker` endpoint returns last prices (close) and 24h volume data for a given market. It allows Nomics to get a current snapshot of a given market. Implementing this endpoint requires the attributes above in addition to a market symbol and timestamp. Optional attributes include open, high, low, bid, and ask prices. 309 | 310 | **We highly recommend implementing the `/trades` endpoint instead of the `/ticker` endpoint.** The `/ticker` endpoint should be used as a last resort if implementing `/trades` is not possible. 311 | 312 | ### Parameters 313 | 314 | - `market` **Required** Your exchange's market ID from the `/markets` endpoint 315 | 316 | ### Response 317 | 318 | JSON object of the current ticker values for the given market. Tickers have the following properties: 319 | 320 | **Required**: 321 | 322 | - `close`: the current price of the asset in the quote currency as a string parseable to a positive number 323 | - `timestamp`: timestamp of the current ticker values in RFC3339 in UTC 324 | - `raw`: the raw ticker values as a JSON object 325 | 326 | One of the following are **required**: 327 | 328 | - `volume`: volume of the asset in the **base** currency as a string parseable to a positive number. Only one of `volume` and `volume_quote` are required but both are encouraged. 329 | - `volume_quote`: volume of the asset in the **quote** currency as a string parseable to a positive number. Only one of `volume` and `volume_quote` are required but both are encouraged. 330 | 331 | _Optional_: 332 | 333 | - `high`: highest price of the asset in the quote currency as a string parseable to a positive number 334 | - `low`: lowest price of the asset in the quote currency as a string parseable to a positive number 335 | - `ask`: open price of the asset in the quote currency as a string parseable to a positive number 336 | - `bid`: open price of the asset in the quote currency as a string parseable to a positive number 337 | 338 | Tickers are expected to include the most current data for a given market. 339 | -------------------------------------------------------------------------------- /lib/audit/test/audit.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: "off" */ 2 | 3 | const au = require('../au') 4 | 5 | const MINUTE = 1000 * 60 6 | const HOUR = MINUTE * 60 7 | const DAY = HOUR * 24 8 | 9 | const TRADE_TYPES = [ 10 | 'market', 11 | 'limit', 12 | 'ask', 13 | 'bid', 14 | 'fill', 15 | 'liquidation', 16 | 'assignment' 17 | ] 18 | 19 | module.exports = { 20 | beforeAll: async () => { 21 | try { 22 | this.info = await au.get('/info') 23 | } catch (err) { 24 | console.log('Error fetching /info', err) 25 | throw err 26 | } 27 | }, 28 | 29 | info: async () => { 30 | try { 31 | au.assertStringProperty(this.info, 'name') 32 | au.assertStringProperty(this.info, 'description', { required: true }) 33 | 34 | if (!process.env.NOMICS_PLATFORM_RELAX) { 35 | au.assert(this.info.description.length >= 1000, 'Expected description to be at least 1,000 characters') 36 | 37 | au.assertStringProperty(this.info, 'location', { required: true }) 38 | au.assertURLProperty(this.info, 'logo', { required: true, https: true }) 39 | au.assertStringProperty(this.info, 'version', { required: true }); 40 | } 41 | 42 | au.assertURLProperty(this.info, 'website', { required: true }) 43 | au.assertStringProperty(this.info, 'twitter', { required: false }) 44 | if (au.assertProperty(this.info, 'capability', { required: false })) { 45 | au.assertPropertyType(this.info, 'capability', 'object') 46 | const capability = this.info.capability 47 | au.assertBooleanProperty(capability, 'candles', { required: false }) 48 | au.assertBooleanProperty(capability, 'markets', { required: false }) 49 | au.assertBooleanProperty(capability, 'orders', { required: false }) 50 | au.assertBooleanProperty(capability, 'ordersSnapshot', { required: false }) 51 | au.assertBooleanProperty(capability, 'ordersSocket', { required: false }) 52 | au.assertBooleanProperty(capability, 'ticker', { required: false }) 53 | au.assertBooleanProperty(capability, 'trades', { required: false }) 54 | au.assertBooleanProperty(capability, 'tradesByTimestamp', { required: false }) 55 | au.assertBooleanProperty(capability, 'tradesSocket', { required: false }) 56 | } 57 | 58 | au.assert( 59 | this.info.capability.trades || 60 | this.info.capability.tradesByTimestamp || 61 | this.info.capability.orders || 62 | this.info.capability.ordersSnapshot || 63 | this.info.capability.ticker || 64 | this.info.capability.candles, 65 | 'Expected at least one data capability of trades, candles, orders, or ticker' 66 | ) 67 | } catch (err) { 68 | console.log(`FAILED: URL /info failed with "${err.message}"`) 69 | 70 | throw err 71 | } 72 | }, 73 | 74 | markets: async () => { 75 | if (!this.info.capability || !this.info.capability.markets) { 76 | return 77 | } 78 | 79 | try { 80 | const data = await au.get('/markets'); 81 | 82 | au.assert(data.length > 0, 'Expected at least one market'); 83 | 84 | // required 85 | // 86 | data.forEach((m) => au.assertStringProperty(m, 'id')); 87 | data.forEach((t) => au.assertPropertyInSet(t, 'type', ['spot', 'derivative', 'index'])); 88 | data.forEach((m) => au.assertStringProperty(m, 'base')); 89 | data.forEach((m) => au.assertStringProperty(m, 'quote')); 90 | 91 | // optional 92 | // 93 | data.forEach((m) => au.assertBooleanProperty(m, 'active', { required: false })); 94 | data.forEach((m) => au.assertStringProperty(m, 'settlement', { required: false })); 95 | data.forEach((m) => au.assertStringProperty(m, 'underlying', { required: false })); 96 | data.forEach((m) => { 97 | if (m.type === 'derivative') { 98 | au.assertPropertyInSet(m, 'subtypes', ['future', 'option', 'perpetual'], { required: false }); 99 | } else { 100 | au.assertPropertyInSet(m, 'subtypes', [], { required: false }); 101 | } 102 | }); 103 | data.forEach((m) => au.assertURLProperty(m, 'market_url', { required: false })); 104 | data.forEach((m) => au.assertStringProperty(m, 'description', { required: false })); 105 | 106 | const idCounts = data.reduce((memo, m) => { 107 | if (!memo[m.id]) { 108 | memo[m.id] = 0 109 | } 110 | memo[m.id]++ 111 | return memo 112 | }, {}) 113 | Object.keys(idCounts).forEach(id => { 114 | au.assert(idCounts[id] === 1, `Duplicate market ID: ${id}`) 115 | }) 116 | } catch (err) { 117 | console.log(`FAILED: URL /markets failed with "${err.message}"`) 118 | 119 | throw err 120 | } 121 | }, 122 | 123 | trades: async () => { 124 | if (!this.info.capability || !this.info.capability.markets || !this.info.capability.trades) { 125 | return 126 | } 127 | 128 | const markets = await au.get('/markets') 129 | if (markets.length === 0) { 130 | // If there are no markets, pass, since markets spec will fail 131 | return 132 | } 133 | 134 | let success = false 135 | let uri 136 | 137 | for (let i = 0; i < markets.length && !success; i++) { 138 | uri = null 139 | 140 | try { 141 | const market = markets[i]; 142 | uri = `/trades?market=${encodeURIComponent(market.id)}`; 143 | 144 | let trades 145 | try { 146 | trades = await au.get(uri) 147 | } catch (e) { 148 | // Used internally to mark certain adapters that do not return historical data. 149 | if (e.status === 410) { 150 | return 151 | } 152 | 153 | throw e 154 | } 155 | 156 | au.assert(trades.length > 2, 'Expected more than two trades'); 157 | au.assert( 158 | !trades.find(t => new Date(t.timestamp).getTime() < new Date('2006-01-01').getTime()), 159 | `Trades for market=${market.id} contained a trade with a timestamp before 2006.` 160 | ) 161 | 162 | trades.forEach((t) => au.assertStringProperty(t, 'id')); 163 | trades.forEach((t) => au.assertTimestampProperty(t, 'timestamp')); 164 | trades.forEach((t) => au.assertNumericStringProperty(t, 'price')); 165 | 166 | trades.forEach((t) => { 167 | if (t.amount) { 168 | au.assertNumericStringProperty(t, 'amount'); 169 | au.assertNumericStringProperty(t, 'amount_quote', { required: false }); 170 | } else { 171 | au.assertNumericStringProperty(t, 'amount_quote'); 172 | au.assertNumericStringProperty(t, 'amount', { required: false }); 173 | } 174 | }); 175 | 176 | trades.forEach(t => au.assertPropertyInSet(t, 'type', TRADE_TYPES, { required: false })) 177 | trades.forEach((t) => au.assertPropertyInSet(t, 'side', ['buy', 'sell'], { required: false })); 178 | trades.forEach((t) => au.assertStringProperty(t, 'order', { required: false })); 179 | 180 | const since = trades[0].id; 181 | const second = await au.get(`/trades?market=${encodeURIComponent(market.id)}&since=${since}`); 182 | 183 | au.assert(second.length > 0, `Trades with since=${since} didn't return any trades`); 184 | au.assert( 185 | !second.find(t => t.id === since), 186 | `Trades with since=${since} contained a trade with the same id as the since parameter. Only trades *after* the since id should be returned` 187 | ) 188 | au.assert( 189 | second.find(t => t.id === trades[1].id), 190 | `Trades with since=${since} didn't contain an overlap with the first page. Expected to see trade with id ${trades[1].id}` 191 | ) 192 | 193 | success = true 194 | } catch (err) { 195 | console.log(`FAILED: URL ${uri || err.stack} failed with "${err.message}"`) 196 | 197 | if (i === markets.length - 1) { 198 | throw err 199 | } 200 | } 201 | } 202 | }, 203 | 204 | tradesByTimestamp: async () => { 205 | if (!this.info.capability || !this.info.capability.markets || !this.info.capability.tradesByTimestamp) { 206 | return 207 | } 208 | 209 | const markets = await au.get('/markets') 210 | if (markets.length === 0) { 211 | // If there are no markets, pass, since markets spec will fail 212 | return 213 | } 214 | 215 | let success = false 216 | let uri 217 | 218 | for (let i = 0; i < markets.length && !success; i++) { 219 | uri = null 220 | try { 221 | const market = markets[i] 222 | uri = `/trades-by-timestamp?market=${encodeURIComponent(market.id)}` 223 | const trades = await au.get(`/trades-by-timestamp?market=${encodeURIComponent(market.id)}`) 224 | 225 | au.assert(trades.length > 2, 'Expected more than two trades'); 226 | 227 | au.assert( 228 | !trades.find(t => new Date(t.timestamp).getTime() < new Date('2006-01-01').getTime()), 229 | `Trades for market=${market.id} contained a trade with a timestamp before 2006.` 230 | ) 231 | 232 | trades.forEach((t) => au.assertStringProperty(t, 'id')); 233 | trades.forEach((t) => au.assertTimestampProperty(t, 'timestamp')); 234 | trades.forEach((t) => au.assertNumericStringProperty(t, 'price')); 235 | 236 | trades.forEach((t) => { 237 | if (t.amount) { 238 | au.assertNumericStringProperty(t, 'amount'); 239 | au.assertNumericStringProperty(t, 'amount_quote', { required: false }); 240 | } else { 241 | au.assertNumericStringProperty(t, 'amount_quote'); 242 | au.assertNumericStringProperty(t, 'amount', { required: false }); 243 | } 244 | }); 245 | 246 | trades.forEach((t) => au.assertStringProperty(t, 'order', { required: false })); 247 | trades.forEach(t => au.assertPropertyInSet(t, 'type', TRADE_TYPES, { required: false })) 248 | trades.forEach((t) => au.assertPropertyInSet(t, 'side', ['buy', 'sell'], { required: false })); 249 | 250 | const since = trades[0].timestamp 251 | const second = await au.get( 252 | `/trades-by-timestamp?market=${encodeURIComponent(market.id)}&since=${encodeURIComponent(since)}` 253 | ) 254 | au.assert(second.length > 0, `Trades with since=${since} didn't return any trades`) 255 | au.assert( 256 | !second.find(t => t.timestamp < trades[0].timestamp), 257 | `Trades with since=${trades[0].timestamp} contained a trade with a timestamp before the since parameter. Only trades *after or equal to* the since id should be returned` 258 | ) 259 | au.assert( 260 | second.find(t2 => t2.id === trades.find(t => t.timestamp > since).id), 261 | `Trades with since=${trades[0].timestamp} didn't contain an overlap with the first page. Expected to see trade with id ${trades[1].id}` 262 | ) 263 | 264 | success = true 265 | } catch (err) { 266 | console.log(`FAILED: URL ${uri || err.stack} failed with "${err.message}"`) 267 | 268 | if (i === markets.length - 1) { 269 | throw err 270 | } 271 | } 272 | } 273 | }, 274 | 275 | tradesSnapshot: async () => { 276 | if (!this.info.capability || !this.info.capability.markets || !this.info.capability.tradesSnapshot) { 277 | return 278 | } 279 | 280 | const markets = await au.get('/markets') 281 | if (markets.length === 0) { 282 | // If there are no markets, pass, since markets spec will fail 283 | return 284 | } 285 | 286 | let success = false 287 | let uri 288 | 289 | for (let i = 0; i < markets.length && !success; i++) { 290 | uri = null 291 | 292 | try { 293 | const market = markets[i] 294 | uri = `/trades/snapshot?market=${encodeURIComponent(market.id)}` 295 | const trades = await au.get(uri) 296 | 297 | au.assert(trades.length > 2, 'Expected more than two trades') 298 | 299 | trades.forEach(t => au.assertStringProperty(t, 'id')) 300 | trades.forEach(t => au.assertTimestampProperty(t, 'timestamp')) 301 | trades.forEach(t => au.assertPropertyInSet(t, 'type', TRADE_TYPES, { required: false })) 302 | trades.forEach(t => au.assertPropertyInSet(t, 'side', ['buy', 'sell'], { required: false })) 303 | trades.forEach((t) => au.assertNumericStringProperty(t, 'price')); 304 | 305 | trades.forEach((t) => { 306 | if (t.amount) { 307 | au.assertNumericStringProperty(t, 'amount'); 308 | au.assertNumericStringProperty(t, 'amount_quote', { required: false }); 309 | } else { 310 | au.assertNumericStringProperty(t, 'amount_quote'); 311 | au.assertNumericStringProperty(t, 'amount', { required: false }); 312 | } 313 | }); 314 | 315 | trades.forEach((t) => au.assertStringProperty(t, 'order', { required: false })); 316 | 317 | success = true 318 | } catch (err) { 319 | console.log(`FAILED: URL ${uri || err.stack} failed with "${err.message}"`) 320 | 321 | throw err 322 | } 323 | } 324 | }, 325 | 326 | ordersSnapshot: async () => { 327 | if (!this.info.capability || !this.info.capability.markets || !this.info.capability.ordersSnapshot) { 328 | return 329 | } 330 | 331 | const markets = await au.get('/markets') 332 | if (markets.length === 0) { 333 | // If there are no markets, pass, since markets spec will fail 334 | return 335 | } 336 | 337 | let success = false 338 | let uri 339 | 340 | for (let i = 0; i < markets.length && !success; i++) { 341 | uri = null 342 | try { 343 | const market = markets[i] 344 | uri = `/orders/snapshot?market=${encodeURIComponent(market.id)}` 345 | const orderBook = await au.get(`/orders/snapshot?market=${encodeURIComponent(market.id)}`) 346 | 347 | au.assertTimestampProperty(orderBook, 'timestamp') 348 | au.assertArrayProperty(orderBook, 'bids') 349 | au.assertArrayProperty(orderBook, 'asks') 350 | 351 | orderBook.bids.forEach(bid => { 352 | const bid0 = parseFloat(bid[0]) 353 | const bid1 = parseFloat(bid[1]) 354 | 355 | au.assert( 356 | typeof bid0 === 'number' && !isNaN(bid0), 357 | 'Expect bid price to be of type number or numeric string' 358 | ) 359 | au.assert( 360 | typeof bid1 === 'number' && !isNaN(bid1), 361 | 'Expect bid amount to be of type number or numeric string' 362 | ) 363 | }) 364 | 365 | orderBook.asks.forEach(ask => { 366 | const ask0 = parseFloat(ask[0]) 367 | const ask1 = parseFloat(ask[1]) 368 | 369 | au.assert( 370 | typeof ask0 === 'number' && !isNaN(ask0), 371 | 'Expect ask price to be of type number or numeric string' 372 | ) 373 | au.assert( 374 | typeof ask1 === 'number' && !isNaN(ask1), 375 | 'Expect ask amount to be of type number or numeric string' 376 | ) 377 | }) 378 | 379 | au.assert( 380 | typeof orderBook.bids[0] === typeof orderBook.asks[0], 381 | 'Expect ask prices and bid prices to be of the same type' 382 | ); 383 | 384 | const bidPrices = orderBook.bids.map(b => b[0]) 385 | const bidPricesSorted = typeof orderBook.bids[0][0] === 'number' 386 | ? orderBook.bids.map(b => b[0]).sort((a, b) => b - a) 387 | : orderBook.bids.map(b => b[0]).sort((a, b) => b.localeCompare(a)); 388 | au.assert( 389 | JSON.stringify(bidPrices) === JSON.stringify(bidPricesSorted), 390 | 'Expected bids to be sorted by price descending' 391 | ) 392 | 393 | const askPrices = orderBook.asks.map(b => b[0]) 394 | const askPricesSorted = typeof orderBook.asks[0][0] === 'number' 395 | ? orderBook.asks.map(b => b[0]).sort((a, b) => a - b) 396 | : orderBook.asks.map(b => b[0]).sort((a, b) => a.localeCompare(b)); 397 | au.assert( 398 | JSON.stringify(askPrices) === JSON.stringify(askPricesSorted), 399 | 'Expected asks to be sorted by price ascending' 400 | ) 401 | 402 | success = true 403 | } catch (err) { 404 | console.log(`FAILED: URL ${uri || err.stack} failed with "${err.message}"`) 405 | 406 | if (i === markets.length - 1) { 407 | throw err 408 | } 409 | } 410 | } 411 | }, 412 | 413 | candles: async () => { 414 | if (!this.info.capability || !this.info.capability.candles) { 415 | return 416 | } 417 | 418 | const markets = await au.get('/markets') 419 | if (markets.length === 0) { 420 | // If there are no markets, pass, since markets spec will fail 421 | return 422 | } 423 | 424 | const intervals = ['1d', '1h', '1m'] 425 | let success = false 426 | 427 | for (let i = 0; i < markets.length && !success; i++) { 428 | const market = markets[i] 429 | let uri 430 | 431 | for (const interval of intervals) { 432 | try { 433 | uri = `/candles?market=${encodeURIComponent(market.id)}&interval=${interval}` 434 | 435 | const candles = await au.get(uri) 436 | 437 | const c = JSON.parse(JSON.stringify(candles)) // deep clone 438 | let t = candles.map(c => c.timestamp) 439 | let tSorted = c.map(c => c.timestamp).sort() 440 | 441 | au.assert( 442 | JSON.stringify(t) === JSON.stringify(tSorted), 443 | `Expected ${interval} candles to be sorted by timestamp ascending` 444 | ) 445 | 446 | if (interval === '1d') { 447 | au.assert(candles.length >= 7, 'Expected at least 7 1d candles') 448 | au.assert( 449 | new Date(candles[candles.length - 1].timestamp).getTime() > new Date().getTime() - 2 * DAY, 450 | 'Expected last 1d candle to be within the last 48 hours' 451 | ) 452 | } 453 | 454 | if (interval === '1h') { 455 | au.assert(candles.length >= 24, 'Expected at least 24 1h candles') 456 | au.assert( 457 | new Date(candles[candles.length - 1].timestamp).getTime() > new Date().getTime() - 2 * HOUR, 458 | 'Expected last 1h candle to be within the last 2 hours' 459 | ) 460 | } 461 | 462 | if (interval === '1m') { 463 | au.assert(candles.length >= 60, 'Expected at least 60 1m candles') 464 | au.assert( 465 | new Date(candles[candles.length - 1].timestamp).getTime() > new Date().getTime() - 10 * MINUTE, 466 | 'Expected last 1m candle to be within the last 10 minutes' 467 | ) 468 | } 469 | 470 | candles.forEach(c => { 471 | au.assertTimestampProperty(c, 'timestamp') 472 | 473 | let date = new Date(c.timestamp) 474 | 475 | if (interval === '1d') { 476 | au.assert(date.getTime() % DAY === 0, 'Expected timestamp to aligned to day candle size in UTC') 477 | } 478 | 479 | if (interval === '1h') { 480 | au.assert(date.getTime() % HOUR === 0, 'Expected timestamp to aligned to hour candle size in UTC') 481 | } 482 | 483 | if (interval === '1d') { 484 | au.assert(date.getTime() % MINUTE === 0, 'Expected timestamp to aligned to minute candle size in UTC') 485 | } 486 | }) 487 | 488 | candles.forEach(c => { 489 | au.assertNumericStringProperty(c, 'open') 490 | 491 | au.assertNumericStringProperty(c, 'high') 492 | au.assert(parseFloat(c.high) >= parseFloat(c.open), 'Expected high to be greater than or equal to open') 493 | au.assert(parseFloat(c.high) >= parseFloat(c.close), 'Expected high to be greater than or equal to close') 494 | au.assert(parseFloat(c.high) >= parseFloat(c.low), 'Expected high to be greater than or equal to low') 495 | 496 | au.assertNumericStringProperty(c, 'low') 497 | au.assert(parseFloat(c.low) > 0, 'Expected low to be greater than 0') 498 | au.assert(parseFloat(c.low) <= parseFloat(c.open), 'Expected low to be less than or equal to open') 499 | au.assert(parseFloat(c.low) <= parseFloat(c.close), 'Expected low to be less than or equal to close') 500 | au.assert(parseFloat(c.low) <= parseFloat(c.high), 'Expected low to be less than or equal to high') 501 | 502 | au.assertNumericStringProperty(c, 'close'); 503 | 504 | if (c.volume) { 505 | au.assertNumericStringProperty(c, 'volume'); 506 | au.assertNumericStringProperty(c, 'volume_quote', { required: false }); 507 | au.assert(parseFloat(c.volume) >= 0, 'Expected volume to be greater than or equal to 0'); 508 | } else { 509 | au.assertNumericStringProperty(c, 'volume_quote'); 510 | au.assertNumericStringProperty(c, 'volume', { required: false }); 511 | au.assert(parseFloat(c.volume_quote) >= 0, 'Expected volume to be greater than or equal to 0'); 512 | } 513 | }); 514 | 515 | success = true 516 | } catch (err) { 517 | console.log(`FAILED: URL ${uri || err.stack} failed with "${err.message}"`) 518 | 519 | if (i === markets.length - 1) { 520 | throw err 521 | } 522 | } 523 | } 524 | } 525 | }, 526 | 527 | ticker: async () => { 528 | if (!this.info.capability || !this.info.capability.ticker) { 529 | return 530 | } 531 | 532 | au.assert( 533 | this.info.capability.ticker && this.info.capability.markets, 534 | 'Expect markets capability if ticker capability is avaialable' 535 | ) 536 | 537 | const markets = await au.get('/markets') 538 | if (markets.length === 0) { 539 | // If there are no markets, pass, since markets spec will fail 540 | return 541 | } 542 | 543 | let success = false 544 | let uri 545 | 546 | for (let i = 0; i < markets.length && !success; i++) { 547 | uri = null 548 | 549 | try { 550 | const market = markets[i] 551 | uri = `/ticker?market=${encodeURIComponent(market.id)}` 552 | const ticker = await au.get(`/ticker?market=${encodeURIComponent(market.id)}`) 553 | 554 | au.assert(ticker !== null, `Expected a ticker response for market ${market.id}`) 555 | 556 | au.assertNumericStringProperty(ticker, 'close', { required: true }) 557 | au.assertNumericStringProperty(ticker, 'high', { required: false }) 558 | au.assertNumericStringProperty(ticker, 'low', { required: false }) 559 | au.assertProperty(ticker, 'raw', { required: true }) 560 | au.assertTimestampProperty(ticker, 'timestamp', { required: true }) 561 | 562 | if (ticker.volume) { 563 | au.assertNumericStringProperty(ticker, 'volume'); 564 | au.assertNumericStringProperty(ticker, 'volume_quote', { required: false }); 565 | au.assert(parseFloat(ticker.volume) >= 0, 'Expected volume to be greater than or equal to 0'); 566 | } else { 567 | au.assertNumericStringProperty(ticker, 'volume_quote'); 568 | au.assertNumericStringProperty(ticker, 'volume', { required: false }); 569 | au.assert(parseFloat(ticker.volume_quote) >= 0, 'Expected volume to be greater than or equal to 0'); 570 | } 571 | 572 | if (ticker.ask && ticker.bid) { 573 | au.assert( 574 | Number(ticker.bid) < Number(ticker.ask), 575 | `Expected ask (${ticker.ask}) to be greater than bid (${ticker.bid})` 576 | ) 577 | } 578 | if (ticker.ask) { 579 | au.assertNumericStringProperty(ticker, 'ask', { required: false }) 580 | au.assert(Number(ticker.ask) > 0, 'Expected ask to be greater than zero') 581 | } 582 | if (ticker.bid) { 583 | au.assertNumericStringProperty(ticker, 'bid', { required: false }) 584 | au.assert(Number(ticker.bid) > 0, 'Expected bid to be greater than zero') 585 | } 586 | if (ticker.high) { 587 | au.assert(Number(ticker.high) > 0, `Expected high (${ticker.high}) to be greater than zero`) 588 | au.assert( 589 | Number(ticker.close) <= Number(ticker.high), 590 | `Expected close (${ticker.close}) to be less than or equal to high (${ticker.high})` 591 | ) 592 | } 593 | if (ticker.low) { 594 | au.assert(Number(ticker.low) > 0, `Expected low (${ticker.low}) to be greater than zero`) 595 | au.assert( 596 | Number(ticker.close) >= Number(ticker.low), 597 | `Expected close (${ticker.close}) to be greater than or equal to low (${ticker.low})` 598 | ) 599 | } 600 | if (ticker.high && ticker.low) { 601 | au.assert( 602 | Number(ticker.high) >= Number(ticker.low), 603 | `Expected high (${ticker.high}) to be higher than or equal to low (${ticker.low})` 604 | ) 605 | } 606 | 607 | au.assert(Number(ticker.close) > 0, `Expected close (${ticker.close}) to be greater than zero`) 608 | 609 | success = true 610 | } catch (err) { 611 | console.log(`FAILED: URL ${uri || err.stack} failed with "${err.message}"`) 612 | 613 | if (i === markets.length - 1) { 614 | throw err 615 | } 616 | } 617 | } 618 | } 619 | } 620 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@^7.0.0": 6 | version "7.0.0" 7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" 8 | integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== 9 | dependencies: 10 | "@babel/highlight" "^7.0.0" 11 | 12 | "@babel/highlight@^7.0.0": 13 | version "7.0.0" 14 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" 15 | integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw== 16 | dependencies: 17 | chalk "^2.0.0" 18 | esutils "^2.0.2" 19 | js-tokens "^4.0.0" 20 | 21 | accepts@~1.3.7: 22 | version "1.3.7" 23 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" 24 | integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== 25 | dependencies: 26 | mime-types "~2.1.24" 27 | negotiator "0.6.2" 28 | 29 | acorn-jsx@^5.0.0: 30 | version "5.0.1" 31 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" 32 | integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg== 33 | 34 | acorn@^3.1.0: 35 | version "3.3.0" 36 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" 37 | integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= 38 | 39 | acorn@^6.0.7: 40 | version "6.3.0" 41 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e" 42 | integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA== 43 | 44 | ajv@^6.10.2, ajv@^6.9.1: 45 | version "6.10.2" 46 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" 47 | integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== 48 | dependencies: 49 | fast-deep-equal "^2.0.1" 50 | fast-json-stable-stringify "^2.0.0" 51 | json-schema-traverse "^0.4.1" 52 | uri-js "^4.2.2" 53 | 54 | ansi-escapes@^3.2.0: 55 | version "3.2.0" 56 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" 57 | integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== 58 | 59 | ansi-regex@^3.0.0: 60 | version "3.0.0" 61 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 62 | integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= 63 | 64 | ansi-regex@^4.1.0: 65 | version "4.1.0" 66 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" 67 | integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== 68 | 69 | ansi-styles@^3.2.0, ansi-styles@^3.2.1: 70 | version "3.2.1" 71 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 72 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 73 | dependencies: 74 | color-convert "^1.9.0" 75 | 76 | argparse@^1.0.7: 77 | version "1.0.10" 78 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 79 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 80 | dependencies: 81 | sprintf-js "~1.0.2" 82 | 83 | array-flatten@1.1.1: 84 | version "1.1.1" 85 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 86 | integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= 87 | 88 | astral-regex@^1.0.0: 89 | version "1.0.0" 90 | resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" 91 | integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== 92 | 93 | async@^2.6.2: 94 | version "2.6.2" 95 | resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" 96 | integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg== 97 | dependencies: 98 | lodash "^4.17.11" 99 | 100 | axios@^0.21.1: 101 | version "0.21.1" 102 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" 103 | integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== 104 | dependencies: 105 | follow-redirects "^1.10.0" 106 | 107 | balanced-match@^1.0.0: 108 | version "1.0.0" 109 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 110 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 111 | 112 | body-parser@1.19.0: 113 | version "1.19.0" 114 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" 115 | integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== 116 | dependencies: 117 | bytes "3.1.0" 118 | content-type "~1.0.4" 119 | debug "2.6.9" 120 | depd "~1.1.2" 121 | http-errors "1.7.2" 122 | iconv-lite "0.4.24" 123 | on-finished "~2.3.0" 124 | qs "6.7.0" 125 | raw-body "2.4.0" 126 | type-is "~1.6.17" 127 | 128 | brace-expansion@^1.1.7: 129 | version "1.1.11" 130 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 131 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 132 | dependencies: 133 | balanced-match "^1.0.0" 134 | concat-map "0.0.1" 135 | 136 | builtin-modules@^1.0.0: 137 | version "1.1.1" 138 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" 139 | integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= 140 | 141 | bytes@3.1.0: 142 | version "3.1.0" 143 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" 144 | integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== 145 | 146 | callsites@^3.0.0: 147 | version "3.1.0" 148 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" 149 | integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== 150 | 151 | chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.2: 152 | version "2.4.2" 153 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 154 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 155 | dependencies: 156 | ansi-styles "^3.2.1" 157 | escape-string-regexp "^1.0.5" 158 | supports-color "^5.3.0" 159 | 160 | chardet@^0.7.0: 161 | version "0.7.0" 162 | resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" 163 | integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== 164 | 165 | cli-cursor@^2.1.0: 166 | version "2.1.0" 167 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" 168 | integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= 169 | dependencies: 170 | restore-cursor "^2.0.0" 171 | 172 | cli-width@^2.0.0: 173 | version "2.2.0" 174 | resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" 175 | integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= 176 | 177 | color-convert@^1.9.0: 178 | version "1.9.3" 179 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 180 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 181 | dependencies: 182 | color-name "1.1.3" 183 | 184 | color-name@1.1.3: 185 | version "1.1.3" 186 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 187 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 188 | 189 | concat-map@0.0.1: 190 | version "0.0.1" 191 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 192 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 193 | 194 | contains-path@^0.1.0: 195 | version "0.1.0" 196 | resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" 197 | integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= 198 | 199 | content-disposition@0.5.3: 200 | version "0.5.3" 201 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" 202 | integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== 203 | dependencies: 204 | safe-buffer "5.1.2" 205 | 206 | content-type@~1.0.4: 207 | version "1.0.4" 208 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" 209 | integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== 210 | 211 | cookie-signature@1.0.6: 212 | version "1.0.6" 213 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 214 | integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= 215 | 216 | cookie@0.4.0: 217 | version "0.4.0" 218 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" 219 | integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== 220 | 221 | cross-spawn@^6.0.5: 222 | version "6.0.5" 223 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" 224 | integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== 225 | dependencies: 226 | nice-try "^1.0.4" 227 | path-key "^2.0.1" 228 | semver "^5.5.0" 229 | shebang-command "^1.2.0" 230 | which "^1.2.9" 231 | 232 | debug@2.6.9, debug@^2.6.8, debug@^2.6.9: 233 | version "2.6.9" 234 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 235 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 236 | dependencies: 237 | ms "2.0.0" 238 | 239 | debug@^4.0.1: 240 | version "4.1.0" 241 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87" 242 | integrity sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg== 243 | dependencies: 244 | ms "^2.1.1" 245 | 246 | deep-is@~0.1.3: 247 | version "0.1.3" 248 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 249 | integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= 250 | 251 | depd@~1.1.2: 252 | version "1.1.2" 253 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 254 | integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= 255 | 256 | destroy@~1.0.4: 257 | version "1.0.4" 258 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 259 | integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= 260 | 261 | doctrine@1.5.0: 262 | version "1.5.0" 263 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" 264 | integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= 265 | dependencies: 266 | esutils "^2.0.2" 267 | isarray "^1.0.0" 268 | 269 | doctrine@^3.0.0: 270 | version "3.0.0" 271 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" 272 | integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== 273 | dependencies: 274 | esutils "^2.0.2" 275 | 276 | ee-first@1.1.1: 277 | version "1.1.1" 278 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 279 | integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= 280 | 281 | emoji-regex@^7.0.1: 282 | version "7.0.3" 283 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" 284 | integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== 285 | 286 | encodeurl@~1.0.2: 287 | version "1.0.2" 288 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" 289 | integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= 290 | 291 | error-ex@^1.2.0: 292 | version "1.3.2" 293 | resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" 294 | integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== 295 | dependencies: 296 | is-arrayish "^0.2.1" 297 | 298 | escape-html@~1.0.3: 299 | version "1.0.3" 300 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 301 | integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= 302 | 303 | escape-string-regexp@^1.0.5: 304 | version "1.0.5" 305 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 306 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 307 | 308 | eslint-config-standard@^12.0.0: 309 | version "12.0.0" 310 | resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz#638b4c65db0bd5a41319f96bba1f15ddad2107d9" 311 | integrity sha512-COUz8FnXhqFitYj4DTqHzidjIL/t4mumGZto5c7DrBpvWoie+Sn3P4sLEzUGeYhRElWuFEf8K1S1EfvD1vixCQ== 312 | 313 | eslint-import-resolver-node@^0.3.1: 314 | version "0.3.2" 315 | resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" 316 | integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q== 317 | dependencies: 318 | debug "^2.6.9" 319 | resolve "^1.5.0" 320 | 321 | eslint-module-utils@^2.2.0: 322 | version "2.2.0" 323 | resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz#b270362cd88b1a48ad308976ce7fa54e98411746" 324 | integrity sha1-snA2LNiLGkitMIl2zn+lTphBF0Y= 325 | dependencies: 326 | debug "^2.6.8" 327 | pkg-dir "^1.0.0" 328 | 329 | eslint-plugin-es@^1.3.1: 330 | version "1.4.0" 331 | resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.4.0.tgz#475f65bb20c993fc10e8c8fe77d1d60068072da6" 332 | integrity sha512-XfFmgFdIUDgvaRAlaXUkxrRg5JSADoRC8IkKLc/cISeR3yHVMefFHQZpcyXXEUUPHfy5DwviBcrfqlyqEwlQVw== 333 | dependencies: 334 | eslint-utils "^1.3.0" 335 | regexpp "^2.0.1" 336 | 337 | eslint-plugin-import@^2.13.0: 338 | version "2.14.0" 339 | resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz#6b17626d2e3e6ad52cfce8807a845d15e22111a8" 340 | integrity sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g== 341 | dependencies: 342 | contains-path "^0.1.0" 343 | debug "^2.6.8" 344 | doctrine "1.5.0" 345 | eslint-import-resolver-node "^0.3.1" 346 | eslint-module-utils "^2.2.0" 347 | has "^1.0.1" 348 | lodash "^4.17.4" 349 | minimatch "^3.0.3" 350 | read-pkg-up "^2.0.0" 351 | resolve "^1.6.0" 352 | 353 | eslint-plugin-node@^8.0.0: 354 | version "8.0.1" 355 | resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-8.0.1.tgz#55ae3560022863d141fa7a11799532340a685964" 356 | integrity sha512-ZjOjbjEi6jd82rIpFSgagv4CHWzG9xsQAVp1ZPlhRnnYxcTgENUVBvhYmkQ7GvT1QFijUSo69RaiOJKhMu6i8w== 357 | dependencies: 358 | eslint-plugin-es "^1.3.1" 359 | eslint-utils "^1.3.1" 360 | ignore "^5.0.2" 361 | minimatch "^3.0.4" 362 | resolve "^1.8.1" 363 | semver "^5.5.0" 364 | 365 | eslint-plugin-promise@^4.0.1: 366 | version "4.2.1" 367 | resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" 368 | integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== 369 | 370 | eslint-plugin-standard@^4.0.0: 371 | version "4.0.0" 372 | resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.0.tgz#f845b45109c99cd90e77796940a344546c8f6b5c" 373 | integrity sha512-OwxJkR6TQiYMmt1EsNRMe5qG3GsbjlcOhbGUBY4LtavF9DsLaTcoR+j2Tdjqi23oUwKNUqX7qcn5fPStafMdlA== 374 | 375 | eslint-scope@^4.0.3: 376 | version "4.0.3" 377 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" 378 | integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== 379 | dependencies: 380 | esrecurse "^4.1.0" 381 | estraverse "^4.1.1" 382 | 383 | eslint-utils@^1.3.0, eslint-utils@^1.3.1: 384 | version "1.4.2" 385 | resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab" 386 | integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q== 387 | dependencies: 388 | eslint-visitor-keys "^1.0.0" 389 | 390 | eslint-visitor-keys@^1.0.0: 391 | version "1.1.0" 392 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" 393 | integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== 394 | 395 | eslint@^5.0.1: 396 | version "5.16.0" 397 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" 398 | integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== 399 | dependencies: 400 | "@babel/code-frame" "^7.0.0" 401 | ajv "^6.9.1" 402 | chalk "^2.1.0" 403 | cross-spawn "^6.0.5" 404 | debug "^4.0.1" 405 | doctrine "^3.0.0" 406 | eslint-scope "^4.0.3" 407 | eslint-utils "^1.3.1" 408 | eslint-visitor-keys "^1.0.0" 409 | espree "^5.0.1" 410 | esquery "^1.0.1" 411 | esutils "^2.0.2" 412 | file-entry-cache "^5.0.1" 413 | functional-red-black-tree "^1.0.1" 414 | glob "^7.1.2" 415 | globals "^11.7.0" 416 | ignore "^4.0.6" 417 | import-fresh "^3.0.0" 418 | imurmurhash "^0.1.4" 419 | inquirer "^6.2.2" 420 | js-yaml "^3.13.0" 421 | json-stable-stringify-without-jsonify "^1.0.1" 422 | levn "^0.3.0" 423 | lodash "^4.17.11" 424 | minimatch "^3.0.4" 425 | mkdirp "^0.5.1" 426 | natural-compare "^1.4.0" 427 | optionator "^0.8.2" 428 | path-is-inside "^1.0.2" 429 | progress "^2.0.0" 430 | regexpp "^2.0.1" 431 | semver "^5.5.1" 432 | strip-ansi "^4.0.0" 433 | strip-json-comments "^2.0.1" 434 | table "^5.2.3" 435 | text-table "^0.2.0" 436 | 437 | espree@^5.0.1: 438 | version "5.0.1" 439 | resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" 440 | integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== 441 | dependencies: 442 | acorn "^6.0.7" 443 | acorn-jsx "^5.0.0" 444 | eslint-visitor-keys "^1.0.0" 445 | 446 | esprima@^4.0.0: 447 | version "4.0.1" 448 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 449 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 450 | 451 | esquery@^1.0.1: 452 | version "1.0.1" 453 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" 454 | integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== 455 | dependencies: 456 | estraverse "^4.0.0" 457 | 458 | esrecurse@^4.1.0: 459 | version "4.2.1" 460 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" 461 | integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== 462 | dependencies: 463 | estraverse "^4.1.0" 464 | 465 | estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: 466 | version "4.2.0" 467 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" 468 | integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= 469 | 470 | esutils@^2.0.2: 471 | version "2.0.2" 472 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 473 | integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= 474 | 475 | etag@~1.8.1: 476 | version "1.8.1" 477 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 478 | integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= 479 | 480 | express@^4.16.3: 481 | version "4.17.1" 482 | resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" 483 | integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== 484 | dependencies: 485 | accepts "~1.3.7" 486 | array-flatten "1.1.1" 487 | body-parser "1.19.0" 488 | content-disposition "0.5.3" 489 | content-type "~1.0.4" 490 | cookie "0.4.0" 491 | cookie-signature "1.0.6" 492 | debug "2.6.9" 493 | depd "~1.1.2" 494 | encodeurl "~1.0.2" 495 | escape-html "~1.0.3" 496 | etag "~1.8.1" 497 | finalhandler "~1.1.2" 498 | fresh "0.5.2" 499 | merge-descriptors "1.0.1" 500 | methods "~1.1.2" 501 | on-finished "~2.3.0" 502 | parseurl "~1.3.3" 503 | path-to-regexp "0.1.7" 504 | proxy-addr "~2.0.5" 505 | qs "6.7.0" 506 | range-parser "~1.2.1" 507 | safe-buffer "5.1.2" 508 | send "0.17.1" 509 | serve-static "1.14.1" 510 | setprototypeof "1.1.1" 511 | statuses "~1.5.0" 512 | type-is "~1.6.18" 513 | utils-merge "1.0.1" 514 | vary "~1.1.2" 515 | 516 | external-editor@^3.0.3: 517 | version "3.1.0" 518 | resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" 519 | integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== 520 | dependencies: 521 | chardet "^0.7.0" 522 | iconv-lite "^0.4.24" 523 | tmp "^0.0.33" 524 | 525 | fast-deep-equal@^2.0.1: 526 | version "2.0.1" 527 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" 528 | integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= 529 | 530 | fast-json-stable-stringify@^2.0.0: 531 | version "2.0.0" 532 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" 533 | integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= 534 | 535 | fast-levenshtein@~2.0.4: 536 | version "2.0.6" 537 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 538 | integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= 539 | 540 | figures@^2.0.0: 541 | version "2.0.0" 542 | resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" 543 | integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= 544 | dependencies: 545 | escape-string-regexp "^1.0.5" 546 | 547 | file-entry-cache@^5.0.1: 548 | version "5.0.1" 549 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" 550 | integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== 551 | dependencies: 552 | flat-cache "^2.0.1" 553 | 554 | finalhandler@~1.1.2: 555 | version "1.1.2" 556 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" 557 | integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== 558 | dependencies: 559 | debug "2.6.9" 560 | encodeurl "~1.0.2" 561 | escape-html "~1.0.3" 562 | on-finished "~2.3.0" 563 | parseurl "~1.3.3" 564 | statuses "~1.5.0" 565 | unpipe "~1.0.0" 566 | 567 | find-up@^1.0.0: 568 | version "1.1.2" 569 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" 570 | integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= 571 | dependencies: 572 | path-exists "^2.0.0" 573 | pinkie-promise "^2.0.0" 574 | 575 | find-up@^2.0.0: 576 | version "2.1.0" 577 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" 578 | integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= 579 | dependencies: 580 | locate-path "^2.0.0" 581 | 582 | flat-cache@^2.0.1: 583 | version "2.0.1" 584 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" 585 | integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== 586 | dependencies: 587 | flatted "^2.0.0" 588 | rimraf "2.6.3" 589 | write "1.0.3" 590 | 591 | flatted@^2.0.0: 592 | version "2.0.1" 593 | resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" 594 | integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== 595 | 596 | follow-redirects@^1.10.0: 597 | version "1.13.1" 598 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7" 599 | integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg== 600 | 601 | forwarded@~0.1.2: 602 | version "0.1.2" 603 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" 604 | integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= 605 | 606 | fresh@0.5.2: 607 | version "0.5.2" 608 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 609 | integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= 610 | 611 | fs.realpath@^1.0.0: 612 | version "1.0.0" 613 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 614 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 615 | 616 | function-bind@^1.1.1: 617 | version "1.1.1" 618 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 619 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 620 | 621 | function-names-at-line@^1.0.0: 622 | version "1.0.0" 623 | resolved "https://registry.yarnpkg.com/function-names-at-line/-/function-names-at-line-1.0.0.tgz#3441d99ed2c7efbaf38a850b193c51e139a54c5f" 624 | integrity sha1-NEHZntLH77rzioULGTxR4TmlTF8= 625 | dependencies: 626 | acorn "^3.1.0" 627 | 628 | functional-red-black-tree@^1.0.1: 629 | version "1.0.1" 630 | resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" 631 | integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= 632 | 633 | glob@^7.1.2, glob@^7.1.3: 634 | version "7.1.4" 635 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" 636 | integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== 637 | dependencies: 638 | fs.realpath "^1.0.0" 639 | inflight "^1.0.4" 640 | inherits "2" 641 | minimatch "^3.0.4" 642 | once "^1.3.0" 643 | path-is-absolute "^1.0.0" 644 | 645 | globals@^11.7.0: 646 | version "11.9.0" 647 | resolved "https://registry.yarnpkg.com/globals/-/globals-11.9.0.tgz#bde236808e987f290768a93d065060d78e6ab249" 648 | integrity sha512-5cJVtyXWH8PiJPVLZzzoIizXx944O4OmRro5MWKx5fT4MgcN7OfaMutPeaTdJCCURwbWdhhcCWcKIffPnmTzBg== 649 | 650 | graceful-fs@^4.1.2: 651 | version "4.1.15" 652 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" 653 | integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== 654 | 655 | has-flag@^3.0.0: 656 | version "3.0.0" 657 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 658 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 659 | 660 | has@^1.0.1: 661 | version "1.0.3" 662 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 663 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 664 | dependencies: 665 | function-bind "^1.1.1" 666 | 667 | hosted-git-info@^2.1.4: 668 | version "2.7.1" 669 | resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" 670 | integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== 671 | 672 | http-errors@1.7.2: 673 | version "1.7.2" 674 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" 675 | integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== 676 | dependencies: 677 | depd "~1.1.2" 678 | inherits "2.0.3" 679 | setprototypeof "1.1.1" 680 | statuses ">= 1.5.0 < 2" 681 | toidentifier "1.0.0" 682 | 683 | http-errors@~1.7.2: 684 | version "1.7.3" 685 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" 686 | integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== 687 | dependencies: 688 | depd "~1.1.2" 689 | inherits "2.0.4" 690 | setprototypeof "1.1.1" 691 | statuses ">= 1.5.0 < 2" 692 | toidentifier "1.0.0" 693 | 694 | iconv-lite@0.4.24, iconv-lite@^0.4.24: 695 | version "0.4.24" 696 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 697 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 698 | dependencies: 699 | safer-buffer ">= 2.1.2 < 3" 700 | 701 | ignore@^4.0.6: 702 | version "4.0.6" 703 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" 704 | integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== 705 | 706 | ignore@^5.0.2: 707 | version "5.0.4" 708 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.0.4.tgz#33168af4a21e99b00c5d41cbadb6a6cb49903a45" 709 | integrity sha512-WLsTMEhsQuXpCiG173+f3aymI43SXa+fB1rSfbzyP4GkPP+ZFVuO0/3sFUGNBtifisPeDcl/uD/Y2NxZ7xFq4g== 710 | 711 | import-fresh@^3.0.0: 712 | version "3.1.0" 713 | resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.1.0.tgz#6d33fa1dcef6df930fae003446f33415af905118" 714 | integrity sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ== 715 | dependencies: 716 | parent-module "^1.0.0" 717 | resolve-from "^4.0.0" 718 | 719 | imurmurhash@^0.1.4: 720 | version "0.1.4" 721 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 722 | integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= 723 | 724 | inflight@^1.0.4: 725 | version "1.0.6" 726 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 727 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 728 | dependencies: 729 | once "^1.3.0" 730 | wrappy "1" 731 | 732 | inherits@2, inherits@2.0.3: 733 | version "2.0.3" 734 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 735 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 736 | 737 | inherits@2.0.4: 738 | version "2.0.4" 739 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 740 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 741 | 742 | inquirer@^6.2.2: 743 | version "6.5.2" 744 | resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" 745 | integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== 746 | dependencies: 747 | ansi-escapes "^3.2.0" 748 | chalk "^2.4.2" 749 | cli-cursor "^2.1.0" 750 | cli-width "^2.0.0" 751 | external-editor "^3.0.3" 752 | figures "^2.0.0" 753 | lodash "^4.17.12" 754 | mute-stream "0.0.7" 755 | run-async "^2.2.0" 756 | rxjs "^6.4.0" 757 | string-width "^2.1.0" 758 | strip-ansi "^5.1.0" 759 | through "^2.3.6" 760 | 761 | ipaddr.js@1.9.0: 762 | version "1.9.0" 763 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" 764 | integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== 765 | 766 | is-arrayish@^0.2.1: 767 | version "0.2.1" 768 | resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" 769 | integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= 770 | 771 | is-builtin-module@^1.0.0: 772 | version "1.0.0" 773 | resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" 774 | integrity sha1-VAVy0096wxGfj3bDDLwbHgN6/74= 775 | dependencies: 776 | builtin-modules "^1.0.0" 777 | 778 | is-fullwidth-code-point@^2.0.0: 779 | version "2.0.0" 780 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 781 | integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= 782 | 783 | is-promise@^2.1.0: 784 | version "2.1.0" 785 | resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" 786 | integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= 787 | 788 | isarray@^1.0.0: 789 | version "1.0.0" 790 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 791 | integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= 792 | 793 | isexe@^2.0.0: 794 | version "2.0.0" 795 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 796 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 797 | 798 | js-tokens@^4.0.0: 799 | version "4.0.0" 800 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 801 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 802 | 803 | js-yaml@^3.13.0: 804 | version "3.13.1" 805 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" 806 | integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== 807 | dependencies: 808 | argparse "^1.0.7" 809 | esprima "^4.0.0" 810 | 811 | json-schema-traverse@^0.4.1: 812 | version "0.4.1" 813 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" 814 | integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 815 | 816 | json-stable-stringify-without-jsonify@^1.0.1: 817 | version "1.0.1" 818 | resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" 819 | integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= 820 | 821 | levn@^0.3.0, levn@~0.3.0: 822 | version "0.3.0" 823 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" 824 | integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= 825 | dependencies: 826 | prelude-ls "~1.1.2" 827 | type-check "~0.3.2" 828 | 829 | load-json-file@^2.0.0: 830 | version "2.0.0" 831 | resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" 832 | integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= 833 | dependencies: 834 | graceful-fs "^4.1.2" 835 | parse-json "^2.2.0" 836 | pify "^2.0.0" 837 | strip-bom "^3.0.0" 838 | 839 | locate-path@^2.0.0: 840 | version "2.0.0" 841 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" 842 | integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= 843 | dependencies: 844 | p-locate "^2.0.0" 845 | path-exists "^3.0.0" 846 | 847 | lodash-deeper@^1.0.0: 848 | version "1.1.0" 849 | resolved "https://registry.yarnpkg.com/lodash-deeper/-/lodash-deeper-1.1.0.tgz#5af51f33cb0bf5e24a08187c326b779a17e489dc" 850 | integrity sha1-WvUfM8sL9eJKCBh8Mmt3mhfkidw= 851 | dependencies: 852 | lodash ">=4.13.1" 853 | 854 | lodash@>=4.13.1, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.4: 855 | version "4.17.20" 856 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" 857 | integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== 858 | 859 | media-typer@0.3.0: 860 | version "0.3.0" 861 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 862 | integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= 863 | 864 | merge-descriptors@1.0.1: 865 | version "1.0.1" 866 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 867 | integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= 868 | 869 | methods@~1.1.2: 870 | version "1.1.2" 871 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 872 | integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= 873 | 874 | mime-db@1.40.0: 875 | version "1.40.0" 876 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" 877 | integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== 878 | 879 | mime-types@~2.1.24: 880 | version "2.1.24" 881 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" 882 | integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== 883 | dependencies: 884 | mime-db "1.40.0" 885 | 886 | mime@1.6.0: 887 | version "1.6.0" 888 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" 889 | integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== 890 | 891 | mimic-fn@^1.0.0: 892 | version "1.2.0" 893 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" 894 | integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== 895 | 896 | minimatch@^3.0.3, minimatch@^3.0.4: 897 | version "3.0.4" 898 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 899 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 900 | dependencies: 901 | brace-expansion "^1.1.7" 902 | 903 | minimist@0.0.8: 904 | version "0.0.8" 905 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 906 | integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= 907 | 908 | minimist@^1.2.0: 909 | version "1.2.0" 910 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 911 | integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= 912 | 913 | mkdirp@^0.5.1: 914 | version "0.5.1" 915 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 916 | integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= 917 | dependencies: 918 | minimist "0.0.8" 919 | 920 | ms@2.0.0: 921 | version "2.0.0" 922 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 923 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 924 | 925 | ms@2.1.1, ms@^2.1.1: 926 | version "2.1.1" 927 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" 928 | integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== 929 | 930 | mute-stream@0.0.7: 931 | version "0.0.7" 932 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" 933 | integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= 934 | 935 | natural-compare@^1.4.0: 936 | version "1.4.0" 937 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 938 | integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= 939 | 940 | negotiator@0.6.2: 941 | version "0.6.2" 942 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" 943 | integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== 944 | 945 | nice-try@^1.0.4: 946 | version "1.0.5" 947 | resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" 948 | integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== 949 | 950 | normalize-package-data@^2.3.2: 951 | version "2.4.0" 952 | resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" 953 | integrity sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw== 954 | dependencies: 955 | hosted-git-info "^2.1.4" 956 | is-builtin-module "^1.0.0" 957 | semver "2 || 3 || 4 || 5" 958 | validate-npm-package-license "^3.0.1" 959 | 960 | on-finished@~2.3.0: 961 | version "2.3.0" 962 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 963 | integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= 964 | dependencies: 965 | ee-first "1.1.1" 966 | 967 | once@^1.3.0: 968 | version "1.4.0" 969 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 970 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 971 | dependencies: 972 | wrappy "1" 973 | 974 | onetime@^2.0.0: 975 | version "2.0.1" 976 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" 977 | integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= 978 | dependencies: 979 | mimic-fn "^1.0.0" 980 | 981 | optionator@^0.8.2: 982 | version "0.8.2" 983 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" 984 | integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= 985 | dependencies: 986 | deep-is "~0.1.3" 987 | fast-levenshtein "~2.0.4" 988 | levn "~0.3.0" 989 | prelude-ls "~1.1.2" 990 | type-check "~0.3.2" 991 | wordwrap "~1.0.0" 992 | 993 | os-tmpdir@~1.0.2: 994 | version "1.0.2" 995 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 996 | integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= 997 | 998 | p-limit@^1.1.0: 999 | version "1.3.0" 1000 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" 1001 | integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== 1002 | dependencies: 1003 | p-try "^1.0.0" 1004 | 1005 | p-locate@^2.0.0: 1006 | version "2.0.0" 1007 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" 1008 | integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= 1009 | dependencies: 1010 | p-limit "^1.1.0" 1011 | 1012 | p-try@^1.0.0: 1013 | version "1.0.0" 1014 | resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" 1015 | integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= 1016 | 1017 | parent-module@^1.0.0: 1018 | version "1.0.1" 1019 | resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" 1020 | integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== 1021 | dependencies: 1022 | callsites "^3.0.0" 1023 | 1024 | parse-json@^2.2.0: 1025 | version "2.2.0" 1026 | resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" 1027 | integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= 1028 | dependencies: 1029 | error-ex "^1.2.0" 1030 | 1031 | parseurl@~1.3.3: 1032 | version "1.3.3" 1033 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" 1034 | integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== 1035 | 1036 | path-exists@^2.0.0: 1037 | version "2.1.0" 1038 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" 1039 | integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= 1040 | dependencies: 1041 | pinkie-promise "^2.0.0" 1042 | 1043 | path-exists@^3.0.0: 1044 | version "3.0.0" 1045 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" 1046 | integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= 1047 | 1048 | path-is-absolute@^1.0.0: 1049 | version "1.0.1" 1050 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 1051 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 1052 | 1053 | path-is-inside@^1.0.2: 1054 | version "1.0.2" 1055 | resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" 1056 | integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= 1057 | 1058 | path-key@^2.0.1: 1059 | version "2.0.1" 1060 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" 1061 | integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= 1062 | 1063 | path-parse@^1.0.6: 1064 | version "1.0.6" 1065 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" 1066 | integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== 1067 | 1068 | path-to-regexp@0.1.7: 1069 | version "0.1.7" 1070 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 1071 | integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= 1072 | 1073 | path-type@^2.0.0: 1074 | version "2.0.0" 1075 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" 1076 | integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= 1077 | dependencies: 1078 | pify "^2.0.0" 1079 | 1080 | pify@^2.0.0: 1081 | version "2.3.0" 1082 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 1083 | integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= 1084 | 1085 | pinkie-promise@^2.0.0: 1086 | version "2.0.1" 1087 | resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" 1088 | integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= 1089 | dependencies: 1090 | pinkie "^2.0.0" 1091 | 1092 | pinkie@^2.0.0: 1093 | version "2.0.4" 1094 | resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" 1095 | integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= 1096 | 1097 | pkg-dir@^1.0.0: 1098 | version "1.0.0" 1099 | resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" 1100 | integrity sha1-ektQio1bstYp1EcFb/TpyTFM89Q= 1101 | dependencies: 1102 | find-up "^1.0.0" 1103 | 1104 | prelude-ls@~1.1.2: 1105 | version "1.1.2" 1106 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" 1107 | integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= 1108 | 1109 | progress@^2.0.0: 1110 | version "2.0.2" 1111 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.2.tgz#db9476f916bcc9ebe8a04efb22b18b0740376c61" 1112 | integrity sha512-/OLz5F9beZUWwSHZDreXgap1XShX6W+DCHQCqwCF7uZ88s6uTlD2cR3JBE77SegCmNtb1Idst+NfmwcdU6KVhw== 1113 | 1114 | proxy-addr@~2.0.5: 1115 | version "2.0.5" 1116 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" 1117 | integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== 1118 | dependencies: 1119 | forwarded "~0.1.2" 1120 | ipaddr.js "1.9.0" 1121 | 1122 | punycode@^2.1.0: 1123 | version "2.1.1" 1124 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 1125 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== 1126 | 1127 | qs@6.7.0: 1128 | version "6.7.0" 1129 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" 1130 | integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== 1131 | 1132 | range-parser@~1.2.1: 1133 | version "1.2.1" 1134 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" 1135 | integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== 1136 | 1137 | raw-body@2.4.0: 1138 | version "2.4.0" 1139 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" 1140 | integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== 1141 | dependencies: 1142 | bytes "3.1.0" 1143 | http-errors "1.7.2" 1144 | iconv-lite "0.4.24" 1145 | unpipe "1.0.0" 1146 | 1147 | read-pkg-up@^2.0.0: 1148 | version "2.0.0" 1149 | resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" 1150 | integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= 1151 | dependencies: 1152 | find-up "^2.0.0" 1153 | read-pkg "^2.0.0" 1154 | 1155 | read-pkg@^2.0.0: 1156 | version "2.0.0" 1157 | resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" 1158 | integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= 1159 | dependencies: 1160 | load-json-file "^2.0.0" 1161 | normalize-package-data "^2.3.2" 1162 | path-type "^2.0.0" 1163 | 1164 | regexpp@^2.0.1: 1165 | version "2.0.1" 1166 | resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" 1167 | integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== 1168 | 1169 | resolve-from@^4.0.0: 1170 | version "4.0.0" 1171 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" 1172 | integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== 1173 | 1174 | resolve@^1.10.0, resolve@^1.5.0, resolve@^1.6.0, resolve@^1.8.1: 1175 | version "1.11.1" 1176 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" 1177 | integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw== 1178 | dependencies: 1179 | path-parse "^1.0.6" 1180 | 1181 | restore-cursor@^2.0.0: 1182 | version "2.0.0" 1183 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" 1184 | integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= 1185 | dependencies: 1186 | onetime "^2.0.0" 1187 | signal-exit "^3.0.2" 1188 | 1189 | rimraf@2.6.3: 1190 | version "2.6.3" 1191 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" 1192 | integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== 1193 | dependencies: 1194 | glob "^7.1.3" 1195 | 1196 | run-async@^2.2.0: 1197 | version "2.3.0" 1198 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" 1199 | integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= 1200 | dependencies: 1201 | is-promise "^2.1.0" 1202 | 1203 | rxjs@^6.4.0: 1204 | version "6.5.2" 1205 | resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.2.tgz#2e35ce815cd46d84d02a209fb4e5921e051dbec7" 1206 | integrity sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg== 1207 | dependencies: 1208 | tslib "^1.9.0" 1209 | 1210 | safe-buffer@5.1.2: 1211 | version "5.1.2" 1212 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 1213 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 1214 | 1215 | "safer-buffer@>= 2.1.2 < 3": 1216 | version "2.1.2" 1217 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 1218 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 1219 | 1220 | "semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.5.1: 1221 | version "5.6.0" 1222 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" 1223 | integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== 1224 | 1225 | send@0.17.1: 1226 | version "0.17.1" 1227 | resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" 1228 | integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== 1229 | dependencies: 1230 | debug "2.6.9" 1231 | depd "~1.1.2" 1232 | destroy "~1.0.4" 1233 | encodeurl "~1.0.2" 1234 | escape-html "~1.0.3" 1235 | etag "~1.8.1" 1236 | fresh "0.5.2" 1237 | http-errors "~1.7.2" 1238 | mime "1.6.0" 1239 | ms "2.1.1" 1240 | on-finished "~2.3.0" 1241 | range-parser "~1.2.1" 1242 | statuses "~1.5.0" 1243 | 1244 | serve-static@1.14.1: 1245 | version "1.14.1" 1246 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" 1247 | integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== 1248 | dependencies: 1249 | encodeurl "~1.0.2" 1250 | escape-html "~1.0.3" 1251 | parseurl "~1.3.3" 1252 | send "0.17.1" 1253 | 1254 | setprototypeof@1.1.1: 1255 | version "1.1.1" 1256 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" 1257 | integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== 1258 | 1259 | shebang-command@^1.2.0: 1260 | version "1.2.0" 1261 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 1262 | integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= 1263 | dependencies: 1264 | shebang-regex "^1.0.0" 1265 | 1266 | shebang-regex@^1.0.0: 1267 | version "1.0.0" 1268 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 1269 | integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= 1270 | 1271 | signal-exit@^3.0.2: 1272 | version "3.0.2" 1273 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 1274 | integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= 1275 | 1276 | slice-ansi@^2.1.0: 1277 | version "2.1.0" 1278 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" 1279 | integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== 1280 | dependencies: 1281 | ansi-styles "^3.2.0" 1282 | astral-regex "^1.0.0" 1283 | is-fullwidth-code-point "^2.0.0" 1284 | 1285 | spdx-correct@^3.0.0: 1286 | version "3.0.2" 1287 | resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.2.tgz#19bb409e91b47b1ad54159243f7312a858db3c2e" 1288 | integrity sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ== 1289 | dependencies: 1290 | spdx-expression-parse "^3.0.0" 1291 | spdx-license-ids "^3.0.0" 1292 | 1293 | spdx-exceptions@^2.1.0: 1294 | version "2.2.0" 1295 | resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" 1296 | integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== 1297 | 1298 | spdx-expression-parse@^3.0.0: 1299 | version "3.0.0" 1300 | resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" 1301 | integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== 1302 | dependencies: 1303 | spdx-exceptions "^2.1.0" 1304 | spdx-license-ids "^3.0.0" 1305 | 1306 | spdx-license-ids@^3.0.0: 1307 | version "3.0.2" 1308 | resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz#a59efc09784c2a5bada13cfeaf5c75dd214044d2" 1309 | integrity sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg== 1310 | 1311 | sprintf-js@~1.0.2: 1312 | version "1.0.3" 1313 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 1314 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 1315 | 1316 | "statuses@>= 1.5.0 < 2", statuses@~1.5.0: 1317 | version "1.5.0" 1318 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" 1319 | integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= 1320 | 1321 | string-width@^2.1.0: 1322 | version "2.1.1" 1323 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" 1324 | integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== 1325 | dependencies: 1326 | is-fullwidth-code-point "^2.0.0" 1327 | strip-ansi "^4.0.0" 1328 | 1329 | string-width@^3.0.0: 1330 | version "3.1.0" 1331 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" 1332 | integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== 1333 | dependencies: 1334 | emoji-regex "^7.0.1" 1335 | is-fullwidth-code-point "^2.0.0" 1336 | strip-ansi "^5.1.0" 1337 | 1338 | strip-ansi@^4.0.0: 1339 | version "4.0.0" 1340 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 1341 | integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= 1342 | dependencies: 1343 | ansi-regex "^3.0.0" 1344 | 1345 | strip-ansi@^5.1.0: 1346 | version "5.2.0" 1347 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" 1348 | integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== 1349 | dependencies: 1350 | ansi-regex "^4.1.0" 1351 | 1352 | strip-bom@^3.0.0: 1353 | version "3.0.0" 1354 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" 1355 | integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= 1356 | 1357 | strip-json-comments@^2.0.1: 1358 | version "2.0.1" 1359 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 1360 | integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= 1361 | 1362 | supports-color@^5.3.0: 1363 | version "5.5.0" 1364 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 1365 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 1366 | dependencies: 1367 | has-flag "^3.0.0" 1368 | 1369 | table@^5.2.3: 1370 | version "5.4.6" 1371 | resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" 1372 | integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== 1373 | dependencies: 1374 | ajv "^6.10.2" 1375 | lodash "^4.17.14" 1376 | slice-ansi "^2.1.0" 1377 | string-width "^3.0.0" 1378 | 1379 | teenytest-promise@^1.0.0: 1380 | version "1.0.0" 1381 | resolved "https://registry.yarnpkg.com/teenytest-promise/-/teenytest-promise-1.0.0.tgz#7a490eec83de9def49e0798aa2319f6c918f3e51" 1382 | integrity sha1-ekkO7IPene9J4HmKojGfbJGPPlE= 1383 | 1384 | teenytest@^5.1.1: 1385 | version "5.2.0" 1386 | resolved "https://registry.yarnpkg.com/teenytest/-/teenytest-5.2.0.tgz#a2ad7a595007eabf24a7e77d60e24b0f3cfb55f7" 1387 | integrity sha512-KG3zsvxzzKAdRF1zWBPiuuFqG6ZddEH0qrojD0hub4JnAc6P+a7sxlpUlRXBoIKQ9YzUr9rRdHkDBP85pOyYlQ== 1388 | dependencies: 1389 | async "^2.6.2" 1390 | function-names-at-line "^1.0.0" 1391 | glob "^7.1.3" 1392 | lodash "^4.17.11" 1393 | lodash-deeper "^1.0.0" 1394 | minimist "^1.2.0" 1395 | resolve "^1.10.0" 1396 | 1397 | text-table@^0.2.0: 1398 | version "0.2.0" 1399 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 1400 | integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= 1401 | 1402 | through@^2.3.6: 1403 | version "2.3.8" 1404 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 1405 | integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= 1406 | 1407 | tmp@^0.0.33: 1408 | version "0.0.33" 1409 | resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" 1410 | integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== 1411 | dependencies: 1412 | os-tmpdir "~1.0.2" 1413 | 1414 | toidentifier@1.0.0: 1415 | version "1.0.0" 1416 | resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" 1417 | integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== 1418 | 1419 | tslib@^1.9.0: 1420 | version "1.9.3" 1421 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" 1422 | integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== 1423 | 1424 | type-check@~0.3.2: 1425 | version "0.3.2" 1426 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 1427 | integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= 1428 | dependencies: 1429 | prelude-ls "~1.1.2" 1430 | 1431 | type-is@~1.6.17, type-is@~1.6.18: 1432 | version "1.6.18" 1433 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" 1434 | integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== 1435 | dependencies: 1436 | media-typer "0.3.0" 1437 | mime-types "~2.1.24" 1438 | 1439 | unpipe@1.0.0, unpipe@~1.0.0: 1440 | version "1.0.0" 1441 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 1442 | integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= 1443 | 1444 | uri-js@^4.2.2: 1445 | version "4.2.2" 1446 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" 1447 | integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== 1448 | dependencies: 1449 | punycode "^2.1.0" 1450 | 1451 | utils-merge@1.0.1: 1452 | version "1.0.1" 1453 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" 1454 | integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= 1455 | 1456 | validate-npm-package-license@^3.0.1: 1457 | version "3.0.4" 1458 | resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" 1459 | integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== 1460 | dependencies: 1461 | spdx-correct "^3.0.0" 1462 | spdx-expression-parse "^3.0.0" 1463 | 1464 | vary@~1.1.2: 1465 | version "1.1.2" 1466 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 1467 | integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= 1468 | 1469 | which@^1.2.9: 1470 | version "1.3.1" 1471 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 1472 | integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== 1473 | dependencies: 1474 | isexe "^2.0.0" 1475 | 1476 | wordwrap@~1.0.0: 1477 | version "1.0.0" 1478 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" 1479 | integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= 1480 | 1481 | wrappy@1: 1482 | version "1.0.2" 1483 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1484 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 1485 | 1486 | write@1.0.3: 1487 | version "1.0.3" 1488 | resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" 1489 | integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== 1490 | dependencies: 1491 | mkdirp "^0.5.1" 1492 | --------------------------------------------------------------------------------