diff --git a/build.sh b/build.sh index 1532442..d6204f8 100755 --- a/build.sh +++ b/build.sh @@ -32,7 +32,7 @@ build_docker_images() { trace "Updating SatoshiPortal repos" trace "Creating cyphernodeconf image" - docker build install/ -t cyphernode/cyphernodeconf:$CONF_VERSION + docker build cyphernodeconf_docker/ -t cyphernode/cyphernodeconf:$CONF_VERSION trace "Creating cyphernode images" docker build api_auth_docker/ -t cyphernode/gatekeeper:$GATEKEEPER_VERSION \ diff --git a/cyphernodeconf_docker/.dockerignore b/cyphernodeconf_docker/.dockerignore new file mode 100644 index 0000000..e178f23 --- /dev/null +++ b/cyphernodeconf_docker/.dockerignore @@ -0,0 +1,12 @@ +node_modules +Dockerfile +.dockerignore +.eslintignore +.eslintrc.json +.gitignore +jest.config.js +LICENSE +build.sh +run.sh +jest.config.js +test diff --git a/cyphernodeconf_docker/.eslintignore b/cyphernodeconf_docker/.eslintignore new file mode 100644 index 0000000..f177a5b --- /dev/null +++ b/cyphernodeconf_docker/.eslintignore @@ -0,0 +1,4 @@ +usr +scripts +config +node_modules diff --git a/cyphernodeconf_docker/.eslintrc.json b/cyphernodeconf_docker/.eslintrc.json new file mode 100644 index 0000000..7b62f1b --- /dev/null +++ b/cyphernodeconf_docker/.eslintrc.json @@ -0,0 +1,67 @@ +{ + "env": { + "es6": true, + "node": true, + "mocha": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 2017, + "sourceType": "module" + }, + "rules": { + "padding-line-between-statements": [ + "error", + { + "blankLine": "always", + "prev": "function", + "next": "*" + } + ], + "newline-per-chained-call": "error", + "no-tabs": "error", + "no-nested-ternary": "error", + "no-trailing-spaces": "error", + "no-multiple-empty-lines": "error", + "no-whitespace-before-property": "error", + "new-cap": "error", + "lines-around-comment": "error", + "key-spacing": "error", + "comma-style": "error", + "brace-style": "error", + "camelcase": "error", + "handle-callback-err": "error", + "no-new-require": "warn", + "no-sync": "warn", + "no-mixed-requires": "error", + "global-require": "error", + "comma-spacing": "error", + "eqeqeq": "error", + "curly": "error", + "keyword-spacing": "error", + "object-curly-spacing": [ + "warn", + "always" + ], + "object-curly-newline": [ + "warn", + "always" + ], + "indent": [ + "error", + 2 + ], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "single" + ], + "semi": [ + "error", + "always" + ] + } +} \ No newline at end of file diff --git a/cyphernodeconf_docker/.gitignore b/cyphernodeconf_docker/.gitignore new file mode 100644 index 0000000..f41bfce --- /dev/null +++ b/cyphernodeconf_docker/.gitignore @@ -0,0 +1,8 @@ +*~ +*.log +*.sw[a-z] +DEADJOE +node_modules +coverage +.DS_Store +.idea diff --git a/cyphernodeconf_docker/Dockerfile b/cyphernodeconf_docker/Dockerfile new file mode 100644 index 0000000..c80d124 --- /dev/null +++ b/cyphernodeconf_docker/Dockerfile @@ -0,0 +1,16 @@ +FROM node:12.2.0-alpine + +ENV EDITOR=/usr/bin/nano + +COPY . /app +WORKDIR /app + +RUN mkdir /data && \ + apk add --update su-exec p7zip openssl nano apache2-utils && \ + rm -rf /var/cache/apk/* && \ + npm ci --production + +WORKDIR /app + +ENTRYPOINT ["/sbin/su-exec"] + diff --git a/cyphernodeconf_docker/build.sh b/cyphernodeconf_docker/build.sh new file mode 100755 index 0000000..1146efc --- /dev/null +++ b/cyphernodeconf_docker/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +VERSION=v0.2.0-test + +docker build . -t cyphernode/cyphernodeconf:${VERSION} \ No newline at end of file diff --git a/cyphernodeconf_docker/features.json b/cyphernodeconf_docker/features.json new file mode 100644 index 0000000..84a1e9a --- /dev/null +++ b/cyphernodeconf_docker/features.json @@ -0,0 +1,10 @@ +[ + { + "name": "Lightning node", + "value": "lightning" + }, + { + "name": "Opentimestamps client", + "value": "otsclient" + } +] diff --git a/cyphernodeconf_docker/help.json b/cyphernodeconf_docker/help.json new file mode 100644 index 0000000..c321031 --- /dev/null +++ b/cyphernodeconf_docker/help.json @@ -0,0 +1,45 @@ +{ + "features": "What optional features do you want me to activate?", + "net": "Which Bitcoin network do you want Cyphernode to run on?", + "run_as_different_user": "I recommend running Cyphernode as a different user when possible. Using your current user would give Cyphernode your current access rights, which could be a security issue especially if you are a sudoer. Please note that this feature is not supported on OSX at runtime, but you will be fine activating it in case you want to use the configuration file on another machine.", + "username": "Run Cyphernode as what user? I recommend user cyphernode. If the user does not exist, I will create it for you.", + "use_xpub": "Cyphernode can derive Bitcoin addresses from an xPub and the derivation path you want. If you want, you can provide your xPub and derivation path right now and call 'derive' with only the index instead of having to pass your xPub and derivation path on each call.", + "xpub": "Cyphernode can derive addresses from your default xPub key. With that functionality, you don't have to provide your xPub every time you call the derivation endpoints.", + "derivation_path": "Cyphernode can derive addresses from your default derivation path. With that functionality, you don't have to provide your derivation path every time you call the derivation endpoints.", + "proxy_datapath": "The Cyphernode proxy container, which routes all the requests to the right services uses a sqlite3 database to keep track of some things. This DB will be mounted from a local path, easy to back up from outside Docker. If running on OSX, check mountable directories in Docker's File Sharing configs.", + "proxy_datapath_custom": " ", + "gatekeeper_clientkeyspassword": "The Gatekeeper checks all the incoming requests for the right permissions before delegating them to the proxy. Following the JWT standard, it uses HMAC signature verification to allow or deny access. Signatures are created and verified using secret keys. I am going to generate the secret keys and keep them in an encrypted file. You will be able to download this encrypted file later. Please provide the encryption passphrase.", + "gatekeeper_clientkeyspassword_c": " ", + "gatekeeper_recreatekeys": "The Gatekeeper keys already exist, do you want to regenerate them? This will overwrite existing ones.", + "gatekeeper_recreatecert": "The Gatekeeper TLS (SSL) certificates already exist, do you want to regenerate them? This will overwrite existing ones.", + "gatekeeper_datapath": "The Gatekeeper's files (TLS certs, HMAC keys, Groups/API) will be stored in a container's mounted directory. Please provide the local mounted path to that directory. If running on OSX, check mountable directories in Docker's File Sharing configs.", + "gatekeeper_datapath_custom": "Provide the full path name where the Gatekeeper's files will be saved.", + "gatekeeper_edit_apiproperties": "If you know what you are doing, it is possible to manually edit the API endpoints/groups authorization. (Not recommended)", + "gatekeeper_apiproperties": "You are about to edit the api.properties file. The format of the file is pretty simple: for each action, you will find what access group can access it. Admin group can do what Spender group can, and Spender group can do what Watcher group can. Internal group is for the endpoints accessible only within the Docker network, like the backoffice tasks used by the Cron container. The access groups for each API id/key are found in the keys.properties file.", + "gatekeeper_cns": "I use domain names and/or IP addresses to create valid TLS certificates. For example, if https://cyphernodehost/getbestblockhash and https://192.168.7.44/getbestblockhash will be used, enter cyphernodehost, 192.168.7.44 as a possible domains. 127.0.0.1, localhost, gatekeeper will be automatically added to your list. Make sure the provided domain names are in your DNS or client's hosts file and is reachable.", + "traefik_datapath": "The Traefik's files will be stored in a container's mounted directory. Please provide the local mounted path to that directory. If running on OSX, check mountable directories in Docker's File Sharing configs.", + "traefik_datapath_custom": "Provide the full path name where the Traefik's files will be saved.", + "bitcoin_mode": "Cyphernode will spawn a new Bitcoin Core full node for its own use. If you already have Bitcoin Core node data, you can use the directory containing that data directly or copy the contents of it to a new directory to be used by cyphernode. Be aware that the files might change ownership, if you run cyphernode as a different user. In case you want to move the blockchain data to another node you might need to change the owner to fit the configuration of that node.", + "bitcoin_node_ip": "Cyphernode uses Bitcoin Core RPC interface for its tasks. Please provide the IP address of your current Bitcoin Core node.", + "bitcoin_rpcuser": "Bitcoin Core's RPC username used by Cyphernode when calling the node.", + "bitcoin_rpcpassword": "Bitcoin Core's RPC password used by Cyphernode when calling the node.", + "bitcoin_prune": "If you don't have at least 350GB of disk space, you should run Bitcoin Core in prune mode. NOTE: when running Bitcoin Core in prune mode, the incoming transactions' fees cannot be computed by Cyphernode and won't be part of the addresses watching's callbacks payload.", + "bitcoin_prune_size": "Minimum size is 550. This option specifies the maximum number in MB Bitcoin Core will allocate for raw block & undo data.", + "bitcoin_uacomment": "User Agent string used by Bitcoin Core. (Optional)", + "bitcoin_datapath": "Path name to where Bitcoin Core's data files (blockchain data, wallets, configs, etc.) are stored. This directory will be mounted into the Bitcoin node's container. If you already have a sync'ed node, you can copy data there to be used by the node, instead of resyncing everything. NOTE: only copy chainstate/ and blocks/ contents. If running on OSX, check mountable directories in Docker's File Sharing configs.", + "bitcoin_datapath_custom": " ", + "bitcoin_expose": "By default, Bitcoin node ports (RPC and protocol) won't be published outside of Docker. Do you want to expose them so that your node can be accessed from outside of the Docker network?", + "lightning_implementation": "Multiple LN implementations exist. Please choose the one you want to use with Cyphernode.", + "lightning_external_ip": "If you want you LN node to be accessible from the Internet, provide the IP address that other LN nodes will use to connect to it. This is usually your router's public IP. NOTE: In case you are running Cyphernode at home. This option won't make your router forward needed LN ports; you still need to configure and manage that part yourself in your router configuration.", + "lightning_nodename": "LN nodes have names. Choose the name you want for yours.", + "lightning_nodecolor": "LN nodes have colors. Choose the color you want for yours in RGB format (RRGGBB). For example, pure red would be ff0000.", + "lightning_datapath": "Path name to where LN's data files are stored. This directory will be mounted into the LN node's container. If running on OSX, check mountable directories in Docker's File Sharing configs.", + "lightning_datapath_custom": " ", + "lightning_expose": "By default, LN node port will be published outside of Docker. Do you want to hide it so that your node can't be accessed from outside of the Docker network?", + "otsclient_datapath": "Full path where the OTS files will be stored. This path will be mounted into the otsclient container which will create the OTS files when stamping and update them when upgrading stamps. It will also be mounted to the proxy container so that it can serve the ots_getfile and send the OTS files to clients. If running on OSX, check mountable directories in Docker's File Sharing configs.", + "otsclient_datapath_custom": " ", + "installer_mode": "Only one installation mode is supported, right now: local docker (self-hosted). Choose wisely ;-)", + "installer_cleanup": "Do you want to remove this configurator Docker image after installation? This would free about 150MB of disk space.", + "docker_mode": "Cyphernode Docker services can be run using Docker Swarm (https://docs.docker.com/engine/swarm/) or docker-compose (https://docs.docker.com/compose/overview/). Both will work, some users prefer one to another depending on deployment types, scalability, current framework, etc.", + "__default__": "" +} diff --git a/cyphernodeconf_docker/index.js b/cyphernodeconf_docker/index.js new file mode 100644 index 0000000..18a7d31 --- /dev/null +++ b/cyphernodeconf_docker/index.js @@ -0,0 +1,12 @@ +const App = require( './lib/app.js' ); + +const main = async ( args ) => { + const app = new App(); + const noWizard = args.indexOf('recreate') !== -1; + await app.start( { + noWizard: noWizard, + noSplashScreen: noWizard + } ); +}; + +main( process.argv.slice( 2, process.argv.length ) ); diff --git a/cyphernodeconf_docker/jest.config.js b/cyphernodeconf_docker/jest.config.js new file mode 100644 index 0000000..5d2efb8 --- /dev/null +++ b/cyphernodeconf_docker/jest.config.js @@ -0,0 +1,185 @@ +// For a detailed explanation regarding each configuration property, visit: +// https://jestjs.io/docs/en/configuration.html + +module.exports = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // Respect "browser" field in package.json when resolving modules + // browser: false, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/private/var/folders/fb/q84grm8d0w3gz0y0yx_bvx700000gn/T/jest_dx", + + // Automatically clear mock calls and instances between every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + collectCoverage: true, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + // collectCoverageFrom: null, + + // The directory where Jest should output its coverage files + coverageDirectory: "coverage", + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // "/node_modules/" + // ], + + // A list of reporter names that Jest uses when writing coverage reports + coverageReporters: [ + //"json", + "text", + //"lcov", + //"clover" + ], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: null, + + // A path to a custom dependency extractor + // dependencyExtractor: null, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // Force coverage collection from ignored files usin a array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: null, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: null, + + // A set of global variables that need to be available in all test environments + // globals: {}, + + // An array of directory names to be searched recursively up from the requiring module's location + // moduleDirectories: [ + // "node_modules" + // ], + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "json", + // "jsx", + // "ts", + // "tsx", + // "node" + // ], + + // A map from regular expressions to module names that allow to stub out resources with a single module + // moduleNameMapper: {}, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: null, + + // Run tests from one or more projects + // projects: null, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state between every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: null, + + // Automatically restore mock state between every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: null, + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + testEnvironment: "node", + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + // testMatch: [ + // "**/__tests__/**/*.[jt]s?(x)", + // "**/?(*.)+(spec|test).[tj]s?(x)" + // ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: null, + + // This option allows use of a custom test runner + // testRunner: "jasmine2", + + // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href + // testURL: "http://localhost", + + // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" + // timers: "real", + + // A map from regular expressions to paths to transformers + // transform: null, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // "/node_modules/" + // ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: null, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; diff --git a/cyphernodeconf_docker/lib/ansi.js b/cyphernodeconf_docker/lib/ansi.js new file mode 100644 index 0000000..4d1d1e0 --- /dev/null +++ b/cyphernodeconf_docker/lib/ansi.js @@ -0,0 +1,4 @@ +module.exports = { + reset: '\u001B8\u001B[u', + clear: '\u001Bc' +}; \ No newline at end of file diff --git a/cyphernodeconf_docker/lib/apikey.js b/cyphernodeconf_docker/lib/apikey.js new file mode 100644 index 0000000..a5aa699 --- /dev/null +++ b/cyphernodeconf_docker/lib/apikey.js @@ -0,0 +1,83 @@ +const spawn = require('child_process').spawn; +const configEntryRegexp = /^kapi_id="(\w+)";kapi_key="(\w+)";kapi_groups="(.+)";(.+)$/; + +module.exports = class ApiKey { + constructor( id, groups, key, script ) { + this.setId(id || '000'); + this.setGroups(groups || ['stats'] ); + this.setScript(script || 'eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}' ); + this.setKey(key); + } + + setFromConfigEntry(configEntry ) { + const match = configEntryRegexp.exec( configEntry ); + if( match ) { + this.setId( match[1] ); + this.setKey( match[2] ); + this.setGroups( match[3].split(',').map( (e)=>e.trim() ) ) + this.setScript( match[4] ); + } + } + + setGroups( groups ) { + this.groups = groups; + } + + setId( id ) { + this.id = id; + } + + setScript( script ) { + this.script = script; + } + + setKey( key ) { + this.key = key; + } + + async randomiseKey() { + try { + + //const dd = spawn('/bin/dd if=/dev/urandom bs=32 count=1 | /usr/bin/xxd -pc 32'); + const dd = spawn("dd if=/dev/urandom bs=32 count=1 | xxd -pc32", [], {stdio: ['ignore', 'pipe', 'ignore' ], shell: true} ); + + const result = await new Promise( function(resolve, reject ) { + + let result = ''; + dd.stdout.on('data', function( a,b,c) { + let chunk = a.toString().trim(); + result += chunk; + }); + + dd.stdout.on('end', function() { + result = result.replace(/[^a-zA-Z0-9]/,''); + resolve(result); + }); + + dd.stdout.on('error', function(err) { + reject(err); + }) + }); + this.key = result; + + } catch( err ) { + reject(err); + } + } + + getKey() { + return this.key; + } + + getConfigEntry() { + if( !this.key ) { + return; + } + return `kapi_id="${this.id}";kapi_key="${this.key}";kapi_groups="${this.groups.join(',')}";${this.script}`; + } + + getClientInformation() { + return `${this.id}=${this.key}`; + } + +}; diff --git a/cyphernodeconf_docker/lib/app.js b/cyphernodeconf_docker/lib/app.js new file mode 100644 index 0000000..93683a7 --- /dev/null +++ b/cyphernodeconf_docker/lib/app.js @@ -0,0 +1,515 @@ +const {promisify} = require('util'); + +const fs = require('fs'); +const path = require('path'); +const chalk = require('chalk'); +const wrap = require('wrap-ansi'); +const validator = require('validator'); +const coinstring = require('coinstring'); +const inquirer = require('inquirer'); +const colorsys = require('colorsys'); +const ejs = require( 'ejs' ); +const ejsRenderFileAsync = promisify( ejs.renderFile ).bind( ejs ); + +const html2ansi = require('./html2ansi.js'); +const name = require('./name.js'); +const Archive = require('./archive.js'); +const ApiKey = require('./apikey.js'); +const Cert = require('./cert.js'); +const htpasswd = require( './htpasswd.js'); +const Config = require('./config.js'); +const SplashScreen = require( './splashScreen.js' ); +const ansi = require( './ansi.js' ); + +const features = require('../features.json'); + +const uaCommentRegexp = /^[a-zA-Z0-9 \.,:_\-\?\/@]+$/; // TODO: look for spec of unsafe chars +const userRegexp = /^[a-zA-Z0-9\._\-]+$/; +const maxWidth = 82; + +const keyIds = { + '000': ['stats'], + '001': ['stats','watcher'], + '002': ['stats','watcher','spender'], + '003': ['stats','watcher','spender','admin'] +}; + +const configArchiveFileName = 'config.7z'; +const keyArchiveFileName = 'client.7z'; + +const prefix = () => { + return chalk.green('Cyphernode')+': '; +}; + +const randomColor = () => { + const hex = colorsys.hslToHex( { h: (Math.random()*360)<<0, s: 50, l: 50 } ); + return hex.substr(1); +}; + +let prompters = []; +fs.readdirSync(path.join(__dirname, '..','prompters')).forEach((file) => { + prompters.push(require(path.join(__dirname, '..','prompters',file))); +}); + +module.exports = class App { + + constructor() { + this.features = features; + + if( fs.existsSync(path.join('/data', 'exitStatus.sh')) ) { + fs.unlinkSync(path.join('/data', 'exitStatus.sh')); + } + + this.splash = new SplashScreen( { + frameDir: path.join(__dirname, '..', 'splash' ), + enableFortune: false, + fortuneChalk: chalk.default.bold, + width: maxWidth, + fortuneSpacing: 3 + } ); + } + + async start( options ) { + + options = options || {}; + + this.sessionData = { + defaultDataDirBase: process.env.DEFAULT_DATADIR_BASE || process.env.HOME, + setupDir: process.env.SETUP_DIR || path.join( process.env.HOME, 'cyphernode' ), + default_username: process.env.DEFAULT_USER || '', + gatekeeper_version: process.env.GATEKEEPER_VERSION, + gatekeeper_cns: process.env.DEFAULT_CERT_HOSTNAME, + proxy_version: process.env.PROXY_VERSION, + proxycron_version: process.env.PROXYCRON_VERSION, + pycoin_version: process.env.PYCOIN_VERSION, + otsclient_version: process.env.OTSCLIENT_VERSION, + bitcoin_version: process.env.BITCOIN_VERSION, + lightning_version: process.env.LIGHTNING_VERSION, + notifier_version: process.env.NOTIFIER_VERSION, + setup_version: process.env.SETUP_VERSION, + noWizard: !!options.noWizard, + noSplashScreen: !!options.noSplashScreen, + lightning_nodename: name.generate(), + lightning_nodecolor: randomColor(), + installer_cleanup: false, + devmode: process.env.DEVMODE || false + }; + + await this.setupConfigArchive(); + + if( !this.sessionData.noSplashScreen ) { + await this.splash.show(); + } + + let missingProperties = []; + + if( this.config.validateErrors && this.config.validateErrors.length ) { + for( let error of this.config.validateErrors ) { + if( error.keyword === 'required' && error.params && error.params.missingProperty ) { + missingProperties.push( error.params.missingProperty ); + } + } + } + + if( this.sessionData.noWizard && missingProperties.length && this.config.isLoaded ) { + console.log(chalk.bold.red('Unable to migrate client.7z non-interactively. Rerun without the -r option') ); + process.exit(1); + } + + if( !this.sessionData.noWizard ) { + // save gatekeeper key password to check if it changed + this.sessionData.gatekeeper_clientkeyspassword = this.config.data.gatekeeper_clientkeyspassword; + if( missingProperties.length && this.config.isLoaded ) { + this.sessionData.markProperties = missingProperties; + } + await this.startWizard(); + } + + await this.processProps(); + await this.writeFiles(); + } + + async setupConfigArchive() { + + this.config = new Config( { + setup_version: this.sessionData.setup_version, + docker_versions: { + 'gatekeeper': this.sessionData.gatekeeper_version, + 'proxy': this.sessionData.proxy_version, + 'proxycron': this.sessionData.proxycron_version, + 'pycoin': this.sessionData.pycoin_version, + 'otsclient': this.sessionData.otsclient_version, + 'bitcoincore': this.sessionData.bitcoin_version, + 'clightning': this.sessionData.lightning_version, + 'notifier': this.sessionData.notifier_version + } + } ); + + if( !fs.existsSync(this.destinationPath(configArchiveFileName)) ) { + let r = {}; + process.stdout.write(ansi.clear+ansi.reset); + while( !r.password0 || !r.password1 || r.password0 !== r.password1 ) { + + if( r.password0 && r.password1 && r.password0 !== r.password1 ) { + console.log(chalk.bold.red('Passwords do not match')+'\n'); + } + + r = await this.prompt([{ + type: 'password', + name: 'password0', + message: prefix()+chalk.bold.blue('Choose your configuration password'), + filter: this.trimFilter + }, + { + type: 'password', + name: 'password1', + message: prefix()+chalk.bold.blue('Confirm your configuration password'), + filter: this.trimFilter + }]); + } + this.sessionData.configurationPassword = r.password0; + } else { + try { + let r = {}; + if( process.env.CFG_PASSWORD ) { + this.sessionData.configurationPassword = process.env.CFG_PASSWORD; + } else { + process.stdout.write(ansi.reset); + while( !r.password ) { + r = await this.prompt([{ + type: 'password', + name: 'password', + message: prefix()+chalk.bold.blue('Enter your configuration password?'), + filter: this.trimFilter + }]); + } + this.sessionData.configurationPassword = r.password; + } + try { + await this.config.deserialize( + this.destinationPath(configArchiveFileName), + this.sessionData.configurationPassword, + ); + + // store clientkeyspassword in sessionData so it can be retrieved by getDefault + // and a simple return will not result in a password mismatch + if( this.config.data.hasOwnProperty('gatekeeper_clientkeyspassword') ) { + this.sessionData.gatekeeper_clientkeyspassword_c = + this.config.data.gatekeeper_clientkeyspassword; + } + + } catch (e) { + console.log(chalk.bold.red(e)); + process.exit(); + } + } catch( err ) { + console.log(chalk.bold.red('config archive is corrupt.')); + process.exit(1); + } + } + + this.config.data.adminhash = await htpasswd(this.sessionData.configurationPassword); + + for( let c of this.features ) { + c.checked = this.isChecked( 'features', c.value ); + } + + } + + async startWizard() { + let r = await this.prompt([{ + type: 'confirm', + name: 'enablehelp', + message: prefix()+'Enable help?', + default: this.getDefault( 'enablehelp' ), + }]); + + this.config.data.enablehelp = r.enablehelp; + + if( this.config.data.enablehelp ) { + this.help = require('../help.json'); + } + + let prompts = []; + + for( let m of prompters ) { + let newPrompts = m.prompts(this); + + if( this.sessionData.markProperties && + this.sessionData.markProperties.length && + this.config.isLoaded ) { + + for( let prompt of newPrompts ) { + if( this.sessionData.markProperties.indexOf(prompt.name) !== -1 ) { + prompt.message = prompt.message+' '+chalk.bgGreen('new option'); + } + } + } + + prompts = prompts.concat(newPrompts); + } + + const props = await this.prompt(prompts); + + this.config.data = Object.assign(this.config.data, props); + } + + async processProps() { + + // creates keys if they don't exist or we say so. + if( this.config.data.gatekeeper_recreatekeys || + this.config.data.gatekeeper_keys.configEntries.length===0 ) { + + delete this.config.data.gatekeeper_recreatekeys; + + let configEntries = []; + let clientInformation = []; + + for( let keyId in keyIds ) { + const apikey = await this.createRandomKey( keyId, keyIds[keyId] ); + configEntries.push(apikey.getConfigEntry()); + clientInformation.push(apikey.getClientInformation()); + } + + this.config.data.gatekeeper_keys = { + configEntries: configEntries, + clientInformation: clientInformation + } + + } + + const cert = new Cert(); + this.sessionData.cns = cert.cns(this.config.data.gatekeeper_cns); + + // create certs if they don't exist or we say so. + if( this.config.data.gatekeeper_recreatecert || + !this.config.data.gatekeeper_sslcert || + !this.config.data.gatekeeper_sslkey ) { + delete this.config.data.gatekeeper_recreatecert; + const cert = new Cert(); + console.log(chalk.bold.green( '☕ Generating gatekeeper cert. This may take a while ☕' )); + try { + const result = await cert.create(this.sessionData.cns); + if( result.code === 0 ) { + this.config.data.gatekeeper_sslkey = result.key.toString(); + this.config.data.gatekeeper_sslcert = result.cert.toString(); + } else { + console.log(chalk.bold.red( 'error! Gatekeeper cert was not created' )); + } + } catch( err ) { + console.log(chalk.bold.red( 'error! Gatekeeper cert was not created' )); + } + } + } + + async createRandomKey( id, groups ) { + if( !id || !groups || !groups.length ) { + return; + } + const apikey = new ApiKey(); + apikey.setId(id); + apikey.setGroups(groups); + await apikey.randomiseKey(); + return apikey + } + + async writeFiles() { + + console.log( chalk.green( ' create' )+' '+configArchiveFileName ); + if( !this.config.serialize( + this.destinationPath(configArchiveFileName), + this.sessionData.configurationPassword + ) ) { + console.log(chalk.bold.red( 'error! Config archive was not written' )); + } + + const pathProps = [ + 'gatekeeper_datapath', + 'traefik_datapath', + 'proxy_datapath', + 'bitcoin_datapath', + 'lightning_datapath', + 'otsclient_datapath' + ]; + + for( let pathProp of pathProps ) { + if( this.config.data[pathProp] === '_custom' ) { + this.config.data[pathProp] = this.config.data[pathProp+'_custom'] || ''; + } + } + + for( let m of prompters ) { + const name = m.name(); + for( let t of m.templates(this.config.data) ) { + const p = path.join(name,t); + const destFile = this.destinationPath(p); + const targetDir = path.dirname( destFile ); + + if( !fs.existsSync(targetDir) ) { + fs.mkdirSync(targetDir, { recursive: true }); + } + const result = await ejsRenderFileAsync( this.templatePath(p), Object.assign({},this.config.data,this.sessionData), {} ); + + console.log( chalk.green( ' create' )+' '+p ); + fs.writeFileSync( destFile, result ); + + } + } + + console.log( chalk.green( ' create' )+' '+keyArchiveFileName ); + + if( this.config.data.gatekeeper_keys && this.config.data.gatekeeper_keys.clientInformation ) { + + if( this.sessionData.gatekeeper_clientkeyspassword !== this.config.data.gatekeeper_clientkeyspassword && + fs.existsSync(this.destinationPath(keyArchiveFileName)) ) { + fs.unlinkSync( this.destinationPath(keyArchiveFileName) ); + } + + const archive = new Archive( this.destinationPath(keyArchiveFileName), this.config.data.gatekeeper_clientkeyspassword ); + if( !await archive.writeEntry( 'keys.txt', this.config.data.gatekeeper_keys.clientInformation.join('\n') ) ) { + console.log(chalk.bold.red( 'error! Client gatekeeper key archive was not written' )); + } + if( !await archive.writeEntry( 'cacert.pem', this.config.data.gatekeeper_sslcert ) ) { + console.log(chalk.bold.red( 'error! Client gatekeeper key archive was not written' )); + } + } + + fs.writeFileSync(path.join('/data', 'exitStatus.sh'), 'EXIT_STATUS=0'); + + } + + async prompt( questions ) { + if( !questions ) { + return {}; + } + + const r = await inquirer.prompt( questions ); + return r; + } + + /* some utils */ + + destinationPath( relPath ) { + return path.join( '/data', relPath ); + } + + templatePath( relPath ) { + return path.join(__dirname, '..','templates',relPath ); + } + + isChecked(name, value ) { + return this.config.data && this.config.data[name] && this.config.data[name].indexOf(value) != -1 ; + } + + getDefault(name) { + + if( this.config && this.config.data && this.config.data.hasOwnProperty(name) ) { + return this.config.data[name]; + } + + if( this.sessionData && this.sessionData.hasOwnProperty(name) ) { + return this.sessionData[name]; + } + + } + + optional(input, validator) { + if( input === undefined || + input === null || + input === '' ) { + return true; + } + return validator(input); + } + + ipOrFQDNValidator(host ) { + host = (host+"").trim(); + if( !(validator.isIP(host) || + validator.isFQDN(host)) ) { + throw new Error( 'No IP address or fully qualified domain name' ) + } + return true; + } + + xkeyValidator(xpub ) { + // TOOD: check for version + if( !coinstring.isValid( xpub ) ) { + throw new Error('Not an extended key.'); + } + return true; + } + + pathValidator(p ) { + return true; + } + + derivationPathValidator(path ) { + return true; + } + + colorValidator(color) { + if( !validator.isHexadecimal(color) ) { + throw new Error('Not a hex color.'); + } + return true; + } + + lightningNodeNameValidator(name) { + if( !name || name.length > 32 ) { + throw new Error('Please enter anything shorter than 32 characters'); + } + return true; + } + + notEmptyValidator(path ) { + if( !path ) { + throw new Error('Please enter something'); + } + return true; + } + + usernameValidator(user ) { + if( !userRegexp.test( user ) ) { + throw new Error('Choose a valid username'); + } + return true; + } + + UACommentValidator(comment ) { + if( !uaCommentRegexp.test( comment ) ) { + throw new Error('Unsafe characters in UA comment. Please use only a-z, A-Z, 0-9, SPACE and .,:_?@'); + } + return true; + } + + trimFilter(input ) { + return (input+"").trim(); + } + + featureChoices() { + return this.features; + } + + setupDir() { + return this.sessionData.setupDir; + } + + defaultDataDirBase() { + return this.sessionData.defaultDataDirBase; + } + + getHelp(topic ) { + if( !this.config.data.enablehelp || !this.help ) { + return ''; + } + + const helpText = this.help[topic] || this.help['__default__']; + + if( !helpText ||helpText === '' ) { + return ''; + } + + return "\n\n"+wrap( html2ansi(helpText),maxWidth )+"\n\n"; + } + +}; diff --git a/cyphernodeconf_docker/lib/archive.js b/cyphernodeconf_docker/lib/archive.js new file mode 100644 index 0000000..d2ff216 --- /dev/null +++ b/cyphernodeconf_docker/lib/archive.js @@ -0,0 +1,76 @@ +const spawn = require('child_process').spawn; +const stringio = require('@rauschma/stringio'); +const defaultArgs = ['-t7z', '-ms=on', '-mhe=on']; + +module.exports = class Archive { + + constructor( file, password ) { + this.file = file || 'archive.7z' + this.password = password; + } + + async readEntry( entryName ) { + if( !entryName ) { + return; + } + let args = defaultArgs.slice(); + args.unshift('x'); + args.push( '-so' ); + if( this.password ) { + args.push('-p'+this.password ); + } + args.push( this.file ) + args.push( entryName ) + const archiver = spawn('7z', args, { stdio: ['ignore', 'pipe', 'ignore'] } ); + const result = await stringio.readableToString(archiver.stdout); + try { + await stringio.onExit( archiver ); + } catch( err ) { + return { error: err }; + } + return { error: null, value: result }; + } + + async writeEntry( entryName, content ) { + if( !entryName ) { + return; + } + let args = defaultArgs.slice(); + args.unshift('a'); + if( this.password ) { + args.push('-p'+this.password ); + } + args.push( '-si'+entryName ); + args.push( this.file ) + const archiver = spawn('7z', args, { stdio: ['pipe', 'ignore', 'ignore' ] } ); + await stringio.streamWrite(archiver.stdin, content); + await stringio.streamEnd(archiver.stdin); + try { + await stringio.onExit( archiver ); + } catch( err ) { + return false; + } + return true; + } + + async deleteEntry( entryName ) { + if( !entryName ) { + return; + } + let args = defaultArgs.slice(); + args.unshift('d'); + if( this.password ) { + args.push('-p'+this.password ); + } + args.push( this.file ) + args.push( entryName ) + const archiver = spawn('7z', args, { stdio: ['ignore', 'pipe','ignore'] } ); + try { + await stringio.onExit( archiver ); + } catch( err ) { + return false; + } + return true; + } + +} diff --git a/cyphernodeconf_docker/lib/cert.js b/cyphernodeconf_docker/lib/cert.js new file mode 100644 index 0000000..33a534b --- /dev/null +++ b/cyphernodeconf_docker/lib/cert.js @@ -0,0 +1,125 @@ +const fs = require('fs'); +const spawn = require('child_process').spawn; +const defaultArgs = ['req', '-x509', '-newkey', 'rsa:4096', '-nodes']; +const path = require('path'); +const tmp = require('tmp'); +const validator = require('validator'); + +const confTmpl = ` +[req] +distinguished_name = req_distinguished_name +x509_extensions = v3_ca +prompt = no +[req_distinguished_name] +CN = %PRIMARY_CN% +[v3_ca] +subjectAltName = @alt_names +[alt_names] +%ALT_DOMAINS% +%ALT_IPS% +`; + +const domainTmpl = 'DNS.%#% = %DOMAIN%'; +const ipTmpl = 'IP.%#% = %IP%'; + +const standardCNs = ['127.0.0.1','localhost','gatekeeper']; + +module.exports = class Cert { + + constructor( options ) { + options = options || {}; + this.args = options.args || { days: 3650 }; + } + + cns( cnsString ) { + if( !cnsString ) { + return standardCNs; + } + const cns = cnsString.split(',').map(e=>e.trim().toLowerCase()).filter(e=>!!e); + + if( cns.length ) { + return standardCNs.concat(cns); + } + return standardCNs; + } + + buildConfig( cns ) { + + let ips = []; + let domains = []; + + for( let cn of cns ) { + if( validator.isIP(cn) ) { + ips.push( cn ); + } else { + domains.push( cn ); + } + } + + let conf = confTmpl; + + if( !domains.length ) { + domains.push('localhost'); + } + + conf = conf.replace( '%PRIMARY_CN%', domains[0] ) + + let domainCount = 0; + domains = domains.map( d => domainTmpl.replace( '%#%', ++domainCount ).replace('%DOMAIN%', d) ); + conf = conf.replace( '%ALT_DOMAINS%', domains.join('\n') || '' ) + + let ipCount = 0; + ips = ips.map( ip => ipTmpl.replace( '%#%', ++ipCount ).replace('%IP%', ip) ); + conf = conf.replace( '%ALT_IPS%', ips.join('\n') || '' ) + + return conf; + } + + async create( cns ) { + if (!cns || !cns.length) { + throw 'No cns'; + } + + let args = defaultArgs.slice(); + + const certFileTmp = tmp.fileSync(); + const keyFileTmp = tmp.fileSync(); + const confFileTmp = tmp.fileSync(); + + args.push( '-out' ); + args.push( certFileTmp.name ); + args.push( '-keyout' ); + args.push( keyFileTmp.name ); + args.push( '-config' ); + args.push( confFileTmp.name ); + + for( let k in this.args ) { + args.push( '-'+k); + args.push( this.args[k] ); + } + + const conf = this.buildConfig( cns ); + fs.writeFileSync( confFileTmp.name, conf ); + + + let code = await new Promise( function(resolve, reject) { + const openssl = spawn('openssl', args, { stdio: ['ignore', 'ignore', 'ignore'] } ); + openssl.on('exit', (code) => { + resolve(code); + }); + }); + + const cert = fs.readFileSync( certFileTmp.name ); + const key = fs.readFileSync( keyFileTmp.name ); + + certFileTmp.removeCallback(); + keyFileTmp.removeCallback(); + confFileTmp.removeCallback(); + + return { + code: code, + key: key, + cert: cert + } + } +}; diff --git a/cyphernodeconf_docker/lib/config.js b/cyphernodeconf_docker/lib/config.js new file mode 100644 index 0000000..d1bd9eb --- /dev/null +++ b/cyphernodeconf_docker/lib/config.js @@ -0,0 +1,186 @@ +const Ajv = require('ajv'); +const fs = require('fs'); +const Archive = require('./archive.js'); +const ApiKey = require('./apikey.js'); +const name = require('./name.js'); +const colorsys = require( 'colorsys'); + +const latestSchemaVersion='0.2.0'; +const defaultSchemaVersion='0.1.0'; + +const schemas = { + '0.1.0': require('../schema/config-v0.1.0.json'), + '0.2.0': require('../schema/config-v0.2.0.json'), +}; + +module.exports = class Config { + + constructor( options ) { + + options = options || {}; + this.setup_version = options.setup_version; + this.docker_versions = options.docker_versions; + + const ajv = new Ajv({ + removeAdditional: true, + useDefaults: true, + coerceTypes: true, + allErrors: true + }); + + this.validators = {}; + + for( let v in schemas ) { + this.validators[v]=ajv.compile(schemas[v]); + } + + + this.migrationPaths = { + '0.1.0|0.2.0': [ this.migrate_0_1_0_to_0_2_0 ] + }; + + this.setData( { schema_version: latestSchemaVersion } ); + this.isLoaded = false; + } + + setData( data ) { + if( !data ) { + return; + } + this.data = data; + this.data.schema_version = this.data.schema_version || this.data.__version || defaultSchemaVersion; + this.data.setup_version = this.data.setup_version || this.setup_version; + this.data.docker_versions = this.data.docker_versions || this.docker_versions; + this.validate(); + } + + async serialize( path, password ) { + this.resolveConfigConflicts(); + this.validate(); + const configJsonString = JSON.stringify(this.data, null, 4); + const archive = new Archive( path, password ); + return await archive.writeEntry( 'config.json', configJsonString ); + } + + async deserialize( path, password ) { + + if( fs.existsSync(path) ) { + + const archive = new Archive( path, password ); + + const r = await archive.readEntry('config.json'); + + if( r.error ) { + throw( 'Password is wrong. Have a nice day.' ); + } + + if( !r.value ) { + throw('config archive is corrupt.'); + } + + this.setData( JSON.parse(r.value) ); + this.isLoaded = true; + } + + //this.resolveConfigConflicts(); + if( this.data.schema_version !== latestSchemaVersion ) { + // migrate here + // create a copy of the old config + fs.copyFileSync( path, path+'-'+this.data.schema_version ); + await this.migrate(this.data.schema_version, latestSchemaVersion ); + // validate again to strip all illegal properties from config with latest version + this.validate(); + } + } + + resolveConfigConflicts() { + // TODO solve this in config schema + if( this.data.features && this.data.features.length && this.data.features.indexOf('lightning') !== -1 ) { + this.data.bitcoin_prune = false; + delete this.data.bitcoin_prune_size; + } + } + + validate() { + + if( !this.data.schema_version || + !this.validators[this.data.schema_version] || + Object.keys( schemas ).indexOf( this.data.schema_version ) == -1 ) { + throw "Unknown version in data" + } + + // this will assign default values from the schema + this.valid = this.validators[this.data.schema_version]( this.data ); + this.validateErrors = this.validators[this.data.schema_version].errors; + + } + + async migrate(sourceVersion, targetVersion) { + const migrations = this.migrationPaths[sourceVersion+'|'+targetVersion]; + + if( !migrations ) { + return; + } + + for( let migration of migrations ) { + await migration.apply(this); + } + } + + async migrate_0_1_0_to_0_2_0() { + if( this.data.schema_version != '0.1.0' ) { + return; + } + + this.data.schema_version = '0.2.0'; + + // rewrite specific properties with incompatible content + + // gatekeeper_keys: add stats group to all keys and add a label containing only + // the stats group + + const gatekeeper_keys = this.data.gatekeeper_keys; + + for( let i=0; i convert(d)).join(''): + data.value?data.value:''; + + switch(data.tagName){ + case 'br': + v += '\n' + break + case 'font': + if( data.attrs && data.attrs.length ) { + for( let attr of data.attrs ) { + if( attr.name === 'color' && /^#[a-f0-9]{6}$/.test(attr.value) ) { + v = chalk.hex(attr.value)(v); + } + if( attr.name === 'bold' && attr.value === 'true' ) { + v = chalk.bold(v); + } + if( attr.name === 'italic' && attr.value === 'true' ) { + v = chalk.italic(v); + } + if( attr.name === 'underline' && attr.value === 'true' ) { + v = chalk.underline(v); + } + if( attr.name === 'strikethrough' && attr.value === 'true' ) { + v = chalk.strikethrough(v); + } + } + } + break; + } + return v; +} + +module.exports = function(html){ + return convert(parse5.parseFragment(html, options)); +} diff --git a/cyphernodeconf_docker/lib/htpasswd.js b/cyphernodeconf_docker/lib/htpasswd.js new file mode 100644 index 0000000..1a0f612 --- /dev/null +++ b/cyphernodeconf_docker/lib/htpasswd.js @@ -0,0 +1,23 @@ +const exec = require('child_process').exec; + +module.exports = async ( password ) => { + + if( !password ) { + return null; + } + + password = password.replace(/'/g, `'\\''`); + + return await new Promise( (resolve) => { + exec('htpasswd -bnB admin \''+password+'\' | cut -sd \':\' -f2', (error, stdout, stderr) => { + if (error) { + return resolve(null); + } + // remove newline at the end + resolve(stdout.substr(0,stdout.length-1)); + }); + }); + + +}; + diff --git a/cyphernodeconf_docker/lib/name.js b/cyphernodeconf_docker/lib/name.js new file mode 100644 index 0000000..92825e0 --- /dev/null +++ b/cyphernodeconf_docker/lib/name.js @@ -0,0 +1,2370 @@ +const MAXLENGTH = 28; + +const ADJECTIVES = [ + /*a*/ ["Abiding", + "Able", + "Able-bodied", + "Abounding", + "Aboveboard", + "Absolute", + "Absolved", + "Abundant", + "Academic", + "Acceptable", + "Accepted", + "Accepting", + "Accessible", + "Acclaimed", + "Accommodating", + "Accomplished", + "Accordant", + "Accountable", + "Accredited", + "Accurate", + "Accustomed", + "Acknowledged", + "Acquainted", + "Active", + "Actual", + "Acuminous", + "Acute", + "Adamant", + "Adaptable", + "Adept", + "Adequate", + "Adjusted", + "Admirable", + "Admired", + "Admissible", + "Adonic", + "Adorable", + "Adored", + "Adroit", + "Advanced", + "Advantaged", + "Advantageous", + "Adventuresome", + "Adventurous", + "Advisable", + "Aesthetic", + "Aesthetical", + "Affable", + "Affecting", + "Affectionate", + "Affective", + "Affiliated", + "Affined", + "Affluent", + "Affluential", + "Ageless", + "Agile", + "Agreeable", + "Aholic", + "Alacritous", + "Alert", + "Alive", + "All-important", + "Allegiant", + "Allied", + "Alluring", + "Alright", + "Alternate", + "Altruistic", + "Amative", + "Amatory", + "Amazing", + "Ambidextrous", + "Ambitious", + "Amelioratory", + "Amenable", + "Amiable", + "Amicable", + "Amusing", + "Anamnestic", + "Angelic", + "Aplenty", + "Apollonian", + "Appealing", + "Appeasing", + "Appetent", + "Appetizing", + "Apposite", + "Appreciated", + "Appreciative", + "Apprehensible", + "Approachable", + "Appropriate", + "Approving", + "Apropos", + "Apt", + "Ardent", + "Aristocratic", + "Arousing", + "Arresting", + "Articulate", + "Artistic", + "Ascendant", + "Ascending", + "Aspirant", + "Aspiring", + "Assertive", + "Assiduous", + "Assistant", + "Assisting", + "Assistive", + "Associate", + "Associated", + "Associative", + "Assured", + "Assuring", + "Astir", + "Astonishing", + "Astounding", + "Astronomical", + "Astute", + "Athletic", + "Attainable", + "Attendant", + "Attentive", + "Attractive", + "Atypical", + "Au fait", + "August", + "Auspicious", + "Authentic", + "Authoritative", + "Authorized", + "Autonomous", + "Available", + "Avant-garde", + "Avid", + "Awaited", + "Awake", + "Aware", + "Awash", + "Awesome", + "Axiological"], + /*b*/ ["Balanced", + "Baronial", + "Beaming", + "Beatific", + "Beauteous", + "Beautified", + "Beautiful", + "Becoming", + "Beefy", + "Believable", + "Beloved", + "Benedictory", + "Benefic", + "Beneficent", + "Beneficial", + "Beneficiary", + "Benevolent", + "Benign", + "Benignant", + "Bent on", + "Best", + "Better", + "Big", + "Big-hearted", + "Big-time", + "Biggest", + "Bijou", + "Blameless", + "Blazing", + "Blessed", + "Blissful", + "Blithe", + "Blooming", + "Blue-ribbon", + "Bodacious", + "Boisterous", + "Bold", + "Bona fide", + "Bonny", + "Bonzer", + "Boss", + "Bound", + "Bounteous", + "Bountiful", + "Brainy", + "Brave", + "Brawny", + "Breezy", + "Brief", + "Bright", + "Brill", + "Brilliant", + "Brimming", + "Brisk", + "Broadminded", + "Brotherly", + "Bubbly", + "Budding", + "Buff", + "Bullish", + "Buoyant", + "Businesslike", + "Bustling", + "Busy", + "Buxom"], + /*c*/ ["Calm", + "Calmative", + "Calming", + "Can-do", + "Candescent", + "Canny", + "Canty", + "Capable", + "Capital", + "Captivating", + "Cared for", + "Carefree", + "Careful", + "Caring", + "Casual", + "Causative", + "Celebrated", + "Celeritous", + "Celestial", + "Centered", + "Central", + "Cerebral", + "Certain", + "Champion", + "Changeable", + "Changeless", + "Charismatic", + "Charitable", + "Charming", + "Cheerful", + "Cherished", + "Cherry", + "Chic", + "Childlike", + "Chipper", + "Chirpy", + "Chivalrous", + "Choice", + "Chosen", + "Chummy", + "Civic", + "Civil", + "Civilized", + "Clairvoyant", + "Classic", + "Classical", + "Classy", + "Clean", + "Clear", + "Clear-cut", + "Clearheaded", + "Clement", + "Clever", + "Close", + "Clubby", + "Coadjutant", + "Coequal", + "Cogent", + "Cognizant", + "Coherent", + "Collected", + "Colossal", + "Colourful", + "Coltish", + "Come-at-able", + "Comely", + "Comfortable", + "Comforting", + "Comic", + "Comical", + "Commanding", + "Commendable", + "Commendatory", + "Commending", + "Commiserative", + "Committed", + "Commodious", + "Commonsensical", + "Communicative", + "Commutual", + "Companionable", + "Compassionate", + "Compatible", + "Compelling", + "Competent", + "Complete", + "Completed", + "Complimentary", + "Composed", + "Comprehensive", + "Concentrated", + "Concise", + "Conclusive", + "Concordant", + "Concrete", + "Condolatory", + "Confederate", + "Conferrable", + "Confident", + "Congenial", + "Congruous", + "Connected", + "Conscientious", + "Conscious", + "Consensual", + "Consentaneous", + "Consentient", + "Consequential", + "Considerable", + "Considerate", + "Consistent", + "Consonant", + "Conspicuous", + "Constant", + "Constitutional", + "Constructive", + "Contemplative", + "Contemporary", + "Content", + "Contributive", + "Convenient", + "Conversant", + "Convictive", + "Convincing", + "Convivial", + "Cool", + "Cooperative", + "Coordinated", + "Copacetic", + "Copious", + "Cordial", + "Correct", + "Coruscant", + "Cosmic", + "Cosy", + "Courageous", + "Courteous", + "Courtly", + "Cozy", + "Crackerjack", + "Creamy", + "Creative", + "Credible", + "Creditable", + "Crisp", + "Crucial", + "Crystal (Clear)", + "Cuddly", + "Cultivated", + "Cultured", + "Cunning", + "Curious", + "Current", + "Curvaceous", + "Cushy", + "Cute", + "Cutting-edge"], + /*d*/ ["Dainty", + "Dandy", + "Dapper", + "Daring", + "Darling", + "Dashing", + "Dauntless", + "Dazzling", + "Dear", + "Debonair", + "Decent", + "Deciding", + "Decisive", + "Decorous", + "Dedicated", + "Deep", + "Defiant", + "Defiantly", + "Definite", + "Deft", + "Delectable", + "Deliberate", + "Delicate", + "Delicious", + "Delighted", + "Delightful", + "Deluxe", + "Demonstrative", + "Demulcent", + "Dependable", + "Deserving", + "Designer", + "Desirable", + "Desired", + "Desirous", + "Destined", + "Determined", + "Developed", + "Developing", + "Devoted", + "Devotional", + "Devout", + "Dexterous", + "Didactic", + "Different", + "Dignified", + "Diligent", + "Dinkum", + "Diplomatic", + "Direct", + "Disarming", + "Discerning", + "Disciplined", + "Discreet", + "Discrete", + "Discriminating", + "Dispassionate", + "Distinct", + "Distinctive", + "Distinguished", + "Distinguishing", + "Diverse", + "Diverting", + "Divine", + "Doable", + "Dominant", + "Doting", + "Doubtless", + "Doughty", + "Down-to-earth", + "Dreamy", + "Driven", + "Driving", + "Durable", + "Dutiful", + "Dynamic", + "Dynamite"], + /*e*/ ["Eager", + "Early", + "Earnest", + "Earthly", + "Earthy", + "Easy", + "Easygoing", + "Ebullient", + "Eclectic", + "Economic", + "Economical", + "Ecstatic", + "Ecumenical", + "Edified", + "Educated", + "Educational", + "Effective", + "Effectual", + "Effervescent", + "Efficient", + "Effortless", + "Elaborate", + "Elated", + "Elating", + "Elder", + "Electric", + "Electrifying", + "Eleemosynary", + "Elegant", + "Elemental", + "Eligible", + "Eloquent", + "Emerging", + "Eminent", + "Empathetic", + "Employable", + "Empowered", + "Enamored", + "Enchanting", + "Encouraged", + "Encouraging", + "Endearing", + "Enduring", + "Energetic", + "Energizing", + "Engaging", + "Enhanced", + "Enjoyable", + "Enlightened", + "Enlightening", + "Enlivened", + "Enlivening", + "Enormous", + "Enough", + "Enriching", + "Enterprising", + "Entertaining", + "Enthralling", + "Enthusiastic", + "Enticing", + "Entrancing", + "Entrepreneurial", + "Epicurean", + "Epideictic", + "Equable", + "Equal", + "Equiponderant", + "Equipped", + "Equitable", + "Equivalent", + "Erotic", + "Erudite", + "Especial", + "Essential", + "Established", + "Esteemed", + "Esthetic", + "Esthetical", + "Eternal", + "Ethical", + "Euphoric", + "Even-handed", + "Eventful", + "Evident", + "Evocative", + "Exact", + "Exalted", + "Exceeding", + "Excellent", + "Exceptional", + "Executive", + "Exhilarating", + "Exotic", + "Expansive", + "Expectant", + "Expeditious", + "Expeditive", + "Expensive", + "Experienced", + "Explorative", + "Expressive", + "Exquisite", + "Extraordinary", + "Exuberant", + "Exultant", + "Eye-catching"], + /*f*/ ["Fab", + "Fabulous", + "Facile", + "Factual", + "Facultative", + "Fain", + "Fair", + "Faithful", + "Famed", + "Familial", + "Familiar", + "Family", + "Famous", + "Fancy", + "Fantastic", + "Far-reaching", + "Far-sighted", + "Fascinating", + "Fashionable", + "Fast", + "Faultless", + "Favorable", + "Favored", + "Favorite", + "Fearless", + "Feasible", + "Fecund", + "Felicitous", + "Fertile", + "Fervent", + "Festal", + "Festive", + "Fetching", + "Fiery", + "Fine", + "Finer", + "Finest", + "Firm", + "First", + "First-class", + "First-rate", + "Fit", + "Fitting", + "Flamboyant", + "Flash", + "Flashy", + "Flavorful", + "Flawless", + "Fleet", + "Flexible", + "Flourishing", + "Fluent", + "Flying", + "Focused", + "Fond", + "For real", + "Forceful", + "Foremost", + "Foresighted", + "Forgiving", + "Formidable", + "Forthcoming", + "Forthright", + "Fortified", + "Fortuitous", + "Fortunate", + "Forward", + "Foundational", + "Four-star", + "Foxy", + "Fragrant", + "Frank", + "Fraternal", + "Free", + "Freely", + "Fresh", + "Friendly", + "Frisky", + "Frolicsome", + "Front-page", + "Fruitful", + "Fulfilled", + "Fulfilling", + "Full", + "Fun", + "Funny", + "Futuristic"], + /*g*/ ["Gainful", + "Gallant", + "Galore", + "Game", + "Gamesome", + "Generous", + "Genial", + "Genteel", + "Gentle", + "Genuine", + "Germane", + "Get-at-able", + "Gettable", + "Giddy", + "Gifted", + "Giving", + "Glad", + "Glamorous", + "Gleaming", + "Gleeful", + "Glorious", + "Glowing", + "Gnarly", + "Goal-oriented", + "Godly", + "Golden", + "Good", + "Good-humored", + "Good-looking", + "Good-natured", + "Goodhearted", + "Goodly", + "Gorgeous", + "Graced", + "Graceful", + "Gracile", + "Gracious", + "Gradely", + "Graithly", + "Grand", + "Grateful", + "Gratified", + "Gratifying", + "Great", + "Greatest", + "Greathearted", + "Gregarious", + "Groovy", + "Grounded", + "Growing", + "Grown", + "Guaranteed", + "Gubernatorial", + "Guided", + "Guiding", + "Guileless", + "Guilt-free", + "Guiltless", + "Gumptious", + "Gustatory", + "Gutsy", + "Gymnastic"], + /*h*/ ["Halcyon", + "Hale", + "Hallowed", + "Handsome", + "Handy", + "Happening", + "Happy", + "Happy-go-lucky", + "Hard-working", + "Hardy", + "Harmless", + "Harmonious", + "Head", + "Healing", + "Healthful", + "Healthy", + "Heart-to-heart", + "Heartfelt", + "Hearty", + "Heavenly", + "Heedful", + "Hegemonic", + "Helpful", + "Hep", + "Heralded", + "Heroic", + "Heteroclite", + "Heuristic", + "High", + "High-class", + "High-minded", + "High-power", + "High-powered", + "High-priority", + "High-reaching", + "High-spirited", + "Highest", + "Highly regarded", + "Highly valued", + "Hilarious", + "Hip", + "Holy", + "Homely", + "Honest", + "Honeyed", + "Honorary", + "Honorable", + "Honored", + "Hopeful", + "Hortative", + "Hospitable", + "Hot", + "Hotshot", + "Huggy", + "Humane", + "Humanitarian", + "Humble", + "Humorous", + "Hunky", + "Hygienic", + "Hypersonic", + "Hypnotic"], + /*i*/ ["Ideal", + "Idealistic", + "Idiosyncratic", + "Idolized", + "Illimitable", + "Illuminated", + "Illuminating", + "Illustrious", + "Imaginative", + "Imitable", + "Immaculate", + "Immeasurable", + "Immediate", + "Immense", + "Immortal", + "Immune", + "Impartial", + "Impassioned", + "Impeccable", + "Impeccant", + "Imperturbable", + "Impish", + "Important", + "Impressive", + "Improved", + "Improving", + "Improvisational", + "In", + "Incisive", + "Included", + "Inclusive", + "Incomparable", + "Incomplex", + "Incontestable", + "Incontrovertible", + "Incorrupt", + "Incredible", + "Inculpable", + "Indefatigable", + "Independent", + "Indestructible", + "Indispensable", + "Indisputable", + "Individual", + "Individualistic", + "Indivisible", + "Indomitable", + "Indubitable", + "Industrious", + "Inerrant", + "Inexhaustible", + "Infallible", + "Infant", + "Infinite", + "Influential", + "Informative", + "Informed", + "Ingenious", + "Inimitable", + "Initiate", + "Initiative", + "Innocent", + "Innovative", + "Innoxious", + "Inquisitive", + "Insightful", + "Inspired", + "Inspiring", + "Inspiriting", + "Instantaneous", + "Instinctive", + "Instructive", + "Instrumental", + "Integral", + "Integrated", + "Intellectual", + "Intelligent", + "Intense", + "Intent", + "Interactive", + "Interconnected", + "Interested", + "Interesting", + "Internal", + "Intertwined", + "Intimate", + "Intoxicating", + "Intrepid", + "Intriguing", + "Introducer", + "Inventive", + "Invigorated", + "Invigorating", + "Invincible", + "Inviolable", + "Inviting", + "Irrefragable", + "Irrefutable", + "Irreplaceable", + "Irrepressible", + "Irreproachable", + "Irresistible"], + /*j*/ ["Jaculable", + "Jam-packed", + "Jaunty", + "Jazzed", + "Jazzy", + "Jessant", + "Jestful", + "Jesting", + "Jewelled", + "Jiggish", + "Jigjog", + "Jimp", + "Jobbing", + "Jocose", + "Jocoserious", + "Jocular", + "Joculatory", + "Jocund", + "Joint", + "Jointed", + "Jolif", + "Jolly", + "Jovial", + "Joyful", + "Joyous", + "Joysome", + "Jubilant", + "Judicious", + "Juicy", + "Jump", + "Just", + "Justified"], + /*k*/ ["Keen", + "Kempt", + "Key", + "Kind", + "Kind-hearted", + "Kindly", + "Kindred", + "Kinetic", + "King-sized", + "Kingly", + "Kissable", + "Knightly", + "Knowable", + "Knowing", + "Knowledgeable", + "Kooky"], + /*l*/ ["Ladylike", + "Laid-back", + "Large", + "Lasting", + "Latitudinarian", + "Laudable", + "Laureate", + "Lavish", + "Law-abiding", + "Lawful", + "Leading", + "Leading-edge", + "Learned", + "Legal", + "Legendary", + "Legible", + "Legit", + "Legitimate", + "Leisured", + "Leisurely", + "Lenien", + "Leonine", + "Lepid", + "Lettered", + "Level-headed", + "Liberal", + "Liberated", + "Liberating", + "Light-hearted", + "Lightly", + "Likable", + "Like", + "Like-minded", + "Liked", + "Likely", + "Limber", + "Lionhearted", + "Literary", + "Literate", + "Lithe", + "Lithesome", + "Live", + "Lively", + "Logical", + "Long-established", + "Long-standing", + "Lordly", + "Lovable", + "Loved", + "Lovely", + "Loving", + "Loyal", + "Lucent", + "Lucid", + "Lucky", + "Lucrative", + "Luminous", + "Luscious", + "Lush", + "Lustrous", + "Lusty", + "Luxuriant", + "Luxurious"], + /*m*/ ["Made", + "Magical", + "Magnanimous", + "Magnetic", + "Magnificent", + "Maiden", + "Main", + "Majestic", + "Major", + "Malleable", + "Manageable", + "Managerial", + "Manifest", + "Manly", + "Mannerly", + "Many", + "Marked", + "Marvelous", + "Masculine", + "Master", + "Masterful", + "Masterly", + "Matchless", + "Maternal", + "Matter-of-fact", + "Mature", + "Maturing", + "Maximal", + "Meaningful", + "Mediate", + "Meditative", + "Meek", + "Mellow", + "Melodious", + "Memorable", + "Merciful", + "Meritable", + "Meritorious", + "Merry", + "Mesmerizing", + "Metaphysical", + "Meteoric", + "Methodical", + "Meticulous", + "Mettlesome", + "Mighty", + "Mindful", + "Minikin", + "Ministerial", + "Mint", + "Miraculous", + "Mirthful", + "Mitigative", + "Mitigatory", + "Model", + "Modern", + "Modernistic", + "Modest", + "Momentous", + "Moneyed", + "Moral", + "More", + "Most", + "Mother", + "Motivated", + "Motivating", + "Motivational", + "Motor", + "Moving", + "Much", + "Mucho", + "Multidimensional", + "Multidisciplined", + "Multifaceted", + "Munificent", + "Muscular", + "Musical", + "Must", + "Mutual"], + /*n*/ ["National", + "Nationwide", + "Native", + "Natty", + "Natural", + "Nearby", + "Neat", + "Necessary", + "Needed", + "Neighborly", + "Neoteric", + "Nestling", + "Never-failing", + "New", + "New-fashioned", + "Newborn", + "Nice", + "Nifty", + "Nimble", + "Nimble-witted", + "Nippy", + "Noble", + "Noetic", + "Nonchalant", + "Nonpareil", + "Normal", + "Notable", + "Noted", + "Noteworthy", + "Noticeable", + "Nourished", + "Nourishing", + "Novel", + "Now", + "Nubile", + "Number one", + "Nutrimental"], + /*o*/ ["Objective", + "Obliging", + "Observant", + "Obtainable", + "Oecumenical", + "Official", + "OK", + "Okay", + "Olympian", + "On", + "Once", + "One", + "Onward", + "Open", + "Open-handed", + "Open-hearted", + "Open-minded", + "Operative", + "Opportune", + "Optimal", + "Optimistic", + "Optimum", + "Opulent", + "Orderly", + "Organic", + "Organized", + "Oriented", + "Original", + "Ornamental", + "Out-of-sight", + "Out-of-this-world", + "Outgoing", + "Outstanding", + "Overflowing", + "Overjoyed", + "Overriding", + "Overt"], + /*p*/ ["Palatable", + "Pally", + "Palpable", + "Par excellence", + "Paradisiac", + "Paradisiacal", + "Paramount", + "Parental", + "Parnassian", + "Participant", + "Participative", + "Particular", + "Partisan", + "Passionate", + "Paternal", + "Patient", + "Peaceable", + "Peaceful", + "Peachy", + "Peerless", + "Penetrating", + "Peppy", + "Perceptive", + "Perfect", + "Perky", + "Permanent", + "Permissive", + "Perseverant", + "Persevering", + "Persistent", + "Personable", + "Perspective", + "Perspicacious", + "Perspicuous", + "Persuasive", + "Pert", + "Pertinent", + "Pet", + "Petite", + "Phenomenal", + "Philanthropic", + "Philoprogenitive", + "Philosophical", + "Picked", + "Picturesque", + "Pierian", + "Pilot", + "Pioneering", + "Pious", + "Piquant", + "Pithy", + "Pivotal", + "Placid", + "Plausible", + "Playful", + "Pleasant", + "Pleased", + "Pleasing", + "Pleasurable", + "Plenary", + "Plenteous", + "Plentiful", + "Plenty", + "Pliable", + "Plucky", + "Plummy", + "Plus", + "Plush", + "Poetic", + "Poignant", + "Poised", + "Polished", + "Polite", + "Popular", + "Posh", + "Positive", + "Possible", + "Potent", + "Potential", + "Powerful", + "Practicable", + "Practical", + "Practised", + "Pragmatic", + "Praiseworthy", + "Prayerful", + "Precious", + "Precise", + "Predominant", + "Preeminent", + "Preferable", + "Preferred", + "Premier", + "Premium", + "Prepared", + "Preponderant", + "Prepotent", + "Present", + "Prestigious", + "Pretty", + "Prevailing", + "Prevalent", + "Prevenient", + "Primal", + "Primary", + "Prime", + "Prime mover", + "Primed", + "Primo", + "Princely", + "Principal", + "Principled", + "Pristine", + "Privileged", + "Prize", + "Prizewinning", + "Prized", + "Pro", + "Proactive", + "Probable", + "Probative", + "Procurable", + "Prodigious", + "Productive", + "Professional", + "Proficient", + "Profitable", + "Profound", + "Profuse", + "Progressive", + "Prolific", + "Prominent", + "Promising", + "Prompt", + "Proper", + "Propertied", + "Prophetic", + "Propitious", + "Prospective", + "Prosperous", + "Protean", + "Protective", + "Proud", + "Provocative", + "Prudent", + "Psyched up", + "Public-spirited", + "Puissant", + "Pukka", + "Pulchritudinous", + "Pumped up", + "Punchy", + "Punctilious", + "Punctual", + "Pure", + "Purposeful"], + /*q*/ ["Quaint", + "Qualified", + "Qualitative", + "Quality", + "Quantifiable", + "Queenly", + "Quemeful", + "Quick", + "Quick-witted", + "Quiet", + "Quietsome", + "Quintessential", + "Quirky", + "Quiver", + "Quixotic", + "Quotable"], + /*r*/ ["Racy", + "Rad", + "Radiant", + "Rapid", + "Rapturous", + "Rational", + "Razor-sharp", + "Reachable", + "Ready", + "Real", + "Realistic", + "Realizable", + "Reasonable", + "Reassuring", + "Receptive", + "Recherche", + "Recipient", + "Reciprocal", + "Recognizable", + "Recognized", + "Recommendable", + "Recuperative", + "Red-carpet", + "Refined", + "Reflective", + "Refreshing", + "Refulgent", + "Regal", + "Regnant", + "Regular", + "Rejuvenescent", + "Relaxed", + "Relevant", + "Reliable", + "Relieved", + "Remarkable", + "Remissive", + "Renowned", + "Reputable", + "Resilient", + "Resolute", + "Resolved", + "Resounding", + "Resourceful", + "Respectable", + "Respectful", + "Resplendent", + "Responsible", + "Responsive", + "Restful", + "Restorative", + "Retentive", + "Revealing", + "Revered", + "Reverent", + "Revitalizing", + "Revolutionary", + "Rewardable", + "Rewarding", + "Rhapsodic", + "Rich", + "Right", + "Righteous", + "Rightful", + "Risible", + "Robust", + "Rollicking", + "Romantic", + "Rooted", + "Rosy", + "Round", + "Rounded", + "Rousing", + "Rugged", + "Ruling"], + /*s*/ ["Saccharine", + "Sacred", + "Sacrosanct", + "Safe", + "Sagacious", + "Sage", + "Saintly", + "Salient", + "Salubrious", + "Salutary", + "Salutiferous", + "Sanctified", + "Sanctimonious", + "Sanctioned", + "Sanguine", + "Sapid", + "Sapient", + "Sapoforic", + "Sassy", + "Satisfactory", + "Satisfied", + "Satisfying", + "Saucy", + "Saving", + "Savory", + "Savvy", + "Scenic", + "Scholarly", + "Scientific", + "Scintillating", + "Scrumptious", + "Scrupulous", + "Seamless", + "Seasonal", + "Seasoned", + "Second-to-none", + "Secure", + "Sedulous", + "Seemly", + "Select", + "Self-assertive", + "Self-assured", + "Self-confident", + "Self-disciplined", + "Self-made", + "Self-sacrificing", + "Self-starting", + "Self-sufficient", + "Selfless", + "Sensational", + "Sensible", + "Sensitive", + "Sensual", + "Sensuous", + "Sentimental", + "Sequacious", + "Serendipitous", + "Serene", + "Service", + "Set", + "Settled", + "Sexual", + "Sexy", + "Shapely", + "Sharp", + "Shatterproof", + "Sheen", + "Shining", + "Shiny", + "Shipshape", + "Showy", + "Shrewd", + "Sightly", + "Significant", + "Silken", + "Silky", + "Silver", + "Silver-toned", + "Silvery", + "Simple", + "Sincere", + "Sinewy", + "Singular", + "Sisterly", + "Sizable", + "Sizzling", + "Skillful", + "Skilled", + "Sleek", + "Slick", + "Slinky", + "Smacking", + "Smart", + "Smashing", + "Smiley", + "Smooth", + "Snap", + "Snappy", + "Snazzy", + "Snod", + "Snug", + "Soaring", + "Sociable", + "Social", + "Societal", + "Soft", + "Soft-hearted", + "Soigne", + "Solicitous", + "Solid", + "Sonsy", + "Sooth", + "Soothing", + "Sophisticated", + "Sought-after", + "Soulful", + "Sound", + "Souped-up", + "Sovereign", + "Spacious", + "Spangly", + "Spanking", + "Sparkling", + "Sparkly", + "Special", + "Spectacular", + "Specular", + "Speedy", + "Spellbinding", + "Spicy", + "Spiffy", + "Spirited", + "Spiritual", + "Splendid", + "Splendiferous", + "Spontaneous", + "Sport", + "Sporting", + "Sportive", + "Sporty", + "Spot", + "Spotless", + "Spot on", + "Sprightly", + "Spruce", + "Spry", + "Spunky", + "Square", + "Stable", + "Stacked", + "Stainless", + "Stalwart", + "Staminal", + "Standard", + "Standing", + "Stand-up", + "Star", + "Starry", + "State", + "Stately", + "Statuesque", + "Staunch", + "Steadfast", + "Steady", + "Steamy", + "Stellar", + "Sterling", + "Sthenic", + "Stick-to-itive", + "Stimulant", + "Stimulating", + "Stimulative", + "Stipendiary", + "Stirred", + "Stirring", + "Stocky", + "Stoical", + "Storied", + "Stout", + "Stouthearted", + "Straight-out", + "Straightforward", + "Strapping", + "Strategic", + "Street-smart", + "Streetwise", + "Strenuous", + "Striking", + "Strong", + "Studious", + "Stunning", + "Stupendous", + "Sturdy", + "Stylish", + "Suasive", + "Suave", + "Sublime", + "Substantial", + "Substant", + "Substantive", + "Subtle", + "Successful", + "Succinct", + "Succulent", + "Sufficient", + "Sugary", + "Suitable", + "Sultry", + "Summary", + "Summery", + "Sumptuous", + "Sun-kissed", + "Sunny", + "Super", + "Superabundant", + "Super-duper", + "Supereminent", + "Superethical", + "Superexcellent", + "Superb", + "Supercalifragilisticexpialidocious", + "Superfluous", + "Superior", + "Superlative", + "Supernal", + "Supersonic", + "Supple", + "Supportive", + "Supreme", + "Sure", + "Sure-fire", + "Sure-footed", + "Sure-handed", + "Surpassing", + "Sustained", + "Svelte", + "Swank", + "Swashbuckling", + "Sweet", + "Swell", + "Swift", + "Swish", + "Sybaritic", + "Sylvan", + "Symmetrical", + "Sympathetic", + "Symphonious", + "Synergistic", + "Systematic"], + /*t*/ ["Tactful", + "Tailor-made", + "Take-charge", + "Talented", + "Tangible", + "Tasteful", + "Tasty", + "Teachable", + "Teeming", + "Tempean", + "Temperate", + "Tenable", + "Tenacious", + "Tender", + "Tender-hearted", + "Terrific", + "Testimonial", + "Thankful", + "Thankworthy", + "Therapeutic", + "Thorough", + "Thoughtful", + "Thrilled", + "Thrilling", + "Thriving", + "Tidy", + "Tight", + "Time-honored", + "Time-saving", + "Timeless", + "Timely", + "Tiptop", + "Tireless", + "Titanic", + "Titillating", + "Today", + "Together", + "Tolerant", + "Top", + "Top drawer", + "Top-notch", + "Tops", + "Total", + "Totally-tubular", + "Touching", + "Tough", + "Trailblazing", + "Tranquil", + "Transcendent", + "Transcendental", + "Transient", + "Transnormal", + "Transparent", + "Transpicuous", + "Traveled", + "Tremendous", + "Tretis", + "Trim", + "Triumphant", + "True", + "True-blue", + "Trustful", + "Trusting", + "Trustworthy", + "Trusty", + "Truthful", + "Tubular", + "Tuneful", + "Turgent", + "Tympanic"], + /*u*/ ["Uber", + "Ultimate", + "Ultra", + "Ultraprecise", + "Unabashed", + "Unadulterated", + "Unaffected", + "Unafraid", + "Unalloyed", + "Unambiguous", + "Unanimous", + "Unarguable", + "Unassuming", + "Unattached", + "Unbeaten", + "Unbelieavable", + "Unbiased", + "Unbigoted", + "Unblemished", + "Unbroken", + "Uncommon", + "Uncomplicated", + "Unconditional", + "Uncontestable", + "Unconventional", + "Uncorrupted", + "Uncritical", + "Undamaged", + "Undauntable", + "Undaunted", + "Undefeated", + "Undefiled", + "Undeniable", + "Under control", + "Understandable", + "Understanding", + "Understood", + "Undesigning", + "Undiminished", + "Undisputed", + "Undivided", + "Undoubted", + "Unencumbered", + "Unequalled", + "Unequivocal", + "Unerring", + "Unfailing", + "Unfaltering", + "Unfaultable", + "Unfeigned", + "Unfettered", + "Unflagging", + "Unflappable", + "Ungrudging", + "Unhampered", + "Unharmed", + "Unhesitating", + "Unhurt", + "Unified", + "Unimpaired", + "Unimpeachable", + "Unimpeded", + "Unique", + "United", + "Universal", + "Unlimited", + "Unmistakable", + "Unmitigated", + "Unobjectionable", + "Unobstructed", + "Unobtrusive", + "Unopposed", + "UnUnprejudiced", + "Unpretentious", + "Unquestionable", + "Unrefuted", + "Unreserved", + "Unrivalled", + "Unruffled", + "Unselfish", + "Unshakable", + "Unshaken", + "Unspoiled", + "Unspoilt", + "Unstoppable", + "Unsullied", + "Unsurpassed", + "Untarnished", + "Untiring", + "Untouched", + "Untroubled", + "Unusual", + "Unwavering", + "Up", + "Up-front", + "Up-to-date", + "Upbeat", + "Upcoming", + "Uplifted", + "Uplifting", + "Uppermost", + "Upright", + "Upstanding", + "Upward", + "Upwardly", + "Urbane", + "Usable", + "Useful", + "User-friendly", + "Utmost"], + /*v*/ ["Valiant", + "Valid", + "Validatory", + "Valorous", + "Valuable", + "Valued", + "Vast", + "Vaulting", + "Vehement", + "Venerable", + "Venturesome", + "Venust", + "Veracious", + "Verdurous", + "Veridical", + "Verified", + "Versatile", + "Versed", + "Very", + "Vestal", + "Veteran", + "Viable", + "Vibrant", + "Vibratile", + "Victor", + "Victorious", + "Vigilant", + "Vigorous", + "Virile", + "Virtuous", + "Visionary", + "Vital", + "Vivacious", + "Vivid", + "Vocal", + "Volant", + "Volitional", + "Voluptuous", + "Vulnerary"], + /*w*/ ["Wanted", + "Warm", + "Warm-hearted", + "Warranted", + "Wealthy", + "Weighty", + "Welcome", + "Welcomed", + "Welcoming", + "Weleful", + "Welfaring", + "Well", + "Well-behaved", + "Well-built", + "Well-disposed", + "Well-established", + "Well-founded", + "Well-grounded", + "Well-informed", + "Well-intentioned", + "Well-known", + "Well-liked", + "Well-made", + "Well-meaning", + "Well-planned", + "Well-read", + "Well-received", + "Well-spoken", + "Well-suited", + "Well-timed", + "Welsome", + "Whimsical", + "Whiz-bang", + "Whole", + "Wholehearted", + "Wholesome", + "Whopping", + "Wide-awake", + "Widely used", + "Willed", + "Willing", + "Winged", + "Winning", + "Winsome", + "Wired", + "Wise", + "With it", + "Within reach", + "Without equal", + "Witty", + "Wizard", + "Wizardly", + "Won", + "Wonderful", + "Wondrous", + "Workable", + "World-class", + "Worldly", + "Worldly-wise", + "Worshipful", + "Worth", + "Worthwhile", + "Worthy"], + /*y*/ ["Yern", + "Young", + "Young-at-Heart", + "Youthful", + "Yummy"], + /*z*/ ["Zaftig", + "Zany", + "Zappy", + "Zazzy", + "Zealed", + "Zealful", + "Zealous", + "Zestful", + "Zesty", + "Zingy", + "Zippy", + "Zootrophic", + "Zooty"], +]; + +const ANIMALS = [ + [ + "Aardvark", + "Abyssinian", + "Affenpinscher", + "Akbash", + "Akita", + "Albatross", + "Alligator", + "Alpaca", + "Angelfish", + "Ant", + "Anteater", + "Antelope", + "Ape", + "Armadillo", + "Avocet", + "Axolotl", + ], + [ + "Baboon", + "Badger", + "Balinese", + "Bandicoot", + "Barb", + "Barnacle", + "Barracuda", + "Bat", + "Beagle", + "Bear", + "Beaver", + "Bee", + "Beetle", + "Binturong", + "Bird", + "Birman", + "Bison", + "Bloodhound", + "Boar", + "Bobcat", + "Bombay", + "Bongo", + "Bonobo", + "Booby", + "Budgerigar", + "Buffalo", + "Bulldog", + "Bullfrog", + "Burmese", + "Butterfly" + ], + [ + "Caiman", + "Camel", + "Capybara", + "Caracal", + "Caribou", + "Cassowary", + "Cat", + "Caterpillar", + "Catfish", + "Cattle", + "Centipede", + "Chameleon", + "Chamois", + "Cheetah", + "Chicken", + "Chihuahua", + "Chimpanzee", + "Chinchilla", + "Chinook", + "Chipmunk", + "Chough", + "Cichlid", + "Clam", + "Coati", + "Cobra", + "Cockroach", + "Cod", + "Collie", + "Coral", + "Cormorant", + "Cougar", + "Cow", + "Coyote", + "Crab", + "Crane", + "Crocodile", + "Crow", + "Curlew", + "Cuscus", + "Cuttlefish" + ], + [ + "Dachshund", + "Dalmatian", + "Deer", + "Dhole", + "Dingo", + "Dinosaur", + "Discus", + "Dodo", + "Dog", + "Dogfish", + "Dolphin", + "Donkey", + "Dormouse", + "Dotterel", + "Dove", + "Dragonfly", + "Drever", + "Duck", + "Dugong", + "Dunker", + "Dunlin" + ], + [ + "Eagle", + "Earwig", + "Echidna", + "Eel", + "Eland", + "Elephant", + "Elephant seal", + "Elk", + "Emu" + ], + [ + "Falcon", + "Ferret", + "Finch", + "Fish", + "Flamingo", + "Flounder", + "Fly", + "Fossa", + "Fox", + "Frigatebird", + "Frog" + ], + [ + "Galago", + "Gar", + "Gaur", + "Gazelle", + "Gecko", + "Gerbil", + "Gharial", + "Giant Panda", + "Gibbon", + "Giraffe", + "Gnat", + "Gnu", + "Goat", + "Goldfinch", + "Goldfish", + "Goose", + "Gopher", + "Gorilla", + "Goshawk", + "Grasshopper", + "Greyhound", + "Grouse", + "Guanaco", + "Guinea fowl", + "Guinea pig", + "Gull", + "Guppy" + ], + [ + "Hamster", + "Hare", + "Harrier", + "Havanese", + "Hawk", + "Hedgehog", + "Heron", + "Herring", + "Himalayan", + "Hippopotamus", + "Hornet", + "Horse", + "Human", + "Hummingbird", + "Hyena" + ], + [ + "Ibis", + "Iguana", + "Impala", + "Indri", + "Insect" + ], + [ + "Jackal", + "Jaguar", + "Javanese", + "Jay", + "Jay, Blue", + "Jellyfish" + ],[ + "Kakapo", + "Kangaroo", + "Kingfisher", + "Kiwi", + "Koala", + "Komodo dragon", + "Kouprey", + "Kudu" + ], + [ + "Labradoodle", + "Ladybird", + "Lapwing", + "Lark", + "Lemming", + "Lemur", + "Leopard", + "Liger", + "Lion", + "Lionfish", + "Lizard", + "Llama", + "Lobster", + "Locust", + "Loris", + "Louse", + "Lynx", + "Lyrebird" + ], + [ + "Macaw", + "Magpie", + "Mallard", + "Maltese", + "Manatee", + "Mandrill", + "Markhor", + "Marten", + "Mastiff", + "Mayfly", + "Meerkat", + "Millipede", + "Mink", + "Mole", + "Molly", + "Mongoose", + "Mongrel", + "Monkey", + "Moorhen", + "Moose", + "Mosquito", + "Moth", + "Mouse", + "Mule" + ], + [ + "Narwhal", + "Neanderthal", + "Newfoundland", + "Newt", + "Nightingale", + "Numbat" + ], + [ + "Ocelot", + "Octopus", + "Okapi", + "Olm", + "Opossum", + "Orang-utan", + "Oryx", + "Ostrich", + "Otter", + "Owl", + "Ox", + "Oyster" + ], + [ + "Pademelon", + "Panther", + "Parrot", + "Partridge", + "Peacock", + "Peafowl", + "Pekingese", + "Pelican", + "Penguin", + "Persian", + "Pheasant", + "Pig", + "Pigeon", + "Pika", + "Pike", + "Piranha", + "Platypus", + "Pointer", + "Pony", + "Poodle", + "Porcupine", + "Porpoise", + "Possum", + "Prairie Dog", + "Prawn", + "Puffin", + "Pug", + "Puma" + ], + [ + "Quail", + "Quelea", + "Quetzal", + "Quokka", + "Quoll" + ], + [ + "Rabbit", + "Raccoon", + "Ragdoll", + "Rail", + "Ram", + "Rat", + "Rattlesnake", + "Raven", + "Red deer", + "Red panda", + "Reindeer", + "Rhinoceros", + "Robin", + "Rook", + "Rottweiler", + "Ruff" + ], + [ + "Salamander", + "Salmon", + "Sand Dollar", + "Sandpiper", + "Saola", + "Sardine", + "Scorpion", + "Sea lion", + "Sea Urchin", + "Seahorse", + "Seal", + "Serval", + "Shark", + "Sheep", + "Shrew", + "Shrimp", + "Siamese", + "Siberian", + "Skunk", + "Sloth", + "Snail", + "Snake", + "Snowshoe", + "Somali", + "Sparrow", + "Spider", + "Sponge", + "Squid", + "Squirrel", + "Starfish", + "Starling", + "Stingray", + "Stinkbug", + "Stoat", + "Stork", + "Swallow", + "Swan" + ], + [ + "Tang", + "Tapir", + "Tarsier", + "Termite", + "Tetra", + "Tiffany", + "Tiger", + "Toad", + "Tortoise", + "Toucan", + "Tropicbird", + "Trout", + "Tuatara", + "Turkey", + "Turtle" + ], + [ + "Uakari", + "Uguisu", + "Umbrellabird" + ], + [ + "Vicuña", + "Viper", + "Vulture" + ], + [ + "Wallaby", + "Walrus", + "Warthog", + "Wasp", + "Water buffalo", + "Weasel", + "Whale", + "Whippet", + "Wildebeest", + "Wolf", + "Wolverine", + "Wombat", + "Woodcock", + "Woodlouse", + "Woodpecker", + "Worm", + "Wrasse", + "Wren" + ], + [ + "Yak" + ], + [ + "Zebra", + "Zebu", + "Zonkey", + "Zorse" + ] +]; + +module.exports.generate = function() { + const index = Math.round((Math.random()*(ADJECTIVES.length-1))); + let name; + do { + name = ( + '🚀 '+ADJECTIVES[index][Math.round(Math.random()*(ADJECTIVES[index].length-1))]+ + ' '+ + ANIMALS[index][Math.round(Math.random()*(ANIMALS[index].length-1))]+' 🚀' + ).replace(/\s+/g, ' '); + } while( name.length >= MAXLENGTH ); + + return name +} + diff --git a/cyphernodeconf_docker/lib/splashScreen.js b/cyphernodeconf_docker/lib/splashScreen.js new file mode 100644 index 0000000..3273e9b --- /dev/null +++ b/cyphernodeconf_docker/lib/splashScreen.js @@ -0,0 +1,135 @@ +const fs = require('fs'); +const path = require('path'); +const ansi = require( './ansi.js' ); +const lunicode = require('lunicode'); + +const easeOutCubic = (t, b, c, d) => { + return c*((t=t/d-1)*t*t+1)+b; +}; + +lunicode.tools.creepify.options.top = true; // add diacritics on top. Default: true +lunicode.tools.creepify.options.middle = true; // add diacritics in the middle. Default: true +lunicode.tools.creepify.options.bottom = true; // add diacritics on the bottom. Default: true +lunicode.tools.creepify.options.maxHeight = 15; // How many diacritic marks shall we put on top/bottom? Default: 15 +lunicode.tools.creepify.options.randomization = 100; // 0-100%. maxHeight 100 and randomization 20%: the height goes from 80 to 100. randomization 70%: height goes from 30 to 100. Default: 100 + +const fortunes = [ + 'Cause fuck central banking', + 'Not your keys, not your bitcoin', + 'Don\'t trust, verify', + 'Craig Wright is a fraud', + 'HODL!' +]; + +module.exports = class SplashScreen { + + constructor( options ) { + options = options || {}; + + if( !options.frameDir ) { + throw "no frame directory to load" + } + + this.width = options.width || 82; + this.fortuneEnabled = !!options.enableFortune; + this.fortuneSpacing = options.fortuneSpacing || 0; + this.fortuneChalk = options.fortuneChalk; + + this.loadFramesFromDir( options.frameDir ); + + if( this.fortuneEnabled ) { + + let fortune = this.fortune(); + if( fortune.length > this.width-2 ) { + fortune = fortune.substr(0,this.width-2); + } + fortune = this.center(fortune); + + let fortuneLines = []; + + + + + fortuneLines.push( this.creepify(fortune) )+'\n'; + + + + + for( let i=0; i { + this.frames.push(fs.readFileSync(path.join(__dirname,'..','splash',file))); + }); + } + + sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + fortune() { + return fortunes[ Math.random()*fortunes.length << 0 ]; + } + + creepify( string ) { + if( this.fortuneChalk ) { + return this.fortuneChalk(lunicode.tools.creepify.encode( string )); + } + return lunicode.tools.creepify.encode( string ); + } + + center( string ) { + const offset = ((this.width - string.length)*0.5) << 0; + for( let i=0; i 1 ) { + await this.sleep(400); + + for( let frame of this.frames ) { + process.stdout.write(ansi.reset); + process.stdout.write(frame.toString()); + await this.sleep(33); + } + } + + await this.sleep(400); + process.stdout.write('\n'); + } + +}; \ No newline at end of file diff --git a/cyphernodeconf_docker/package-lock.json b/cyphernodeconf_docker/package-lock.json new file mode 100644 index 0000000..f4c4d86 --- /dev/null +++ b/cyphernodeconf_docker/package-lock.json @@ -0,0 +1,5486 @@ +{ + "name": "cyphernodeconf", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/core": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.5.tgz", + "integrity": "sha512-OvjIh6aqXtlsA8ujtGKfC7LYWksYSX8yQcM8Ay3LuvVeQ63lcOKgoZWVqcpFwkd29aYU9rVx7jxhfhiEDV9MZA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.4.4", + "@babel/helpers": "^7.4.4", + "@babel/parser": "^7.4.5", + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.4.5", + "@babel/types": "^7.4.4", + "convert-source-map": "^1.1.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.11", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", + "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4", + "jsesc": "^2.5.1", + "lodash": "^4.17.11", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", + "dev": true + }, + "@babel/helper-split-export-declaration": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", + "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@babel/helpers": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.4.tgz", + "integrity": "sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==", + "dev": true, + "requires": { + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", + "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==", + "dev": true + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", + "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", + "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/traverse": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz", + "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.4.4", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.4.5", + "@babel/types": "^7.4.4", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.11" + } + }, + "@babel/types": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", + "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.11", + "to-fast-properties": "^2.0.0" + } + }, + "@cnakazawa/watch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", + "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "@jest/console": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", + "integrity": "sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg==", + "dev": true, + "requires": { + "@jest/source-map": "^24.3.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/core": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.8.0.tgz", + "integrity": "sha512-R9rhAJwCBQzaRnrRgAdVfnglUuATXdwTRsYqs6NMdVcAl5euG8LtWDe+fVkN27YfKVBW61IojVsXKaOmSnqd/A==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/reporters": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-changed-files": "^24.8.0", + "jest-config": "^24.8.0", + "jest-haste-map": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-resolve-dependencies": "^24.8.0", + "jest-runner": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-snapshot": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "jest-watcher": "^24.8.0", + "micromatch": "^3.1.10", + "p-each-series": "^1.0.0", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "rimraf": "^2.5.4", + "strip-ansi": "^5.0.0" + } + }, + "@jest/environment": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.8.0.tgz", + "integrity": "sha512-vlGt2HLg7qM+vtBrSkjDxk9K0YtRBi7HfRFaDxoRtyi+DyVChzhF20duvpdAnKVBV6W5tym8jm0U9EfXbDk1tw==", + "dev": true, + "requires": { + "@jest/fake-timers": "^24.8.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "jest-mock": "^24.8.0" + } + }, + "@jest/fake-timers": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.8.0.tgz", + "integrity": "sha512-2M4d5MufVXwi6VzZhJ9f5S/wU4ud2ck0kxPof1Iz3zWx6Y+V2eJrES9jEktB6O3o/oEyk+il/uNu9PvASjWXQw==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-mock": "^24.8.0" + } + }, + "@jest/reporters": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.8.0.tgz", + "integrity": "sha512-eZ9TyUYpyIIXfYCrw0UHUWUvE35vx5I92HGMgS93Pv7du+GHIzl+/vh8Qj9MCWFK/4TqyttVBPakWMOfZRIfxw==", + "dev": true, + "requires": { + "@jest/environment": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "istanbul-lib-coverage": "^2.0.2", + "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.1", + "istanbul-reports": "^2.1.1", + "jest-haste-map": "^24.8.0", + "jest-resolve": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-util": "^24.8.0", + "jest-worker": "^24.6.0", + "node-notifier": "^5.2.1", + "slash": "^2.0.0", + "source-map": "^0.6.0", + "string-length": "^2.0.0" + } + }, + "@jest/source-map": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz", + "integrity": "sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.8.0.tgz", + "integrity": "sha512-+YdLlxwizlfqkFDh7Mc7ONPQAhA4YylU1s529vVM1rsf67vGZH/2GGm5uO8QzPeVyaVMobCQ7FTxl38QrKRlng==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/types": "^24.8.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/test-sequencer": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.8.0.tgz", + "integrity": "sha512-OzL/2yHyPdCHXEzhoBuq37CE99nkme15eHkAzXRVqthreWZamEMA0WoetwstsQBCXABhczpK03JNbc4L01vvLg==", + "dev": true, + "requires": { + "@jest/test-result": "^24.8.0", + "jest-haste-map": "^24.8.0", + "jest-runner": "^24.8.0", + "jest-runtime": "^24.8.0" + } + }, + "@jest/transform": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.8.0.tgz", + "integrity": "sha512-xBMfFUP7TortCs0O+Xtez2W7Zu1PLH9bvJgtraN1CDST6LBM/eTOZ9SfwS/lvV8yOfcDpFmwf9bq5cYbXvqsvA==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.8.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-util": "^24.8.0", + "micromatch": "^3.1.10", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + } + }, + "@jest/types": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.8.0.tgz", + "integrity": "sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^12.0.9" + } + }, + "@rauschma/stringio": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rauschma/stringio/-/stringio-1.4.0.tgz", + "integrity": "sha512-3uor2f/MXZkmX5RJf8r+OC3WvZVzpSme0yyL0rQDPEnatE02qRcqwEwnsgpgriEck0S/n4vWtUd6tTtrJwk45Q==", + "requires": { + "@types/node": "^10.0.3" + } + }, + "@types/babel__core": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz", + "integrity": "sha512-cfCCrFmiGY/yq0NuKNxIQvZFy9kY/1immpSpTngOnyIbD4+eJOG5mxphhHDv3CHL9GltO4GcKr54kGBg3RNdbg==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz", + "integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", + "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.6.tgz", + "integrity": "sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", + "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", + "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", + "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "@types/node": { + "version": "10.14.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.8.tgz", + "integrity": "sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw==" + }, + "@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", + "dev": true + }, + "@types/yargs": { + "version": "12.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.12.tgz", + "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==", + "dev": true + }, + "abab": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", + "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", + "dev": true + }, + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "dev": true + }, + "acorn-globals": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.2.tgz", + "integrity": "sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==", + "dev": true, + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + } + }, + "acorn-jsx": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", + "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", + "dev": true + }, + "acorn-walk": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", + "dev": true + }, + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "babel-jest": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.8.0.tgz", + "integrity": "sha512-+5/kaZt4I9efoXzPlZASyK/lN9qdRKmmUav9smVc0ruPQD7IsfucQ87gpOE8mn2jbDuS6M/YOW6n3v9ZoIfgnw==", + "dev": true, + "requires": { + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.6.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.4.tgz", + "integrity": "sha512-dySz4VJMH+dpndj0wjJ8JPs/7i1TdSPb1nRrn56/92pKOF9VKC1FMFJmMXjzlGGusnCAqujP6PBCiKq0sVA+YQ==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + } + }, + "babel-plugin-jest-hoist": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", + "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", + "dev": true, + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-jest": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", + "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", + "dev": true, + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.6.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "browser-process-hrtime": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", + "dev": true + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "bs58": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-2.0.1.tgz", + "integrity": "sha1-VZCNWPGYKrogCPob7Y+RmYopv40=" + }, + "bser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", + "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + } + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "coinstring": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/coinstring/-/coinstring-2.3.0.tgz", + "integrity": "sha1-zbYzY6lhUCQEolr7gsLibV/2J6Q=", + "requires": { + "bs58": "^2.0.1", + "create-hash": "^1.1.1" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "colorsys": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/colorsys/-/colorsys-1.0.22.tgz", + "integrity": "sha512-KCqF23oqkOD0IUCTLCl0obwGIMyeGFlNWuJ4oRRVKmawvKQeb3x5UvajVeH9AShZWU9hNaIhjXeTGw3iPNtl/Q==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true, + "optional": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "cssom": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", + "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==", + "dev": true + }, + "cssstyle": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", + "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", + "dev": true, + "requires": { + "cssom": "0.3.x" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", + "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, + "diff-sequences": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", + "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dev": true, + "requires": { + "webidl-conversions": "^4.0.2" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ejs": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", + "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==" + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", + "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", + "dev": true, + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true + } + } + }, + "eslint": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.9.1", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^5.0.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "js-yaml": "^3.13.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", + "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "dev": true + }, + "espree": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "dev": true, + "requires": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "exec-sh": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", + "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "expect": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.8.0.tgz", + "integrity": "sha512-/zYvP8iMDrzaaxHVa724eJBCKqSHmO0FA7EDkBiRHxg6OipmMn1fN+C8T9L9K8yr7UONkOifu6+LLH+z76CnaA==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.8.0", + "jest-matcher-utils": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-regex-util": "^24.3.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "dependencies": { + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fb-watchman": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", + "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", + "dev": true, + "requires": { + "bser": "^2.0.0" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", + "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "handlebars": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "dev": true, + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.1" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", + "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "inquirer": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.3.1.tgz", + "integrity": "sha512-MmL624rfkFt4TG9y/Jvmt8vdmOo836U7Y0Hxr2aFk3RelZEGX4Igk0KabWrcaaZaTv9uzglOqWh1Vly+FAWAXA==", + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.11", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", + "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", + "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "dev": true, + "requires": { + "handlebars": "^4.1.2" + } + }, + "jest": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.8.0.tgz", + "integrity": "sha512-o0HM90RKFRNWmAWvlyV8i5jGZ97pFwkeVoGvPW1EtLTgJc2+jcuqcbbqcSZLE/3f2S5pt0y2ZBETuhpWNl1Reg==", + "dev": true, + "requires": { + "import-local": "^2.0.0", + "jest-cli": "^24.8.0" + }, + "dependencies": { + "jest-cli": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.8.0.tgz", + "integrity": "sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA==", + "dev": true, + "requires": { + "@jest/core": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^12.0.2" + } + } + } + }, + "jest-changed-files": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.8.0.tgz", + "integrity": "sha512-qgANC1Yrivsq+UrLXsvJefBKVoCsKB0Hv+mBb6NMjjZ90wwxCDmU3hsCXBya30cH+LnPYjwgcU65i6yJ5Nfuug==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + } + }, + "jest-config": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.8.0.tgz", + "integrity": "sha512-Czl3Nn2uEzVGsOeaewGWoDPD8GStxCpAe0zOYs2x2l0fZAgPbCr3uwUkgNKV3LwE13VXythM946cd5rdGkkBZw==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.8.0", + "@jest/types": "^24.8.0", + "babel-jest": "^24.8.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.8.0", + "jest-environment-node": "^24.8.0", + "jest-get-type": "^24.8.0", + "jest-jasmine2": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.8.0", + "realpath-native": "^1.1.0" + } + }, + "jest-diff": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.8.0.tgz", + "integrity": "sha512-wxetCEl49zUpJ/bvUmIFjd/o52J+yWcoc5ZyPq4/W1LUKGEhRYDIbP1KcF6t+PvqNrGAFk4/JhtxDq/Nnzs66g==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.3.0", + "jest-get-type": "^24.8.0", + "pretty-format": "^24.8.0" + } + }, + "jest-docblock": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.3.0.tgz", + "integrity": "sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==", + "dev": true, + "requires": { + "detect-newline": "^2.1.0" + } + }, + "jest-each": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.8.0.tgz", + "integrity": "sha512-NrwK9gaL5+XgrgoCsd9svsoWdVkK4gnvyhcpzd6m487tXHqIdYeykgq3MKI1u4I+5Zf0tofr70at9dWJDeb+BA==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.8.0", + "jest-util": "^24.8.0", + "pretty-format": "^24.8.0" + } + }, + "jest-environment-jsdom": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.8.0.tgz", + "integrity": "sha512-qbvgLmR7PpwjoFjM/sbuqHJt/NCkviuq9vus9NBn/76hhSidO+Z6Bn9tU8friecegbJL8gzZQEMZBQlFWDCwAQ==", + "dev": true, + "requires": { + "@jest/environment": "^24.8.0", + "@jest/fake-timers": "^24.8.0", + "@jest/types": "^24.8.0", + "jest-mock": "^24.8.0", + "jest-util": "^24.8.0", + "jsdom": "^11.5.1" + } + }, + "jest-environment-node": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.8.0.tgz", + "integrity": "sha512-vIGUEScd1cdDgR6sqn2M08sJTRLQp6Dk/eIkCeO4PFHxZMOgy+uYLPMC4ix3PEfM5Au/x3uQ/5Tl0DpXXZsJ/Q==", + "dev": true, + "requires": { + "@jest/environment": "^24.8.0", + "@jest/fake-timers": "^24.8.0", + "@jest/types": "^24.8.0", + "jest-mock": "^24.8.0", + "jest-util": "^24.8.0" + } + }, + "jest-get-type": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.8.0.tgz", + "integrity": "sha512-RR4fo8jEmMD9zSz2nLbs2j0zvPpk/KCEz3a62jJWbd2ayNo0cb+KFRxPHVhE4ZmgGJEQp0fosmNz84IfqM8cMQ==", + "dev": true + }, + "jest-haste-map": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.8.0.tgz", + "integrity": "sha512-ZBPRGHdPt1rHajWelXdqygIDpJx8u3xOoLyUBWRW28r3tagrgoepPrzAozW7kW9HrQfhvmiv1tncsxqHJO1onQ==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.4.0", + "jest-util": "^24.8.0", + "jest-worker": "^24.6.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.8.0.tgz", + "integrity": "sha512-cEky88npEE5LKd5jPpTdDCLvKkdyklnaRycBXL6GNmpxe41F0WN44+i7lpQKa/hcbXaQ+rc9RMaM4dsebrYong==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.8.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.8.0", + "jest-matcher-utils": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-snapshot": "^24.8.0", + "jest-util": "^24.8.0", + "pretty-format": "^24.8.0", + "throat": "^4.0.0" + } + }, + "jest-leak-detector": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.8.0.tgz", + "integrity": "sha512-cG0yRSK8A831LN8lIHxI3AblB40uhv0z+SsQdW3GoMMVcK+sJwrIIyax5tu3eHHNJ8Fu6IMDpnLda2jhn2pD/g==", + "dev": true, + "requires": { + "pretty-format": "^24.8.0" + } + }, + "jest-matcher-utils": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.8.0.tgz", + "integrity": "sha512-lex1yASY51FvUuHgm0GOVj7DCYEouWSlIYmCW7APSqB9v8mXmKSn5+sWVF0MhuASG0bnYY106/49JU1FZNl5hw==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.8.0", + "jest-get-type": "^24.8.0", + "pretty-format": "^24.8.0" + } + }, + "jest-message-util": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.8.0.tgz", + "integrity": "sha512-p2k71rf/b6ns8btdB0uVdljWo9h0ovpnEe05ZKWceQGfXYr4KkzgKo3PBi8wdnd9OtNh46VpNIJynUn/3MKm1g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.8.0.tgz", + "integrity": "sha512-6kWugwjGjJw+ZkK4mDa0Df3sDlUTsV47MSrT0nGQ0RBWJbpODDQ8MHDVtGtUYBne3IwZUhtB7elxHspU79WH3A==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", + "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", + "dev": true + }, + "jest-regex-util": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", + "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", + "dev": true + }, + "jest-resolve": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.8.0.tgz", + "integrity": "sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-resolve-dependencies": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.8.0.tgz", + "integrity": "sha512-hyK1qfIf/krV+fSNyhyJeq3elVMhK9Eijlwy+j5jqmZ9QsxwKBiP6qukQxaHtK8k6zql/KYWwCTQ+fDGTIJauw==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.8.0" + } + }, + "jest-runner": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.8.0.tgz", + "integrity": "sha512-utFqC5BaA3JmznbissSs95X1ZF+d+4WuOWwpM9+Ak356YtMhHE/GXUondZdcyAAOTBEsRGAgH/0TwLzfI9h7ow==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.8.0", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.8.0", + "jest-jasmine2": "^24.8.0", + "jest-leak-detector": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-resolve": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-util": "^24.8.0", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + } + }, + "jest-runtime": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.8.0.tgz", + "integrity": "sha512-Mq0aIXhvO/3bX44ccT+czU1/57IgOMyy80oM0XR/nyD5zgBcesF84BPabZi39pJVA6UXw+fY2Q1N+4BiVUBWOA==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.8.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "@types/yargs": "^12.0.2", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.8.0", + "jest-haste-map": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-mock": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.8.0", + "jest-snapshot": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^12.0.2" + } + }, + "jest-serializer": { + "version": "24.4.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", + "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==", + "dev": true + }, + "jest-snapshot": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.8.0.tgz", + "integrity": "sha512-5ehtWoc8oU9/cAPe6fez6QofVJLBKyqkY2+TlKTOf0VllBB/mqUNdARdcjlZrs9F1Cv+/HKoCS/BknT0+tmfPg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "expect": "^24.8.0", + "jest-diff": "^24.8.0", + "jest-matcher-utils": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-resolve": "^24.8.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.8.0", + "semver": "^5.5.0" + } + }, + "jest-util": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.8.0.tgz", + "integrity": "sha512-DYZeE+XyAnbNt0BG1OQqKy/4GVLPtzwGx5tsnDrFcax36rVE3lTA5fbvgmbVPUZf9w77AJ8otqR4VBbfFJkUZA==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/fake-timers": "^24.8.0", + "@jest/source-map": "^24.3.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "jest-validate": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.8.0.tgz", + "integrity": "sha512-+/N7VOEMW1Vzsrk3UWBDYTExTPwf68tavEPKDnJzrC6UlHtUDU/fuEdXqFoHzv9XnQ+zW6X3qMZhJ3YexfeLDA==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "camelcase": "^5.0.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.8.0", + "leven": "^2.1.0", + "pretty-format": "^24.8.0" + } + }, + "jest-watcher": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.8.0.tgz", + "integrity": "sha512-SBjwHt5NedQoVu54M5GEx7cl7IGEFFznvd/HNT8ier7cCAx/Qgu9ZMlaTQkvK22G1YOpcWBLQPFSImmxdn3DAw==", + "dev": true, + "requires": { + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "@types/yargs": "^12.0.9", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.8.0", + "string-length": "^2.0.0" + } + }, + "jest-worker": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz", + "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==", + "dev": true, + "requires": { + "merge-stream": "^1.0.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", + "dev": true + }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lunicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lunicode/-/lunicode-2.0.1.tgz", + "integrity": "sha512-tFXPCYVUH9NENfyAfsyj477g2oN/W0aJumjODy0QbIZ4c68dLoGzCc6Ovr46QpF+rMuHa8Qgku6nrMjK78WeDw==" + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + } + } + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "dev": true + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dev": true, + "requires": { + "mime-db": "1.40.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "node-notifier": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", + "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==", + "dev": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "nwsapi": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz", + "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "dev": true, + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", + "dev": true + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==" + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", + "dev": true + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "pretty-format": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.8.0.tgz", + "integrity": "sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "prompts": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.1.0.tgz", + "integrity": "sha512-+x5TozgqYdOwWsQFZizE/Tra3fKvAoy037kOyU6cgz84n8f6zxngLOV4O32kTwt9FcLCxAqw0P/c8rOr9y+Gfg==", + "dev": true, + "requires": { + "kleur": "^3.0.2", + "sisteransi": "^1.0.0" + } + }, + "psl": { + "version": "1.1.32", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz", + "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "react-is": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "dev": true, + "requires": { + "util.promisify": "^1.0.0" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + } + } + }, + "request-promise-core": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", + "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, + "request-promise-native": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", + "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", + "dev": true, + "requires": { + "request-promise-core": "1.1.2", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", + "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rsvp": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz", + "integrity": "sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA==", + "dev": true + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "requires": { + "is-promise": "^2.1.0" + } + }, + "rxjs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", + "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sisteransi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz", + "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", + "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", + "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, + "string-length": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", + "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "dev": true, + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + } + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "symbol-tree": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", + "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", + "dev": true + }, + "table": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.0.tgz", + "integrity": "sha512-nHFDrxmbrkU7JAFKqKbDJXfzrX2UBsWmrieXFTGxiI5e4ncg3VqsZeI4EzNmX0ncp4XNGVeoxIWJXfCIXwrsvw==", + "dev": true, + "requires": { + "ajv": "^6.9.1", + "lodash": "^4.17.11", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "requires": { + "rimraf": "^2.6.3" + } + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "uglify-js": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", + "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "dev": true, + "optional": true, + "requires": { + "commander": "~2.20.0", + "source-map": "~0.6.1" + } + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validator": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", + "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "w3c-hr-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", + "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "dev": true, + "requires": { + "browser-process-hrtime": "^0.1.2" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", + "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + }, + "dependencies": { + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + } + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/cyphernodeconf_docker/package.json b/cyphernodeconf_docker/package.json new file mode 100644 index 0000000..16bce4b --- /dev/null +++ b/cyphernodeconf_docker/package.json @@ -0,0 +1,37 @@ +{ + "name": "cyphernodeconf", + "version": "0.0.0", + "description": "", + "homepage": "", + "scripts": { + "test": "jest", + "coverageHtml": "jest --coverage --coverageReporters html", + "lint": "./node_modules/.bin/eslint lib/**/*.js", + "lintfix": "./node_modules/.bin/eslint lib/**/**.js --fix" + }, + "author": "jash ", + "main": "lib/index.js", + "keywords": [ + "cyphernode" + ], + "dependencies": { + "@rauschma/stringio": "^1.4.0", + "ajv": "^6.10.0", + "chalk": "^2.4.2", + "coinstring": "^2.3.0", + "colorsys": "^1.0.22", + "ejs": "^2.6.1", + "inquirer": "^6.3.1", + "lunicode": "^2.0.1", + "parse5": "^5.1.0", + "tmp": "^0.1.0", + "validator": "^10.11.0", + "wrap-ansi": "^5.1.0" + }, + "devDependencies": { + "eslint": "^5.16.0", + "jest": "^24.8.0" + }, + "repository": "git@github.com:schulterklopfer/cyphernodeconf.git", + "license": "MIT" +} diff --git a/cyphernodeconf_docker/prompters/000_cyphernode.js b/cyphernodeconf_docker/prompters/000_cyphernode.js new file mode 100644 index 0000000..e5c1ed4 --- /dev/null +++ b/cyphernodeconf_docker/prompters/000_cyphernode.js @@ -0,0 +1,88 @@ +const chalk = require('chalk'); + +const name = 'cyphernode'; + +const capitalise = function( txt ) { + return txt.charAt(0).toUpperCase() + txt.substr(1); +}; + +const prefix = function() { + return chalk.green(capitalise(name)+': '); +}; + +module.exports = { + name: function() { + return name; + }, + prompts: function( utils ) { + return [{ + // https://github.com/SBoudrias/Inquirer.js#question + // input, confirm, list, rawlist, expand, checkbox, password, editor + type: 'checkbox', + name: 'features', + message: prefix()+'What features do you want to add to your cyphernode?'+utils.getHelp('features'), + choices: utils.featureChoices() + }, + { + type: 'list', + name: 'net', + default: utils.getDefault( 'net' ), + message: prefix()+'What net do you want to run on?'+utils.getHelp('net'), + choices: [{ + name: "Testnet", + value: "testnet" + },{ + name: "Mainnet", + value: "mainnet" + }] + }, + { + type: 'confirm', + name: 'run_as_different_user', + default: utils.getDefault( 'run_as_different_user' ), + message: prefix()+'Run as different user?'+utils.getHelp('run_as_different_user') + }, + { + when: function( props ) { + return props.run_as_different_user; + }, + type: 'input', + name: 'username', + default: utils.getDefault( 'username' ), + message: prefix()+'What username will cyphernode run under?'+utils.getHelp('username'), + filter: utils.trimFilter, + validate: utils.usernameValidator + }, + { + type: 'confirm', + name: 'use_xpub', + default: utils.getDefault( 'use_xpub' )||false, + message: prefix()+'Use a default xpub key to watch or generate adresses?'+utils.getHelp('use_xpub'), + }, + { + when: function( props ) { + return props.use_xpub; + }, + type: 'input', + name: 'xpub', + default: utils.getDefault( 'xpub' ), + message: prefix()+'What is your default xpub key?'+utils.getHelp('xpub'), + filter: utils.trimFilter, + validate: utils.xkeyValidator + }, + { + when: function( props ) { + return props.use_xpub; + }, + type: 'input', + name: 'derivation_path', + default: utils.getDefault( 'derivation_path' ), + message: prefix()+'What is your default derivation path?'+utils.getHelp('derivation_path'), + filter: utils.trimFilter, + validate: utils.derivationPathValidator + }]; + }, + templates: function( props ) { + return []; + } +}; diff --git a/cyphernodeconf_docker/prompters/010_gatekeeper.js b/cyphernodeconf_docker/prompters/010_gatekeeper.js new file mode 100644 index 0000000..74d0666 --- /dev/null +++ b/cyphernodeconf_docker/prompters/010_gatekeeper.js @@ -0,0 +1,95 @@ +const chalk = require('chalk'); + +const name = 'gatekeeper'; + +const capitalise = function( txt ) { + return txt.charAt(0).toUpperCase() + txt.substr(1); +}; + +const prefix = function() { + return chalk.green(capitalise(name)+': '); +}; + +const hasAuthKeys = function( props ) { + return props && + props.gatekeeper_keys && + props.gatekeeper_keys.configEntries && + props.gatekeeper_keys.configEntries.length > 0; +} + +const hasCert = function( props ) { + return props && + props.gatekeeper_sslkey && + props.gatekeeper_sslcert +} + +let password = ''; + +module.exports = { + name: function() { + return name; + }, + prompts: function( utils ) { + // TODO: delete clientKeys archive when password chnages + return [{ + type: 'password', + name: 'gatekeeper_clientkeyspassword', + default: utils.getDefault( 'gatekeeper_clientkeyspassword' ), + message: prefix()+'Enter a password to protect your client keys with'+utils.getHelp('gatekeeper_clientkeyspassword'), + filter: utils.trimFilter, + validate: utils.notEmptyValidator + }, + { + when: function( props ) { + // hacky hack + password = props.gatekeeper_clientkeyspassword; + return true; + }, + type: 'password', + name: 'gatekeeper_clientkeyspassword_c', + default: utils.getDefault( 'gatekeeper_clientkeyspassword_c' ), + message: prefix()+'Confirm your client keys password.'+utils.getHelp('gatekeeper_clientkeyspassword_c'), + filter: utils.trimFilter, + validate: function( input ) { + if(input !== password) { + throw new Error( 'Client keys passwords do not match' ); + } + return true; + } + }, + { + type: 'input', + name: 'gatekeeper_port', + default: utils.getDefault( 'gatekeeper_port' ), + message: prefix()+'The port gatekeeper will listen on for requests'+utils.getHelp('gatekeeper_port'), + filter: utils.trimFilter, + validate: function( port ) { + return utils.notEmptyValidator( port ) && !isNaN( parseInt(port) ) + } + }, + { + when: function() { return hasAuthKeys( utils.props ); }, + type: 'confirm', + name: 'gatekeeper_recreatekeys', + default: false, + message: prefix()+'Recreate gatekeeper keys?'+utils.getHelp('gatekeeper_recreatekeys') + }, + { + when: function() { return hasCert( utils.props ); }, + type: 'confirm', + name: 'gatekeeper_recreatecert', + default: false, + message: prefix()+'Recreate gatekeeper certificate?'+utils.getHelp('gatekeeper_recreatecert') + }, + { + when: function(props) { return !hasCert( utils.props ) || props.gatekeeper_recreatecert }, + type: 'input', + name: 'gatekeeper_cns', + default: utils.getDefault( 'gatekeeper_cns' ), + message: prefix()+'Gatekeeper cert CNS (ips, domains, wildcard domains seperated by comma)?'+utils.getHelp('gatekeeper_cns') + }]; + }, + templates: function( props ) { + return [ 'keys.properties', 'api.properties', 'cert.pem', 'key.pem' ]; + } +}; diff --git a/cyphernodeconf_docker/prompters/030_traefik.js b/cyphernodeconf_docker/prompters/030_traefik.js new file mode 100644 index 0000000..7561e97 --- /dev/null +++ b/cyphernodeconf_docker/prompters/030_traefik.js @@ -0,0 +1,15 @@ +const chalk = require('chalk'); + +const name = 'traefik'; + +module.exports = { + name: function() { + return name; + }, + prompts: function( utils ) { + return []; + }, + templates: function( props ) { + return [ 'acme.json', 'traefik.toml', 'htpasswd' ]; + } +}; diff --git a/cyphernodeconf_docker/prompters/100_lightning.js b/cyphernodeconf_docker/prompters/100_lightning.js new file mode 100644 index 0000000..9e40bd8 --- /dev/null +++ b/cyphernodeconf_docker/prompters/100_lightning.js @@ -0,0 +1,90 @@ +const path = require('path'); +const chalk = require('chalk'); + +const name = 'lightning'; + +const capitalise = function( txt ) { + return txt.charAt(0).toUpperCase() + txt.substr(1); +}; + +const prefix = function() { + return chalk.green(capitalise(name)+': '); +}; + +const featureCondition = function(props) { + return props.features && props.features.indexOf( name ) != -1; +}; + +const templates = { + 'lnd': [ path.join('lnd','lnd.conf') ], + 'c-lightning': [ path.join('c-lightning','config'), path.join('c-lightning','bitcoin.conf') ] +}; + +module.exports = { + name: function() { + return name; + }, + prompts: function( utils ) { + return [ + /* + { + when: featureCondition, + type: 'list', + name: 'lightning_implementation', + default: utils.getDefault( 'lightning_implementation' ), + message: prefix()+'What lightning implementation do you want to use?'+utils.getHelp('lightning_implementation'), + choices: [ + { + name: 'C-lightning', + value: 'c-lightning' + }, + { + name: 'LND', + value: 'lnd' + } + + ] + }, + */ + { + when: featureCondition, + type: 'input', + name: 'lightning_external_ip', + default: utils.getDefault( 'lightning_external_ip' ), + filter: utils.trimFilter, + validate: utils.ipOrFQDNValidator, + message: prefix()+'What external ip does your lightning node have?'+utils.getHelp('lightning_external_ip'), + }, + { + when: featureCondition, + type: 'input', + name: 'lightning_nodename', + default: utils.getDefault( 'lightning_nodename' ), + filter: utils.trimFilter, + validate: (input)=>{ + if( !input.trim() ) { + return true; + } + return utils.lightningNodeNameValidator(input); + }, + message: prefix()+'What name has your lightning node?'+utils.getHelp('lightning_nodename'), + }, + { + when: featureCondition, + type: 'input', + name: 'lightning_nodecolor', + default: utils.getDefault( 'lightning_nodecolor' ), + filter: utils.trimFilter, + validate: (input)=>{ + if( !input.trim() ) { + return true; + } + return utils.colorValidator(input); + }, + message: prefix()+'What color has your lightning node?'+utils.getHelp('lightning_nodecolor'), + }]; + }, + templates: function( props ) { + return featureCondition(props)?templates[props.lightning_implementation]:[]; + } +}; diff --git a/cyphernodeconf_docker/prompters/900_bitcoin.js b/cyphernodeconf_docker/prompters/900_bitcoin.js new file mode 100644 index 0000000..a974e64 --- /dev/null +++ b/cyphernodeconf_docker/prompters/900_bitcoin.js @@ -0,0 +1,109 @@ +const chalk = require('chalk'); + +const name = 'bitcoin'; + +const capitalise = function( txt ) { + return txt.charAt(0).toUpperCase() + txt.substr(1); +}; + +const prefix = function() { + return chalk.green(capitalise(name)+': '); +}; + +const bitcoinExternal = function(props) { + return props.bitcoin_mode === 'external' +}; + +const bitcoinInternal = function(props) { + return props.bitcoin_mode === 'internal' +}; + +const bitcoinInternalAndPrune = function(props) { + return bitcoinInternal(props) && props.bitcoin_prune; +}; + +module.exports = { + name: function() { + return name; + }, + prompts: function( utils ) { + return [ + { + type: 'list', + name: 'bitcoin_mode', + default: utils.getDefault( 'bitcoin_mode' ), + message: prefix()+'Cyphernode will manage your bitcoin full node.'+utils.getHelp('bitcoin_mode'), + choices: [ + { + name: 'Ok. That is awesome', + value: 'internal' + } + ] + }, + { + when: bitcoinExternal, + type: 'input', + name: 'bitcoin_node_ip', + default: utils.getDefault( 'bitcoin_node_ip' ), + filter: utils.trimFilter, + validate: utils.ipOrFQDNValidator, + message: prefix()+'What is your full node ip address?'+utils.getHelp('bitcoin_node_ip'), + }, + { + type: 'input', + name: 'bitcoin_rpcuser', + default: utils.getDefault( 'bitcoin_rpcuser' ), + message: prefix()+'Name of bitcoin rpc user?'+utils.getHelp('bitcoin_rpcuser'), + filter: utils.trimFilter, + }, + { + type: 'password', + name: 'bitcoin_rpcpassword', + default: utils.getDefault( 'bitcoin_rpcpassword' ), + message: prefix()+'Password of bitcoin rpc user?'+utils.getHelp('bitcoin_rpcpassword'), + filter: utils.trimFilter, + }, + { + when: function(props) { + return bitcoinInternal( props ) && props.features.indexOf('lightning') === -1; + }, + type: 'confirm', + name: 'bitcoin_prune', + default: utils.getDefault( 'bitcoin_prune' ), + message: prefix()+'Run bitcoin node in prune mode?'+utils.getHelp('bitcoin_prune'), + }, + { + when: function(props) { + return bitcoinInternalAndPrune( props ) && props.features.indexOf('lightning') === -1; + }, + type: 'input', + name: 'bitcoin_prune_size', + default: utils.getDefault( 'bitcoin_prune_size' ), + message: prefix()+'What is the maximum size of your blockchain data in megabytes?'+utils.getHelp('bitcoin_prune_size'), + validate: function( input ) { + if( ! /^\d+$/.test(input) ) { + throw new Error( "Not a number"); + } + if( input < 550 ) { + throw new Error( "At least 550 is required"); + } + return true; + } + }, // TODO: ask for size of prune + { + when: bitcoinInternal, + type: 'input', + name: 'bitcoin_uacomment', + default: utils.getDefault( 'bitcoin_uacomment' ), + message: prefix()+'Any UA comment?'+utils.getHelp('bitcoin_uacomment'), + filter: utils.trimFilter, + validate: (input)=> {return utils.optional(input,utils.UACommentValidator) } + }]; + }, + env: function( props ) { + return 'VAR0=VALUE0\nVAR1=VALUE1' + }, + templates: function( props ) { + return ['bitcoin.conf'] + } +}; \ No newline at end of file diff --git a/cyphernodeconf_docker/prompters/999_installer.js b/cyphernodeconf_docker/prompters/999_installer.js new file mode 100644 index 0000000..a41fd37 --- /dev/null +++ b/cyphernodeconf_docker/prompters/999_installer.js @@ -0,0 +1,303 @@ +const path = require('path'); +const chalk = require('chalk'); + +const name = 'installer'; + +const capitalise = function( txt ) { + return txt.charAt(0).toUpperCase() + txt.substr(1); +}; + +const prefix = function() { + return chalk.green(capitalise(name)+': '); +}; + +const installerDocker = function(props) { + return props.installer_mode === 'docker' +}; + +module.exports = { + name: function() { + return name; + }, + prompts: function( utils ) { + return [{ + type: 'list', + name: 'installer_mode', + default: utils.getDefault( 'installer_mode' ), + message: prefix()+chalk.red('Where do you want to install cyphernode?')+utils.getHelp('installer_mode'), + choices: [{ + name: "Docker", + value: "docker" + }] + }, + { + when: installerDocker, + type: 'list', + name: 'traefik_datapath', + default: utils.getDefault( 'traefik_datapath' ), + choices: [ + { + name: utils.setupDir()+"/cyphernode/traefik", + value: utils.setupDir()+"/cyphernode/traefik" + }, + { + name: utils.defaultDataDirBase()+"/cyphernode/traefik", + value: utils.defaultDataDirBase()+"/cyphernode/traefik" + }, + { + name: utils.defaultDataDirBase()+"/.cyphernode/traefik", + value: utils.defaultDataDirBase()+"/.cyphernode/traefik" + }, + { + name: utils.defaultDataDirBase()+"/traefik", + value: utils.defaultDataDirBase()+"/traefik" + }, + { + name: "Custom path", + value: "_custom" + } + ], + message: prefix()+'Where do you want to store your traefik data?'+utils.getHelp('traefik_datapath'), + }, + { + when: (props)=>{ return installerDocker(props) && (props.traefik_datapath === '_custom') }, + type: 'input', + name: 'traefik_datapath_custom', + default: utils.getDefault( 'traefik_datapath_custom' ), + filter: utils.trimFilter, + validate: utils.pathValidator, + message: prefix()+'Custom path for traefik data?'+utils.getHelp('traefik_datapath_custom'), + }, + { + when: installerDocker, + type: 'list', + name: 'gatekeeper_datapath', + default: utils.getDefault( 'gatekeeper_datapath' ), + choices: [ + { + name: utils.setupDir()+"/cyphernode/gatekeeper", + value: utils.setupDir()+"/cyphernode/gatekeeper" + }, + { + name: utils.defaultDataDirBase()+"/cyphernode/gatekeeper", + value: utils.defaultDataDirBase()+"/cyphernode/gatekeeper" + }, + { + name: utils.defaultDataDirBase()+"/.cyphernode/gatekeeper", + value: utils.defaultDataDirBase()+"/.cyphernode/gatekeeper" + }, + { + name: utils.defaultDataDirBase()+"/gatekeeper", + value: utils.defaultDataDirBase()+"/gatekeeper" + }, + { + name: "Custom path", + value: "_custom" + } + ], + message: prefix()+'Where do you want to store your gatekeeper data?'+utils.getHelp('gatekeeper_datapath'), + }, + { + when: (props)=>{ return installerDocker(props) && (props.gatekeeper_datapath === '_custom') }, + type: 'input', + name: 'gatekeeper_datapath_custom', + default: utils.getDefault( 'gatekeeper_datapath_custom' ), + filter: utils.trimFilter, + validate: utils.pathValidator, + message: prefix()+'Custom path for gatekeeper data?'+utils.getHelp('gatekeeper_datapath_custom'), + }, + { + when: installerDocker, + type: 'list', + name: 'proxy_datapath', + default: utils.getDefault( 'proxy_datapath' ), + choices: [ + { + name: utils.setupDir()+"/cyphernode/proxy", + value: utils.setupDir()+"/cyphernode/proxy" + }, + { + name: utils.defaultDataDirBase()+"/cyphernode/proxy", + value: utils.defaultDataDirBase()+"/cyphernode/proxy" + }, + { + name: utils.defaultDataDirBase()+"/.cyphernode/proxy", + value: utils.defaultDataDirBase()+"/.cyphernode/proxy" + }, + { + name: utils.defaultDataDirBase()+"/proxy", + value: utils.defaultDataDirBase()+"/proxy" + }, + { + name: "Custom path", + value: "_custom" + } + ], + message: prefix()+'Where do you want to store your proxy data?'+utils.getHelp('proxy_datapath'), + }, + { + when: (props)=>{ return installerDocker(props) && (props.proxy_datapath === '_custom') }, + type: 'input', + name: 'proxy_datapath_custom', + default: utils.getDefault( 'proxy_datapath_custom' ), + filter: utils.trimFilter, + validate: utils.pathValidator, + message: prefix()+'Custom path for your proxy data?'+utils.getHelp('proxy_datapath_custom'), + }, + { + when: function(props) { return installerDocker(props) && props.bitcoin_mode === 'internal' }, + type: 'list', + name: 'bitcoin_datapath', + default: utils.getDefault( 'bitcoin_datapath' ), + choices: [ + { + name: utils.setupDir()+"/cyphernode/bitcoin", + value: utils.setupDir()+"/cyphernode/bitcoin" + }, + { + name: utils.defaultDataDirBase()+"/cyphernode/bitcoin", + value: utils.defaultDataDirBase()+"/cyphernode/bitcoin" + }, + { + name: utils.defaultDataDirBase()+"/.cyphernode/bitcoin", + value: utils.defaultDataDirBase()+"/.cyphernode/bitcoin" + }, + { + name: utils.defaultDataDirBase()+"/bitcoin", + value: utils.defaultDataDirBase()+"/bitcoin" + }, + { + name: "Custom path", + value: "_custom" + } + ], + message: prefix()+'Where do you want to store your bitcoin full node data?'+utils.getHelp('bitcoin_datapath'), + }, + { + when: function(props) { return installerDocker(props) && props.bitcoin_mode === 'internal' && props.bitcoin_datapath === '_custom' }, + type: 'input', + name: 'bitcoin_datapath_custom', + default: utils.getDefault( 'bitcoin_datapath_custom' ), + filter: utils.trimFilter, + validate: utils.pathValidator, + message: prefix()+'Custom path for your bitcoin full node data?'+utils.getHelp('bitcoin_datapath_custom'), + }, + { + when: function(props) { return installerDocker(props) && props.features.indexOf('lightning') !== -1 }, + type: 'list', + name: 'lightning_datapath', + default: utils.getDefault( 'lightning_datapath' ), + choices: [ + { + name: utils.setupDir()+"/cyphernode/lightning", + value: utils.setupDir()+"/cyphernode/lightning" + }, + { + name: utils.defaultDataDirBase()+"/cyphernode/lightning", + value: utils.defaultDataDirBase()+"/cyphernode/lightning" + }, + { + name: utils.defaultDataDirBase()+"/.cyphernode/lightning", + value: utils.defaultDataDirBase()+"/.cyphernode/lightning" + }, + { + name: utils.defaultDataDirBase()+"/lightning", + value: utils.defaultDataDirBase()+"/lightning" + }, + { + name: "Custom path", + value: "_custom" + } + ], + message: prefix()+'Where do you want to store your lightning node data?'+utils.getHelp('lightning_datapath'), + }, + { + when: function(props) { return installerDocker(props) && props.features.indexOf('lightning') !== -1 && props.lightning_datapath === '_custom'}, + type: 'input', + name: 'lightning_datapath_custom', + default: utils.getDefault( 'lightning_datapath_custom' ), + filter: utils.trimFilter, + validate: utils.pathValidator, + message: prefix()+'Custom path for your lightning node data?'+utils.getHelp('lightning_datapath_custom'), + }, + { + when: function(props) { return installerDocker(props) && props.features.indexOf('otsclient') !== -1 }, + type: 'list', + name: 'otsclient_datapath', + default: utils.getDefault( 'otsclient_datapath' ), + choices: [ + { + name: utils.setupDir()+"/cyphernode/otsclient", + value: utils.setupDir()+"/cyphernode/otsclient" + }, + { + name: utils.defaultDataDirBase()+"/cyphernode/otsclient", + value: utils.defaultDataDirBase()+"/cyphernode/otsclient" + }, + { + name: utils.defaultDataDirBase()+"/.cyphernode/otsclient", + value: utils.defaultDataDirBase()+"/.cyphernode/otsclient" + }, + { + name: utils.defaultDataDirBase()+"/otsclient", + value: utils.defaultDataDirBase()+"/otsclient" + }, + { + name: "Custom path", + value: "_custom" + } + ], + message: prefix()+'Where do you want to store your OTS data?'+utils.getHelp('otsclient_datapath'), + }, + { + when: function(props) { return installerDocker(props) && props.features.indexOf('otsclient') !== -1 && props.otsclient_datapath === '_custom' }, + type: 'input', + name: 'otsclient_datapath_custom', + default: utils.getDefault( 'otsclient_datapath_custom' ), + filter: utils.trimFilter, + validate: utils.pathValidator, + message: prefix()+'Where is your otsclient data?'+utils.getHelp('otsclient_datapath_custom'), + }, + { + when: function(props) { return installerDocker(props) && props.bitcoin_mode === 'internal' }, + type: 'confirm', + name: 'bitcoin_expose', + default: utils.getDefault( 'bitcoin_expose' ), + message: prefix()+'Expose bitcoin full node outside of the docker network?'+utils.getHelp('bitcoin_expose'), + }, + { + when: function(props) { return installerDocker(props) && props.features.indexOf('lightning') !== -1 }, + type: 'confirm', + name: 'lightning_expose', + default: utils.getDefault( 'lightning_expose' ), + message: prefix()+'Expose lightning node outside of the docker network?'+utils.getHelp('lightning_expose'), + }, + { + when: installerDocker, + type: 'list', + name: 'docker_mode', + default: utils.getDefault( 'docker_mode' ), + message: prefix()+'What docker mode: docker swarm or docker-compose?'+utils.getHelp('docker_mode'), + choices: [{ + name: "docker swarm", + value: "swarm" + }, + { + name: "docker-compose", + value: "compose" + }] + }, + { + type: 'confirm', + name: 'installer_cleanup', + default: utils.getDefault( 'installer_cleanup' ), + message: prefix()+'Cleanup installer after installation?'+utils.getHelp('installer_cleanup'), + }]; + }, + templates: function( props ) { + if( props.installer_mode === 'docker' ) { + return ['config.sh','start.sh', 'stop.sh', 'testfeatures.sh', path.join('docker', 'docker-compose.yaml')]; + } + return ['config.sh','start.sh', 'stop.sh', 'testfeatures.sh']; + } +}; diff --git a/cyphernodeconf_docker/run.sh b/cyphernodeconf_docker/run.sh new file mode 100755 index 0000000..21fffd3 --- /dev/null +++ b/cyphernodeconf_docker/run.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +SETUP_DIR=/Users/jash/src/cyphernode_satoshiportal/dist DEFAULT_CERT_HOSTNAME=disk0book.local PROXYCRON_VERSION=v0.2.0-rc.5 PYCOIN_VERSION=v0.2.0-rc.5 SETUP_VERSION=v0.2.0-rc.5 BITCOIN_VERSION=v0.17.1 LIGHTNING_VERSION=v0.7.0 DEFAULT_DATADIR_BASE=/Users/jash GATEKEEPER_VERSION=v0.2.0-rc.5 PROXY_VERSION=v0.2.0-rc.5 OTSCLIENT_VERSION=v0.2.0-rc.5 DEFAULT_USER=jash EDITOR=/usr/bin/nano node index.js $@ \ No newline at end of file diff --git a/cyphernodeconf_docker/schema/config-v0.1.0.json b/cyphernodeconf_docker/schema/config-v0.1.0.json new file mode 100644 index 0000000..f72734b --- /dev/null +++ b/cyphernodeconf_docker/schema/config-v0.1.0.json @@ -0,0 +1,456 @@ +{ + "definitions": {}, + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://cyphernode.io/config-v0.1.0.json", + "type": "object", + "title": "Cyphernode config file structure v0.1.0", + "additionalProperties": false, + "required": [ + "__version", + "features", + "net", + "xpub", + "derivation_path", + "installer_mode", + "run_as_different_user", + "username", + "docker_mode", + "bitcoin_rpcuser", + "bitcoin_rpcpassword", + "bitcoin_prune", + "bitcoin_datapath", + "bitcoin_node_ip", + "bitcoin_mode", + "bitcoin_expose", + "gatekeeper_apiproperties", + "gatekeeper_keys", + "gatekeeper_sslcert", + "gatekeeper_sslkey", + "gatekeeper_cns", + "gatekeeper_clientkeyspassword", + "gatekeeper_clientkeyspassword_c", + "gatekeeper_statuspw", + "gatekeeper_datapath", + "lightning_expose", + "lightning_implementation", + "lightning_datapath", + "lightning_nodename", + "lightning_nodecolor", + "otsclient_datapath", + "proxy_datapath" + ], + "allOf": [ + { + "if": { + "properties": { + "run_as_different_user": { + "enum": [ + true + ] + } + } + }, + "then": { + "required": [ + "username" + ] + } + }, + { + "if": { + "properties": { + "bitcoin_prune": { + "enum": [ + true + ] + } + } + }, + "then": { + "required": [ + "bitcoin_prune_size" + ] + } + } + ], + "properties": { + "__version": { + "type": "string", + "enum": ["0.1.0"], + "default": "0.1.0", + "examples": ["0.1.0"] + }, + "features": { + "$id": "#/properties/features", + "type": "array", + "title": "The optional features of this cyphernode", + "default": [], + "items": { + "$id": "#/properties/features/items", + "type": "string", + "enum": [ + "lightning", + "otsclient" + ], + "title": "The feature", + "examples": [ + "lightning", + "otsclient" + ] + } + }, + "net": { + "$id": "#/properties/net", + "type": "string", + "enum": [ + "testnet", + "mainnet" + ], + "title": "The net cyphernode is running on", + "default": "testnet", + "examples": [ + "testnet" + ] + }, + "xpub": { + "$id": "#/properties/xpub", + "type": "string", + "title": "Default xpub to derive addresses from", + "pattern": "^(\\w+)$" + }, + "derivation_path": { + "$id": "#/properties/derivation_path", + "type": "string", + "title": "Default derivation path", + "default": "0/n", + "examples": [ + "0/n" + ] + }, + "installer_mode": { + "$id": "#/properties/installer_mode", + "type": "string", + "enum": [ + "docker" + ], + "title": "Install mode", + "default": "docker", + "examples": [ + "docker" + ] + }, + "run_as_different_user": { + "$id": "#/properties/run_as_different_user", + "type": "boolean", + "title": "Run as different user", + "default": true, + "examples": [ + true + ] + }, + "username": { + "$id": "#/properties/username", + "type": "string", + "title": "Username to run under", + "default": "cyphernode", + "examples": [ + "cyphernode" + ] + }, + "docker_mode": { + "$id": "#/properties/docker_mode", + "type": "string", + "enum": [ + "swarm", + "compose" + ], + "title": "How to run the containers", + "default": "compose", + "examples": [ + "compose" + ] + }, + "bitcoin_rpcuser": { + "$id": "#/properties/bitcoin_rpcuser", + "type": "string", + "title": "Bitcoin rpc user", + "default": "bitcoin", + "examples": [ + "bitcoin" + ] + }, + "bitcoin_rpcpassword": { + "$id": "#/properties/bitcoin_rpcpassword", + "type": "string", + "title": "Bitcoin rpc password", + "default": "CHANGEME", + "examples": [ + "CHANGEME" + ] + }, + "bitcoin_uacomment": { + "$id": "#/properties/bitcoin_uacomment", + "type": "string", + "title": "Bitcoin user agent comment", + "examples": [ + "cyphernode" + ] + }, + "bitcoin_prune": { + "$id": "#/properties/bitcoin_prune", + "type": "boolean", + "title": "Bitcoin prune", + "default": false, + "examples": [ + "false" + ] + }, + "bitcoin_prune_size": { + "$id": "#/properties/bitcoin_prune_size", + "type": "integer", + "title": "Bitcoin prune size", + "default": 550, + "examples": [ + 550 + ] + }, + "bitcoin_datapath": { + "$id": "#/properties/bitcoin_datapath", + "type": "string", + "title": "Bitcoin datapath", + "examples": [ + "/tmp/cyphernode/bitcoin" + ] + }, + "bitcoin_datapath_custom": { + "$id": "#/properties/bitcoin_datapath_custom", + "type": "string", + "title": "Bitcoin custom datapath", + "examples": [ + "/tmp/cyphernode/bitcoin" + ] + }, + "lightning_datapath": { + "$id": "#/properties/lightning_datapath", + "type": "string", + "title": "Lightning datapath", + "examples": [ + "/tmp/cyphernode/lightning" + ] + }, + "lightning_datapath_custom": { + "$id": "#/properties/lightning_datapath_custom", + "type": "string", + "title": "Lightning custom datapath", + "examples": [ + "/tmp/cyphernode/lightning" + ] + }, + "proxy_datapath": { + "$id": "#/properties/proxy_datapath", + "type": "string", + "title": "Proxy datapath", + "examples": [ + "/tmp/cyphernode/proxy" + ] + }, + "proxy_datapath_custom": { + "$id": "#/properties/proxy_datapath_custom", + "type": "string", + "title": "Proxy custom datapath", + "examples": [ + "/tmp/cyphernode/proxy" + ] + }, + "otsclient_datapath": { + "$id": "#/properties/otsclient_datapath", + "type": "string", + "title": "OTS Client datapath", + "examples": [ + "/tmp/cyphernode/otsclient" + ] + }, + "otsclient_datapath_custom": { + "$id": "#/properties/otsclient_datapath_custom", + "type": "string", + "title": "OTS Client custom datapath", + "examples": [ + "/tmp/cyphernode/otsclient" + ] + }, + "bitcoin_node_ip": { + "$id": "#/properties/bitcoin_node_ip", + "type": "string", + "format": "ipv4", + "title": "Bitcoin node ip", + "examples": [ + "123.123.123.123" + ] + }, + "bitcoin_mode": { + "$id": "#/properties/bitcoin_mode", + "type": "string", + "enum": [ + "internal" + ], + "title": "Bitcoin mode", + "examples": [ + "internal" + ] + }, + "bitcoin_expose": { + "$id": "#/properties/bitcoin_expose", + "type": "boolean", + "title": "Expose bitcoin node", + "default": false, + "examples": [ + false + ] + }, + "lightning_expose": { + "$id": "#/properties/lightning_expose", + "type": "boolean", + "title": "Expose lightning node", + "default": false, + "examples": [ + false + ] + }, + "gatekeeper_datapath": { + "$id": "#/properties/gatekeeper_datapath", + "type": "string", + "title": "Gatekeeper datapath", + "examples": [ + "/tmp/cyphernode/gatekeeper" + ] + }, + "gatekeeper_datapath_custom": { + "$id": "#/properties/gatekeeper_datapath_custom", + "type": "string", + "title": "Gatekeeper custom datapath", + "examples": [ + "/tmp/cyphernode/gatekeeper" + ] + }, + "gatekeeper_apiproperties": { + "$id": "#/properties/gatekeeper_apiproperties", + "type": "string", + "title": "API properties", + "examples": [ + "# Stats can:\naction_getblockchaininfo=stats\n\n# Watcher can:\naction_watch=watcher\naction_unwatch=watcher\naction_watchxpub=watcher\naction_unwatchxpubbyxpub=watcher\naction_unwatchxpubbylabel=watcher\naction_getactivewatchesbyxpub=watcher\naction_getactivewatchesbylabel=watcher\naction_getactivexpubwatches=watcher\naction_watchtxid=watcher\naction_getactivewatches=watcher\naction_getbestblockhash=watcher\naction_getbestblockinfo=watcher\naction_getblockinfo=watcher\naction_gettransaction=watcher\naction_ln_getinfo=watcher\naction_ln_create_invoice=watcher\naction_ln_getconnectionstring=watcher\naction_ln_decodebolt11=watcher\n\n# Spender can do what the watcher can do, plus:\naction_getbalance=spender\naction_getbalancebyxpub=spender\naction_getbalancebyxpublabel=spender\naction_getnewaddress=spender\naction_spend=spender\naction_addtobatch=spender\naction_batchspend=spender\naction_deriveindex=spender\naction_derivepubpath=spender\naction_ln_pay=spender\naction_ln_newaddr=spender\naction_ots_stamp=spender\naction_ots_getfile=spender\naction_ln_getinvoice=spender\naction_ln_decodebolt11=spender\naction_ln_connectfund=spender\n\n# Admin can do what the spender can do, plus:\n\n\n# Should be called from inside the Docker network only:\naction_conf=internal\naction_newblock=internal\naction_executecallbacks=internal\naction_ots_backoffice=internal" + ] + }, + "gatekeeper_keys": { + "$id": "#/properties/gatekeeper_keys", + "type": "object", + "title": "Gatekeeper keys", + "default": { + "configEntries": [], + "clientInformation": [] + }, + "required": [ + "configEntries", + "clientInformation" + ], + "properties": { + "configEntries": { + "$id": "#/properties/gatekeeper_keys/configEntries", + "type": "array", + "items": { + "$id": "#/properties/gatekeeper_keys/configEntries/entry", + "type": "string", + "pattern": "^kapi_id=\".+\";kapi_key=\".+\";kapi_groups=\".+\";.+$" + }, + "examples": [ + [ + "kapi_id=\"001\";kapi_key=\"a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"watcher\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}", + "kapi_id=\"002\";kapi_key=\"fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d\";kapi_groups=\"watcher,spender\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}", + "kapi_id=\"003\";kapi_key=\"f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417\";kapi_groups=\"watcher,spender,admin\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}" + ] + ] + }, + "clientInformation": { + "$id": "#/properties/gatekeeper_keys/clientInformation", + "type": "array", + "items": { + "$id": "#/properties/gatekeeper_keys/clientInformation/entry", + "type": "string", + "pattern": "^.+=.+$" + }, + "examples": [ + [ + "001=a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a", + "002=fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d", + "003=f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417" + ] + ] + } + } + }, + "gatekeeper_clientkeyspassword": { + "$id": "#/properties/gatekeeper_clientkeyspassword", + "type": "string", + "title": "Password for the encrypted client keys archive" + }, + "gatekeeper_clientkeyspassword_c": { + "$id": "#/properties/gatekeeper_clientkeyspassword", + "type": "string", + "title": "Password for the encrypted client keys archive, verified" + }, + "gatekeeper_statuspw": { + "$id": "#/properties/adminhash", + "type": "string", + "title": "MD5 hash of admin password" + }, + "gatekeeper_sslcert": { + "$id": "#/properties/gatekeeper_sslcert", + "type": "string", + "title": "Gatekeeper SSL Cert" + }, + "gatekeeper_sslkey": { + "$id": "#/properties/gatekeeper_sslkey", + "type": "string", + "title": "Gatekeeper SSL Key" + }, + "gatekeeper_cns": { + "$id": "#/properties/gatekeeper_cns", + "type": "string", + "title": "Gatekeeper cns", + "examples": [ + "myhost.mydomain.com,*.myotherdomain.com,123.123.123.123" + ] + }, + "lightning_implementation": { + "$id": "#/properties/lightning_implementation", + "type": "string", + "enum": [ + "c-lightning" + ], + "title": "The lightning implementation", + "default": "c-lightning", + "examples": [ + "c-lightning" + ] + }, + "lightning_nodename": { + "$id": "#/properties/lightning_nodename", + "type": "string", + "title": "The lightning node name", + "examples": [ + "🚀 Mighty Moose 🚀" + ] + }, + "lightning_nodecolor": { + "$id": "#/properties/lightning_nodecolor", + "type": "string", + "pattern": "^[0-9A-Fa-f]{6}$", + "title": "The lightning node color", + "default": "ff0000", + "examples": [ + "ff0000", + "00ff00", + "00ffff" + ] + } + } +} \ No newline at end of file diff --git a/cyphernodeconf_docker/schema/config-v0.2.0.json b/cyphernodeconf_docker/schema/config-v0.2.0.json new file mode 100644 index 0000000..9bea159 --- /dev/null +++ b/cyphernodeconf_docker/schema/config-v0.2.0.json @@ -0,0 +1,515 @@ +{ + "definitions": {}, + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://cyphernode.io/config-v0.2.0.json", + "type": "object", + "title": "Cyphernode config file structure v0.2.0", + "additionalProperties": false, + "required": [ + "schema_version", + "setup_version", + "features", + "net", + "use_xpub", + "installer_mode", + "run_as_different_user", + "docker_mode", + "docker_versions", + "adminhash", + "bitcoin_rpcuser", + "bitcoin_rpcpassword", + "bitcoin_prune", + "bitcoin_datapath", + "bitcoin_mode", + "bitcoin_expose", + "gatekeeper_keys", + "gatekeeper_sslcert", + "gatekeeper_sslkey", + "gatekeeper_cns", + "gatekeeper_clientkeyspassword", + "gatekeeper_datapath", + "gatekeeper_port", + "lightning_expose", + "lightning_implementation", + "lightning_datapath", + "lightning_nodename", + "lightning_nodecolor", + "proxy_datapath", + "otsclient_datapath", + "traefik_datapath" + ], + "allOf": [ + { + "if": { + "properties": { + "run_as_different_user": { + "enum": [ + true + ] + } + } + }, + "then": { + "required": [ + "username" + ] + } + }, + { + "if": { + "properties": { + "use_xpub": { + "enum": [ + true + ] + } + } + }, + "then": { + "required": [ + "xpub", + "derivation_path" + ] + } + }, + { + "if": { + "properties": { + "bitcoin_prune": { + "enum": [ + true + ] + } + } + }, + "then": { + "required": [ + "bitcoin_prune_size" + ] + } + } + ], + "properties": { + "schema_version": { + "type": "string", + "enum": [ + "0.2.0" + ], + "default": "0.2.0", + "examples": [ + "0.2.0" + ] + }, + "setup_version": { + "type": "string", + "examples": [ + "v0.2.0" + ] + }, + "docker_versions": { + "$id": "#/properties/dockerVersions", + "type": "object", + "title": "All versions of the docker containers", + "default": {}, + "additionalProperties": { + "type": "string" + } + }, + "features": { + "$id": "#/properties/features", + "type": "array", + "title": "The optional features of this cyphernode", + "default": [], + "items": { + "$id": "#/properties/features/items", + "type": "string", + "enum": [ + "lightning", + "otsclient" + ], + "title": "The feature", + "default": "", + "examples": [ + "lightning", + "otsclient" + ] + } + }, + "net": { + "$id": "#/properties/net", + "type": "string", + "enum": [ + "testnet", + "mainnet" + ], + "title": "The net cyphernode is running on", + "default": "testnet", + "examples": [ + "testnet" + ] + }, + "use_xpub": { + "$id": "#/properties/use_xpub", + "type": "boolean", + "title": "Use xpub key?", + "default": false, + "examples": [ + false + ] + }, + "xpub": { + "$id": "#/properties/xpub", + "type": "string", + "title": "Default xpub to derive addresses from", + "pattern": "^(\\w+)$" + }, + "derivation_path": { + "$id": "#/properties/derivation_path", + "type": "string", + "title": "Default derivation path", + "default": "0/n", + "examples": [ + "0/n" + ] + }, + "installer_mode": { + "$id": "#/properties/installer_mode", + "type": "string", + "enum": [ + "docker" + ], + "title": "Install mode", + "default": "docker", + "examples": [ + "docker" + ] + }, + "run_as_different_user": { + "$id": "#/properties/run_as_different_user", + "type": "boolean", + "title": "Run as different user", + "default": true, + "examples": [ + true + ] + }, + "username": { + "$id": "#/properties/username", + "type": "string", + "title": "Username to run under", + "default": "cyphernode", + "examples": [ + "cyphernode" + ] + }, + "docker_mode": { + "$id": "#/properties/docker_mode", + "type": "string", + "enum": [ + "swarm", + "compose" + ], + "title": "How to run the containers", + "default": "compose", + "examples": [ + "compose" + ] + }, + "bitcoin_rpcuser": { + "$id": "#/properties/bitcoin_rpcuser", + "type": "string", + "title": "Bitcoin rpc user", + "default": "bitcoin", + "examples": [ + "bitcoin" + ] + }, + "bitcoin_rpcpassword": { + "$id": "#/properties/bitcoin_rpcpassword", + "type": "string", + "title": "Bitcoin rpc password", + "default": "CHANGEME", + "examples": [ + "CHANGEME" + ] + }, + "bitcoin_uacomment": { + "$id": "#/properties/bitcoin_uacomment", + "type": "string", + "title": "Bitcoin user agent comment", + "examples": [ + "cyphernode" + ] + }, + "bitcoin_prune": { + "$id": "#/properties/bitcoin_prune", + "type": "boolean", + "title": "Bitcoin prune", + "default": false, + "examples": [ + "false" + ] + }, + "bitcoin_prune_size": { + "$id": "#/properties/bitcoin_prune_size", + "type": "integer", + "title": "Bitcoin prune size", + "default": 550, + "examples": [ + 550 + ] + }, + "bitcoin_datapath": { + "$id": "#/properties/bitcoin_datapath", + "type": "string", + "title": "Bitcoin datapath", + "examples": [ + "/tmp/cyphernode/bitcoin" + ] + }, + "bitcoin_datapath_custom": { + "$id": "#/properties/bitcoin_datapath_custom", + "type": "string", + "title": "Bitcoin custom datapath", + "examples": [ + "/tmp/cyphernode/bitcoin" + ] + }, + "lightning_datapath": { + "$id": "#/properties/lightning_datapath", + "type": "string", + "title": "Lightning datapath", + "examples": [ + "/tmp/cyphernode/lightning" + ] + }, + "lightning_datapath_custom": { + "$id": "#/properties/lightning_datapath_custom", + "type": "string", + "title": "Lightning custom datapath", + "examples": [ + "/tmp/cyphernode/lightning" + ] + }, + "proxy_datapath": { + "$id": "#/properties/proxy_datapath", + "type": "string", + "title": "Proxy datapath", + "examples": [ + "/tmp/cyphernode/proxy" + ] + }, + "proxy_datapath_custom": { + "$id": "#/properties/proxy_datapath_custom", + "type": "string", + "title": "Proxy custom datapath", + "examples": [ + "/tmp/cyphernode/proxy" + ] + }, + "otsclient_datapath": { + "$id": "#/properties/otsclient_datapath", + "type": "string", + "title": "OTS Client datapath", + "examples": [ + "/tmp/cyphernode/otsclient" + ] + }, + "otsclient_datapath_custom": { + "$id": "#/properties/otsclient_datapath_custom", + "type": "string", + "title": "OTS Client custom datapath", + "examples": [ + "/tmp/cyphernode/otsclient" + ] + }, + "traefik_datapath": { + "$id": "#/properties/traefik_datapath", + "type": "string", + "title": "Traefik datapath", + "examples": [ + "/tmp/cyphernode/traefik" + ] + }, + "traefik_datapath_custom": { + "$id": "#/properties/traefik_datapath_custom", + "type": "string", + "title": "Traefik custom datapath", + "examples": [ + "/tmp/cyphernode/traefik" + ] + }, + "lightning_external_ip": { + "$id": "#/properties/lightning_external_ip", + "type": "string", + "format": "ipv4", + "title": "External lightning node ip", + "examples": [ + "123.123.123.123" + ] + }, + "bitcoin_mode": { + "$id": "#/properties/bitcoin_mode", + "type": "string", + "enum": [ + "internal" + ], + "title": "Bitcoin mode", + "default": "internal", + "examples": [ + "internal" + ] + }, + "bitcoin_expose": { + "$id": "#/properties/bitcoin_expose", + "type": "boolean", + "title": "Expose bitcoin node", + "default": false, + "examples": [ + false + ] + }, + "lightning_expose": { + "$id": "#/properties/lightning_expose", + "type": "boolean", + "title": "Expose lightning node", + "default": false, + "examples": [ + false + ] + }, + "gatekeeper_datapath": { + "$id": "#/properties/gatekeeper_datapath", + "type": "string", + "title": "Gatekeeper datapath", + "examples": [ + "/tmp/cyphernode/gatekeeper" + ] + }, + "gatekeeper_datapath_custom": { + "$id": "#/properties/gatekeeper_datapath_custom", + "type": "string", + "title": "Gatekeeper custom datapath", + "examples": [ + "/tmp/cyphernode/gatekeeper" + ] + }, + "gatekeeper_port": { + "$id": "#/properties/gatekeeper_port", + "type": "integer", + "title": "Gatekeeper port", + "default": 2009, + "examples": [ + 2009 + ] + }, + "gatekeeper_keys": { + "$id": "#/properties/gatekeeper_keys", + "type": "object", + "title": "Gatekeeper keys", + "default": { + "configEntries": [], + "clientInformation": [] + }, + "required": [ + "configEntries", + "clientInformation" + ], + "properties": { + "configEntries": { + "$id": "#/properties/gatekeeper_keys/configEntries", + "type": "array", + "items": { + "$id": "#/properties/gatekeeper_keys/configEntries/entry", + "type": "string", + "pattern": "^kapi_id=\".+\";kapi_key=\".+\";kapi_groups=\".+\";.+$" + }, + "examples": [ + [ + "kapi_id=\"000\";kapi_key=\"a27f9e73fdde6a5005879c259c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"stats\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}", + "kapi_id=\"001\";kapi_key=\"a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"stats,watcher\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}", + "kapi_id=\"002\";kapi_key=\"fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d\";kapi_groups=\"stats,watcher,spender\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}", + "kapi_id=\"003\";kapi_key=\"f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417\";kapi_groups=\"stats,watcher,spender,admin\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}" + ] + ] + }, + "clientInformation": { + "$id": "#/properties/gatekeeper_keys/clientInformation", + "type": "array", + "items": { + "$id": "#/properties/gatekeeper_keys/clientInformation/entry", + "type": "string", + "pattern": "^.+=.+$" + }, + "examples": [ + [ + "000=a27f9e73fdde6a5005879c259c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a", + "001=a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a", + "002=fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d", + "003=f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417" + ] + ] + } + } + }, + "gatekeeper_sslcert": { + "$id": "#/properties/gatekeeper_sslcert", + "type": "string", + "title": "Gatekeeper SSL Cert" + }, + "gatekeeper_sslkey": { + "$id": "#/properties/gatekeeper_sslkey", + "type": "string", + "title": "Gatekeeper SSL Key" + }, + "gatekeeper_cns": { + "$id": "#/properties/gatekeeper_cns", + "type": "string", + "title": "Gatekeeper cns", + "examples": [ + "myhost.mydomain.com,*.myotherdomain.com,123.123.123.123" + ] + }, + "gatekeeper_clientkeyspassword": { + "$id": "#/properties/gatekeeper_clientkeyspassword", + "type": "string", + "title": "Password for the encrypted client keys archive" + }, + "adminhash": { + "$id": "#/properties/adminhash", + "type": "string", + "title": "Bcrypted hash of admin password" + }, + "lightning_implementation": { + "$id": "#/properties/lightning_implementation", + "type": "string", + "enum": [ + "c-lightning" + ], + "title": "The lightning implementation", + "default": "c-lightning", + "examples": [ + "c-lightning" + ] + }, + "lightning_nodename": { + "$id": "#/properties/lightning_nodename", + "type": "string", + "title": "The lightning node name", + "examples": [ + "🚀 Mighty Moose 🚀" + ] + }, + "lightning_nodecolor": { + "$id": "#/properties/lightning_nodecolor", + "type": "string", + "pattern": "^[0-9A-Fa-f]{6}$", + "title": "The lightning node color", + "examples": [ + "ff0000", + "00ff00", + "00ffff" + ] + } + } +} \ No newline at end of file diff --git a/cyphernodeconf_docker/splash/01.png.txt b/cyphernodeconf_docker/splash/01.png.txt new file mode 100644 index 0000000..74e17cb --- /dev/null +++ b/cyphernodeconf_docker/splash/01.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/splash/02.png.txt b/cyphernodeconf_docker/splash/02.png.txt new file mode 100644 index 0000000..dd52cfd --- /dev/null +++ b/cyphernodeconf_docker/splash/02.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/splash/03.png.txt b/cyphernodeconf_docker/splash/03.png.txt new file mode 100644 index 0000000..02f1d96 --- /dev/null +++ b/cyphernodeconf_docker/splash/03.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/splash/04.png.txt b/cyphernodeconf_docker/splash/04.png.txt new file mode 100644 index 0000000..33c66f4 --- /dev/null +++ b/cyphernodeconf_docker/splash/04.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/splash/05.png.txt b/cyphernodeconf_docker/splash/05.png.txt new file mode 100644 index 0000000..162026e --- /dev/null +++ b/cyphernodeconf_docker/splash/05.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/splash/06.png.txt b/cyphernodeconf_docker/splash/06.png.txt new file mode 100644 index 0000000..dd23868 --- /dev/null +++ b/cyphernodeconf_docker/splash/06.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/splash/07.png.txt b/cyphernodeconf_docker/splash/07.png.txt new file mode 100644 index 0000000..93be9b3 --- /dev/null +++ b/cyphernodeconf_docker/splash/07.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/splash/08.png.txt b/cyphernodeconf_docker/splash/08.png.txt new file mode 100644 index 0000000..f8990e0 --- /dev/null +++ b/cyphernodeconf_docker/splash/08.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/splash/09.png.txt b/cyphernodeconf_docker/splash/09.png.txt new file mode 100644 index 0000000..e662e1b --- /dev/null +++ b/cyphernodeconf_docker/splash/09.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/splash/10.png.txt b/cyphernodeconf_docker/splash/10.png.txt new file mode 100644 index 0000000..a56b5c7 --- /dev/null +++ b/cyphernodeconf_docker/splash/10.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/splash/11.png.txt b/cyphernodeconf_docker/splash/11.png.txt new file mode 100644 index 0000000..386643d --- /dev/null +++ b/cyphernodeconf_docker/splash/11.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/splash/12.png.txt b/cyphernodeconf_docker/splash/12.png.txt new file mode 100644 index 0000000..2d261ab --- /dev/null +++ b/cyphernodeconf_docker/splash/12.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/splash/13.png.txt b/cyphernodeconf_docker/splash/13.png.txt new file mode 100644 index 0000000..ae6ffa7 --- /dev/null +++ b/cyphernodeconf_docker/splash/13.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/splash/14.png.txt b/cyphernodeconf_docker/splash/14.png.txt new file mode 100644 index 0000000..7228c81 --- /dev/null +++ b/cyphernodeconf_docker/splash/14.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/splash/15.png.txt b/cyphernodeconf_docker/splash/15.png.txt new file mode 100644 index 0000000..74e17cb --- /dev/null +++ b/cyphernodeconf_docker/splash/15.png.txt @@ -0,0 +1,25 @@ +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          +                                          diff --git a/cyphernodeconf_docker/templates/bitcoin/bitcoin.conf b/cyphernodeconf_docker/templates/bitcoin/bitcoin.conf new file mode 100644 index 0000000..0f6af2d --- /dev/null +++ b/cyphernodeconf_docker/templates/bitcoin/bitcoin.conf @@ -0,0 +1,46 @@ +<% if (net === 'testnet') { %> +# testnet +testnet=1 +<% } %> + +<% if (bitcoin_prune && bitcoin_mode === 'internal') { %> +prune=<%= bitcoin_prune_size || 550 %> +<% } else { %> +txindex=1 +<% } %> + +zmqpubrawblock=tcp://0.0.0.0:18501 +zmqpubrawtx=tcp://0.0.0.0:18502 + +#tor +#proxy=127.0.0.1:9050 +#listen=1 + +maxmempool=64 +dbcache=64 + +rpcuser=<%= bitcoin_rpcuser %> +rpcpassword=<%= bitcoin_rpcpassword %> + +# ATTENTION: VERY DANGEROUS OUTSIDE THE DOCKER NETWORK +rpcallowip=0.0.0.0/0 +server=1 + +<% if (net === 'testnet') { %> +test.wallet=watching01.dat +test.wallet=xpubwatching01.dat +test.wallet=spending01.dat +test.wallet=ln01.dat +<% } else { %> +main.wallet=watching01.dat +main.wallet=xpubwatching01.dat +main.wallet=spending01.dat +main.wallet=ln01.dat +<% } %> + +walletnotify=/usr/bin/curl proxy:8888/conf/%s +blocknotify=/usr/bin/curl proxy:8888/newblock/%s + +<% if ( bitcoin_uacomment != null && bitcoin_uacomment != '' ) { %> +uacomment=<%= bitcoin_uacomment %> +<% } %> diff --git a/cyphernodeconf_docker/templates/gatekeeper/api.properties b/cyphernodeconf_docker/templates/gatekeeper/api.properties new file mode 100644 index 0000000..d3834fb --- /dev/null +++ b/cyphernodeconf_docker/templates/gatekeeper/api.properties @@ -0,0 +1,54 @@ + +# Watcher can do stuff +# Spender can do what the watcher can do plus more stuff +# Admin can do what the spender can do plus even more stuff + +# Stats can: +action_getblockchaininfo=stats + +# Watcher can: +action_watch=watcher +action_unwatch=watcher +action_watchxpub=watcher +action_unwatchxpubbyxpub=watcher +action_unwatchxpubbylabel=watcher +action_getactivewatchesbyxpub=watcher +action_getactivewatchesbylabel=watcher +action_getactivexpubwatches=watcher +action_watchtxid=watcher +action_getactivewatches=watcher +action_getbestblockhash=watcher +action_getbestblockinfo=watcher +action_getblockinfo=watcher +action_gettransaction=watcher +action_ln_getinfo=watcher +action_ln_create_invoice=watcher +action_ln_getconnectionstring=watcher +action_ln_decodebolt11=watcher + +# Spender can do what the watcher can do, plus: +action_getbalance=spender +action_getbalancebyxpub=spender +action_getbalancebyxpublabel=spender +action_getnewaddress=spender +action_spend=spender +action_addtobatch=spender +action_batchspend=spender +action_deriveindex=spender +action_derivepubpath=spender +action_ln_pay=spender +action_ln_newaddr=spender +action_ots_stamp=spender +action_ots_getfile=spender +action_ln_getinvoice=spender +action_ln_decodebolt11=spender +action_ln_connectfund=spender + +# Admin can do what the spender can do, plus: + + +# Should be called from inside the Docker network only: +action_conf=internal +action_newblock=internal +action_executecallbacks=internal +action_ots_backoffice=internal \ No newline at end of file diff --git a/cyphernodeconf_docker/templates/gatekeeper/cert.pem b/cyphernodeconf_docker/templates/gatekeeper/cert.pem new file mode 100644 index 0000000..a1bab17 --- /dev/null +++ b/cyphernodeconf_docker/templates/gatekeeper/cert.pem @@ -0,0 +1 @@ +<%- gatekeeper_sslcert %> \ No newline at end of file diff --git a/cyphernodeconf_docker/templates/gatekeeper/key.pem b/cyphernodeconf_docker/templates/gatekeeper/key.pem new file mode 100644 index 0000000..affc9b4 --- /dev/null +++ b/cyphernodeconf_docker/templates/gatekeeper/key.pem @@ -0,0 +1 @@ +<%- gatekeeper_sslkey %> \ No newline at end of file diff --git a/cyphernodeconf_docker/templates/gatekeeper/keys.properties b/cyphernodeconf_docker/templates/gatekeeper/keys.properties new file mode 100644 index 0000000..dc4c8c8 --- /dev/null +++ b/cyphernodeconf_docker/templates/gatekeeper/keys.properties @@ -0,0 +1 @@ +<%- gatekeeper_keys.configEntries.join('\n') %> diff --git a/cyphernodeconf_docker/templates/installer/config.sh b/cyphernodeconf_docker/templates/installer/config.sh new file mode 100644 index 0000000..e8af685 --- /dev/null +++ b/cyphernodeconf_docker/templates/installer/config.sh @@ -0,0 +1,21 @@ +INSTALLER_MODE=<%= installer_mode %> +BITCOIN_INTERNAL=<%= (bitcoin_mode==="internal"?'true':'false') %> +FEATURE_LIGHTNING=<%= (features.indexOf('lightning') != -1)?'true':'false' %> +FEATURE_OTSCLIENT=<%= (features.indexOf('otsclient') != -1)?'true':'false' %> +LIGHTNING_IMPLEMENTATION=<%= lightning_implementation %> +PROXY_DATAPATH=<%= proxy_datapath %> +GATEKEEPER_DATAPATH=<%= gatekeeper_datapath %> +TRAEFIK_DATAPATH=<%= traefik_datapath %> +DOCKER_MODE=<%= docker_mode %> +RUN_AS_USER=<%= run_as_different_user?username:'' %> +CLEANUP=<%= installer_cleanup?'true':'false' %> +SHARED_HTPASSWD_PATH=<%= traefik_datapath %>/htpasswd +<% if ( features.indexOf('lightning') !== -1 && lightning_implementation === 'c-lightning' ) { %> +LIGHTNING_DATAPATH=<%= lightning_datapath %> +<% } %> +<% if ( features.indexOf('otsclient') !== -1 ) { %> +OTSCLIENT_DATAPATH=<%= otsclient_datapath %> +<% } %> +<% if ( bitcoin_mode==="internal" ) { %> +BITCOIN_DATAPATH=<%= bitcoin_datapath %> +<% } %> diff --git a/cyphernodeconf_docker/templates/installer/docker/docker-compose.yaml b/cyphernodeconf_docker/templates/installer/docker/docker-compose.yaml new file mode 100644 index 0000000..b0bbb1e --- /dev/null +++ b/cyphernodeconf_docker/templates/installer/docker/docker-compose.yaml @@ -0,0 +1,200 @@ +version: "3" + +services: + gatekeeper: + # HTTP authentication API gate + environment: + - "TRACING=1" + image: cyphernode/gatekeeper:<%= gatekeeper_version %> + ports: + - "<%= gatekeeper_port %>:443" + volumes: + - "<%= gatekeeper_datapath %>/certs:/etc/ssl/certs" + - "<%= gatekeeper_datapath %>/private:/etc/ssl/private" + - "<%= gatekeeper_datapath %>/keys.properties:/etc/nginx/conf.d/keys.properties" + - "<%= gatekeeper_datapath %>/api.properties:/etc/nginx/conf.d/api.properties" + - "<%= gatekeeper_datapath %>/htpasswd:/etc/nginx/conf.d/status/htpasswd" + - "<%= gatekeeper_datapath %>/installation.json:/etc/nginx/conf.d/s/stats/installation.json" + - "<%= gatekeeper_datapath %>/client.7z:/etc/nginx/conf.d/s/stats/client.7z" + - "<%= gatekeeper_datapath %>/config.7z:/etc/nginx/conf.d/s/stats/config.7z" + command: $USER + +# deploy: +# placement: +# constraints: [node.hostname==dev] + networks: + - cyphernodenet + - cyphernodeappsnet + restart: always + + traefik: + image: traefik:v1.7.9-alpine + restart: always + ports: + - 80:80 + - 443:443 +# deploy: +# placement: +# constraints: [node.hostname==dev] + volumes: + - "/var/run/docker.sock:/var/run/docker.sock" + - "<%= traefik_datapath%>/traefik.toml:/traefik.toml" + - "<%= traefik_datapath%>/acme.json:/acme.json" + - "<%= traefik_datapath%>/htpasswd:/htpasswd/htpasswd" + networks: + - cyphernodeappsnet + + proxy: + command: $USER ./startproxy.sh + # Bitcoin Mini Proxy + environment: + - "TRACING=1" + - "WATCHER_BTC_NODE_RPC_URL=<%= (bitcoin_mode === 'internal')?'bitcoin':bitcoin_node_ip %>:<%= (net === 'mainnet')?'8332':'18332' %>/wallet" + - "WATCHER_BTC_NODE_DEFAULT_WALLET=watching01.dat" + - "WATCHER_BTC_NODE_XPUB_WALLET=xpubwatching01.dat" + - "WATCHER_BTC_NODE_RPC_USER=<%= bitcoin_rpcuser %>:<%= bitcoin_rpcpassword %>" + - "WATCHER_BTC_NODE_RPC_CFG=/tmp/watcher_btcnode_curlcfg.properties" + - "SPENDER_BTC_NODE_RPC_URL=<%= (bitcoin_mode === 'internal')?'bitcoin':bitcoin_node_ip %>:<%= (net === 'mainnet')?'8332':'18332' %>/wallet" + - "SPENDER_BTC_NODE_DEFAULT_WALLET=spending01.dat" + - "SPENDER_BTC_NODE_RPC_USER=<%= bitcoin_rpcuser %>:<%= bitcoin_rpcpassword %>" + - "SPENDER_BTC_NODE_RPC_CFG=/tmp/spender_btcnode_curlcfg.properties" + - "PROXY_LISTENING_PORT=8888" + - "DB_PATH=/proxy/db" + - "DB_FILE=/proxy/db/proxydb" + - "PYCOIN_CONTAINER=pycoin:7777" +<% if ( use_xpub && xpub ) { %> + - "DERIVATION_PUB32=<%= xpub %>" + - "DERIVATION_PATH=<%= derivation_path %>" +<% } %> + - "WATCHER_BTC_NODE_PRUNED=<%= bitcoin_prune?'true':'false' %>" + - "OTSCLIENT_CONTAINER=otsclient:6666" + - "OTS_FILES=/proxy/otsfiles" + - "XPUB_DERIVATION_GAP=100" + image: cyphernode/proxy:<%= proxy_version %> +<% if ( devmode ) { %> + ports: + - "8888:8888" +<% } %> + volumes: + - "<%= proxy_datapath %>:/proxy/db" + <% if ( features.indexOf('lightning') !== -1 && lightning_implementation === 'c-lightning' ) { %> + - "<%= lightning_datapath %>:/.lightning" + <% } %> + <% if ( features.indexOf('otsclient') !== -1 ) { %> + - "<%= otsclient_datapath %>:/proxy/otsfiles" + <% } %> + +# deploy: +# placement: +# constraints: [node.hostname==dev] + networks: + - cyphernodenet + restart: always + proxycron: + environment: + - "TX_CONF_URL=proxy:8888/executecallbacks" + - "OTS_URL=proxy:8888/ots_backoffice" + image: cyphernode/proxycron:<%= proxycron_version %> +# deploy: +# placement: +# constraints: [node.hostname==dev] + networks: + - cyphernodenet + restart: always + pycoin: + # Pycoin + command: $USER ./startpycoin.sh + image: cyphernode/pycoin:<%= pycoin_version %> + environment: + - "TRACING=1" + - "PYCOIN_LISTENING_PORT=7777" +<% if ( devmode ) { %> + ports: + - "7777:7777" +<% } %> +# deploy: +# placement: +# constraints: [node.hostname==dev] + networks: + - cyphernodenet + restart: always +<% if ( features.indexOf('lightning') !== -1 && lightning_implementation === 'c-lightning' ) { %> + lightning: + command: $USER lightningd + image: cyphernode/clightning:<%= lightning_version %> + + <% if( lightning_expose ) { %> + ports: + - "9735:9735" + <% } %> + volumes: + - "<%= lightning_datapath %>:/.lightning" + - "<%= lightning_datapath %>/bitcoin.conf:/.bitcoin/bitcoin.conf" +# deploy: +# placement: +# constraints: [node.hostname==dev] + networks: + - cyphernodenet + restart: always + +<% } %> +<% if ( features.indexOf('otsclient') !== -1 ) { %> + otsclient: + environment: + - "TRACING=1" + - "OTSCLIENT_LISTENING_PORT=6666" + image: cyphernode/otsclient:<%= otsclient_version %> +# deploy: +# placement: +# constraints: [node.hostname==dev] + volumes: + - "<%= otsclient_datapath %>:/otsfiles" + command: $USER /script/startotsclient.sh + networks: + - cyphernodenet + restart: always +<% } %> + +<% if( bitcoin_mode === 'internal' ) { %> + bitcoin: + command: $USER bitcoind + image: cyphernode/bitcoin:<%= bitcoin_version %> +<% if( bitcoin_expose ) { %> + ports: + - "<%= (net === 'mainnet')?'8332:8332':'18332:18332' %>" +<% } %> +# deploy: +# placement: +# constraints: [node.hostname==dev] + volumes: + - "<%= bitcoin_datapath %>:/.bitcoin" + networks: + - cyphernodenet + restart: always +<% } %> + + broker: + image: eclipse-mosquitto:1.6 +# deploy: +# placement: +# constraints: [node.hostname==dev] + networks: + - cyphernodenet + restart: always + + notifier: + image: cyphernode/notifier:<%= notifier_version %> + command: $USER ./startnotifier.sh +# deploy: +# placement: +# constraints: [node.hostname==dev] + networks: + - cyphernodenet + - cyphernodeappsnet + restart: always + +networks: + cyphernodenet: + external: true + cyphernodeappsnet: + external: true \ No newline at end of file diff --git a/cyphernodeconf_docker/templates/installer/start.sh b/cyphernodeconf_docker/templates/installer/start.sh new file mode 100644 index 0000000..7429929 --- /dev/null +++ b/cyphernodeconf_docker/templates/installer/start.sh @@ -0,0 +1,130 @@ +#!/bin/sh + +. ./installer/config.sh + +# be aware that randomly downloaded cyphernode apps will have access to +# your configuration and filesystem. +# !!!!!!!!! DO NOT INCLUDE APPS WITHOUT REVIEW !!!!!!!!!! +# TODO: Test if we can mitigate this security issue by +# running app dockers inside a docker container + +start_apps() { + local SCRIPT_NAME="start.sh" + local APP_SCRIPT_PATH + local APP_START_SCRIPT_PATH + local APP_ID + + for i in $current_path/apps/* + do + APP_SCRIPT_PATH=$(echo $i) + if [ -d "$APP_SCRIPT_PATH" ] && [ ! -f "$APP_SCRIPT_PATH/ignoreThisApp" ]; then + APP_START_SCRIPT_PATH="$APP_SCRIPT_PATH/$SCRIPT_NAME" + APP_ID=$(basename $APP_SCRIPT_PATH) + + if [ -f "$APP_START_SCRIPT_PATH" ]; then + . $APP_START_SCRIPT_PATH + elif [ -f "$APP_SCRIPT_PATH/docker-compose.yaml" ]; then + export SHARED_HTPASSWD_PATH + export GATEKEEPER_DATAPATH + export LIGHTNING_DATAPATH + export BITCOIN_DATAPATH + export APP_SCRIPT_PATH + export APP_ID + export DOCKER_MODE + + if [ "$DOCKER_MODE" = "swarm" ]; then + docker stack deploy -c $APP_SCRIPT_PATH/docker-compose.yaml $APP_ID + elif [ "$DOCKER_MODE" = "compose" ]; then + docker-compose -f $APP_SCRIPT_PATH/docker-compose.yaml up -d --remove-orphans + fi + fi + fi + done +} + +test_apps() { + local SCRIPT_NAME="test.sh" + local APP_SCRIPT_PATH + local APP_START_SCRIPT_PATH + local APP_ID + local returncode=0 + + for i in $current_path/apps/* + do + APP_SCRIPT_PATH=$(echo $i) + if [ -d "$APP_SCRIPT_PATH" ]; then + APP_TEST_SCRIPT_PATH="$APP_SCRIPT_PATH/$SCRIPT_NAME" + + if [ -f "$APP_TEST_SCRIPT_PATH" ] && [ ! -f "$APP_SCRIPT_PATH/ignoreThisApp" ]; then + APP_ID=$(basename "$APP_SCRIPT_PATH") + printf "\r\n\e[1;36mTesting $APP_ID... \e[1;0m" + . $APP_TEST_SCRIPT_PATH + local rc=$? + + if [ ""$rc -eq "0" ]; then + printf "\e[1;36m$APP_ID rocks!\e[1;0m" + fi + returncode=$(($rc | ${returncode})) + echo "" + fi + fi + done + return $returncode +} + +<% if (run_as_different_user) { %> +OS=$(uname -s) +if [ "$OS" = "Darwin" ]; then + printf "\r\n\033[0;91m'Run as another user' feature is not supported on OSX. User <%= default_username %> will be used to run Cyphernode.\033[0m\r\n\r\n" + export USER=$(id -u <%= default_username %>):$(id -g <%= default_username %>) +else + export USER=$(id -u <%= username %>):$(id -g <%= username %>) +fi +<% } else { %> +export USER=$(id -u <%= default_username %>):$(id -g <%= default_username %>) +<% } %> + +export ARCH=$(uname -m) +current_path="$(cd "$(dirname "$0")" >/dev/null && pwd)" + +<% if (docker_mode == 'swarm') { %> +docker stack deploy -c $current_path/docker-compose.yaml cyphernode +<% } else if(docker_mode == 'compose') { %> +docker-compose -f $current_path/docker-compose.yaml up -d --remove-orphans +<% } %> + +start_apps + +arch=$(uname -m) +case "${arch}" in arm*) + printf "\r\n\033[1;31mSince we're on a slow RPi, let's give Docker 60 more seconds before performing our tests...\033[0m\r\n\r\n" + sleep 60 +;; +esac + +# Will test if Cyphernode is fully up and running... +docker run --rm -it -v $current_path/testfeatures.sh:/testfeatures.sh \ +-v <%= gatekeeper_datapath %>:/gatekeeper \ +-v $current_path:/dist \ +--network cyphernodenet eclipse-mosquitto:1.6.2 /testfeatures.sh + +if [ -f $current_path/exitStatus.sh ]; then + . $current_path/exitStatus.sh + rm -f $current_path/exitStatus.sh +fi + +test_apps + +EXIT_STATUS=$(($? | ${EXIT_STATUS})) + +printf "\r\n\e[1;32mTests finished.\e[0m\n" + +if [ "$EXIT_STATUS" -ne "0" ]; then + printf "\r\n\033[1;31mThere was an error during cyphernode installation. Please see Docker's logs for more information. Run ./stop.sh to stop cyphernode.\r\n\r\n\033[0m" + exit 1 +fi + +printf "\r\n\033[0;92mDepending on your current location and DNS settings, point your favorite browser to one of the following URLs to access Cyphernode's status page:\r\n" +printf "\r\n" +printf "\033[0;95m<% cns.forEach(cn => { %><%= ('https://' + cn + '/welcome\\r\\n') %><% }) %>\033[0m\r\n" +printf "\033[0;92mUse 'admin' as the username with the configuration password you selected at the beginning of the configuration process.\r\n\r\n\033[0m" diff --git a/cyphernodeconf_docker/templates/installer/stop.sh b/cyphernodeconf_docker/templates/installer/stop.sh new file mode 100644 index 0000000..88b28bc --- /dev/null +++ b/cyphernodeconf_docker/templates/installer/stop.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +current_path="$(cd "$(dirname "$0")" >/dev/null && pwd)" + + +# be aware that randomly downloaded cyphernode apps will have access to +# your configuration and filesystem. +# !!!!!!!!! DO NOT INCLUDE APPS WITHOUT REVIEW !!!!!!!!!! +# TODO: Test if we can mitigate this security issue by +# running app dockers inside a docker container + +stop_apps() { + local SCRIPT_NAME="stop.sh" + local APP_SCRIPT_PATH + local APP_START_SCRIPT_PATH + local APP_ID + + for i in $current_path/apps/* + do + APP_SCRIPT_PATH=$(echo $i) + if [ -d "$APP_SCRIPT_PATH" ] && [ ! -f "$APP_SCRIPT_PATH/ignoreThisApp" ]; then + APP_STOP_SCRIPT_PATH="$APP_SCRIPT_PATH/$SCRIPT_NAME" + APP_ID=$(basename $APP_SCRIPT_PATH) + + if [ -f "$APP_STOP_SCRIPT_PATH" ]; then + . $APP_STOP_SCRIPT_PATH + elif [ -f "$APP_SCRIPT_PATH/docker-compose.yaml" ]; then + export SHARED_HTPASSWD_PATH + export GATEKEEPER_DATAPATH + export LIGHTNING_DATAPATH + export BITCOIN_DATAPATH + export APP_SCRIPT_PATH + export APP_ID + export DOCKER_MODE + + if [ "$DOCKER_MODE" = "swarm" ]; then + docker stack rm $APP_ID + elif [ "$DOCKER_MODE" = "compose" ]; then + docker-compose -f $APP_SCRIPT_PATH/docker-compose.yaml down + fi + + fi + fi + done +} + +. ./installer/config.sh +stop_apps + +<% if (docker_mode == 'swarm') { %> +export USER=$(id -u):$(id -g) +export ARCH=$(uname -m) +docker stack rm cyphernode +<% } else if(docker_mode == 'compose') { %> +export USER=$(id -u):$(id -g) +export ARCH=$(uname -m) +docker-compose -f $current_path/docker-compose.yaml down +<% } %> diff --git a/cyphernodeconf_docker/templates/installer/testfeatures.sh b/cyphernodeconf_docker/templates/installer/testfeatures.sh new file mode 100644 index 0000000..109d2bd --- /dev/null +++ b/cyphernodeconf_docker/templates/installer/testfeatures.sh @@ -0,0 +1,374 @@ +#!/bin/sh + +apk add --update --no-cache openssl curl jq > /dev/null + +. /gatekeeper/keys.properties + +checkgatekeeper() { + echo -e "\r\n\e[1;36mTesting Gatekeeper...\e[0;32m" > /dev/console + + local rc + local id="001" + local k + eval k='$ukey_'$id + + local h64=$(echo "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64) + + # Let's test expiration: 1 second in payload, request 2 seconds later + + local p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+1))}" | base64) + local s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1) + local token="$h64.$p64.$s" + + echo -e " Sleeping 2 seconds... " > /dev/console + sleep 2 + + echo " Testing expired request... " > /dev/console + rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/getblockinfo) + [ "${rc}" -ne "403" ] && return 10 + + # Let's test authentication (signature) + + p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64) + s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1) + token="$h64.$p64.a$s" + + echo " Testing bad signature... " > /dev/console + rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/getblockinfo) + [ "${rc}" -ne "403" ] && return 30 + + # Let's test authorization (action access for groups) + + token="$h64.$p64.$s" + + echo " Testing watcher trying to do a spender action... " > /dev/console + rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/getbalance) + [ "${rc}" -ne "403" ] && return 40 + + id="002" + eval k='$ukey_'$id + p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64) + s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1) + token="$h64.$p64.$s" + + echo " Testing spender trying to do an internal action call... " > /dev/console + rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/conf) + [ "${rc}" -ne "403" ] && return 50 + + + id="003" + eval k='$ukey_'$id + p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64) + s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1) + token="$h64.$p64.$s" + + echo " Testing admin trying to do an internal action call... " > /dev/console + rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/conf) + [ "${rc}" -ne "403" ] && return 60 + + echo -e "\e[1;36mGatekeeper rocks!" > /dev/console + + return 0 +} + +checkpycoin() { + echo -en "\r\n\e[1;36mTesting Pycoin... " > /dev/console + local rc + + rc=$(curl -H "Content-Type: application/json" -d "{\"pub32\":\"upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb\",\"path\":\"0/25-30\"}" -s -o /dev/null -w "%{http_code}" http://proxy:8888/derivepubpath) + [ "${rc}" -ne "200" ] && return 100 + + echo -e "\e[1;36mPycoin rocks!" > /dev/console + + return 0 +} + +checkbroker() { + echo -en "\r\n\e[1;36mTesting Broker... " > /dev/console + local rc + + rc=$(mosquitto_pub -h broker -t "testtopic" -m "testbroker") + [ "$?" -ne "0" ] && return 110 + + echo -e "\e[1;36mBroker rocks!" > /dev/console + + return 0 +} + +checknotifier() { + echo -en "\r\n\e[1;36mTesting Notifier... " > /dev/console + local response + local returncode + + response=$(mosquitto_rr -h broker -W 5 -t notifier -e "response/$$" -m "{\"response-topic\":\"response/$$\",\"cmd\":\"web\",\"url\":\"http://proxy:8888/getbestblockhash\"}") + returncode=$? + [ "${returncode}" -ne "0" ] && return 115 + http_code=$(echo "${response}" | jq ".http_code" | tr -d '"') + [ "${http_code}" -ge "400" ] && return 118 + + echo -e "\e[1;36mNotifier rocks!" > /dev/console + + return 0 +} + +checkots() { + echo -en "\r\n\e[1;36mTesting OTSclient... " > /dev/console + local rc + + rc=$(curl -s -H "Content-Type: application/json" -d '{"hash":"123","callbackUrl":"http://callback"}' http://proxy:8888/ots_stamp) + echo "${rc}" | grep "Invalid hash 123 for sha256" > /dev/null + [ "$?" -ne "0" ] && return 200 + + echo -e "\e[1;36mOTSclient rocks!" > /dev/console + + return 0 +} + +checkbitcoinnode() { + echo -en "\r\n\e[1;36mTesting Bitcoin... " > /dev/console + local rc + + rc=$(curl -s -o /dev/null -w "%{http_code}" http://proxy:8888/getbestblockhash) + [ "${rc}" -ne "200" ] && return 300 + + echo -e "\e[1;36mBitcoin node rocks!" > /dev/console + + return 0 +} + +checklnnode() { + echo -en "\r\n\e[1;36mTesting Lightning... " > /dev/console + local rc + + rc=$(curl -s -o /dev/null -w "%{http_code}" http://proxy:8888/ln_getinfo) + [ "${rc}" -ne "200" ] && return 400 + + echo -e "\e[1;36mLN node rocks!" > /dev/console + + return 0 +} + +checkservice() { + local interval=15 + local totaltime=180 + local outcome + local returncode=0 + local endtime=$(($(date +%s) + ${totaltime})) + local result + + echo -e "\r\n\e[1;36mTesting if Cyphernode is up and running... \e[0;36mI will keep trying during up to $((${totaltime} / 60)) minutes to give time to Docker to deploy everything...\e[0;32m" > /dev/console + + while : + do + outcome=0 + for container in gatekeeper proxy proxycron broker notifier pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do + echo -e " \e[0;32mVerifying \e[0;33m${container}\e[0;32m..." > /dev/console + (ping -c 10 ${container} 2> /dev/null | grep "0% packet loss" > /dev/null) & + eval ${container}=$! + done + for container in gatekeeper proxy proxycron broker notifier pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do + eval wait '$'${container} ; returncode=$? ; outcome=$((${outcome} + ${returncode})) + eval c_${container}=${returncode} + done + + # If '0% packet loss' everywhere or 5 minutes passed, we get out of this loop + ([ "${outcome}" -eq "0" ] || [ $(date +%s) -gt ${endtime} ]) && break + + echo -e "\e[1;31mCyphernode still not ready, will retry every ${interval} seconds for $((${totaltime} / 60)) minutes ($((${endtime} - $(date +%s))) seconds left)." > /dev/console + + sleep ${interval} + done + + # "containers": [ + # { "name": "gatekeeper", "active":true }, + # { "name": "proxy", "active":true }, + # { "name": "proxycron", "active":true }, + # { "name": "pycoin", "active":true }, + # { "name": "otsclient", "active":true }, + # { "name": "bitcoin", "active":true }, + # { "name": "lightning", "active":true }, + # ] + for container in gatekeeper proxy proxycron broker notifier pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do + [ -n "${result}" ] && result="${result}," + result="${result}{\"name\":\"${container}\",\"active\":" + eval "returncode=\$c_${container}" + if [ "${returncode}" -eq "0" ]; then + result="${result}true}" + else + result="${result}false}" + fi + done + + result="\"containers\":[${result}]" + + echo $result + + return ${outcome} +} + +timeout_feature() { + local interval=15 + local totaltime=120 + local testwhat=${1} + local returncode + local endtime=$(($(date +%s) + ${totaltime})) + + while : + do + eval ${testwhat} + returncode=$? + + # If no error or 2 minutes passed, we get out of this loop + ([ "${returncode}" -eq "0" ] || [ $(date +%s) -gt ${endtime} ]) && break + + echo -e "\e[1;31mMaybe it's too early, I'll retry every ${interval} seconds for $((${totaltime} / 60)) minutes ($((${endtime} - $(date +%s))) seconds left)." > /dev/console + + sleep ${interval} + done + + return ${returncode} +} + +feature_status() { + local returncode=${1} + local errormsg=${2} + + [ "${returncode}" -eq "0" ] && echo "true" + [ "${returncode}" -ne "0" ] && echo "false" && echo -e "\e[1;31m${errormsg}" > /dev/console +} + +# /proxy/installation.json will contain something like that: +#{ +# "containers": [ +# { "name": "gatekeeper", "active":true }, +# { "name": "proxy", "active":true }, +# { "name": "proxycron", "active":true }, +# { "name": "pycoin", "active":true }, +# { "name": "otsclient", "active":true }, +# { "name": "bitcoin", "active":true }, +# { "name": "lightning", "active":true }, +# ], +# "features": [ +# { "name": "gatekeeper", "working":true }, +# { "name": "pycoin", "working":true }, +# { "name": "otsclient", "working":true }, +# { "name": "bitcoin", "working":true }, +# { "name": "lightning", "working":true }, +# ] +#} + +# Let's first see if everything is up. + +echo "EXIT_STATUS=1" > /dist/exitStatus.sh + +brokenproxy="false" +containers=$(checkservice) +returncode=$? +finalreturncode=${returncode} +if [ "${returncode}" -ne "0" ]; then + echo -e "\e[1;31mCyphernode could not fully start properly within delay." > /dev/console + status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"proxy\") | .active") + if [ "${status}" = "false" ]; then + echo -e "\e[1;31mThe Proxy, the main Cyphernode's component, is not responding. We will only test the gatekeeper if its container is up, but you'll see errors for the other components. Please check the logs." > /dev/console + brokenproxy="true" + fi +else + echo -e "\e[1;36mCyphernode seems to be correctly deployed. Let's run more thourough tests..." > /dev/console +fi + +# Let's now check each feature fonctionality... +# "features": [ +# { "name": "gatekeeper", "working":true }, +# { "name": "pycoin", "working":true }, +# { "name": "otsclient", "working":true }, +# { "name": "bitcoin", "working":true }, +# { "name": "lightning", "working":true }, +# ] + +result="${containers},\"features\":[{\"coreFeature\":true, \"name\":\"cyphernode proxy\",\"working\":true}, {\"coreFeature\":true, \"name\":\"gatekeeper\",\"working\":" +status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"gatekeeper\") | .active") +if [ "${status}" = "true" ]; then + timeout_feature checkgatekeeper + returncode=$? +else + returncode=1 +fi +finalreturncode=$((${returncode} | ${finalreturncode})) +result="${result}$(feature_status ${returncode} 'Gatekeeper error!')}" + +result="${result},{\"coreFeature\":true, \"name\":\"bitcoin\",\"working\":" +status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"bitcoin\") | .active") +if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then + timeout_feature checkbitcoinnode + returncode=$? +else + returncode=1 +fi +finalreturncode=$((${returncode} | ${finalreturncode})) +result="${result}$(feature_status ${returncode} 'Bitcoin error!')}" + +result="${result},{\"coreFeature\":true, \"name\":\"broker\",\"working\":" +status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"broker\") | .active") +if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then + timeout_feature checkbroker + returncode=$? +else + returncode=1 +fi +finalreturncode=$((${returncode} | ${finalreturncode})) +result="${result}$(feature_status ${returncode} 'Broker error!')}" + +result="${result},{\"coreFeature\":true, \"name\":\"notifier\",\"working\":" +status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"notifier\") | .active") +if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then + timeout_feature checknotifier + returncode=$? +else + returncode=1 +fi +finalreturncode=$((${returncode} | ${finalreturncode})) +result="${result}$(feature_status ${returncode} 'Notifier error!')}" + +result="${result},{\"coreFeature\":true, \"name\":\"pycoin\",\"working\":" +status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"pycoin\") | .active") +if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then + timeout_feature checkpycoin + returncode=$? +else + returncode=1 +fi +finalreturncode=$((${returncode} | ${finalreturncode})) +result="${result}$(feature_status ${returncode} 'Pycoin error!')}" + +<% if (features.indexOf('otsclient') != -1) { %> +result="${result},{\"coreFeature\":false, \"name\":\"otsclient\",\"working\":" +status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"otsclient\") | .active") +if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then + timeout_feature checkots + returncode=$? +else + returncode=1 +fi +finalreturncode=$((${returncode} | ${finalreturncode})) +result="${result}$(feature_status ${returncode} 'OTSclient error!')}" +<% } %> + +<% if (features.indexOf('lightning') != -1) { %> +result="${result},{\"coreFeature\":false, \"name\":\"lightning\",\"working\":" +status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"lightning\") | .active") +if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then + timeout_feature checklnnode + returncode=$? +else + returncode=1 +fi +finalreturncode=$((${returncode} | ${finalreturncode})) +result="${result}$(feature_status ${returncode} 'Lightning error!')}" + +<% } %> + +result="{${result}]}" + +echo "${result}" > /gatekeeper/installation.json + +echo "EXIT_STATUS=${finalreturncode}" > /dist/exitStatus.sh diff --git a/cyphernodeconf_docker/templates/lightning/c-lightning/README.md b/cyphernodeconf_docker/templates/lightning/c-lightning/README.md new file mode 100644 index 0000000..d2989c1 --- /dev/null +++ b/cyphernodeconf_docker/templates/lightning/c-lightning/README.md @@ -0,0 +1,5 @@ +# How to create the hmac for the cookie file: + +``` +# echo -n "access-key" | openssl dgst -hmac "cyphernode:sparkwallet" -sha256 -binary | base64 | sed 's/[\+\W]//g' +``` diff --git a/cyphernodeconf_docker/templates/lightning/c-lightning/bitcoin.conf b/cyphernodeconf_docker/templates/lightning/c-lightning/bitcoin.conf new file mode 100644 index 0000000..bed4730 --- /dev/null +++ b/cyphernodeconf_docker/templates/lightning/c-lightning/bitcoin.conf @@ -0,0 +1,8 @@ +<% if (net === 'testnet') { %> +# testnet +testnet=1 +<% } %> + +rpcconnect=<%= (bitcoin_mode === 'internal')?'bitcoin':bitcoin_node_ip %> +rpcuser=<%= bitcoin_rpcuser %> +rpcpassword=<%= bitcoin_rpcpassword %> diff --git a/cyphernodeconf_docker/templates/lightning/c-lightning/config b/cyphernodeconf_docker/templates/lightning/c-lightning/config new file mode 100644 index 0000000..5fd5012 --- /dev/null +++ b/cyphernodeconf_docker/templates/lightning/c-lightning/config @@ -0,0 +1,18 @@ +<% if (net === 'testnet') { %> +# testnet +network=testnet +<% } else if (net === 'mainnet') { %> +network=bitcoin +<% } %> +<% if( lightning_nodename ) { %> +alias=<%= lightning_nodename %> +<% } %> +<% if( lightning_nodecolor ) { %> +rgb=<%= lightning_nodecolor %> +<% } %> +bitcoin-rpcconnect=<%= (bitcoin_mode === 'internal')?'bitcoin':bitcoin_node_ip %> +bitcoin-rpcuser=<%= bitcoin_rpcuser %> +bitcoin-rpcpassword=<%= bitcoin_rpcpassword %> + +addr=0.0.0.0:9735 +announce-addr=<%= lightning_external_ip %>:9735 diff --git a/cyphernodeconf_docker/templates/lightning/c-lightning/cookie b/cyphernodeconf_docker/templates/lightning/c-lightning/cookie new file mode 100644 index 0000000..830e6e6 --- /dev/null +++ b/cyphernodeconf_docker/templates/lightning/c-lightning/cookie @@ -0,0 +1 @@ +cyphernode:sparkwallet:FoeDdQw5yl7pPfqdlGy3OEk/txGqyJjSbVtffhzs7kc= \ No newline at end of file diff --git a/cyphernodeconf_docker/templates/lightning/lnd/lnd.conf b/cyphernodeconf_docker/templates/lightning/lnd/lnd.conf new file mode 100644 index 0000000..071ea40 --- /dev/null +++ b/cyphernodeconf_docker/templates/lightning/lnd/lnd.conf @@ -0,0 +1,27 @@ +[Application Options] +debuglevel=info +maxpendingchannels=10 +externalip=88.198.55.131 +color=#a111ff +alias=SatoshiPortal01 +rpclisten=0.0.0.0:10009 +tlsextraip=lnd +tlsextradomain=lnd + +[Bitcoin] +bitcoin.active=1 +bitcoin.node=bitcoind +bitcoin.mainnet=1 + +[Bitcoind] +bitcoind.rpcuser=<%= bitcoin_rpcuser %> +bitcoind.rpcpass=<%= bitcoin_rpcpassword %> +bitcoind.zmqpubrawblock=tcp://bitcoin:18501 +bitcoind.zmqpubrawtx=tcp://bitcoin:18502 +#bitcoind.zmqpath=tcp://bitcoin:18501 +bitcoind.rpchost=bitcoin + +[autopilot] +autopilot.active=1 +autopilot.maxchannels=5 +autopilot.allocation=0.6 \ No newline at end of file diff --git a/cyphernodeconf_docker/templates/traefik/acme.json b/cyphernodeconf_docker/templates/traefik/acme.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/cyphernodeconf_docker/templates/traefik/acme.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/cyphernodeconf_docker/templates/traefik/htpasswd b/cyphernodeconf_docker/templates/traefik/htpasswd new file mode 100644 index 0000000..2f17faa --- /dev/null +++ b/cyphernodeconf_docker/templates/traefik/htpasswd @@ -0,0 +1 @@ +admin:<%- adminhash %> diff --git a/cyphernodeconf_docker/templates/traefik/traefik.toml b/cyphernodeconf_docker/templates/traefik/traefik.toml new file mode 100644 index 0000000..0b6d48c --- /dev/null +++ b/cyphernodeconf_docker/templates/traefik/traefik.toml @@ -0,0 +1,31 @@ +debug = false + +logLevel = "ERROR" +defaultEntryPoints = ["https","http"] + +[entryPoints] + [entryPoints.http] + address = ":80" + [entryPoints.http.redirect] + entryPoint = "https" + [entryPoints.https] + address = ":443" + [entryPoints.https.tls] + +[retry] + +[docker] +endpoint = "unix:///var/run/docker.sock" +domain = "cyphernode.localhost" +watch = true +exposedByDefault = false + +[acme] +email = "letsencrypt@yourdomain.com" +storage = "acme.json" +entryPoint = "https" +onHostRule = true +[acme.httpChallenge] +entryPoint = "http" +[[acme.domains]] + main = "cyphernode.yourdomain.com" diff --git a/cyphernodeconf_docker/test/apikey.test.js b/cyphernodeconf_docker/test/apikey.test.js new file mode 100644 index 0000000..9edf40e --- /dev/null +++ b/cyphernodeconf_docker/test/apikey.test.js @@ -0,0 +1,42 @@ +const ApiKey = require('../lib/apikey.js'); + + +test( 'Create ApiKey instance', ()=>{ + const apiKey = new ApiKey('testId',['group1','group2']); + expect( apiKey ).not.toBe( undefined ); + expect( apiKey.id ).toEqual( 'testId' ); + expect( apiKey.groups ).toEqual( ['group1','group2'] ); + expect( apiKey.key ).toBe( undefined ); + expect( apiKey.script ).toEqual( 'eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}' ); +}); + +test( 'Create ApiKey instance and randomise it', async ()=>{ + const apiKey = new ApiKey('testId',['group1','group2']); + await apiKey.randomiseKey(); + expect( apiKey ).not.toBe( undefined ); + expect( apiKey.id ).toEqual( 'testId' ); + expect( apiKey.groups ).toEqual( ['group1','group2'] ); + expect( apiKey.key ).not.toBe( undefined ); + expect( apiKey.script ).toEqual( 'eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}' ); +}); + +test( 'Create ApiKey instance, randomise it and use getters', async ()=>{ + const apiKey = new ApiKey('testId',['group1','group2']); + await apiKey.randomiseKey(); + const keyString = apiKey.getKey(); + const script = apiKey.script; + expect( keyString ).not.toBe( undefined ); + expect( apiKey.id ).toEqual( 'testId' ); + expect( apiKey.getClientInformation() ).toEqual( 'testId='+keyString ); + expect( apiKey.getConfigEntry() ).toEqual( `kapi_id="testId";kapi_key="${keyString}";kapi_groups="group1,group2";${script}` ); +}); + +test( 'Set properties of ApiKey instance from config entry', async () => { + const configEntry = 'kapi_id="000";kapi_key="b1fdc782037609f8ecc063ac192e92d57544263a950c637ed6b7d79cc9eb9f95";kapi_groups="stats";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}'; + const apiKey = new ApiKey(); + apiKey.setFromConfigEntry(configEntry); + expect( apiKey.id ).toEqual('000'); + expect( apiKey.groups ).toEqual(['stats']); + expect( apiKey.key ).toEqual('b1fdc782037609f8ecc063ac192e92d57544263a950c637ed6b7d79cc9eb9f95'); + expect( apiKey.script ).toEqual('eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}') +}) \ No newline at end of file diff --git a/cyphernodeconf_docker/test/archive.test.js b/cyphernodeconf_docker/test/archive.test.js new file mode 100644 index 0000000..386609a --- /dev/null +++ b/cyphernodeconf_docker/test/archive.test.js @@ -0,0 +1,26 @@ +const Archive = require('../lib/archive.js'); +const tmp = require('tmp'); +const path = require('path'); + +test( 'Create Archive instance', ()=>{ + new Archive( '/tmp/testArchive.7z', 'test123' ); +}); + +test( 'Write, Read, Delete', async ()=>{ + const tmpDir = tmp.dirSync(); + const archive = new Archive( path.join(tmpDir.name,'archive.7z'), 'test123' ); + + await archive.writeEntry('testEntry', 'testContent' ); + + const c0 = await archive.readEntry( 'testEntry' ); + + expect( c0.value ).toEqual( 'testContent' ); + + await archive.deleteEntry('testEntry'); + + const c1 = await archive.readEntry( 'testEntry' ); + + expect( c1.value ).toBe( '' ); + + tmpDir.removeCallback(); +}); diff --git a/cyphernodeconf_docker/test/cert.test.js b/cyphernodeconf_docker/test/cert.test.js new file mode 100644 index 0000000..9b1a937 --- /dev/null +++ b/cyphernodeconf_docker/test/cert.test.js @@ -0,0 +1,60 @@ +const Cert = require('../lib/cert.js'); + +test( 'Create Cert instance', ()=>{ + const cert = new Cert(); + expect( cert.args.days ).toBe( 3650 ); +}); + +test( 'buildConfig', ()=>{ + const cert = new Cert(); + const conf = cert.buildConfig(['127.0.0.1','localhost','gatekeeper']); + expect( conf ).toEqual(` +[req] +distinguished_name = req_distinguished_name +x509_extensions = v3_ca +prompt = no +[req_distinguished_name] +CN = localhost +[v3_ca] +subjectAltName = @alt_names +[alt_names] +DNS.1 = localhost +DNS.2 = gatekeeper +IP.1 = 127.0.0.1 +`); +}); + + +test( 'cns', () => { + const cert = new Cert(); + const cns = cert.cns(' abc, cde' ); + expect( cns ).toEqual([ + '127.0.0.1', + 'localhost', + 'gatekeeper', + 'abc', + 'cde' + ]); +}); + +test( 'create', async ()=>{ + jest.setTimeout(999999); + const cert = new Cert(); + const cns = cert.cns('abc,cde' ); + const r = await cert.create( cns ); + + expect( r.code ).toBe(0); + expect( r.key ).not.toBe(undefined); + expect( r.cert ).not.toBe(undefined); +}); + +test( 'create throws', async ()=>{ + const cert = new Cert(); + let err; + try { + await cert.create(); + } catch( e ) { + err = e; + } + expect( err ).not.toBe(undefined); +}); \ No newline at end of file diff --git a/cyphernodeconf_docker/test/config.test.js b/cyphernodeconf_docker/test/config.test.js new file mode 100644 index 0000000..ce8e2f6 --- /dev/null +++ b/cyphernodeconf_docker/test/config.test.js @@ -0,0 +1,84 @@ +const Config = require('../lib/config.js'); +const ApiKey = require('../lib/apikey.js'); +const fs = require('fs'); + +const configV010 = require('./data/config.v1.json'); +const configV020 = require('./data/config.v2.json'); + +test( 'create config v0.1.0', () => { + const config = new Config(configV010); +}); + + +test( 'create config v0.2.0', () => { + const config = new Config(configV020); +}); + + +test( 'validate config v0.1.0', () => { + const config = new Config(configV010); + config.data.foo = "bar"; + config.data.bar = "foo"; + config.validate(); + expect( config.data.foo ).toBe( undefined ); + expect( config.data.bar ).toBe( undefined ); +}); + + +test( 'validate config v0.2.0', () => { + const config = new Config(configV020); + config.data.foo = "bar"; + config.data.bar = "foo"; + config.validate(); + expect( config.data.foo ).toBe( undefined ); + expect( config.data.bar ).toBe( undefined ); +}); + +test( 'serialise', async () => { + const config = new Config(); + config.setData( configV020 ) + await config.serialize('/tmp/config.7z','test123' ); + const exists = fs.existsSync('/tmp/config.7z' ); + expect( exists ).toBe( true ); +}); + +test( 'deserialise', async () => { + const config = new Config(); + await config.deserialize('/tmp/config.7z','test123' ); + expect( config.data ).toEqual( configV020 ); +}); + + +test( 'migrate 0.1.0 -> 0.2.0', async () => { + const config = new Config(); + config.setData( configV010 ); + // deep clone gatekeeper_keys + const gatekeeper_keys = JSON.parse(JSON.stringify(configV010.gatekeeper_keys)); + await config.migrate_0_1_0_to_0_2_0(); + expect( config.data.gatekeeper_keys.configEntries.length ).toBe( 4 ); + + for( let i=0; i<3; i++ ) { + const configEntry = config.data.gatekeeper_keys.configEntries[i+1]; + const oldConfigEntry = gatekeeper_keys.configEntries[i]; + + const key = new ApiKey(); + key.setFromConfigEntry( configEntry ) + + const oldKey = new ApiKey(); + oldKey.setFromConfigEntry( oldConfigEntry ); + + expect( key.id ).toEqual( oldKey.id ); + expect( key.key ).toEqual( oldKey.key ); + expect( key.script ).toEqual( oldKey.script ); + + for( let oldGroup of oldKey.groups ) { + expect( key.groups ).toContain(oldGroup); + } + + expect( key.groups ).toContain('stats'); + + } + +}); + + diff --git a/cyphernodeconf_docker/test/data/config.v1.json b/cyphernodeconf_docker/test/data/config.v1.json new file mode 100644 index 0000000..c1a6f87 --- /dev/null +++ b/cyphernodeconf_docker/test/data/config.v1.json @@ -0,0 +1,52 @@ +{ + "__version": "0.1.0", + "features": [ + "lightning", + "otsclient" + ], + "net": "testnet", + "use_xpub": true, + "installer_mode": "docker", + "run_as_different_user": true, + "username": "cyphernode", + "docker_mode": "compose", + "bitcoin_rpcuser": "bitcoin", + "bitcoin_rpcpassword": "test123", + "bitcoin_uacomment": "", + "bitcoin_prune": true, + "bitcoin_prune_size": 550, + "bitcoin_node_ip": "123.123.123.123", + "bitcoin_datapath": "/Users/jash/.cyphernode/bitcoin", + "bitcoin_mode": "internal", + "bitcoin_expose": false, + "lightning_expose": true, + "gatekeeper_apiproperties": "GKAP", + "gatekeeper_keys": { + "configEntries": [ + "kapi_id=\"001\";kapi_key=\"a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"watcher\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}", + "kapi_id=\"002\";kapi_key=\"fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d\";kapi_groups=\"watcher,spender\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}", + "kapi_id=\"003\";kapi_key=\"f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417\";kapi_groups=\"watcher,spender,admin\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}" + ], + "clientInformation": [ + "001=a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a", + "002=fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d", + "003=f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417" + ] + }, + "gatekeeper_sslcert": "-----BEGIN CERTIFICATE-----\nMIIE/jCCAuagAwIBAgIJAIBv4aiI2NRtMA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNV\nBAMME2Rpc2swYm9vay5mcml0ei5ib3gwHhcNMTkwMTE3MTcwMDA5WhcNMjkwMTE0\nMTcwMDA5WjAeMRwwGgYDVQQDDBNkaXNrMGJvb2suZnJpdHouYm94MIICIjANBgkq\nhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyVKNTGlPfd4QX9HaDc9a6prbc9il4jtR\niChSlMf3/6UfAMcS+xVgR2iR8FK/DQuFzxn+6BybpoiD333rjDr7zR7y9px3Xph2\nbmsjZy0hv9SIBbx0DJvvwODTlWTAH8qgU2DN6xWc7vjgeGi5uTpnmwWrkH6BjtVr\nwoBkF0JmfH7KiLS/QjWqPKeI6o/GpvCP9meD131Sq/ReoOTrJ4F5aNdhAril4nU5\n6e7Y+Iyp35DZSLuU+pDJAhxEvkYGas1ted5RRxlho8ukaoABCbmaTeNmgsJxK2SC\nABjfUc38aAlNLuMbMMR7Q85Z84OTJiUqanVczwdSj1QHlNCWZK1McBPhj2m2Wdge\ngedrq5XcjQGChzTEozcFntU0qzY3ja1+DOE8UaMaTrDH4saUXCMZk3W1m5mmiZW3\nmcB0cKGdeg6K6USg1BwBTU9qolUusxz5T0tNxjcMlXU93P17d4s5IXfliXhMNr/6\n4fl78Ey3FNprTix4alW7hBAp/eA/LhS55s3jwdoVzJl4RELC0284pahj5exYQwU6\nzjLedMxzC+7veQYwWfZOs9jVCTP0YStuT0j9xD3ausLZyB1Egbsajyy71IeoYOf1\n9S6dFIXE5LHAw2j3D3bh5wb019I8V5szGbeemdBpb3m+bzT8qjLSNranuT41CIHd\nIYjq85vDEJsCAwEAAaM/MD0wOwYDVR0RBDQwMoITZGlzazBib29rLmZyaXR6LmJv\neIIJbG9jYWxob3N0ggpnYXRla2VlcGVyhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IC\nAQBrE4bJsIMwSRPng94PcqR5F6Cux0bkwezALJCHpjHTuqok/wHHE5dZsAXcSsYc\n5givuBESih6CpY5h21Od0TBugyv3FCRY8OoaBXtlO6FYlEnVeJ8AOexJTb3qcbBS\nHU8MBWEydUh5HFA3PRKAG0Y4cvUK4WXJZ42Et3td0NkGFOv6bxdtVGB4Vz7FGn+3\nqd9fpmFCdQYDp6RSZDDz4B8XLsVuTeTES5GbUMSQAGanP7jxMr04wQ3MuoZrRODN\nFatifOJfq0fZddsBjJbrTLxArIqaPh3J4xzwiNE5du4CQDQrbbHXG22kuvbr5foA\ncixLnuyWMq0a5a70mSNS6TZ3nq4ATXNNa0cZ8fBxHqHGTLM8gQisW8vTaZfIFh/i\nhnFcGxtpo1ryi7JG9HCWsh0x20677iag5MuZfv2s4TbK71Ol6WV4FravCqU0qgbn\nTTl+BnYw3H67FO/a6RD4ISlFWK+8EVEQdMgvPoRuw323YznT0Nd8Q/Gq8raYF2wa\nz9T9OXu6TcVGtfPAgX+AM/+hDqWGxyiFR9ZtLpGOHGP8f+TZA5uCawc8Zry4yN6L\nE0yPIx96pJz59T3k8XbRHTQCaPsSUGRAZIY9LpJj0fIG7zCr9eCBpp2qyzmpyNfx\negN3ILYy1Y8JbJj73HWyP0F3Am7i76tkCWB7tQeFOb5FMg==\n-----END CERTIFICATE-----\n", + "gatekeeper_sslkey": "-----BEGIN PRIVATE KEY-----\nMIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDJUo1MaU993hBf\n0doNz1rqmttz2KXiO1GIKFKUx/f/pR8AxxL7FWBHaJHwUr8NC4XPGf7oHJumiIPf\nfeuMOvvNHvL2nHdemHZuayNnLSG/1IgFvHQMm+/A4NOVZMAfyqBTYM3rFZzu+OB4\naLm5OmebBauQfoGO1WvCgGQXQmZ8fsqItL9CNao8p4jqj8am8I/2Z4PXfVKr9F6g\n5OsngXlo12ECuKXidTnp7tj4jKnfkNlIu5T6kMkCHES+RgZqzW153lFHGWGjy6Rq\ngAEJuZpN42aCwnErZIIAGN9RzfxoCU0u4xswxHtDzlnzg5MmJSpqdVzPB1KPVAeU\n0JZkrUxwE+GPabZZ2B6B52urldyNAYKHNMSjNwWe1TSrNjeNrX4M4TxRoxpOsMfi\nxpRcIxmTdbWbmaaJlbeZwHRwoZ16DorpRKDUHAFNT2qiVS6zHPlPS03GNwyVdT3c\n/Xt3izkhd+WJeEw2v/rh+XvwTLcU2mtOLHhqVbuEECn94D8uFLnmzePB2hXMmXhE\nQsLTbzilqGPl7FhDBTrOMt50zHML7u95BjBZ9k6z2NUJM/RhK25PSP3EPdq6wtnI\nHUSBuxqPLLvUh6hg5/X1Lp0UhcTkscDDaPcPduHnBvTX0jxXmzMZt56Z0Glveb5v\nNPyqMtI2tqe5PjUIgd0hiOrzm8MQmwIDAQABAoICAQCI5uA7M+ngd9++qR+VAIqc\nus28y3iSjS/2XSU7E3irmYepqbZYk8KzDIMhX8OXhVxq5wyWns2hw3eZxTEmXP3a\nEM+7r87kvtzaXXTntqMapdYRwINSB8BT8w8uqiKT++Bmko+06y+auhc7Ckwxj2vg\n2Uw/qCdGEA+FZnWp83dp9XaY3ACrb37iXDMY/shhwXjEYMQhB5HuaPDojIL0jHEZ\nQE0x4oq7omfNkqRs8IqcAw4fDaBTe52VF9APa+L1QdjOZMX0iWgCUHrwCTere1FY\n4ehVxw/aKDDXDBLguCiKPrkDx2A4G4SPKYW1uKWZ7PAZENIZ3qrf2I6HPgjnUYmG\nAHQiR3JcwsXFZZAMW/kbqzRCS7CrvNnrzcUL9JAlpFmMDeAFIlbVkFED+kOtVioR\nPAcDWKtlWOWbX3Kn218FCblH86XdzB9H/pgbHxf8cXFcnaqVApxC1zv+uIaV526p\nU9maF8CMVX5bZ21e+dpP1BQ3DDRn3DCQno/QrGGMxK66EBVrHrllHBYpUppcj4w8\nCn5RDhp2KJjYsgX9zjuXif1gdP2jqBCDWCog+YwsoQP1Qp613D6rl5TKaa/rfZmX\nfG5Q98/wfAHwLjIDfwCXWqKOFoGdMzxg9hxk0bSNn64m1UY6OYB4yJs/o6spqGXq\nZRaX2LStSq5fhvB+tjl3AQKCAQEA7wBS3t2dHOVLZGVycxSq1LRoeW44KxZaNSiv\nXj5Xrw/jAnnAEAcVFrCGFY40MC4SNM//VUWHs4zKXxAhK/vFOgSvmnNh6mWDd/sO\nFyzo17fhwhi4u8BIjvchHgwg5aMi4uCLFM8RBZjI5MpMyNBwxT5VLKKKv3N9YtUK\n5JPhXkZJnhOhKj3vTaCeJFBMvcknoqqNcUcEEMo0d+YqUAYh9+8qpjBSKBV+8/VZ\nbQwyjd+EN5ajqRLIjKOy1GrbRwBcxadGdZzqMDYlSVBdAxAssPiUqhTLeHAuGfbK\nB0iF0DLgwl0N/6qx0WtLTA3MY7NcQJ/cjUioJKqbnvouEP2f2wKCAQEA16Qulg5I\nyDbJsiFzmeLNpKSXeh9y1q/hvqizd8R9GAUh9TVSgAnoiCpucguvMYBsXeGU3srr\no9AvxmkmrMWVNZbolaMRv0p+nXPR6uF5tFQJ/jYm1H/jI3ieF2ZXJveQBkqjRsOD\nsWI4HNuarGnsOo8rqV0ybYGFks1dhuvBZp7RemZbXqaFnk/D8FtkmHRsWUdEQifd\njHCATRbUHjAm8tk7HjdJbhYIMCZkbN1HRkx044pk+os37Eqi4Ok+s/MbQ7g2lY+R\nt1sxrJo4dsqxwflz9U6U5ECwH2hxuzpowGXwfcqtPLvVJT1p+TUxIHNb2eUvJg7R\ntsx4BzC4FbaOQQKCAQBMaHYV+hO4oTWxKx2j3P+gcOzVpX8fh03foNov7w5pUVLp\n/7J/bUQ4tMapLYVRwejgKX8f69KufFxWd/mi7iLnoYfigPDU1w9o1EJ09k6aaJcs\nTmsA20BXNHrJ+GasA7OrhM3yISD4ARh4zJQvvzPOW1cvpedlva5gYmvRF9X9Jctz\nViM4NgTDdI4aXfGq2xxozM3bYTDTjVGo41SzsMI7WaLw7pHVbsnfhJjKXBPtd3/1\npZs8+lxTWiJZ9q+Ty3HdXtUP1NKqByV0gtS5nforuc0Ncwh5wKN8eYGtQmpFXX6t\npMJM8m1W3k58Sg0F8tmTb4g7Qvc+gayu7py7odnxAoIBAQC5pJqGjF2UH7acJ7hB\nrsOjDh9p/1D6Cgip/soiPYID/8cwNmuD1wPc1cqnW+/DCfBBEkb7Vm5uZHf8s+Gb\ns620qOoqiGxq5lMCcgcx0lLYL9E6jJv5LO/6RYi0VGKLJW1UPUU7Um45c3kjPtt1\nuuqnr0HDWHxlUbAQpcPyt1uUYP2uBhh675jwpXLlpYiAxxnP8k6NNYzBrsiFlTWf\nl4ywXNtMNAR/RXBfI24pWoZVutSWXzp3hwrp3YdDYQmeGZhLQHedYi/sThIBqfMa\nMX2+pGZztObxac81+tCOgsZTfG8BnE5vjrT8jlaBOI3Ghgl5GJjyhqd8W6KpbgPM\njZEBAoIBAQCuI/dv6opNniMmOY+iI1Oh6kyNnctKe/VF2SqwfeK3I6fCfcNEm46u\nQHnJWqVCIQ1ZsU8b/Pn0+mJYB/n+OYh585DsDR2YEJPXE+qrVHG1qIEEyTgcTb30\n+nehJ6d5SYMej4VMdepgMD7HeG7Nly1wwu0VYTUTLP3Z812NUdnBYmb5QSRnsHrk\nOVlAkJAaunAk1O3rwwdyGwMmNVIgzf0foa/c8uFQCbUHocQUwr//vN+U+Nhi4VQC\nnRiPT36rWxLqwyn8sa7dcE9A6Hp2KDBc97DJTDNvA3StF1JHAf/EoiTq3mWvVNhR\nmPN+iWirFH2RN5KkSaTzEtrXAgbLvic9\n-----END PRIVATE KEY-----\n", + "gatekeeper_cns": "disk0book.fritz.box", + "proxy_datapath": "/Users/jash/.cyphernode/proxy", + "lightning_implementation": "c-lightning", + "lightning_datapath": "/Users/jash/.cyphernode/lightning", + "lightning_nodename": "🚀 Disciplined Dormouse 🚀", + "lightning_nodecolor": "ff0000", + "otsclient_datapath": "/Users/jash/.cyphernode/otsclient", + "adminhash": "BsJFlh7q4JmwI\n", + "use_xpub": true, + "xpub": "abc", + "derivation_path": "da", + "gatekeeper_clientkeyspassword": "test123", + "gatekeeper_datapath": "/Users/jash/.cyphernode/gatekeeper", + "traefik_datapath": "foo" +} \ No newline at end of file diff --git a/cyphernodeconf_docker/test/data/config.v2.json b/cyphernodeconf_docker/test/data/config.v2.json new file mode 100644 index 0000000..6be7a95 --- /dev/null +++ b/cyphernodeconf_docker/test/data/config.v2.json @@ -0,0 +1,52 @@ +{ + "__version": "0.2.0", + "features": [ + "lightning", + "otsclient" + ], + "net": "testnet", + "use_xpub": true, + "installer_mode": "docker", + "run_as_different_user": true, + "username": "cyphernode", + "docker_mode": "compose", + "bitcoin_rpcuser": "bitcoin", + "bitcoin_rpcpassword": "test123", + "bitcoin_uacomment": "", + "bitcoin_prune": true, + "bitcoin_prune_size": 550, + "bitcoin_datapath": "/Users/jash/.cyphernode/bitcoin", + "bitcoin_mode": "internal", + "bitcoin_expose": false, + "lightning_expose": true, + "gatekeeper_keys": { + "configEntries": [ + "kapi_id=\"000\";kapi_key=\"a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"watcher\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}", + "kapi_id=\"001\";kapi_key=\"a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"watcher\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}", + "kapi_id=\"002\";kapi_key=\"fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d\";kapi_groups=\"watcher,spender\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}", + "kapi_id=\"003\";kapi_key=\"f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417\";kapi_groups=\"watcher,spender,admin\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}" + ], + "clientInformation": [ + "000=a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a", + "001=a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a", + "002=fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d", + "003=f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417" + ] + }, + "gatekeeper_sslcert": "-----BEGIN CERTIFICATE-----\nMIIE/jCCAuagAwIBAgIJAIBv4aiI2NRtMA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNV\nBAMME2Rpc2swYm9vay5mcml0ei5ib3gwHhcNMTkwMTE3MTcwMDA5WhcNMjkwMTE0\nMTcwMDA5WjAeMRwwGgYDVQQDDBNkaXNrMGJvb2suZnJpdHouYm94MIICIjANBgkq\nhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyVKNTGlPfd4QX9HaDc9a6prbc9il4jtR\niChSlMf3/6UfAMcS+xVgR2iR8FK/DQuFzxn+6BybpoiD333rjDr7zR7y9px3Xph2\nbmsjZy0hv9SIBbx0DJvvwODTlWTAH8qgU2DN6xWc7vjgeGi5uTpnmwWrkH6BjtVr\nwoBkF0JmfH7KiLS/QjWqPKeI6o/GpvCP9meD131Sq/ReoOTrJ4F5aNdhAril4nU5\n6e7Y+Iyp35DZSLuU+pDJAhxEvkYGas1ted5RRxlho8ukaoABCbmaTeNmgsJxK2SC\nABjfUc38aAlNLuMbMMR7Q85Z84OTJiUqanVczwdSj1QHlNCWZK1McBPhj2m2Wdge\ngedrq5XcjQGChzTEozcFntU0qzY3ja1+DOE8UaMaTrDH4saUXCMZk3W1m5mmiZW3\nmcB0cKGdeg6K6USg1BwBTU9qolUusxz5T0tNxjcMlXU93P17d4s5IXfliXhMNr/6\n4fl78Ey3FNprTix4alW7hBAp/eA/LhS55s3jwdoVzJl4RELC0284pahj5exYQwU6\nzjLedMxzC+7veQYwWfZOs9jVCTP0YStuT0j9xD3ausLZyB1Egbsajyy71IeoYOf1\n9S6dFIXE5LHAw2j3D3bh5wb019I8V5szGbeemdBpb3m+bzT8qjLSNranuT41CIHd\nIYjq85vDEJsCAwEAAaM/MD0wOwYDVR0RBDQwMoITZGlzazBib29rLmZyaXR6LmJv\neIIJbG9jYWxob3N0ggpnYXRla2VlcGVyhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IC\nAQBrE4bJsIMwSRPng94PcqR5F6Cux0bkwezALJCHpjHTuqok/wHHE5dZsAXcSsYc\n5givuBESih6CpY5h21Od0TBugyv3FCRY8OoaBXtlO6FYlEnVeJ8AOexJTb3qcbBS\nHU8MBWEydUh5HFA3PRKAG0Y4cvUK4WXJZ42Et3td0NkGFOv6bxdtVGB4Vz7FGn+3\nqd9fpmFCdQYDp6RSZDDz4B8XLsVuTeTES5GbUMSQAGanP7jxMr04wQ3MuoZrRODN\nFatifOJfq0fZddsBjJbrTLxArIqaPh3J4xzwiNE5du4CQDQrbbHXG22kuvbr5foA\ncixLnuyWMq0a5a70mSNS6TZ3nq4ATXNNa0cZ8fBxHqHGTLM8gQisW8vTaZfIFh/i\nhnFcGxtpo1ryi7JG9HCWsh0x20677iag5MuZfv2s4TbK71Ol6WV4FravCqU0qgbn\nTTl+BnYw3H67FO/a6RD4ISlFWK+8EVEQdMgvPoRuw323YznT0Nd8Q/Gq8raYF2wa\nz9T9OXu6TcVGtfPAgX+AM/+hDqWGxyiFR9ZtLpGOHGP8f+TZA5uCawc8Zry4yN6L\nE0yPIx96pJz59T3k8XbRHTQCaPsSUGRAZIY9LpJj0fIG7zCr9eCBpp2qyzmpyNfx\negN3ILYy1Y8JbJj73HWyP0F3Am7i76tkCWB7tQeFOb5FMg==\n-----END CERTIFICATE-----\n", + "gatekeeper_sslkey": "-----BEGIN PRIVATE KEY-----\nMIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDJUo1MaU993hBf\n0doNz1rqmttz2KXiO1GIKFKUx/f/pR8AxxL7FWBHaJHwUr8NC4XPGf7oHJumiIPf\nfeuMOvvNHvL2nHdemHZuayNnLSG/1IgFvHQMm+/A4NOVZMAfyqBTYM3rFZzu+OB4\naLm5OmebBauQfoGO1WvCgGQXQmZ8fsqItL9CNao8p4jqj8am8I/2Z4PXfVKr9F6g\n5OsngXlo12ECuKXidTnp7tj4jKnfkNlIu5T6kMkCHES+RgZqzW153lFHGWGjy6Rq\ngAEJuZpN42aCwnErZIIAGN9RzfxoCU0u4xswxHtDzlnzg5MmJSpqdVzPB1KPVAeU\n0JZkrUxwE+GPabZZ2B6B52urldyNAYKHNMSjNwWe1TSrNjeNrX4M4TxRoxpOsMfi\nxpRcIxmTdbWbmaaJlbeZwHRwoZ16DorpRKDUHAFNT2qiVS6zHPlPS03GNwyVdT3c\n/Xt3izkhd+WJeEw2v/rh+XvwTLcU2mtOLHhqVbuEECn94D8uFLnmzePB2hXMmXhE\nQsLTbzilqGPl7FhDBTrOMt50zHML7u95BjBZ9k6z2NUJM/RhK25PSP3EPdq6wtnI\nHUSBuxqPLLvUh6hg5/X1Lp0UhcTkscDDaPcPduHnBvTX0jxXmzMZt56Z0Glveb5v\nNPyqMtI2tqe5PjUIgd0hiOrzm8MQmwIDAQABAoICAQCI5uA7M+ngd9++qR+VAIqc\nus28y3iSjS/2XSU7E3irmYepqbZYk8KzDIMhX8OXhVxq5wyWns2hw3eZxTEmXP3a\nEM+7r87kvtzaXXTntqMapdYRwINSB8BT8w8uqiKT++Bmko+06y+auhc7Ckwxj2vg\n2Uw/qCdGEA+FZnWp83dp9XaY3ACrb37iXDMY/shhwXjEYMQhB5HuaPDojIL0jHEZ\nQE0x4oq7omfNkqRs8IqcAw4fDaBTe52VF9APa+L1QdjOZMX0iWgCUHrwCTere1FY\n4ehVxw/aKDDXDBLguCiKPrkDx2A4G4SPKYW1uKWZ7PAZENIZ3qrf2I6HPgjnUYmG\nAHQiR3JcwsXFZZAMW/kbqzRCS7CrvNnrzcUL9JAlpFmMDeAFIlbVkFED+kOtVioR\nPAcDWKtlWOWbX3Kn218FCblH86XdzB9H/pgbHxf8cXFcnaqVApxC1zv+uIaV526p\nU9maF8CMVX5bZ21e+dpP1BQ3DDRn3DCQno/QrGGMxK66EBVrHrllHBYpUppcj4w8\nCn5RDhp2KJjYsgX9zjuXif1gdP2jqBCDWCog+YwsoQP1Qp613D6rl5TKaa/rfZmX\nfG5Q98/wfAHwLjIDfwCXWqKOFoGdMzxg9hxk0bSNn64m1UY6OYB4yJs/o6spqGXq\nZRaX2LStSq5fhvB+tjl3AQKCAQEA7wBS3t2dHOVLZGVycxSq1LRoeW44KxZaNSiv\nXj5Xrw/jAnnAEAcVFrCGFY40MC4SNM//VUWHs4zKXxAhK/vFOgSvmnNh6mWDd/sO\nFyzo17fhwhi4u8BIjvchHgwg5aMi4uCLFM8RBZjI5MpMyNBwxT5VLKKKv3N9YtUK\n5JPhXkZJnhOhKj3vTaCeJFBMvcknoqqNcUcEEMo0d+YqUAYh9+8qpjBSKBV+8/VZ\nbQwyjd+EN5ajqRLIjKOy1GrbRwBcxadGdZzqMDYlSVBdAxAssPiUqhTLeHAuGfbK\nB0iF0DLgwl0N/6qx0WtLTA3MY7NcQJ/cjUioJKqbnvouEP2f2wKCAQEA16Qulg5I\nyDbJsiFzmeLNpKSXeh9y1q/hvqizd8R9GAUh9TVSgAnoiCpucguvMYBsXeGU3srr\no9AvxmkmrMWVNZbolaMRv0p+nXPR6uF5tFQJ/jYm1H/jI3ieF2ZXJveQBkqjRsOD\nsWI4HNuarGnsOo8rqV0ybYGFks1dhuvBZp7RemZbXqaFnk/D8FtkmHRsWUdEQifd\njHCATRbUHjAm8tk7HjdJbhYIMCZkbN1HRkx044pk+os37Eqi4Ok+s/MbQ7g2lY+R\nt1sxrJo4dsqxwflz9U6U5ECwH2hxuzpowGXwfcqtPLvVJT1p+TUxIHNb2eUvJg7R\ntsx4BzC4FbaOQQKCAQBMaHYV+hO4oTWxKx2j3P+gcOzVpX8fh03foNov7w5pUVLp\n/7J/bUQ4tMapLYVRwejgKX8f69KufFxWd/mi7iLnoYfigPDU1w9o1EJ09k6aaJcs\nTmsA20BXNHrJ+GasA7OrhM3yISD4ARh4zJQvvzPOW1cvpedlva5gYmvRF9X9Jctz\nViM4NgTDdI4aXfGq2xxozM3bYTDTjVGo41SzsMI7WaLw7pHVbsnfhJjKXBPtd3/1\npZs8+lxTWiJZ9q+Ty3HdXtUP1NKqByV0gtS5nforuc0Ncwh5wKN8eYGtQmpFXX6t\npMJM8m1W3k58Sg0F8tmTb4g7Qvc+gayu7py7odnxAoIBAQC5pJqGjF2UH7acJ7hB\nrsOjDh9p/1D6Cgip/soiPYID/8cwNmuD1wPc1cqnW+/DCfBBEkb7Vm5uZHf8s+Gb\ns620qOoqiGxq5lMCcgcx0lLYL9E6jJv5LO/6RYi0VGKLJW1UPUU7Um45c3kjPtt1\nuuqnr0HDWHxlUbAQpcPyt1uUYP2uBhh675jwpXLlpYiAxxnP8k6NNYzBrsiFlTWf\nl4ywXNtMNAR/RXBfI24pWoZVutSWXzp3hwrp3YdDYQmeGZhLQHedYi/sThIBqfMa\nMX2+pGZztObxac81+tCOgsZTfG8BnE5vjrT8jlaBOI3Ghgl5GJjyhqd8W6KpbgPM\njZEBAoIBAQCuI/dv6opNniMmOY+iI1Oh6kyNnctKe/VF2SqwfeK3I6fCfcNEm46u\nQHnJWqVCIQ1ZsU8b/Pn0+mJYB/n+OYh585DsDR2YEJPXE+qrVHG1qIEEyTgcTb30\n+nehJ6d5SYMej4VMdepgMD7HeG7Nly1wwu0VYTUTLP3Z812NUdnBYmb5QSRnsHrk\nOVlAkJAaunAk1O3rwwdyGwMmNVIgzf0foa/c8uFQCbUHocQUwr//vN+U+Nhi4VQC\nnRiPT36rWxLqwyn8sa7dcE9A6Hp2KDBc97DJTDNvA3StF1JHAf/EoiTq3mWvVNhR\nmPN+iWirFH2RN5KkSaTzEtrXAgbLvic9\n-----END PRIVATE KEY-----\n", + "gatekeeper_cns": "disk0book.fritz.box", + "proxy_datapath": "/Users/jash/.cyphernode/proxy", + "lightning_implementation": "c-lightning", + "lightning_datapath": "/Users/jash/.cyphernode/lightning", + "lightning_nodename": "🚀 Disciplined Dormouse 🚀", + "lightning_nodecolor": "ff0000", + "otsclient_datapath": "/Users/jash/.cyphernode/otsclient", + "adminhash": "BsJFlh7q4JmwI\n", + "use_xpub": true, + "xpub": "abc", + "derivation_path": "da", + "gatekeeper_clientkeyspassword": "test123", + "gatekeeper_datapath": "/Users/jash/.cyphernode/gatekeeper", + "traefik_datapath": "foo" +} \ No newline at end of file diff --git a/cyphernodeconf_docker/test/htpasswd.test.js b/cyphernodeconf_docker/test/htpasswd.test.js new file mode 100644 index 0000000..9490db0 --- /dev/null +++ b/cyphernodeconf_docker/test/htpasswd.test.js @@ -0,0 +1,6 @@ +const htpasswd = require('../lib/htpasswd.js'); + +test( 'generate htpasswd', async ()=>{ + const pw = await htpasswd( 'test123' ); + expect( pw ).not.toBe( undefined ); +}); \ No newline at end of file diff --git a/cyphernodeconf_docker/test/name.test.js b/cyphernodeconf_docker/test/name.test.js new file mode 100644 index 0000000..59cead5 --- /dev/null +++ b/cyphernodeconf_docker/test/name.test.js @@ -0,0 +1,7 @@ +const name = require('../lib/name.js'); + + +test( 'Create new random name', ()=>{ + const n = name.generate(); + expect( n ).not.toBe( undefined ) +}); \ No newline at end of file diff --git a/dist/setup.sh b/dist/setup.sh index 455bab7..253f810 100755 --- a/dist/setup.sh +++ b/dist/setup.sh @@ -139,7 +139,7 @@ configure() { local recreate="" if [[ $1 == 1 ]]; then - recreate="recreate" + recreate=" recreate" fi @@ -151,8 +151,6 @@ configure() { if [[ -t 1 ]]; then interactive=' -it' - else - gen_options=' --force 2' fi if [[ $CFG_PASSWORD ]]; then @@ -186,7 +184,6 @@ configure() { -e DEFAULT_DATADIR_BASE=$HOME \ -e SETUP_DIR=$SETUP_DIR \ -e DEFAULT_CERT_HOSTNAME=$(hostname) \ - -e VERSION_OVERRIDE=$VERSION_OVERRIDE \ -e GATEKEEPER_VERSION=$GATEKEEPER_VERSION \ -e PROXY_VERSION=$PROXY_VERSION \ -e NOTIFIER_VERSION=$NOTIFIER_VERSION \ @@ -195,9 +192,10 @@ configure() { -e PYCOIN_VERSION=$PYCOIN_VERSION \ -e BITCOIN_VERSION=$BITCOIN_VERSION \ -e LIGHTNING_VERSION=$LIGHTNING_VERSION \ + -e SETUP_VERSION=$SETUP_VERSION \ --log-driver=none$pw_env \ --network none \ - --rm$interactive cyphernode/cyphernodeconf:$CONF_VERSION $user yo --no-insight cyphernode$gen_options $recreate + --rm$interactive cyphernode/cyphernodeconf:$CONF_VERSION $user node index.js$recreate if [[ -f $current_path/exitStatus.sh ]]; then . $current_path/exitStatus.sh rm $current_path/exitStatus.sh @@ -701,7 +699,6 @@ install() { fi } - CONFIGURE=0 INSTALL=0 RECREATE=0 @@ -710,8 +707,8 @@ ALWAYSYES=0 SUDO_REQUIRED=0 AUTOSTART=0 -# CYPHERNODE VERSION "v0.1.1" -VERSION_OVERRIDE="true" +# CYPHERNODE VERSION "v0.2.0" +SETUP_VERSION="v0.2.0" CONF_VERSION="v0.2.0" GATEKEEPER_VERSION="v0.2.0" PROXY_VERSION="v0.2.0" @@ -734,31 +731,6 @@ function ctrl_c() { export current_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" - -#*************************************************************** -# Temporary code for upgrading from v0.1 to v0.2 -#*************************************************************** - -grep "xpub" gatekeeper/api.properties > /dev/null -returncode=$? -if [[ $returncode -eq 1 ]]; then - # grep found the file but didn't find xpub in it - - echo "\nPrevious Cyphernode installation detected." - echo "Running migration scripts...\n" - - echo "You will be asked to enter your admin passphrase twice while migrating. It is the passphrase you used when installing previous verison of Cyphernode.\n" - - # We want to add the 000 KEY_ID (Stats) and update the api.properties file with new endpoints - docker run --rm -it -v "$SETUP_DIR:/conf" alpine:3.8 sh -c "apk add --no-cache --update curl ; curl -fsSL https://raw.githubusercontent.com/SatoshiPortal/cyphernode/${GATEKEEPER_VERSION}/api_auth_docker/api-sample.properties -o /conf/api-sample.properties" - docker run --rm -it -v "$SETUP_DIR:/conf" alpine:3.8 sh -c 'apk add --no-cache --update jq p7zip;apk add --no-cache --update jq curl p7zip;cd conf;7z e config.7z;k=$(dd if=/dev/urandom bs=32 count=1 2> /dev/null | xxd -pc 32) && l="kapi_id=\\\"000\\\";kapi_key=\\\"$k\\\";kapi_groups=\\\"stats\\\";eval ugroups_\${kapi_id}=\${kapi_groups};eval ukey_\${kapi_id}=\${kapi_key}" && cat config.json | sed 's/kapi_groups=\\"/kapi_groups=\\"stats,/g' | jq ".gatekeeper_keys.configEntries = [\"$l\"] + .gatekeeper_keys.configEntries" | jq ".gatekeeper_keys.clientInformation = [\"000=$k\"] + .gatekeeper_keys.clientInformation" | jq ".gatekeeper_apiproperties = \"$(cat api-sample.properties | paste -s -d '\\\\n')\"" > config.json;7z u config.7z config.json;' -fi - -#*************************************************************** -# Temporary code for upgrading from v0.1 to v0.2 -#*************************************************************** - - while getopts ":cirhys" opt; do case $opt in r) @@ -832,10 +804,12 @@ if [[ $INSTALL == 1 ]]; then modify_owner modify_permissions install_apps + if [[ ! $AUTOSTART == 1 ]]; then + cowsay + fi + fi if [[ $AUTOSTART == 1 ]]; then exec $current_path/start.sh -else - cowsay fi