From 7bce7db202ac0ee9ddece36b4c72cbedbdd8b617 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Fri, 5 Jun 2020 16:39:37 -0500 Subject: [PATCH 01/39] refactor: Implemented new config command. feat: Added project service handler. feat: Added new command placeholder. chore: Updated deps. --- CHANGELOG.md | 17 ++++- gulpfile.js | 2 +- package.json | 7 +- src/cli/auth/login.js | 88 +++++++++++++++++++++++++ src/cli/cli-service.js | 5 +- src/cli/commands/config.js | 132 +++++++++++++++++++------------------ src/cli/commands/login.js | 88 +++++-------------------- src/cli/commands/new.js | 33 ++++++---- src/cli/commands/switch.js | 80 ++++++++++++++++++++-- src/index.js | 12 +++- 10 files changed, 299 insertions(+), 165 deletions(-) create mode 100644 src/cli/auth/login.js diff --git a/CHANGELOG.md b/CHANGELOG.md index da1af54..1795bac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,20 @@ -# v1.9.0 +# v2.0.0 - * refactor: Updated to cli-kit@0.14.0 adding support for the new client/server architecture. + * BREAKING CHANGE(config): `config` command no longer returns status as apart of JSON output. + * BREAKING CHANGE(config): `config` command does not return current value when doing a `set`, `push`, or + `unshift`. + * BREAKING CHANGE(config): `config list` command no longer supports filtering, use `config get` instead. + * BREAKING CHANGE(config): Write operations such as `set` return `"OK"` instead of `"Saved"`. * feat(info): Added `filter` argument to `ti info`. + * feat: Added project service handler. [(DAEMON-26)](https://jira.appcelerator.org/browse/DAEMON-26) + * feat: Added `new` command. [(DAEMON-321)](https://jira.appcelerator.org/browse/DAEMON-321) + * feat: Added auth commands `login`, `logout`, `whoami`, and `switch`. + [(DAEMON-300)](https://jira.appcelerator.org/browse/DAEMON-300) + * refactor: Updated to cli-kit@0.14.0 adding support for the new client/server architecture. + * refactor: Updated `config` command actions to be subcommands with improved help output. + * chore: Added plugin API version 2.x. + * chore: Transpile for Node 10 instead of Node 8. Not a breaking change as appcd has always + guaranteed Node 10 or newer. * chore: Updated dependencies. # v1.8.1 (Jan 15, 2020) diff --git a/gulpfile.js b/gulpfile.js index e36fd15..797c461 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -4,5 +4,5 @@ require('appcd-gulp')({ exports, pkgJson: require('./package.json'), template: 'standard', - babel: 'node8' + babel: 'node10' }); diff --git a/package.json b/package.json index 5259fee..a8b593e 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,11 @@ }, "dependencies": { "chalk": "^4.0.0", - "cli-kit": "^1.1.0", + "cli-kit": "^1.2.0", "dateformat": "^3.0.3", + "enquirer": "^2.3.5", "filesize": "^6.1.0", - "fs-extra": "^9.0.0", + "fs-extra": "^9.0.1", "gawk": "^4.7.1", "get-port": "^5.1.1", "node-pty-prebuilt-multiarch": "^0.9.0", @@ -39,7 +40,7 @@ "bugs": "https://github.com/appcelerator/appcd-plugin-titanium/issues", "repository": "https://github.com/appcelerator/appcd-plugin-titanium", "appcd": { - "apiVersion": "1.x || 2.x", + "apiVersion": "^1.1.0 || 2.x", "config": "./conf/config.js", "name": "titanium", "type": "external" diff --git a/src/cli/auth/login.js b/src/cli/auth/login.js new file mode 100644 index 0000000..3caff3c --- /dev/null +++ b/src/cli/auth/login.js @@ -0,0 +1,88 @@ +/** + * Performs a login. + * + * @param {Object} opts - Various options. + * @param {Array.} opts.argv - The parsed command line arguments. + * @param {Console} opts.console - The console instance to write to. + * @param {Function} opts.setExitCode - A function to set the exit code. + * @param {Terminal} opts.terminal - A cli-kit Terminal instance. + * @returns {Object} The account info. + */ +export async function login({ argv, console, setExitCode, terminal }) { + const { prompt } = require('enquirer'); + const { snooplogg } = require('appcd-logger'); + const { alert, highlight } = snooplogg.styles; + + const data = { + baseUrl: argv.baseUrl, + clientId: argv.clientId, + clientSecret: argv.clientSecret, + env: argv.env, + force: argv.force, + password: argv.password, + secretFile: argv.secretFile, + username: argv.username + }; + + if (Object.prototype.hasOwnProperty.call(argv, 'username')) { + const questions = []; + const { stdin, stdout } = terminal; + + if (!argv.username || typeof argv.username !== 'string') { + questions.push({ + type: 'input', + name: 'username', + message: 'Username:', + stdin, + stdout, + validate(s) { + return !!s || 'Please enter your username'; + } + }); + } + + if (!argv.password || typeof argv.password !== 'string') { + questions.push({ + type: 'password', + name: 'password', + message: 'Password:', + stdin, + stdout, + validate(s) { + return !!s || 'Please enter your password'; + } + }); + } + + if (questions.length && argv.json) { + throw new Error('--username and --password are required when --json is set'); + } + + Object.assign(data, await prompt(questions)); + + if (!argv.json) { + // add a newline after prompting has completed + console.log(); + } + } + + try { + return (await appcd.call('/amplify/1.x/auth/login', { data })).response; + } catch (err) { + if (err.code === 'EAUTHENTICATED') { + const { account } = err; + if (argv.json) { + console.log(JSON.stringify(account, null, 2)); + } else { + console.log(`You are already logged into ${highlight(account.org.name)} as ${highlight(account.user.email || account.name)}.`); + } + } else if (err.code === 'ERR_AUTH_FAILED') { + console.error(alert(err.message)); + setExitCode(1); + } else { + throw err; + } + } +} + +export default login; diff --git a/src/cli/cli-service.js b/src/cli/cli-service.js index 75a1b73..76b520b 100644 --- a/src/cli/cli-service.js +++ b/src/cli/cli-service.js @@ -31,6 +31,9 @@ export default class CLIService extends Dispatcher { help: true, helpExitCode: 2, name: 'titanium', + options: { + '--no-prompt': 'Disable interactive prompting' + }, version: ({ data }) => parseVersion(data.userAgent) }); @@ -52,7 +55,7 @@ export default class CLIService extends Dispatcher { } /** - * Perform any necessary cleanup. + * Stop the CLI server. * * @returns {Promise} * @access public diff --git a/src/cli/commands/config.js b/src/cli/commands/config.js index 81345e2..9649e7a 100644 --- a/src/cli/commands/config.js +++ b/src/cli/commands/config.js @@ -1,73 +1,55 @@ -const readActions = { - get: 'get', - ls: 'get', - list: 'get' -}; - -const writeActions = { - set: 'set', - - delete: 'delete', - rm: 'delete', - unset: 'delete', - - push: 'push', - pop: 'pop', - shift: 'shift', - unshift: 'unshift' -}; - export default { aliases: 'conf', - args: [ - { - name: '', - desc: 'The action to run', - values: { - 'ls, list': 'Display all settings', - get: 'Display a specific setting', - set: 'Change a setting', - 'rm, delete': 'Remove a setting', - push: 'Add a value to the end of a list', - pop: 'Remove the last value in a list' - } + commands: { + '@ls, list': { + desc: 'Display all config settings', + action: ctx => runConfig('get', ctx) + }, + 'get [key]': { + desc: 'Display a specific config setting', + action: ctx => runConfig('get', ctx) + }, + 'set ': { + desc: 'Change a config setting', + action: ctx => runConfig('set', ctx) + }, + '@rm, delete, !remove, !unset ': { + desc: 'Remove a config setting', + action: ctx => runConfig('delete', ctx) }, - { name: 'key', desc: '' }, - { name: 'value', desc: '' } - ], + 'push ': { + desc: 'Add a value to the end of a list', + action: ctx => runConfig('push', ctx) + }, + 'pop ': { + desc: 'Remove the last value in a list', + action: ctx => runConfig('pop', ctx) + }, + 'shift ': { + desc: 'Remove the first value in a list', + action: ctx => runConfig('shift', ctx) + }, + 'unshift ': { + desc: 'Add a value ot the beginning of a list', + action: ctx => runConfig('unshift', ctx) + } + }, desc: 'Manage configuration options', options: { '--json': 'Outputs the config as JSON' - }, - async action({ argv, console }) { - let { action, key, value } = argv; - - if (!readActions[action] && !writeActions[action]) { - throw new Error(`Unknown action: ${action}`); - } + } +}; - let { response } = await appcd.call('/appcd/config', { - data: { - action: readActions[action] || writeActions[action] || action, - key: /^titanium\./.test(key) ? key : key ? `titanium.${key}` : 'titanium', - value - } - }); +async function runConfig(action, { argv, cmd, console, setExitCode }) { + let { json, key, value } = argv; - let result = 'Saved'; - if (response !== 'OK') { - result = response; - } else if (argv.json) { - // if a pop() or shift() returns OK, then that means there's no more items and - // thus we have to force undefined - if (/^pop|shift$/.test(action)) { - result = ''; - } - } + const print = ({ code = 0, key = null, value }) => { + setExitCode(code); + cmd.banner = false; - if (argv.json) { - console.log(JSON.stringify({ code: 0, result }, null, 2)); - } else if (result && typeof result === 'object') { + if (json) { + console.log(JSON.stringify(value, null, 2)); + } else if (value && typeof value === 'object') { let width = 0; const rows = []; @@ -90,7 +72,7 @@ export default { } segments.pop(); } - }(result, key ? key.split('.') : [])); + }(value, key ? key.split('.') : [])); if (rows.length) { for (const row of rows) { @@ -100,7 +82,29 @@ export default { console.log('No config settings found'); } } else { - console.log(result); + console.log(value); } + }; + + try { + const { response } = await appcd.call('/appcd/config', { + data: { + action, + key: /^titanium\./.test(key) ? key : key ? `titanium.${key}` : 'titanium', + value + } + }); + + print({ key, value: response }); + } catch (err) { + if (err.status === 404) { + return print({ code: 6, key }); + } + + if (json) { + cmd.showHelpOnError = false; + err.json = json; + } + throw err; } -}; +} diff --git a/src/cli/commands/login.js b/src/cli/commands/login.js index 297b55d..7cbccc7 100644 --- a/src/cli/commands/login.js +++ b/src/cli/commands/login.js @@ -6,95 +6,39 @@ export default { '--env [name]': 'The environment to use', '--realm [realm]': { hidden: true }, '--force': 'Re-authenticate even if the account is already authenticated', - '--json': 'Outputs accounts as JSON', + '--json': { + callback({ ctx }) { + while (ctx.parent) { + ctx = ctx.parent; + } + ctx.banner = false; + }, + desc: 'Outputs accounts as JSON' + }, '-c, --client-secret [key]': 'A secret key used to authenticate', '-s, --secret-file [path]': 'Path to the PEM key used to authenticate', '-u, --username [user]': 'Username to authenticate with', '-p, --password [pass]': 'Password to authenticate with' }, - async action({ argv, console, exitCode, terminal }) { + async action(params) { const [ { snooplogg }, - { prompt } + { login } ] = await Promise.all([ import('appcd-logger'), - import('prompts') + import('../auth/login') ]); - const { alert, highlight } = snooplogg.styles; - const data = { - baseUrl: argv.baseUrl, - clientId: argv.clientId, - clientSecret: argv.clientSecret, - env: argv.env, - force: argv.force, - password: argv.password, - secretFile: argv.secretFile, - username: argv.username - }; - - if (Object.prototype.hasOwnProperty.call(argv, 'username')) { - const questions = []; - - if (!argv.username || typeof argv.username !== 'string') { - questions.push({ - type: 'text', - name: 'username', - message: 'Username:', - stdin: terminal.stdin, - stdout: terminal.stdout, - validate(s) { - return !!s || 'Please enter your username'; - } - }); - } - - if (!argv.password || typeof argv.password !== 'string') { - questions.push({ - type: 'password', - name: 'password', - message: 'Password:', - stdin: terminal.stdin, - stdout: terminal.stdout, - validate(s) { - return !!s || 'Please enter your password'; - } - }); - } - - if (questions.length && argv.json) { - throw new Error('--username and --password are required when --json is set'); - } - Object.assign(data, await prompt(questions)); - - if (!argv.json) { - // add a newline after prompting has completed - console.log(); - } - } - - try { - const { response: account } = await appcd.call('/amplify/1.x/auth/login', { data }); + const { highlight } = snooplogg.styles; + const { argv, console } = params; + const account = await login(params); + if (account) { if (argv.json) { console.log(JSON.stringify(account, null, 2)); } else { console.log(`You are logged into ${highlight(account.org.name)} as ${highlight(account.user.email || account.name)}.`); } - } catch (err) { - if (err.code === 'EAUTHENTICATED') { - const { account } = err; - if (argv.json) { - console.log(JSON.stringify(account, null, 2)); - } else { - console.log(`You are already logged into ${highlight(account.org.name)} as ${highlight(account.user.email || account.name)}.`); - } - } else if (err.code === 'ERR_AUTH_FAILED') { - console.error(alert(err.message)); - exitCode(1); - } else { - throw err; - } } } }; diff --git a/src/cli/commands/new.js b/src/cli/commands/new.js index db0aa2e..6c144dd 100644 --- a/src/cli/commands/new.js +++ b/src/cli/commands/new.js @@ -2,18 +2,27 @@ export default { aliases: '!create', desc: 'Create a new project', options: { - '-d, --workspace-dir': 'The directory to place the project in', - '-f, --force': 'Force project creation even if path already exists', - // '--id ' - // '--url ' - // '--name ' - '--template ': '?', - '-t, --type ': { - default: 'app', - desc: 'The type of project to create' - } + '-d, --workspace-dir': 'The directory to place the project in', + '-f, --force': 'Force project creation even if path already exists', + '--id [id]': 'A project ID in the format \'com.companyname.appname\'', + '-n, --name [name]': 'The name of the project', + '-p, --platform [name]': 'One or more target platforms; defaults to \'all\'', + '-t, --type [type]': 'The type of project to create', + '--template [name]': 'The name of a project template, path to a local dir/zip, url, git repo, or npm package', + '-u, --url [value]': 'Your company/personal URL' }, - action({ console }) { - console.log('Hi from new'); + async action({ argv, console }) { + const { prompt } = require('enquirer'); + + console.log('CREATING NEW PROJECT!'); + console.log(argv); + + // const { response } = await appcd.call('/project/new', { + // data: { + // // + // } + // }); + + // console.log(response); } }; diff --git a/src/cli/commands/switch.js b/src/cli/commands/switch.js index 7bf8d65..b8e75d0 100644 --- a/src/cli/commands/switch.js +++ b/src/cli/commands/switch.js @@ -5,19 +5,85 @@ export default { '--json': 'Outputs accounts as JSON', '--org [guid|id|name]': 'The organization to switch to' }, - async action({ argv, console }) { - const { response: account } = await appcd.call('/amplify/1.x/auth/switch', { - data: { - accountName: argv.account, - org: argv.org + async action(params) { + const { prompt } = require('enquirer'); + const { login } = require('../auth/login'); + const { argv, console, terminal } = params; + const { response: accounts } = await appcd.call('/amplify/1.x/auth'); + let account; + let { org } = argv; + let loggedIn = false; + let previousOrg; + + if (accounts.length) { + account = argv.account + && accounts.find(a => a.name === argv.account) + || accounts.find(a => a.active) + || accounts[0]; + } + + if (!account) { + account = await login(params); + loggedIn = true; + } + + if (!account.orgs?.length) { + console.log(`Account ${account.name} does not have any orgs.`); + return; + } + + previousOrg = account?.org.guid; + + if (account.orgs.length === 1) { + account = await appcd.call('/amplify/1.x/switch', { + accountName: account.name, + org: account.orgs[0].guid + }); + } else if (!org && argv.json) { + throw new Error('--org is required when --json is set'); + } else { + if (!org) { + let initial; + const orgs = account.orgs.sort((a, b) => a.name.localeCompare(b.name)); + + ({ org } = await prompt({ + choices: orgs + .map((org, i) => { + if (org.guid === account.org.guid) { + initial = i; + } + return { + name: org.name, + message: `${org.name} (${org.guid} : ${org.id})`, + value: org.guid + }; + }) + .sort((a, b) => a.message.localeCompare(b.message)), + initial, + message: 'Select an organization to switch to', + name: 'org', + stdin: terminal.stdin, + stdout: terminal.stdout, + type: 'select' + })); + + console.log(); } - }); + + account = (await appcd.call('/amplify/1.x/auth/switch', { + data: { + accountName: account.name, + org + } + })).response; + } if (argv.json) { console.log(JSON.stringify(account, null, 2)); } else { const { highlight } = require('appcd-logger').snooplogg.styles; - console.log(`You are logged into ${highlight(account.org.name)} as ${highlight(account.user.email || account.name)}.`); + const msg = loggedIn ? 'are logged into' : previousOrg !== account.org.guid ? 'have switched to' : 'are already switched to'; + console.log(`You ${msg} ${highlight(account.org.name)} as ${highlight(account.user.email || account.name)}.`); } } }; diff --git a/src/index.js b/src/index.js index 93e13fd..724b2a2 100644 --- a/src/index.js +++ b/src/index.js @@ -7,14 +7,16 @@ if (!Error.prepareStackTrace) { import gawk from 'gawk'; import CLIService from './cli/cli-service'; import ModuleService from './module/module-service'; +import ProjectService from './project/project-service'; import SDKService from './sdk/sdk-service'; import { debounce, get } from 'appcd-util'; import { modules, options, sdk } from 'titaniumlib'; -const cliSvc = new CLIService(); -const moduleSvc = new ModuleService(); -const sdkSvc = new SDKService(); +const cliSvc = new CLIService(); +const moduleSvc = new ModuleService(); +const projectSvc = new ProjectService(); +const sdkSvc = new SDKService(); /** * Wires up plugin services. @@ -55,6 +57,9 @@ export async function activate(cfg) { await moduleSvc.activate(cfg); appcd.register([ '/module', '/modules' ], moduleSvc); + await projectSvc.activate(); + appcd.register('/project', projectSvc); + await sdkSvc.activate(cfg); appcd.register('/sdk', sdkSvc); } @@ -68,6 +73,7 @@ export async function deactivate() { await Promise.all([ cliSvc.deactivate(), moduleSvc.deactivate(), + projectSvc.deactivate(), sdkSvc.deactivate() ]); } From 0008483be8bec4db07eb627bca142c8d8992c594 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Mon, 8 Jun 2020 12:19:52 -0500 Subject: [PATCH 02/39] Added missing project service file and add command. --- src/cli/commands/add.js | 9 ++++++++ src/project/project-service.js | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/cli/commands/add.js create mode 100644 src/project/project-service.js diff --git a/src/cli/commands/add.js b/src/cli/commands/add.js new file mode 100644 index 0000000..08b5a24 --- /dev/null +++ b/src/cli/commands/add.js @@ -0,0 +1,9 @@ +export default { + desc: 'Add a component to a project', + options: { + '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory' + }, + action({ console }) { + console.log('Add!'); + } +}; diff --git a/src/project/project-service.js b/src/project/project-service.js new file mode 100644 index 0000000..4e11b75 --- /dev/null +++ b/src/project/project-service.js @@ -0,0 +1,39 @@ +import Dispatcher from 'appcd-dispatcher'; + +/** + * Service for creating and building Titanium applications. + */ +export default class ProjectService extends Dispatcher { + /** + * Registers all of the endpoints. + * + * @param {Object} cfg - The Appc Daemon config object. + * @returns {Promise} + * @access public + */ + async activate() { + this.register('/', () => { + }); + + this.register('/build', () => { + }); + + this.register('/clean', () => { + }); + + this.register('/new', () => { + }); + + this.register('/run', () => { + }); + } + + /** + * Perform any necessary cleanup. + * + * @returns {Promise} + * @access public + */ + async deactivate() { + } +} From 7b5b40c69768cf4b1c05662adaf05c17558eca78 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Wed, 17 Jun 2020 03:16:49 -0500 Subject: [PATCH 03/39] Added prompter. Added new command prompting and verbiage. --- .eslintrc | 5 +++ package.json | 6 +-- src/cli/commands/new.js | 55 +++++++++++++++++--------- src/lib/prompter.js | 71 ++++++++++++++++++++++++++++++++++ src/project/project-service.js | 11 +++++- 5 files changed, 125 insertions(+), 23 deletions(-) create mode 100644 .eslintrc create mode 100644 src/lib/prompter.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..dab3030 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,5 @@ +{ + "rules": { + "no-loop-func": "off" + } +} diff --git a/package.json b/package.json index a8b593e..cff9db2 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,8 @@ "test": "gulp test" }, "dependencies": { - "chalk": "^4.0.0", - "cli-kit": "^1.2.0", + "chalk": "^4.1.0", + "cli-kit": "^1.2.1", "dateformat": "^3.0.3", "enquirer": "^2.3.5", "filesize": "^6.1.0", @@ -34,7 +34,7 @@ "yauzl": "^2.10.0" }, "devDependencies": { - "appcd-gulp": "^3.0.0" + "appcd-gulp": "^3.0.1" }, "homepage": "https://github.com/appcelerator/appc-daemon", "bugs": "https://github.com/appcelerator/appcd-plugin-titanium/issues", diff --git a/src/cli/commands/new.js b/src/cli/commands/new.js index 6c144dd..adc8cb0 100644 --- a/src/cli/commands/new.js +++ b/src/cli/commands/new.js @@ -1,28 +1,47 @@ +import prompter from '../../lib/prompter'; + export default { aliases: '!create', desc: 'Create a new project', options: { - '-d, --workspace-dir': 'The directory to place the project in', - '-f, --force': 'Force project creation even if path already exists', - '--id [id]': 'A project ID in the format \'com.companyname.appname\'', - '-n, --name [name]': 'The name of the project', - '-p, --platform [name]': 'One or more target platforms; defaults to \'all\'', - '-t, --type [type]': 'The type of project to create', - '--template [name]': 'The name of a project template, path to a local dir/zip, url, git repo, or npm package', - '-u, --url [value]': 'Your company/personal URL' + '-d, --workspace-dir [path]': 'The directory to place the project in', + '-f, --force': 'Force project creation even if path already exists', + '--id [id]': 'A project ID in the format \'com.companyname.appname\'', + '-n, --name [name]': 'The name of the project', + '--template [name]': 'The name of a project template, path to a local dir/zip, url, git repo, or npm package' }, - async action({ argv, console }) { - const { prompt } = require('enquirer'); + async action(ctx) { + const { argv } = ctx; + const { blue, bold, cyan, green, note } = appcd.logger.styles; + + await prompter({ + ctx, + data: { + force: argv.force, + id: argv.id, + name: argv.name, + template: argv.template, + workspaceDir: argv.workspaceDir + }, + header: `${bold(blue('Welcome! Let\'s create a new Titanium project!'))} +First, we need to ask you a few questions about your project: +`, + footer: proj => proj && ` +${green('Success!')} - console.log('CREATING NEW PROJECT!'); - console.log(argv); +Next steps: - // const { response } = await appcd.call('/project/new', { - // data: { - // // - // } - // }); + ${cyan(`cd ${proj.name}`)} + ${cyan('ti run -p android')} + ${note(' or ')} + ${cyan('ti run -p ios')} - // console.log(response); +For help, visit: https://docs.axway.com/category/appdev +` || ` +Failed to create project! +`, + path: '/project/new', + ns: 'cli:new', + }); } }; diff --git a/src/lib/prompter.js b/src/lib/prompter.js new file mode 100644 index 0000000..e36254b --- /dev/null +++ b/src/lib/prompter.js @@ -0,0 +1,71 @@ +import { prompt } from 'enquirer'; +import { Readable } from 'stream'; + +const { highlight } = appcd.logger.styles; + +export default async function prompter({ ctx, data, footer, header, ns, path, print }) { + const { argv, console, terminal } = ctx; + const { stdin, stdout } = terminal; + const logger = appcd.logger(ns); + + if (print === undefined) { + print = console.log; + } + + while (true) { + try { + const { response } = await appcd.call(path, { data }); + if (response instanceof Readable) { + await new Promise((resolve, reject) => { + response.on('data', print); + response.on('end', resolve); + response.on('error', reject); + }); + } else if (footer) { + print(typeof footer === 'function' ? await footer(response) : footer); + } else { + print(response); + } + return; + } catch (err) { + if (!err.prompt || !argv.prompt) { + throw err; + } + + if (header) { + print(typeof header === 'function' ? await header() : header); + header = null; + } + + // prompt and try again + let ask = err.prompt; + let { name } = ask; + let result; + logger.warn(`${err.toString()}, prompting for ${highlight(`"${name}"`)}`); + + while (ask) { + result = await prompt({ + validate(value) { + return !!value || ask.validateMessage || false; + }, + name: ask.name || name, + ...ask, + format() { + return this.style(this.focused?.name || this.value); + }, + stdin, + stdout, + styles: { + em(msg) { + return this.primary(msg); + } + } + }); + + ask = result?.[ask.name || name]?.prompt; + } + + Object.assign(data, result); + } + } +} diff --git a/src/project/project-service.js b/src/project/project-service.js index 4e11b75..13304bc 100644 --- a/src/project/project-service.js +++ b/src/project/project-service.js @@ -1,4 +1,9 @@ import Dispatcher from 'appcd-dispatcher'; +import snooplogg from 'snooplogg'; + +import { Project, templates } from 'titaniumlib'; + +const { log } = snooplogg('project-service'); /** * Service for creating and building Titanium applications. @@ -21,11 +26,13 @@ export default class ProjectService extends Dispatcher { this.register('/clean', () => { }); - this.register('/new', () => { - }); + this.register('/new', ctx => new Project().create(ctx.request.data)); this.register('/run', () => { }); + + // init the templates + await templates.getTemplates(); } /** From 963b7d6a74cec94f2df459e20645f2dd6a8d4fc6 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Thu, 18 Jun 2020 22:53:09 -0500 Subject: [PATCH 04/39] feat(project): Added project template service. refactor: Unified prompting. style: Cleaned up logging methods to use appcd.logger. --- .eslintrc | 3 +- package.json | 3 +- src/cli/auth/login.js | 15 ++---- src/cli/cli-service.js | 4 +- src/cli/commands/login.js | 13 ++--- src/cli/commands/logout.js | 2 +- src/cli/commands/new.js | 5 +- src/cli/commands/switch.js | 11 ++-- src/cli/commands/whoami.js | 2 +- src/lib/{prompter.js => prompt.js} | 63 ++++++++++++++++------ src/project/project-service.js | 33 ++++++++---- src/project/templates-service.js | 85 ++++++++++++++++++++++++++++++ src/sdk/sdk-service.js | 20 +++---- 13 files changed, 190 insertions(+), 69 deletions(-) rename src/lib/{prompter.js => prompt.js} (52%) create mode 100644 src/project/templates-service.js diff --git a/.eslintrc b/.eslintrc index dab3030..10f0016 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,5 +1,6 @@ { "rules": { - "no-loop-func": "off" + "no-loop-func": "off", + "promise/always-return": "off" } } diff --git a/package.json b/package.json index cff9db2..8027d4c 100644 --- a/package.json +++ b/package.json @@ -17,13 +17,14 @@ }, "dependencies": { "chalk": "^4.1.0", - "cli-kit": "^1.2.1", + "cli-kit": "^1.2.2", "dateformat": "^3.0.3", "enquirer": "^2.3.5", "filesize": "^6.1.0", "fs-extra": "^9.0.1", "gawk": "^4.7.1", "get-port": "^5.1.1", + "global-modules": "^2.0.0", "node-pty-prebuilt-multiarch": "^0.9.0", "pluralize": "^8.0.0", "prompts": "^2.3.2", diff --git a/src/cli/auth/login.js b/src/cli/auth/login.js index 3caff3c..b7b9dc2 100644 --- a/src/cli/auth/login.js +++ b/src/cli/auth/login.js @@ -1,3 +1,7 @@ +import { prompt } from '../../lib/prompt'; + +const { alert, highlight } = appcd.logger.styles; + /** * Performs a login. * @@ -9,10 +13,6 @@ * @returns {Object} The account info. */ export async function login({ argv, console, setExitCode, terminal }) { - const { prompt } = require('enquirer'); - const { snooplogg } = require('appcd-logger'); - const { alert, highlight } = snooplogg.styles; - const data = { baseUrl: argv.baseUrl, clientId: argv.clientId, @@ -26,15 +26,12 @@ export async function login({ argv, console, setExitCode, terminal }) { if (Object.prototype.hasOwnProperty.call(argv, 'username')) { const questions = []; - const { stdin, stdout } = terminal; if (!argv.username || typeof argv.username !== 'string') { questions.push({ type: 'input', name: 'username', message: 'Username:', - stdin, - stdout, validate(s) { return !!s || 'Please enter your username'; } @@ -46,8 +43,6 @@ export async function login({ argv, console, setExitCode, terminal }) { type: 'password', name: 'password', message: 'Password:', - stdin, - stdout, validate(s) { return !!s || 'Please enter your password'; } @@ -58,7 +53,7 @@ export async function login({ argv, console, setExitCode, terminal }) { throw new Error('--username and --password are required when --json is set'); } - Object.assign(data, await prompt(questions)); + Object.assign(data, await prompt(questions, terminal)); if (!argv.json) { // add a newline after prompting has completed diff --git a/src/cli/cli-service.js b/src/cli/cli-service.js index 76b520b..2c8aa6b 100644 --- a/src/cli/cli-service.js +++ b/src/cli/cli-service.js @@ -1,4 +1,4 @@ -import CLI, { snooplogg } from 'cli-kit'; +import CLI from 'cli-kit'; import Dispatcher from 'appcd-dispatcher'; import fs from 'fs'; import getPort from 'get-port'; @@ -7,7 +7,7 @@ import path from 'path'; import { get } from 'appcd-util'; import { parseVersion } from '../lib/util'; -const { highlight } = snooplogg.styles; +const { highlight } = appcd.logger.styles; /** * Defines a service endpoint for defining, processing, and dispatching Titanium CLI commands. diff --git a/src/cli/commands/login.js b/src/cli/commands/login.js index 7cbccc7..7fb6aa8 100644 --- a/src/cli/commands/login.js +++ b/src/cli/commands/login.js @@ -1,3 +1,7 @@ +import { login } from '../auth/login'; + +const { highlight } = appcd.logger.styles; + export default { desc: 'Log in to the Axway AMPLIFY platform', options: { @@ -21,15 +25,6 @@ export default { '-p, --password [pass]': 'Password to authenticate with' }, async action(params) { - const [ - { snooplogg }, - { login } - ] = await Promise.all([ - import('appcd-logger'), - import('../auth/login') - ]); - - const { highlight } = snooplogg.styles; const { argv, console } = params; const account = await login(params); diff --git a/src/cli/commands/logout.js b/src/cli/commands/logout.js index c62cbf7..75ca0a4 100644 --- a/src/cli/commands/logout.js +++ b/src/cli/commands/logout.js @@ -24,7 +24,7 @@ export default { // pretty output if (revoked.length) { - const { highlight } = require('appcd-logger').snooplogg.styles; + const { highlight } = appcd.logger.styles; console.log('Revoked authenticated accounts:'); for (const account of revoked) { console.log(` ${highlight(account.name)}`); diff --git a/src/cli/commands/new.js b/src/cli/commands/new.js index adc8cb0..ba1fa55 100644 --- a/src/cli/commands/new.js +++ b/src/cli/commands/new.js @@ -1,4 +1,4 @@ -import prompter from '../../lib/prompter'; +import { promptLoop } from '../../lib/prompt'; export default { aliases: '!create', @@ -14,9 +14,10 @@ export default { const { argv } = ctx; const { blue, bold, cyan, green, note } = appcd.logger.styles; - await prompter({ + await promptLoop({ ctx, data: { + cwd: ctx.data.cwd, force: argv.force, id: argv.id, name: argv.name, diff --git a/src/cli/commands/switch.js b/src/cli/commands/switch.js index b8e75d0..28a442b 100644 --- a/src/cli/commands/switch.js +++ b/src/cli/commands/switch.js @@ -1,3 +1,6 @@ +import { login } from '../auth/login'; +import { prompt } from '../../lib/prompt'; + export default { desc: 'Select default account and organization', options: { @@ -6,8 +9,6 @@ export default { '--org [guid|id|name]': 'The organization to switch to' }, async action(params) { - const { prompt } = require('enquirer'); - const { login } = require('../auth/login'); const { argv, console, terminal } = params; const { response: accounts } = await appcd.call('/amplify/1.x/auth'); let account; @@ -62,10 +63,8 @@ export default { initial, message: 'Select an organization to switch to', name: 'org', - stdin: terminal.stdin, - stdout: terminal.stdout, type: 'select' - })); + }, terminal)); console.log(); } @@ -81,7 +80,7 @@ export default { if (argv.json) { console.log(JSON.stringify(account, null, 2)); } else { - const { highlight } = require('appcd-logger').snooplogg.styles; + const { highlight } = appcd.logger.styles; const msg = loggedIn ? 'are logged into' : previousOrg !== account.org.guid ? 'have switched to' : 'are already switched to'; console.log(`You ${msg} ${highlight(account.org.name)} as ${highlight(account.user.email || account.name)}.`); } diff --git a/src/cli/commands/whoami.js b/src/cli/commands/whoami.js index 7d34833..e4b6c35 100644 --- a/src/cli/commands/whoami.js +++ b/src/cli/commands/whoami.js @@ -16,7 +16,7 @@ export default { if (argv.json) { console.log(JSON.stringify(account, null, 2)); } else { - const { highlight } = require('appcd-logger').snooplogg.styles; + const { highlight } = appcd.logger.styles; console.log(`You are logged into ${highlight(account.org.name)} as ${highlight(account.user.email || account.name)}.`); } } diff --git a/src/lib/prompter.js b/src/lib/prompt.js similarity index 52% rename from src/lib/prompter.js rename to src/lib/prompt.js index e36254b..d29c61f 100644 --- a/src/lib/prompter.js +++ b/src/lib/prompt.js @@ -1,11 +1,52 @@ -import { prompt } from 'enquirer'; +import { prompt as enquire } from 'enquirer'; import { Readable } from 'stream'; const { highlight } = appcd.logger.styles; -export default async function prompter({ ctx, data, footer, header, ns, path, print }) { +/** + * ? + * + * @param {Object|Array.} questions - + * @returns {Promise} + */ +export function prompt(questions, { stdin, stdout } = {}) { + if (!Array.isArray(questions)) { + questions = [ questions ]; + } + + for (let i = 0, len = questions.length; i < len; i++) { + questions[i] = { + format() { + // for some reason, enquirer doesn't print the selected value using the primary + // (green) color for select prompts, so we just force it for all prompts + return this.style(this.value); + }, + styles: { + em(msg) { + // stylize emphasised text with just the primary color, no underline + return this.primary(msg); + } + }, + ...questions[i], + onSubmit() { + this.cursorShow(); + }, + stdin, + stdout + }; + } + + return enquire(questions); +} + +/** + * ? + * + * @param {Object} opts - Various options. + * @returns {Promise} + */ +export async function promptLoop({ ctx, data, footer, header, ns, path, print }) { const { argv, console, terminal } = ctx; - const { stdin, stdout } = terminal; const logger = appcd.logger(ns); if (print === undefined) { @@ -46,21 +87,11 @@ export default async function prompter({ ctx, data, footer, header, ns, path, pr while (ask) { result = await prompt({ validate(value) { - return !!value || ask.validateMessage || false; + return !!value || !this.required || ask.validateMessage || false; }, name: ask.name || name, - ...ask, - format() { - return this.style(this.focused?.name || this.value); - }, - stdin, - stdout, - styles: { - em(msg) { - return this.primary(msg); - } - } - }); + ...ask + }, terminal); ask = result?.[ask.name || name]?.prompt; } diff --git a/src/project/project-service.js b/src/project/project-service.js index 13304bc..78e6e40 100644 --- a/src/project/project-service.js +++ b/src/project/project-service.js @@ -1,14 +1,16 @@ import Dispatcher from 'appcd-dispatcher'; -import snooplogg from 'snooplogg'; +import TemplateService from './templates-service'; -import { Project, templates } from 'titaniumlib'; +import { Project } from 'titaniumlib'; -const { log } = snooplogg('project-service'); +const { log } = appcd.logger('project-service'); /** * Service for creating and building Titanium applications. */ export default class ProjectService extends Dispatcher { + templateSvc = new TemplateService(); + /** * Registers all of the endpoints. * @@ -16,23 +18,31 @@ export default class ProjectService extends Dispatcher { * @returns {Promise} * @access public */ - async activate() { - this.register('/', () => { + async activate(cfg) { + this.register('/', ctx => { + log(ctx.request.data); }); - this.register('/build', () => { + this.register('/build', ctx => { + log(ctx.request.data); }); - this.register('/clean', () => { + this.register('/clean', ctx => { + log(ctx.request.data); }); - this.register('/new', ctx => new Project().create(ctx.request.data)); + this.register('/new', async ctx => { + return await new Project({ + templates: (await this.call('/templates')).response + }).create(ctx.request.data); + }); - this.register('/run', () => { + this.register('/run', ctx => { + log(ctx.request.data); }); - // init the templates - await templates.getTemplates(); + await this.templateSvc.activate(cfg); + this.register('/templates', this.templateSvc); } /** @@ -42,5 +52,6 @@ export default class ProjectService extends Dispatcher { * @access public */ async deactivate() { + await this.templateSvc.deactivate(); } } diff --git a/src/project/templates-service.js b/src/project/templates-service.js new file mode 100644 index 0000000..2e31305 --- /dev/null +++ b/src/project/templates-service.js @@ -0,0 +1,85 @@ +import DetectEngine from 'appcd-detect'; +import fs from 'fs-extra'; +import gawk from 'gawk'; +import globalModules from 'global-modules'; +import path from 'path'; + +import { DataServiceDispatcher } from 'appcd-dispatcher'; +import { mergeDeep } from 'appcd-util'; +import { templates } from 'titaniumlib'; + +/** + * Detects global Titanium project templates. + */ +export default class TemplateService extends DataServiceDispatcher { + /** + * Starts detecting templates. + * + * @returns {Promise} + * @access public + */ + async activate() { + const keywordRE = /^titanium-(?:(\w*)-)?template$/; + + this.detectEngine = new DetectEngine({ + checkDir(dir) { + try { + const pkg = fs.readJsonSync(path.join(dir, 'package.json')); + for (const keyword of pkg.keywords) { + const m = keyword.match(keywordRE); + if (m) { + return { + name: pkg.name, + desc: pkg.description, + path: dir, + pkg, + type: m[1] || undefined + }; + } + } + } catch (e) { + // 'dir' is not a template + } + }, + depth: 2, // allow for scoped packages + multiple: true, + name: 'titanium:templates', + paths: [ globalModules ], + processResults(results) { + results.sort((a, b) => { + return a.name.localeCompare(b.name); + }); + }, + redetect: true, + watch: true + }); + + const format = results => { + const data = mergeDeep({}, templates); + for (const template of results) { + let type = template.type || 'other'; + if (!data[type]) { + data[type] = []; + } + data[type].push(template); + } + return data; + }; + + this.detectEngine.on('results', results => gawk.set(this.data, format(results))); + this.data = gawk(format(await this.detectEngine.start())); + } + + /** + * Stops the detect engine. + * + * @returns {Promise} + * @access public + */ + async deactivate() { + if (this.detectEngine) { + await this.detectEngine.stop(); + this.detectEngine = null; + } + } +} diff --git a/src/sdk/sdk-service.js b/src/sdk/sdk-service.js index 19cf50a..fd80693 100644 --- a/src/sdk/sdk-service.js +++ b/src/sdk/sdk-service.js @@ -47,34 +47,36 @@ export default class SDKService extends Dispatcher { /** * Install SDK service handler. + * Note: This method does not return a promise because we want the response to be sent + * immediately and receive install events as they occur. It relies on the * * @param {Context} ctx - A request context. * @access private */ - install(ctx) { - const { data, params } = ctx.request; + install({ request, response }) { + const { data, params } = request; sdk.install({ downloadDir: this.config.home && expandPath(this.config.home, 'downloads'), keep: data.keep, onProgress(evt) { if (data.progress) { - ctx.response.write(evt); + response.write(evt); } }, overwrite: data.overwrite, uri: data.uri || params.name }).then(tisdk => { - ctx.response.write({ fin: true, message: `Titanium SDK ${tisdk.name} installed` }); - ctx.response.end(); - }, err => { + response.write({ fin: true, message: `Titanium SDK ${tisdk.name} installed` }); + response.end(); + }).catch(err => { try { if (err.code === 'ENOTFOUND') { - ctx.response.write(new AppcdError(codes.NOT_FOUND, err.message)); + response.write(new AppcdError(codes.NOT_FOUND, err.message)); } else { - ctx.response.write(new AppcdError(err)); + response.write(new AppcdError(err)); } - ctx.response.end(); + response.end(); } catch (e) { // stream is probably closed } From 7969bb20861f5181111478bf4a33f5e74f54d139 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Thu, 18 Jun 2020 23:21:47 -0500 Subject: [PATCH 05/39] doc: Added jsdoc to prompt utils. --- src/lib/prompt.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/lib/prompt.js b/src/lib/prompt.js index d29c61f..c09876f 100644 --- a/src/lib/prompt.js +++ b/src/lib/prompt.js @@ -4,9 +4,11 @@ import { Readable } from 'stream'; const { highlight } = appcd.logger.styles; /** - * ? + * Prompts for a value with unified settings and improved style consistency. * - * @param {Object|Array.} questions - + * @param {Object|Array.} questions - A question or list of questions to prompt for. + * @param {Object} [terminal] - An object containing a `stdin` and `stdout` such as a cli-kit + * `Terminal` instance. * @returns {Promise} */ export function prompt(questions, { stdin, stdout } = {}) { @@ -40,9 +42,17 @@ export function prompt(questions, { stdin, stdout } = {}) { } /** - * ? + * Calls the specified appcd service and if the response returns an error with a `prompt`, then + * it will prompt for the value and retry the request. * * @param {Object} opts - Various options. + * @param {Object} opts.ctx - A cli-kit execution context. + * @param {Object} opts.data - The data payload to send to the appcd service. + * @param {String|Function} [opts.footer] - A message to display after the service call has completed successfully. + * @param {String|Function} [opts.header] - A message to display before the first prompt. + * @param {String} opts.ns - The debug log namespace for this prompt loop. + * @param {String} opts.path - The appcd service to call. + * @param {Function} [opts.print] - A custom print function. Defaults to `console.log()`. * @returns {Promise} */ export async function promptLoop({ ctx, data, footer, header, ns, path, print }) { From 00e2827bb1497e4112c9602180c28ca8b27520d1 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Fri, 19 Jun 2020 11:11:19 -0500 Subject: [PATCH 06/39] chore: Updated deps. style: Added terminal size debug logging to prompt. --- package.json | 2 +- src/lib/prompt.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 8027d4c..d011f57 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "chalk": "^4.1.0", - "cli-kit": "^1.2.2", + "cli-kit": "^1.2.3", "dateformat": "^3.0.3", "enquirer": "^2.3.5", "filesize": "^6.1.0", diff --git a/src/lib/prompt.js b/src/lib/prompt.js index c09876f..d367655 100644 --- a/src/lib/prompt.js +++ b/src/lib/prompt.js @@ -1,6 +1,7 @@ import { prompt as enquire } from 'enquirer'; import { Readable } from 'stream'; +const { log } = appcd.logger('prompt'); const { highlight } = appcd.logger.styles; /** @@ -16,6 +17,8 @@ export function prompt(questions, { stdin, stdout } = {}) { questions = [ questions ]; } + log(`Prompting with terminal size ${highlight(stdout.columns)} x ${highlight(stdout.rows)}`); + for (let i = 0, len = questions.length; i < len; i++) { questions[i] = { format() { From b96955a7798ee6b1707fef2a7e65110b820eeb19 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Fri, 19 Jun 2020 17:19:35 -0500 Subject: [PATCH 07/39] Wired up build, clean, project, and run cli commands to the project service. --- src/cli/commands/build.js | 25 +++++++++++++-------- src/cli/commands/clean.js | 22 +++++++++++------- src/cli/commands/new.js | 13 +++++------ src/cli/commands/project.js | 14 ++++++++++-- src/cli/commands/run.js | 29 +++++++++++++++--------- src/project/project-service.js | 41 ++++++++++++++++++++++++---------- 6 files changed, 95 insertions(+), 49 deletions(-) diff --git a/src/cli/commands/build.js b/src/cli/commands/build.js index 48a3e8e..dfcf145 100644 --- a/src/cli/commands/build.js +++ b/src/cli/commands/build.js @@ -1,16 +1,23 @@ +import { promptLoop } from '../../lib/prompt'; + export default { desc: 'Builds a project', options: { '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory', - '-f, --force': 'Force a full rebuild', - '-p, --platform [name]': 'The target build platform' + '-f, --force': 'Force a full rebuild', + '-p, --platform [name]': 'The target build platform' }, - action({ console }) { - console.log('Building!'); - - // read the tiapp - // load the sdk - // validate - // run the build logic + async action(ctx) { + await promptLoop({ + ctx, + data: { + cwd: ctx.data.cwd, + force: ctx.argv.force, + platform: ctx.argv.platform, + projectDir: ctx.argv.projectDir + }, + path: '/project/build', + ns: 'cli:build' + }); } }; diff --git a/src/cli/commands/clean.js b/src/cli/commands/clean.js index 84f4bc4..09ca8a8 100644 --- a/src/cli/commands/clean.js +++ b/src/cli/commands/clean.js @@ -1,15 +1,21 @@ +import { promptLoop } from '../../lib/prompt'; + export default { desc: 'Remove previous build directories', options: { '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory', - '-p, --platforms [names]': 'One or more platforms to clean or empty for all' + '-p, --platforms [names]': 'A comma separated list of platforms; defaults to all platforms' }, - action({ console }) { - // read the tiapp - // load the sdk - // validate the platform names - // run the clean logic - - console.log('Cleaning!'); + async action(ctx) { + await promptLoop({ + ctx, + data: { + cwd: ctx.data.cwd, + platforms: ctx.argv.platforms, + projectDir: ctx.argv.projectDir + }, + path: '/project/clean', + ns: 'cli:clean' + }); } }; diff --git a/src/cli/commands/new.js b/src/cli/commands/new.js index ba1fa55..c5a3ed6 100644 --- a/src/cli/commands/new.js +++ b/src/cli/commands/new.js @@ -11,18 +11,17 @@ export default { '--template [name]': 'The name of a project template, path to a local dir/zip, url, git repo, or npm package' }, async action(ctx) { - const { argv } = ctx; const { blue, bold, cyan, green, note } = appcd.logger.styles; await promptLoop({ ctx, data: { cwd: ctx.data.cwd, - force: argv.force, - id: argv.id, - name: argv.name, - template: argv.template, - workspaceDir: argv.workspaceDir + force: ctx.argv.force, + id: ctx.argv.id, + name: ctx.argv.name, + template: ctx.argv.template, + workspaceDir: ctx.argv.workspaceDir }, header: `${bold(blue('Welcome! Let\'s create a new Titanium project!'))} First, we need to ask you a few questions about your project: @@ -42,7 +41,7 @@ For help, visit: https://docs.axway.com/category/appdev Failed to create project! `, path: '/project/new', - ns: 'cli:new', + ns: 'cli:new' }); } }; diff --git a/src/cli/commands/project.js b/src/cli/commands/project.js index 520b356..5034cf7 100644 --- a/src/cli/commands/project.js +++ b/src/cli/commands/project.js @@ -1,9 +1,19 @@ +import { promptLoop } from '../../lib/prompt'; + export default { desc: 'Manage project settings', options: { '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory' }, - action({ console }) { - console.log('Project info!'); + async action(ctx) { + await promptLoop({ + ctx, + data: { + cwd: ctx.data.cwd, + projectDir: ctx.argv.projectDir + }, + path: '/project', + ns: 'cli:project' + }); } }; diff --git a/src/cli/commands/run.js b/src/cli/commands/run.js index 2d04e3a..a19ced9 100644 --- a/src/cli/commands/run.js +++ b/src/cli/commands/run.js @@ -1,18 +1,25 @@ +import { promptLoop } from '../../lib/prompt'; + export default { desc: 'Build and runs a project', options: { - '--build-only': 'Builds the project without running it in the simulator/emulator or installing it on device', + '--build-only': 'Builds the project without running it in the simulator/emulator or installing it on device', '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory', - '-f, --force': 'Force a full rebuild', - '-p, --platform [name]': 'The target build platform' + '-f, --force': 'Force a full rebuild', + '-p, --platform [name]': 'The target build platform' }, - action({ console }) { - console.log('Building and running!'); - - // read the tiapp - // load the sdk - // validate - // run the build logic - // run the app + async action(ctx) { + await promptLoop({ + ctx, + data: { + buildOnly: ctx.argv.buildOnly, + cwd: ctx.data.cwd, + force: ctx.argv.force, + platform: ctx.argv.platform, + projectDir: ctx.argv.projectDir + }, + path: '/project/run', + ns: 'cli:run' + }); } }; diff --git a/src/project/project-service.js b/src/project/project-service.js index 78e6e40..21c5319 100644 --- a/src/project/project-service.js +++ b/src/project/project-service.js @@ -1,6 +1,8 @@ import Dispatcher from 'appcd-dispatcher'; +import path from 'path'; import TemplateService from './templates-service'; +import { AppcdError, codes } from 'appcd-response'; import { Project } from 'titaniumlib'; const { log } = appcd.logger('project-service'); @@ -19,17 +21,33 @@ export default class ProjectService extends Dispatcher { * @access public */ async activate(cfg) { - this.register('/', ctx => { - log(ctx.request.data); - }); + const handler = action => { + return async ctx => { + // get project dir + let { cwd, projectDir } = ctx.request.data; - this.register('/build', ctx => { - log(ctx.request.data); - }); + if (projectDir !== undefined && typeof projectDir !== 'string') { + throw new AppcdError(codes.BAD_REQUEST, 'Missing project directory'); + } - this.register('/clean', ctx => { - log(ctx.request.data); - }); + if (projectDir === undefined || !path.isAbsolute(projectDir)) { + if (!cwd || typeof cwd !== 'string') { + throw new AppcdError(codes.BAD_REQUEST, 'Current working directory required when project directory is relative'); + } + projectDir = path.resolve(cwd, projectDir || '.'); + } + + return await new Project({ + path: projectDir + })[action](ctx.request.data); + }; + }; + + this.register('/', handler('tiapp')); + + this.register('/build', handler('build')); + + this.register('/clean', handler('clean')); this.register('/new', async ctx => { return await new Project({ @@ -37,9 +55,8 @@ export default class ProjectService extends Dispatcher { }).create(ctx.request.data); }); - this.register('/run', ctx => { - log(ctx.request.data); - }); + // TODO: in the future, run will call project.build and we'll "run" it ourselves + this.register('/run', handler('run')); await this.templateSvc.activate(cfg); this.register('/templates', this.templateSvc); From 13c649fa9806eb60d084692fb19d53952bb7ecf3 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Tue, 30 Jun 2020 14:43:47 -0500 Subject: [PATCH 08/39] Added app guid to config. feat(sdk): Added 'find' endpoint to SDK service to get info about an installed Titanium SDK. feat(sdk): Added progress bars during SDK installation. feat(cli:sdk): Added aliases to sdk commands (i, ls, rm). feat(project): Added legacy Titanium CLI command bootstrap. chore: Updated deps. --- CHANGELOG.md | 18 +- conf/config.js | 7 - conf/config.json | 6 + package.json | 6 +- src/cli/commands/sdk.js | 2 +- src/cli/sdk/install.js | 122 ++++++-- src/cli/sdk/list.js | 1 + src/cli/sdk/uninstall.js | 4 +- src/project/legacy/bootstrap.js | 53 ++++ src/project/legacy/patch/android.js | 104 +++++++ src/project/legacy/patch/fields.js | 26 ++ src/project/legacy/patch/index.js | 25 ++ src/project/legacy/patch/ios.js | 215 ++++++++++++++ src/project/legacy/ti/cli.js | 445 ++++++++++++++++++++++++++++ src/project/legacy/ti/config.js | 126 ++++++++ src/project/legacy/ti/logger.js | 30 ++ src/project/legacy/tunnel.js | 71 +++++ src/project/project-service.js | 136 +++++++-- src/sdk/sdk-service.js | 26 +- 19 files changed, 1343 insertions(+), 80 deletions(-) delete mode 100644 conf/config.js create mode 100644 conf/config.json create mode 100644 src/project/legacy/bootstrap.js create mode 100644 src/project/legacy/patch/android.js create mode 100644 src/project/legacy/patch/fields.js create mode 100644 src/project/legacy/patch/index.js create mode 100644 src/project/legacy/patch/ios.js create mode 100644 src/project/legacy/ti/cli.js create mode 100644 src/project/legacy/ti/config.js create mode 100644 src/project/legacy/ti/logger.js create mode 100644 src/project/legacy/tunnel.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 1795bac..6f4bc1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,21 @@ # v2.0.0 * BREAKING CHANGE(config): `config` command no longer returns status as apart of JSON output. - * BREAKING CHANGE(config): `config` command does not return current value when doing a `set`, `push`, or - `unshift`. - * BREAKING CHANGE(config): `config list` command no longer supports filtering, use `config get` instead. + * BREAKING CHANGE(config): `config` command does not return current value when doing a `set`, + `push`, or `unshift`. + * BREAKING CHANGE(config): `config list` command no longer supports filtering, use `config get` + instead. * BREAKING CHANGE(config): Write operations such as `set` return `"OK"` instead of `"Saved"`. * feat(info): Added `filter` argument to `ti info`. - * feat: Added project service handler. [(DAEMON-26)](https://jira.appcelerator.org/browse/DAEMON-26) - * feat: Added `new` command. [(DAEMON-321)](https://jira.appcelerator.org/browse/DAEMON-321) - * feat: Added auth commands `login`, `logout`, `whoami`, and `switch`. + * feat(project): Added project service handler. + [(DAEMON-26)](https://jira.appcelerator.org/browse/DAEMON-26) + * feat(cli): Added `new` command. [(DAEMON-321)](https://jira.appcelerator.org/browse/DAEMON-321) + * feat(cli): Added auth commands `login`, `logout`, `whoami`, and `switch`. [(DAEMON-300)](https://jira.appcelerator.org/browse/DAEMON-300) + * feat(cli:sdk): Added aliases to sdk commands (i, ls, rm). + * feat(sdk): Added `find` endpoint to SDK service to get info about an installed Titanium SDK. + * feat(sdk): Added progress bars during SDK installation. + * feat(project): Added legacy Titanium CLI command bootstrap. * refactor: Updated to cli-kit@0.14.0 adding support for the new client/server architecture. * refactor: Updated `config` command actions to be subcommands with improved help output. * chore: Added plugin API version 2.x. diff --git a/conf/config.js b/conf/config.js deleted file mode 100644 index a88ea52..0000000 --- a/conf/config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - /** - * The default Titanium SDK installation path. - * @type {Array.} - */ - searchPaths: [] -}; diff --git a/conf/config.json b/conf/config.json new file mode 100644 index 0000000..7d553c3 --- /dev/null +++ b/conf/config.json @@ -0,0 +1,6 @@ +{ + "searchPaths": [], + "telemetry": { + "app": "de93eb9e-e53c-4f3a-9222-9e0d124950e5" + } +} diff --git a/package.json b/package.json index d011f57..ec1c9cf 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,10 @@ }, "dependencies": { "chalk": "^4.1.0", - "cli-kit": "^1.2.3", + "cli-kit": "^1.2.4", "dateformat": "^3.0.3", "enquirer": "^2.3.5", + "figures": "^3.2.0", "filesize": "^6.1.0", "fs-extra": "^9.0.1", "gawk": "^4.7.1", @@ -27,6 +28,7 @@ "global-modules": "^2.0.0", "node-pty-prebuilt-multiarch": "^0.9.0", "pluralize": "^8.0.0", + "progress": "^2.0.3", "prompts": "^2.3.2", "sort-object-keys": "^1.1.3", "source-map-support": "^0.5.19", @@ -42,7 +44,7 @@ "repository": "https://github.com/appcelerator/appcd-plugin-titanium", "appcd": { "apiVersion": "^1.1.0 || 2.x", - "config": "./conf/config.js", + "config": "./conf/config.json", "name": "titanium", "type": "external" }, diff --git a/src/cli/commands/sdk.js b/src/cli/commands/sdk.js index a9dae6c..a759407 100644 --- a/src/cli/commands/sdk.js +++ b/src/cli/commands/sdk.js @@ -4,12 +4,12 @@ import select from '../sdk/select'; import uninstall from '../sdk/uninstall'; export default { + action: list.action, commands: { install, list, select, uninstall }, - defaultCommand: 'list', desc: 'Manage Titanium SDKs.' }; diff --git a/src/cli/sdk/install.js b/src/cli/sdk/install.js index 427bea2..631e1a6 100644 --- a/src/cli/sdk/install.js +++ b/src/cli/sdk/install.js @@ -1,5 +1,13 @@ +import ProgressBar from 'progress'; + +import { ansi } from 'cli-kit'; +import { arrowRight, bullet, tick } from 'figures'; + +const { log } = appcd.logger('sdk:install'); +const { cyan, gray, green, highlight } = appcd.logger.styles; + export default { - async action({ console, argv }) { + async action({ argv, console, terminal }) { const { response } = await appcd.call('/sdk/install', { data: { keep: argv.keep, @@ -9,39 +17,93 @@ export default { } }); - return new Promise((resolve, reject) => { - let tasks = []; + try { + if (argv.progress) { + terminal.stderr.write(ansi.cursor.hide); + } + + await new Promise((resolve, reject) => { + let bar = null; + let tasks = []; + const tokens = {}; + + response + .on('data', evt => { + if (!evt || typeof evt !== 'object') { + return; + } + + try { + switch (evt.type) { + case 'tasks': + tasks = evt.tasks; + log('Received tasks:', tasks); + break; - response - .on('data', evt => { - switch (evt && evt.type) { - case 'tasks': - tasks = evt.tasks; - break; + case 'task-start': + { + const name = tasks[evt.task - 1] || null; + log(`Starting task: ${highlight(name)}`); - case 'task-start': - const name = tasks[evt.task - 1]; - if (name) { - console.log(`${name}...`); + if (argv.progress) { + tokens.name = name; + tokens.paddedPercent = ' 0%'; + tokens.symbol = cyan(arrowRight); + + if (evt.hasProgress) { + bar = new ProgressBar(' :symbol :name :paddedPercent [:bar]', { + clear: true, + complete: cyan('='), + incomplete: gray('.'), + stream: terminal.stderr, + total: 100, + width: 40 + }); + bar.render(tokens); + } else { + terminal.stderr.write(` ${tokens.symbol} ${name}`); + } + } else { + console.log(` ${green(bullet)} ${name}...`); + } + break; + } + + case 'task-progress': + if (bar) { + tokens.paddedPercent = (evt.progress * 100).toFixed(0).padStart(3) + '%'; + bar.update(evt.progress, tokens); + } + break; + + case 'task-end': + { + terminal.stderr.cursorTo(0); + terminal.stderr.clearLine(); + const name = tasks[evt.task - 1] || null; + log(`Finished task: ${highlight(name)}`); + bar = null; + console.log(` ${green(tick)} ${name}`); + break; + } + + default: + console.log(evt.message || evt); } - break; - - case 'task-progress': - // evt.progress - // console.log(evt.progress); - break; - - case 'task-end': - break; - - default: - console.log(evt.message || evt); - } - }) - .once('end', resolve) - .once('error', reject); - }); + } catch (e) { + reject(e); + } + }) + .once('end', resolve) + .once('error', reject); + }); + } finally { + if (argv.progress) { + terminal.stderr.write(ansi.cursor.show); + } + } }, + aliases: [ 'i' ], args: [ { name: 'version', diff --git a/src/cli/sdk/list.js b/src/cli/sdk/list.js index caef054..f806b7c 100644 --- a/src/cli/sdk/list.js +++ b/src/cli/sdk/list.js @@ -126,6 +126,7 @@ export default { } } }, + aliases: [ 'ls' ], desc: 'Print a list of installed SDK versions.', options: { '-b, --branches': 'Retreive and print all branches', diff --git a/src/cli/sdk/uninstall.js b/src/cli/sdk/uninstall.js index 479dac0..c5445cf 100644 --- a/src/cli/sdk/uninstall.js +++ b/src/cli/sdk/uninstall.js @@ -4,8 +4,9 @@ import { unique } from 'appcd-util'; export default { async action({ console, argv }) { try { + const { highlight } = appcd.logger.styles; const { response } = await appcd.call('/sdk/uninstall', { data: { uri: argv.version } }); - console.log(`Titanium SDK ${unique(response.map(r => r.name)).sort().join(', ')} uninstalled`); + console.log(`Titanium SDK ${unique(response.map(r => highlight(r.name))).sort().join(', ')} uninstalled`); } catch (e) { if (e.status === codes.NOT_FOUND) { console.error(e.message); @@ -14,6 +15,7 @@ export default { } } }, + aliases: [ 'rm' ], args: [ { name: 'version', diff --git a/src/project/legacy/bootstrap.js b/src/project/legacy/bootstrap.js new file mode 100644 index 0000000..f7e27c1 --- /dev/null +++ b/src/project/legacy/bootstrap.js @@ -0,0 +1,53 @@ +/* istanbul ignore if */ +if (!Error.prepareStackTrace) { + require('source-map-support/register'); +} + +if (!process.connected) { + console.error('The Titanium SDK bootstrap cannot be directly executed.'); + process.exit(2); +} + +// import CLI from './ti/cli'; +import './patch'; + +process.title = 'titanium-legacy-bootstrap'; + +// the Titanium SDK commands call process.exit() directly and that will interfere with the async output, so +// we need to monkey patch stdout/stderr to make sure the buffers are flushed +const { exit, stdout, stderr } = process; +process.exit = code => Promise + .all([ stdout, stderr ].map(stream => new Promise(resolve => { + if (stream._writableState && stream._writableState.needDrain) { + stream.on('drain', resolve); + } else { + resolve(); + } + }))) + .then(() => exit(code)); + +process + .on('uncaughtException', err => console.error('Caught unhandled exception:', err)) + .on('unhandledRejection', (reason, p) => console.error('Caught unhandled rejection at: Promise ', p, reason)) + .once('message', onMessage); + +async function onMessage(msg) { + try { + // const cli = new CLI(msg); + // await cli.go(msg.command); + console.log('Hi from the bootstrap!'); + + // the command is complete, but the IPC channel is still open, so we simply disconnect it and + // this process should exit whenever the command finishes + process.disconnect(); + } catch (err) { + process.send({ + ...err, + message: err.message || err, + stack: err.stack, + status: err.status || 500, + type: 'error' + }); + process.exit(1); + } +} diff --git a/src/project/legacy/patch/android.js b/src/project/legacy/patch/android.js new file mode 100644 index 0000000..e19d639 --- /dev/null +++ b/src/project/legacy/patch/android.js @@ -0,0 +1,104 @@ +import tunnel from '../tunnel'; +import * as version from '../../../lib/version'; + +export function detect(config = {}, opts = {}, callback) { + tunnel.call('/android/1.x/info') + .then(info => { + const results = { + devices: info.devices, + emulators: info.emulators, + issues: [], + ndk: processSDK(info.ndk), + sdk: processSDK(info.sdk, config, opts.vendorDependencies), + targets: results.targets, + vendorDependencies: opts.vendorDependencies || {} + }; + + // TODO: issues + + callback(results); + }) + .catch(err => { + console.error(err); + callback(); + }); +} + +function processNDK(ndks) { + return null; +} + +function processSDK(sdks, config, vendorDependencies = {}) { + const sdk = sdks.sort(a => (a.default ? -1 : 1))[0]; + if (!sdk) { + return null; + } + + const results = { + path: sdk.path, + executables: { + adb: null, + android: null, + emulator: null, + mksdcard: null, + zipalign: null, + aapt: null, + aidl: null, + dx: null, + apksigner: null + }, + dx: null, + proguard: null, + tools: { + path: null, + supported: null, + version: null + }, + platformTools: { + path: null, + supported: null, + version: null + }, + buildTools: { + path: null, + supported: null, + version: null, + maxSupported: null + } + }; + + if (sdk.platformTools) { + results.executables.adb = sdk.platformTools.executables.adb; + } + + if (sdk.tools) { + results.executables.emulator = sdk.tools.executables.emulator; + } + + if (sdk.buildTools) { + const supportedRange = vendorDependencies['android build tools']; + const min = supportedRange && version.parseMin(supportedRange); + const preferred = config.get('android.buildTools.selectedVersion'); + const buildTools = sdk.buildTools + .map(b => { + b.supported = supportedRange && version.satisfies(b.version, supportedRange) ? true : min && version.lt(b.version, min) ? false : 'maybe'; + return b; + }) + .sort((a, b) => { + return preferred && version.eq(a.version, preferred) ? -1 : a.supported && b.supported ? 0 : a.supported ? -1 : b.supported ? 1 : version.compare(a.version, b.version); + })[0]; + + if (buildTools) { + results.buildTools.path = buildTools.path; + results.buildTools.version = buildTools.version; + results.buildTools.supported = buildTools.supported; + if (supportedRange) { + results.buildTools.maxSupported = version.parseMax(supportedRange); + } + results.dx = buildTools.dx; + Object.assign(results.executables, buildTools.executables); + } + } + + return results; +} diff --git a/src/project/legacy/patch/fields.js b/src/project/legacy/patch/fields.js new file mode 100644 index 0000000..ef6311a --- /dev/null +++ b/src/project/legacy/patch/fields.js @@ -0,0 +1,26 @@ +export function setup() { +} + +export function file(args) { + return { + prompt(callback) { + // callback(err, value) + } + }; +} + +export function select(args) { + return { + prompt(callback) { + // callback(err, value) + } + }; +} + +export function text(args) { + return { + prompt(callback) { + // callback(err, value) + } + }; +} diff --git a/src/project/legacy/patch/index.js b/src/project/legacy/patch/index.js new file mode 100644 index 0000000..f715844 --- /dev/null +++ b/src/project/legacy/patch/index.js @@ -0,0 +1,25 @@ +import Module from 'module'; +import path from 'path'; + +/** + * Patches our system info plugins into Titanium SDK's system info library calls. + */ +const lookup = { + fields: { + file: path.resolve(__dirname, 'fields.js'), + parent: '_build.js' + }, + ioslib: { + file: path.resolve(__dirname, 'ios.js'), + parent: '_build.js' + }, + 'node-titanium-sdk/lib/android': { + file: path.resolve(__dirname, 'android.js'), + parent: '_build.js' + } +}; + +const resolveFilename = Module._resolveFilename; +Module._resolveFilename = (request, parent, isMain) => { + return lookup[request] && parent && path.basename(parent.filename) === lookup[request].parent && lookup[request].file || resolveFilename(request, parent, isMain); +}; diff --git a/src/project/legacy/patch/ios.js b/src/project/legacy/patch/ios.js new file mode 100644 index 0000000..83e3ccd --- /dev/null +++ b/src/project/legacy/patch/ios.js @@ -0,0 +1,215 @@ +import tunnel from '../tunnel'; +import * as version from '../../../lib/version'; + +export function detect(opts = {}, callback) { + tunnel.call('/ios/1.x/info') + .then(info => { + const results = { + certs: {}, + devices: info.devices, + iosSDKtoXcode: {}, + issues: [], + provisioning: {}, + selectedXcode: null, + simulators: info.simulators, + teams: Object.entries(info.teams).map(([ id, name ]) => ({ id, name })), + xcode: {} + }; + + processCerts(info, results); + processProvisioning(info, results); + processXcodes(info, results, opts); + + callback(null, results); + }) + .catch(err => { + console.error(err); + callback(); + }); +} + +function processCerts(info, results) { + const keychains = {}; + results.certs = { keychains, wwdr: info.certs.wwdr }; + + for (const type of [ 'developer', 'distribution' ]) { + for (const cert of info.certs[type]) { + if (!keychains[cert.keychain]) { + keychains[cert.keychain] = { + developer: [], + distribution: [] + }; + } + keychains[cert.keychain][type].push(cert); + } + } + + if (!results.certs.wwdr) { + results.issues.push({ + id: 'IOS_NO_WWDR_CERT_FOUND', + type: 'error', + message: 'Apple’s World Wide Developer Relations (WWDR) intermediate certificate is not installed.\nThis will prevent you from building apps for iOS devices or package for distribution.' + }); + } + + if (!Object.keys(keychains).length) { + results.issues.push({ + id: 'IOS_NO_KEYCHAINS_FOUND', + type: 'warning', + message: 'Unable to find any keychains found.' + }); + } + + let validDevCerts = 0; + let validDistCerts = 0; + + for (const keychain of Object.keys(keychains)) { + validDevCerts += (results.certs.keychains[keychain].developer || []).filter(c => !c.invalid).length; + validDistCerts += (results.certs.keychains[keychain].distribution || []).filter(c => !c.invalid).length; + } + + if (!validDevCerts) { + results.issues.push({ + id: 'IOS_NO_VALID_DEV_CERTS_FOUND', + type: 'warning', + message: 'Unable to find any valid iOS developer certificates.\nThis will prevent you from building apps for iOS devices.' + }); + } + + if (!validDistCerts) { + results.issues.push({ + id: 'IOS_NO_VALID_DIST_CERTS_FOUND', + type: 'warning', + message: 'Unable to find any valid iOS production distribution certificates.\nThis will prevent you from packaging apps for distribution.' + }); + } +} + +function processProvisioning(info, results) { + results.provisioning = info.provisioning; + + const valid = { + development: 0, + adhoc: 0, + enterprise: 0, + distribution: 0 + }; + + for (const type of Object.keys(results.provisioning)) { + for (const profile of Object.keys(results.provisioning[type])) { + if (!profile.expired) { + valid[type]++; + } + } + } + + if (!results.provisioning.development.length || !valid.development) { + results.issues.push({ + id: 'IOS_NO_VALID_DEVELOPMENT_PROVISIONING_PROFILES', + type: 'warning', + message: 'Unable to find any valid iOS development provisioning profiles.\nThis will prevent you from building apps for testing on iOS devices.' + }); + } + + if (!results.provisioning.adhoc.length || !valid.adhoc) { + results.issues.push({ + id: 'IOS_NO_VALID_ADHOC_PROVISIONING_PROFILES', + type: 'warning', + message: 'Unable to find any valid iOS adhoc provisioning profiles.\nThis will prevent you from packaging apps for adhoc distribution.' + }); + } + + if (!results.provisioning.distribution.length || !valid.distribution) { + results.issues.push({ + id: 'IOS_NO_VALID_DISTRIBUTION_PROVISIONING_PROFILES', + type: 'warning', + message: 'Unable to find any valid iOS distribution provisioning profiles.\nThis will prevent you from packaging apps for AppStore distribution.' + }); + } +} + +function processXcodes(info, results, opts) { + results.xcode = info.xcode; + + const eulaNotAccepted = []; + const { minIosVersion, supportedVersions } = opts; + const xcodes = Object.entries(info.xcode); + let validXcodes = 0; + let sdkCounter = 0; + + results.iosSDKtoXcode = {}; + + if (xcodes.length) { + for (const [ xcodeId, xcode ] of xcodes) { + if (xcode.default) { + results.selectedXcode = xcode; + } + + xcode.supported = supportedVersions ? version.satisfies(xcode.version, supportedVersions) : true; + if (xcode.supported) { + validXcodes++; + } else { + const min = version.parseMin(supportedVersions); + results.issues.push({ + id: 'IOS_XCODE_TOO_OLD', + type: 'warning', + message: `Xcode ${xcode.version} is too old and is no longer supported.\nThe minimum supported Xcode version is Xcode ${min}.`, + xcodeVer: xcode.version, + minSupportedVer: min + }); + } + + if (!xcode.eulaAccepted) { + eulaNotAccepted.push(xcode); + } + + if (minIosVersion) { + xcode.sdks.ios = xcode.sdks.ios.filter(ver => version.gte(ver, minIosVersion)); + } + + xcode.sdks = xcode.sdks.ios; + + for (const sdk of xcode.sdks) { + if (xcode.default || !results.iosSDKtoXcode[sdk]) { + results.iosSDKtoXcode[sdk] = xcodeId; + } + } + + xcode.sims = Object.values(xcode.simRuntimes).map(r => r.version); + + sdkCounter += xcode.sdks.length; + } + + if (eulaNotAccepted.length) { + results.issues.push({ + id: 'IOS_XCODE_EULA_NOT_ACCEPTED', + type: 'warning', + message: eulaNotAccepted.length === 1 + ? 'Xcode EULA has not been accepted.\nLaunch Xcode and accept the license.' + : `Multiple Xcode versions have not had their EULA accepted:\n${eulaNotAccepted.map(xc => ` ${xc.version} (${xc.xcodeapp})`).join('\n')}\nLaunch each Xcode and accept the license.` + }); + } + + if (supportedVersions && !validXcodes) { + results.issues.push({ + id: 'IOS_NO_SUPPORTED_XCODE_FOUND', + type: 'warning', + message: 'There are no supported Xcode installations found.' + }); + } + + if (!sdkCounter) { + results.issues.push({ + id: 'IOS_NO_IOS_SDKS', + type: 'error', + message: 'There are no iOS SDKs found\nLaunch Xcode and download the mobile support packages.' + }); + } + } else { + results.issues.push({ + id: 'IOS_XCODE_NOT_INSTALLED', + type: 'error', + message: 'No Xcode installations found.\nYou can download it from the App Store or from https://developer.apple.com/xcode/.' + }); + } +} diff --git a/src/project/legacy/ti/cli.js b/src/project/legacy/ti/cli.js new file mode 100644 index 0000000..6d58302 --- /dev/null +++ b/src/project/legacy/ti/cli.js @@ -0,0 +1,445 @@ +// import config from './config'; +// import fs from 'fs'; +// import logger from './logger'; +// import path from 'path'; +// import util from 'util'; +// import version from '../version'; +// import vm from 'vm'; + +// function GracefulShutdown() {} +// util.inherits(GracefulShutdown, Error); + +// export default class CLI { +// argv = { +// colors: true +// }; + +// hooks = { +// erroredFilenames: [], +// errors: {}, +// ids: {}, +// incompatibleFilenames: [], +// loadedFilenames: [], +// post: {}, +// pre: {}, +// scannedPaths: {} +// }; + +// startTime = null; + +// HOOK_PRIORITY_DEFAULT = 1000; + +// constructor(argv) { +// if (!argv.sdkPath) { +// throw new Error('Missing Titanium SDK path (sdkPath) paramater'); +// } +// argv.sdkPath = path.resolve(argv.sdkPath); +// if (!fs.existsSync(argv.sdkPath)) { +// throw new Error(`Specified Titanium SDK does not exist: ${argv.sdkPath}`); +// } + +// Object.assign(this.argv, argv); +// this.argv.$_ = []; + +// this.config = config; +// this.GracefulShutdown = GracefulShutdown; +// this.logger = logger; +// this.sdk = { +// name: path.basename(argv.sdkPath), +// path: argv.sdkPath +// }; + +// const manifest = JSON.parse(fs.readFileSync(path.join(argv.sdkPath, 'manifest.json'))); +// this.env = { +// sdks: { +// [manifest.version]: { +// manifest +// } +// } +// }; + +// this.scanHooks(path.resolve(argv.sdkPath, 'cli', 'hooks')); +// } + +// addAnalyticsEvent() { +// // noop +// } + +// addHook(...args) { +// return this.on(...args); +// } + +// createHook(name, ctx, fn) { +// let dataPayload = {}; + +// if (typeof ctx === 'function') { +// fn = ctx; +// ctx = null; +// } else if (ctx && typeof ctx === 'object' && !fn) { +// dataPayload = ctx; +// ctx = null; +// } + +// return (...args) => { +// const callback = args.length && typeof args[args.length - 1] === 'function' ? args.pop() : null; +// let data = Object.assign(dataPayload, { +// type: name, +// args, +// callback, +// fn: fn, +// ctx: ctx +// }); +// const pres = this.hooks.pre[name] || []; +// const posts = this.hooks.post[name] || []; + +// Promise.resolve() +// .then(async () => { +// // call all pre filters +// await pres +// .reduce((promise, pre) => { +// return promise.then(() => new Promise((resolve, reject) => { +// if (pre.length >= 2) { +// pre.call(ctx, data, (err, newData) => { +// if (err) { +// return reject(err); +// } else if (newData) { +// data = newData; +// } +// resolve(); +// }); +// } else { +// pre.call(ctx, data); +// resolve(); +// } +// })); +// }, Promise.resolve()); + +// if (data.fn) { +// data.result = await new Promise((resolve, reject) => { +// // call the function +// data.args.push((err, data) => { +// err ? reject(err) : resolve(data); +// }); +// data.fn.apply(data.ctx, data.args); +// }); +// } + +// // call all post filters +// await posts +// .reduce((promise, post) => promise.then(() => new Promise((resolve, reject) => { +// if (post.length >= 2) { +// post.call(ctx, data, (err, newData) => { +// if (err) { +// return reject(err); +// } else if (newData && typeof newData === 'object' && newData.type) { +// data = newData; +// } +// resolve(); +// }); +// } else { +// post.call(ctx, data); +// resolve(); +// } +// })), Promise.resolve()); + +// const { callback } = data; +// if (typeof callback === 'function') { +// data.callback = null; +// if (callback.length > 1) { +// callback.call(data, null, data.result); +// } else { +// // this is because the original hook system was bad and didn't handle +// // errors correctly :( +// callback.call(data, data.result); +// } +// } +// }) +// .catch(err => { +// // this is the primary error handler +// if (typeof data.callback === 'function') { +// data.callback(err); +// } else { +// console.log('Hook completion callback threw unhandled error:'); +// console.log(err.stack); +// process.exit(1); +// } +// }); +// }; +// } + +// emit(hookNames, data, callback) { +// if (typeof data === 'function') { +// callback = data; +// data = null; +// } + +// // make sure hookNames is an array +// if (!Array.isArray(hookNames)) { +// hookNames = [ hookNames ]; +// } + +// // create each hook and immediately fire them +// const promise = hookNames +// .reduce((promise, name) => promise.then(() => new Promise((resolve, reject) => { +// const hook = this.createHook(name, data); +// console.error(`Emitting ${name}`); +// hook((err, result) => { +// err ? reject(err) : resolve(result); +// }); +// })), Promise.resolve()); + +// if (typeof callback !== 'function') { +// return promise; +// } + +// promise +// .then(result => callback(null, result)) +// .catch(callback); + +// return this; +// } + +// fireHook(...args) { +// return this.emit(...args); +// } + +// on(name, callback) { +// let priority = this.HOOK_PRIORITY_DEFAULT; +// let i; + +// if (typeof callback === 'function') { +// callback = { post: callback }; +// } else if (callback && typeof callback === 'object') { +// priority = parseInt(callback.priority) || priority; +// } + +// if (callback.pre) { +// const h = this.hooks.pre[name] || (this.hooks.pre[name] = []); +// callback.pre.priority = priority; +// for (i = 0; i < h.length && priority >= h[i].priority; i++) {} +// h.splice(i, 0, callback.pre); +// } + +// if (callback.post) { +// const h = this.hooks.post[name] || (this.hooks.post[name] = []); +// callback.post.priority = priority; +// for (i = 0; i < h.length && priority >= h[i].priority; i++) {} +// h.splice(i, 0, callback.post); +// } + +// return this; +// } + +// scanHooks(dir) { +// if (this.hooks.scannedPaths[dir]) { +// return; +// } + +// try { +// // eslint-disable-next-line security/detect-non-literal-require +// const appc = require(path.join(this.argv.sdkPath, 'node_modules', 'node-appc')); +// const jsfile = /\.js$/; +// const ignore = /^[._]/; +// const files = fs.statSync(dir).isDirectory() ? fs.readdirSync(dir).map(n => path.join(dir, n)) : [ dir ]; + +// for (const file of files) { +// try { +// if (fs.statSync(file).isFile() && jsfile.test(file) && !ignore.test(path.basename(path.dirname(file)))) { +// // test the file for syntax errors +// vm.runInThisContext(`(function (exports, require, module, __filename, __dirname){${fs.readFileSync(file).toString()}\n});`, file, 0, false); + +// // eslint-disable-next-line security/detect-non-literal-require +// var mod = require(file); +// if (mod.id) { +// if (!Array.isArray(this.hooks.ids[mod.id])) { +// this.hooks.ids[mod.id] = []; +// } +// this.hooks.ids[mod.id].push({ +// file: file, +// version: mod.version || null +// }); + +// // don't load duplicate ids +// if (this.hooks.ids[mod.id].length > 1) { +// continue; +// } +// } + +// if (!this.version || !mod.cliVersion || version.satisfies(this.version, mod.cliVersion)) { +// mod.init && mod.init(this.logger, this.config, this, appc); +// this.hooks.loadedFilenames.push(file); +// console.error(`Loaded CLI hook: ${file}`); +// } else { +// this.hooks.incompatibleFilenames.push(file); +// } +// } +// } catch (ex) { +// this.hooks.erroredFilenames.push(file); +// this.hooks.errors[file] = ex; +// } +// } +// } catch (e) { +// // squelch +// } +// } + +// async go(command) { +// // load the command +// // try { +// // this.command = require(path.resolve(__dirname, '..', 'commands', `${argv.command}.js`)); +// // } catch (e) { +// // throw new Error(`Invalid command "${argv.command}"`); +// // } + +// // await cmd(cli, msg); +// // await this.validate(); +// // +// // await this.executeCommand(); +// } + +// async validate({ options }) { +// const { argv } = this; +// const orderedOptionNames = Object.keys(options).sort((a, b) => { +// if (options[a].order && options[b].order) { +// return options[a].order - options[b].order; +// } +// return options[a].order ? -1 : options[b].order ? 1 : 0; +// }); + +// for (const [ name, option ] of Object.entries(options)) { +// if (argv[name] === undefined && option.default !== undefined) { +// argv[name] = option.default; +// } +// } + +// const issues = []; + +// for (let name of orderedOptionNames) { +// const opt = options[name]; +// opt.name = name; + +// if (opt.validated) { +// continue; +// } + +// if (argv[name] === undefined) { +// // check if the option is required +// if (opt.required || (opt.conf && opt.conf.required)) { +// // ok, we have a required option, but it's possible that this option +// // replaces some legacy option in which case we need to check if the +// // legacy options were defined + +// if (typeof opt.verifyIfRequired === 'function') { +// await new Promise(resolve => opt.verifyIfRequired(stillRequired => { +// if (stillRequired) { +// issues.push({ opt }); +// } +// resolve(); +// })); +// continue; +// } + +// issues.push({ opt }); +// } + +// } else if (Array.isArray(opt.values) && !opt.skipValueCheck && opt.values.indexOf(argv[name]) === -1) { +// issues.push({ opt }); + +// } else if (!opt.validated && typeof opt.validate === 'function') { +// try { +// await new Promise(resolve => opt.validate(argv[name], (err, value) => { +// if (err) { +// opt._err = err; +// issues.push({ +// opt, +// err, +// value: argv[name] +// }); +// } else { +// argv[name] = value; +// opt.validated = true; +// if (opt.callback) { +// const val = opt.callback(argv[name] || ''); +// if (val !== undefined) { +// argv[name] = val; +// } +// delete opt.callback; +// } +// } +// resolve(); +// })); +// } catch (ex) { +// if (ex instanceof GracefulShutdown) { +// // simply return and cb() is never called which effectively cause the cli +// // to gracefully exit +// continue; +// } +// throw ex; +// } + +// } else if (opt.callback) { +// opt.validated = true; +// const val = opt.callback(argv[name] || ''); +// if (val !== undefined) { +// argv[name] = val; +// } +// delete opt.callback; +// } +// } + +// if (issues.length) { +// // we are going to throw an error, but we first need to build up the info for each missing +// // or invalid parameter +// const err = new Error('Bad request'); +// err.status = 400; +// err.issues = await Promise.all(issues.map(async ({ opt, err, value }) => { +// const issue = { +// name: opt.name +// }; + +// if (err) { +// issue.error = err; +// } + +// if (value) { +// issue.value = value; +// } + +// if (typeof opt.prompt === 'function') { +// const field = await new Promise(opt.prompt); +// issue.field = { +// type: field.constructor.name.toLowerCase(), +// message: field.promptLabel +// }; +// } else if (Array.isArray(opt.values)) { +// issue.field = { +// type: 'select', +// message: `Please select a ${opt.name}`, +// options: opt.values +// // suggest +// // complete +// // numbered +// }; +// } else { +// issue.field = { +// type: opt.password ? 'password' : 'text', +// message: `Please enter a valid ${opt.name}` +// }; +// } + +// return issue; +// })); +// throw err; +// } + +// // detect missing arguments + +// // callCommandValidate + +// // callOptionCallbacks +// } + +// async executeCommand() { +// // +// } +// } diff --git a/src/project/legacy/ti/config.js b/src/project/legacy/ti/config.js new file mode 100644 index 0000000..4afd23d --- /dev/null +++ b/src/project/legacy/ti/config.js @@ -0,0 +1,126 @@ +// import fs from 'fs'; +// import path from 'path'; + +// // default config +// export const config = { +// app: { +// workspace: '' +// }, + +// cli: { +// colors: true, +// completion: false, +// logLevel: 'trace', +// prompt: true, +// progressBars: true, +// failOnWrongSDK: false, +// httpProxyServer: '', +// rejectUnauthorized: true, +// width: 100, +// ignoreDirs: '^(\\.svn|_svn|\\.git|\\.hg|\\.?[Cc][Vv][Ss]|\\.bzr|\\$RECYCLE\\.BIN)$', +// ignoreFiles: '^(\\.gitignore|\\.npmignore|\\.cvsignore|\\.DS_Store|\\._.*|[Tt]humbs.db|\\.vspscc|\\.vssscc|\\.sublime-project|\\.sublime-workspace|\\.project|\\.tmproj)$' +// }, + +// // additional search paths for commands and hooks +// paths: { +// commands: [], +// hooks: [], +// modules: [], +// plugins: [], +// sdks: [], +// templates: [] +// }, + +// user: {} +// }; + +// export default config; + +// const configFile = path.join(process.env[process.platform === 'win32' ? 'USERPROFILE' : 'HOME'], '.titanium', 'config.json'); +// if (fs.existsSync(configFile)) { +// try { +// (function mix(src, dest) { +// for (let [ key, value ] of Object.entries(src)) { +// if (value && typeof value === 'object' && !Array.isArray(value)) { +// if (!dest[key] || typeof dest[key] !== 'object') { +// dest[key] = {}; +// } +// mix(value, dest[key]); +// } else if (typeof value === 'string') { +// value = value === undefined ? '' : String(value).trim(); +// if (value === 'null') { +// value = null; +// } else if (value === 'true') { +// value = true; +// } else if (value === 'false') { +// value = false; +// } +// dest[key] = value; +// } +// } +// }(JSON.parse(fs.readFileSync(configFile)), config)); +// } catch (e) { +// console.error(`Failed to parse Titanium CLI config: ${e.message}`); +// } +// } + +// Object.defineProperties(config, { +// get: { +// value: function (key, defaultValue) { +// if (!key) { +// return this; +// } + +// const parts = key.split('.'); +// const q = parts.pop(); +// let obj = this; +// let i = 0; +// let p = parts.length && parts[i++]; + +// if (p) { +// do { +// if (obj.hasOwnProperty(p)) { +// obj = obj[p]; +// } else { +// return defaultValue; +// } +// } while (obj && (p = parts[i++])); +// } + +// return obj && q && obj.hasOwnProperty(q) ? obj[q] : defaultValue; +// } +// }, +// set: { +// value: function (key, value) { +// const parts = key.split('.'); +// const q = parts.pop(); +// let obj = this; +// let i = 0; +// let p = parts.length && parts[i++]; + +// if (p) { +// do { +// obj = obj.hasOwnProperty(p) ? obj[p] : (obj[p] = {}); +// } while (obj && (p = parts[i++])); +// } + +// // if not an array, try to cast to null, true, false, int or leave as string +// if (!Array.isArray(value)) { +// value = value === undefined ? '' : String(value).trim(); +// if (value === 'null') { +// value = null; +// } else if (value === 'true') { +// value = true; +// } else if (value === 'false') { +// value = false; +// } else if (String(~~value) === value) { +// value = ~~value; +// } +// } + +// if (obj && q) { +// obj[q] = value; +// } +// } +// } +// }); diff --git a/src/project/legacy/ti/logger.js b/src/project/legacy/ti/logger.js new file mode 100644 index 0000000..e4a0b47 --- /dev/null +++ b/src/project/legacy/ti/logger.js @@ -0,0 +1,30 @@ +// const logger = { +// debug: console.log, +// error: console.log, +// info: console.log, +// log: console.log, +// trace: console.log, +// warn: console.log, + +// levels: { +// trace: {}, +// debug: {}, +// info: {}, +// warn: {}, +// error: {} +// }, + +// banner() { +// // noop +// }, + +// getLevels() { +// return Object.keys(this.levels); +// }, + +// setLevel() { +// // noop +// } +// }; + +// export default logger; diff --git a/src/project/legacy/tunnel.js b/src/project/legacy/tunnel.js new file mode 100644 index 0000000..b2a027c --- /dev/null +++ b/src/project/legacy/tunnel.js @@ -0,0 +1,71 @@ +import uuid from 'uuid'; + +/** + * A simple state manager for sending and receiving requests to the parent process. + * + * Note that this tunnel implementation does NOT support chunked/streamed responses. + */ +class Tunnel { + /** + * A map of all pending request ids and their associated promise callbacks. + * @type {Object} + */ + pending = {}; + + /** + * Wires up the IPC message handler. + * + * @access public + */ + constructor() { + process.on('message', data => { + const { id } = data; + const req = id && this.pending[id]; + if (!req) { + return; + } + + const { resolve, reject } = req; + + switch (data.type) { + case 'response': + delete this.pending[id]; + resolve(data.response); + return; + + case 'error': + delete this.pending[id]; + reject(new Error(data.error)); + return; + } + }); + } + + /** + * Makes a request to the parent process. + * + * @param {String} path - The path to request. + * @param {Object} [data] - An optional data payload to send with the request. + * @returns {Promise} + * @access public + */ + call(path, data) { + return new Promise((resolve, reject) => { + const id = uuid.v4(); + this.pending[id] = { resolve, reject }; + process.send({ + id, + type: 'request', + path, + data + }); + }); + } +} + +/** + * The global tunnel instance. It is global because `process` is global and there's no single place + * of instantiation. + * @type {Tunnel} + */ +export default new Tunnel(); diff --git a/src/project/project-service.js b/src/project/project-service.js index 21c5319..afc8656 100644 --- a/src/project/project-service.js +++ b/src/project/project-service.js @@ -4,6 +4,7 @@ import TemplateService from './templates-service'; import { AppcdError, codes } from 'appcd-response'; import { Project } from 'titaniumlib'; +import { spawn } from 'appcd-subprocess'; const { log } = appcd.logger('project-service'); @@ -21,42 +22,29 @@ export default class ProjectService extends Dispatcher { * @access public */ async activate(cfg) { - const handler = action => { - return async ctx => { - // get project dir - let { cwd, projectDir } = ctx.request.data; - - if (projectDir !== undefined && typeof projectDir !== 'string') { - throw new AppcdError(codes.BAD_REQUEST, 'Missing project directory'); - } - - if (projectDir === undefined || !path.isAbsolute(projectDir)) { - if (!cwd || typeof cwd !== 'string') { - throw new AppcdError(codes.BAD_REQUEST, 'Current working directory required when project directory is relative'); - } - projectDir = path.resolve(cwd, projectDir || '.'); - } - - return await new Project({ - path: projectDir - })[action](ctx.request.data); - }; - }; - - this.register('/', handler('tiapp')); + this.register('/', ctx => { + return 'tiapp coming soon!'; + }); - this.register('/build', handler('build')); + this.register('/build', ctx => this.exec('build', ctx)); - this.register('/clean', handler('clean')); + this.register('/clean', ctx => this.exec('clean', ctx)); this.register('/new', async ctx => { - return await new Project({ - templates: (await this.call('/templates')).response - }).create(ctx.request.data); + try { + return await new Project({ + templates: (await this.call('/templates')).response + }).create(ctx.request.data); + } catch (err) { + if (err.prompt) { + err.telemetry = false; + } + throw err; + } }); // TODO: in the future, run will call project.build and we'll "run" it ourselves - this.register('/run', handler('run')); + this.register('/run', ctx => this.exec('run', ctx)); await this.templateSvc.activate(cfg); this.register('/templates', this.templateSvc); @@ -71,4 +59,94 @@ export default class ProjectService extends Dispatcher { async deactivate() { await this.templateSvc.deactivate(); } + + /** + * Executes a Titanium SDK "build" or "clean" command. Commands are the old Titanium CLI v5 + * format and must be run via the bootstrap. + * + * @param {String} command - The name of the command to run. + * @param {Object} ctx - The dispatcher context. + * @returns {Promise} + * @access private + */ + async exec(command, ctx) { + let { cwd, projectDir } = ctx.request.data; + if (projectDir !== undefined && typeof projectDir !== 'string') { + throw new AppcdError(codes.BAD_REQUEST, 'Missing project directory'); + } + + if (projectDir === undefined || !path.isAbsolute(projectDir)) { + if (!cwd || typeof cwd !== 'string') { + throw new AppcdError(codes.BAD_REQUEST, 'Current working directory required when project directory is relative'); + } + projectDir = path.resolve(cwd, projectDir || '.'); + } + + const project = new Project({ + path: projectDir + }); + + // const { sdk } = project.tiapp.get('sdk'); + const sdk = '9.0.2.GA'; + + const sdkInfo = (await appcd.call('/sdk/find', { data: { name: sdk } })).response; + + const data = { + ...ctx.request.data, + command, + projectDir, + sdk, + sdkPath: sdkInfo.path + }; + + log(data); + + // spawn the bootstrap with the cmd and args + const { child } = spawn({ + command: process.execPath, + args: [ path.resolve(__dirname, 'legacy/bootstrap.js') ], + options: { + env: Object.assign({ FORCE_COLOR: 1 }, process.env), + stdio: [ 'pipe', 'pipe', 'pipe', 'ipc' ] + } + }); + + child.stdout.on('data', data => ctx.response.write(data.toString())); + child.stderr.on('data', data => ctx.response.write(data.toString())); + child.on('close', () => ctx.response.end()); + child.on('message', async msg => { + if (msg.type === 'error') { + ctx.response.end(msg); + } else if (msg.type === 'request') { + const { id, path, data } = msg; + if (id && path) { + let response; + try { + response = await appcd.call(path, data); + } catch (err) { + child.send({ + id, + type: 'error', + error: err + }); + throw err; + } + + try { + child.send({ + id, + type: 'response', + response + }); + } catch (err) { + console.error(err); + } + } + } else { + console.log('GOT IPC MESSAGE!'); + console.log(msg); + } + }); + child.send(data); + } } diff --git a/src/sdk/sdk-service.js b/src/sdk/sdk-service.js index fd80693..81d1cfd 100644 --- a/src/sdk/sdk-service.js +++ b/src/sdk/sdk-service.js @@ -1,4 +1,4 @@ -import Dispatcher from 'appcd-dispatcher'; +import Dispatcher, { DispatcherError } from 'appcd-dispatcher'; import SDKListService from './sdk-list-service'; import { AppcdError, codes } from 'appcd-response'; @@ -26,6 +26,7 @@ export default class SDKService extends Dispatcher { ctx.path = '/list'; return next(); }) + .register('/find/:name?', ctx => this.find(ctx)) .register('/list', this.installed) .register('/branches', () => sdk.getBranches()) .register('/builds/:branch?', ctx => sdk.getBuilds(ctx.request.params.branch)) @@ -45,6 +46,23 @@ export default class SDKService extends Dispatcher { await this.installed.deactivate(); } + /** + * Scans installed Titanium SDKs to find an SDK by name. + * + * @param {Context} ctx - A request context. + * @returns {Object} + * @access private + */ + find(ctx) { + const { data, params } = ctx.request; + const name = data.name || params.name; + const result = this.installed.data.find(s => s.name === name); + if (result) { + return result; + } + throw new DispatcherError(`Titanium SDK ${name} not found`); + } + /** * Install SDK service handler. * Note: This method does not return a promise because we want the response to be sent @@ -67,12 +85,12 @@ export default class SDKService extends Dispatcher { overwrite: data.overwrite, uri: data.uri || params.name }).then(tisdk => { - response.write({ fin: true, message: `Titanium SDK ${tisdk.name} installed` }); + response.write({ fin: true, message: `\nTitanium SDK ${tisdk.name} installed` }); response.end(); }).catch(err => { try { if (err.code === 'ENOTFOUND') { - response.write(new AppcdError(codes.NOT_FOUND, err.message)); + response.write(new DispatcherError(err.message)); } else { response.write(new AppcdError(err)); } @@ -101,7 +119,7 @@ export default class SDKService extends Dispatcher { try { return await sdk.uninstall(uri); } catch (err) { - throw err.code === 'ENOTFOUND' ? new AppcdError(codes.NOT_FOUND, err) : err; + throw err.code === 'ENOTFOUND' ? new DispatcherError(err) : err; } } } From ba47c83af21ba50e79723cee0b4d4c731984d44a Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Tue, 14 Jul 2020 18:05:33 -0500 Subject: [PATCH 09/39] refactor: Moved OS detection code to shared library. doc: Added legacy CLI readme. feat(cli): Added --platform alias to clean command. style(cli:sdk:install): Improved progress bar rendering. fix(project): Properly pass in the appcd config. fix(prompt): Handle line returns when printing response. fix(prompt): Forward request headers when trying call() during prompt loop. fix(project:legacy:bootstrap): Initialize colors and tunnel. fix(project:legacy:bootstrap): Move IPC tunnel logic to its own file. feat(project:legacy:cli): Bare bones legacy CLI runner. feat(project:legacy:tunnel): Added debug logging and telemetry support. fix(project): Improved IPC tunnel communication protocol. feat(sdk): Added find endpoint. chore: Updated npm dependencies. --- README.md | 18 + package.json | 7 +- src/cli/cli-service.js | 10 +- src/cli/commands/build.js | 1 - src/cli/commands/clean.js | 6 +- src/cli/info/os.js | 74 +-- src/cli/sdk/install.js | 27 +- src/index.js | 2 +- src/lib/os.js | 77 +++ src/lib/prompt.js | 15 +- src/project/legacy/README.md | 55 ++ src/project/legacy/bootstrap.js | 27 +- src/project/legacy/ti/cli.js | 1087 ++++++++++++++++++------------- src/project/legacy/ti/config.js | 126 ---- src/project/legacy/tunnel.js | 59 +- src/project/project-service.js | 105 +-- src/sdk/sdk-list-service.js | 33 +- src/sdk/sdk-service.js | 4 +- 18 files changed, 990 insertions(+), 743 deletions(-) create mode 100644 src/lib/os.js create mode 100644 src/project/legacy/README.md delete mode 100644 src/project/legacy/ti/config.js diff --git a/README.md b/README.md index 256baa3..993da44 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,24 @@ $ appcd exec /titanium/latest/sdk/list/releases } ``` +### `/sdk/find/:name?` + +Returns information about the specified installed SDK. + +#### CLI Usage + +```sh +$ appcd exec /titanium/latest/sdk/find +``` + +```sh +$ appcd exec /titanium/latest/sdk/find/latest +``` + +```sh +$ appcd exec /titanium/latest/sdk/find/9.0.0.GA +``` + ### `/sdk/list/branches` Returns a list of continuous integration branches and which one is the default. diff --git a/package.json b/package.json index ec1c9cf..f0ab082 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,14 @@ "test": "gulp test" }, "dependencies": { + "appcd-fs": "^2.0.0", + "appcd-path": "^2.0.1", + "appcd-util": "^3.0.0", "chalk": "^4.1.0", "cli-kit": "^1.2.4", + "colors": "^1.4.0", "dateformat": "^3.0.3", - "enquirer": "^2.3.5", + "enquirer": "^2.3.6", "figures": "^3.2.0", "filesize": "^6.1.0", "fs-extra": "^9.0.1", @@ -30,6 +34,7 @@ "pluralize": "^8.0.0", "progress": "^2.0.3", "prompts": "^2.3.2", + "semver": "^7.3.2", "sort-object-keys": "^1.1.3", "source-map-support": "^0.5.19", "titaniumlib": "^3.0.0", diff --git a/src/cli/cli-service.js b/src/cli/cli-service.js index 2c8aa6b..73bda61 100644 --- a/src/cli/cli-service.js +++ b/src/cli/cli-service.js @@ -7,6 +7,7 @@ import path from 'path'; import { get } from 'appcd-util'; import { parseVersion } from '../lib/util'; +const { log } = appcd.logger('cli-service'); const { highlight } = appcd.logger.styles; /** @@ -43,9 +44,12 @@ export default class CLIService extends Dispatcher { this.server = await cli.listen({ port }); - this.register('/', () => ({ - url: `ws://127.0.0.1:${port}` - })); + this.register('/', () => { + log(`Returning CLI server URL: ${highlight(`ws://127.0.0.1:${port}`)}`); + return { + url: `ws://127.0.0.1:${port}` + }; + }); this.register('/schema', ({ headers }) => cli.schema({ data: { diff --git a/src/cli/commands/build.js b/src/cli/commands/build.js index dfcf145..335add1 100644 --- a/src/cli/commands/build.js +++ b/src/cli/commands/build.js @@ -11,7 +11,6 @@ export default { await promptLoop({ ctx, data: { - cwd: ctx.data.cwd, force: ctx.argv.force, platform: ctx.argv.platform, projectDir: ctx.argv.projectDir diff --git a/src/cli/commands/clean.js b/src/cli/commands/clean.js index 09ca8a8..934658c 100644 --- a/src/cli/commands/clean.js +++ b/src/cli/commands/clean.js @@ -4,13 +4,15 @@ export default { desc: 'Remove previous build directories', options: { '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory', - '-p, --platforms [names]': 'A comma separated list of platforms; defaults to all platforms' + '-p, --platforms [names]': { + aliases: [ '--platform' ], + desc: 'A comma separated list of platforms; defaults to all platforms' + } }, async action(ctx) { await promptLoop({ ctx, data: { - cwd: ctx.data.cwd, platforms: ctx.argv.platforms, projectDir: ctx.argv.projectDir }, diff --git a/src/cli/info/os.js b/src/cli/info/os.js index 0254409..8f5fcab 100644 --- a/src/cli/info/os.js +++ b/src/cli/info/os.js @@ -1,76 +1,8 @@ -import fs from 'fs'; -import os from 'os'; - -import { arch } from 'appcd-util'; -import { isFile } from 'appcd-fs'; -import { run } from 'appcd-subprocess'; +import getOSInfo from '../../lib/os'; export default { - async fetch() { - const { platform } = process; - const info = { - platform, - name: 'Unknown', - version: '', - arch: arch(), - numcpus: os.cpus().length, - memory: os.totalmem() - }; - - switch (platform) { - case 'darwin': - { - const { stdout } = await run('sw_vers'); - let m = stdout.match(/ProductName:\s+(.+)/i); - if (m) { - info.name = m[1]; - } - m = stdout.match(/ProductVersion:\s+(.+)/i); - if (m) { - info.version = m[1]; - } - } - break; - - case 'linux': - info.name = 'GNU/Linux'; - - if (isFile('/etc/lsb-release')) { - const contents = fs.readFileSync('/etc/lsb-release', 'utf8'); - let m = contents.match(/DISTRIB_DESCRIPTION=(.+)/i); - if (m) { - info.name = m[1].replace(/"/g, ''); - } - m = contents.match(/DISTRIB_RELEASE=(.+)/i); - if (m) { - info.version = m[1].replace(/"/g, ''); - } - } else if (isFile('/etc/system-release')) { - const parts = fs.readFileSync('/etc/system-release', 'utf8').split(' '); - if (parts[0]) { - info.name = parts[0]; - } - if (parts[2]) { - info.version = parts[2]; - } - } - break; - - case 'win32': - { - const { stdout } = await run('wmic', [ 'os', 'get', 'Caption,Version' ]); - const s = stdout.split('\n')[1].split(/ {2,}/); - if (s.length > 0) { - info.name = s[0].trim() || 'Windows'; - } - if (s.length > 1) { - info.version = s[1].trim() || ''; - } - } - break; - } - - return info; + fetch() { + return getOSInfo(); }, render(console, info) { diff --git a/src/cli/sdk/install.js b/src/cli/sdk/install.js index 631e1a6..a12af38 100644 --- a/src/cli/sdk/install.js +++ b/src/cli/sdk/install.js @@ -4,7 +4,7 @@ import { ansi } from 'cli-kit'; import { arrowRight, bullet, tick } from 'figures'; const { log } = appcd.logger('sdk:install'); -const { cyan, gray, green, highlight } = appcd.logger.styles; +const { alert, cyan, gray, green, highlight } = appcd.logger.styles; export default { async action({ argv, console, terminal }) { @@ -77,18 +77,25 @@ export default { break; case 'task-end': + default: { - terminal.stderr.cursorTo(0); - terminal.stderr.clearLine(); - const name = tasks[evt.task - 1] || null; - log(`Finished task: ${highlight(name)}`); - bar = null; - console.log(` ${green(tick)} ${name}`); + if (bar || evt.type === 'task-end') { + terminal.stderr.cursorTo(0); + terminal.stderr.clearLine(); + bar = null; + } + + if (evt.type === 'task-end') { + const name = tasks[evt.task - 1] || null; + log(`Finished task: ${highlight(name)}`); + console.log(` ${green(tick)} ${name}`); + } else if (evt instanceof Error) { + console.error(`\n${alert(`Error: ${evt.message}`)}`); + } else { + console.log(`\n${evt.message || evt}`); + } break; } - - default: - console.log(evt.message || evt); } } catch (e) { reject(e); diff --git a/src/index.js b/src/index.js index 724b2a2..69c562b 100644 --- a/src/index.js +++ b/src/index.js @@ -57,7 +57,7 @@ export async function activate(cfg) { await moduleSvc.activate(cfg); appcd.register([ '/module', '/modules' ], moduleSvc); - await projectSvc.activate(); + await projectSvc.activate(cfg); appcd.register('/project', projectSvc); await sdkSvc.activate(cfg); diff --git a/src/lib/os.js b/src/lib/os.js new file mode 100644 index 0000000..644b754 --- /dev/null +++ b/src/lib/os.js @@ -0,0 +1,77 @@ +import fs from 'fs'; +import os from 'os'; + +import { arch } from 'appcd-util'; +import { isFile } from 'appcd-fs'; +import { spawnSync } from 'child_process'; + +/** + * Detects operating system information. + * + * @returns {Object} + */ +export default function getOSInfo() { + const info = { + platform: process.platform, + name: 'Unknown', + version: '', + arch: arch(), + numcpus: os.cpus().length, + memory: os.totalmem() + }; + + switch (process.platform) { + case 'darwin': + { + const stdout = spawnSync('sw_vers').stdout.toString(); + let m = stdout.match(/ProductName:\s+(.+)/i); + if (m) { + info.name = m[1]; + } + m = stdout.match(/ProductVersion:\s+(.+)/i); + if (m) { + info.version = m[1]; + } + } + break; + + case 'linux': + info.name = 'GNU/Linux'; + + if (isFile('/etc/lsb-release')) { + const contents = fs.readFileSync('/etc/lsb-release', 'utf8'); + let m = contents.match(/DISTRIB_DESCRIPTION=(.+)/i); + if (m) { + info.name = m[1].replace(/"/g, ''); + } + m = contents.match(/DISTRIB_RELEASE=(.+)/i); + if (m) { + info.version = m[1].replace(/"/g, ''); + } + } else if (isFile('/etc/system-release')) { + const parts = fs.readFileSync('/etc/system-release', 'utf8').split(' '); + if (parts[0]) { + info.name = parts[0]; + } + if (parts[2]) { + info.version = parts[2]; + } + } + break; + + case 'win32': + { + const stdout = spawnSync('wmic', [ 'os', 'get', 'Caption,Version' ]).stdout.toString(); + const s = stdout.split('\n')[1].split(/ {2,}/); + if (s.length > 0) { + info.name = s[0].trim() || 'Windows'; + } + if (s.length > 1) { + info.version = s[1].trim() || ''; + } + } + break; + } + + return info; +} diff --git a/src/lib/prompt.js b/src/lib/prompt.js index d367655..dc11a8a 100644 --- a/src/lib/prompt.js +++ b/src/lib/prompt.js @@ -1,8 +1,10 @@ +import { DispatcherContext } from 'appcd-dispatcher'; +import { format } from 'util'; import { prompt as enquire } from 'enquirer'; import { Readable } from 'stream'; const { log } = appcd.logger('prompt'); -const { highlight } = appcd.logger.styles; +const { alert, highlight } = appcd.logger.styles; /** * Prompts for a value with unified settings and improved style consistency. @@ -63,12 +65,19 @@ export async function promptLoop({ ctx, data, footer, header, ns, path, print }) const logger = appcd.logger(ns); if (print === undefined) { - print = console.log; + print = (msg, ...args) => { + if (msg && typeof msg === 'object' && msg.type === 'error') { + console.error(alert(`Error: ${msg.message}`)); + } else { + terminal.stdout.write(format(msg, ...args)); + } + }; } while (true) { try { - const { response } = await appcd.call(path, { data }); + // ctx.data contains `cwd`, `env`, and `userAgent` via cli-kit + const { response } = await appcd.call(path, new DispatcherContext({ headers: ctx.data, request: { data } })); if (response instanceof Readable) { await new Promise((resolve, reject) => { response.on('data', print); diff --git a/src/project/legacy/README.md b/src/project/legacy/README.md new file mode 100644 index 0000000..a9eda5b --- /dev/null +++ b/src/project/legacy/README.md @@ -0,0 +1,55 @@ +# Legacy Titanium CLI + +The Titanium SDK contains commands such as `build` and `clean`. These commands are tightly coupled +with the Titanium CLI v5 and older. In order to be able to execute these commands, we need to shim +the bare minimum APIs and features of the Titanium CLI v5. + +## Deprecations + +While this legacy CLI attempts to maintain backwards compatibility, the following APIs and +features are no longer supported. + +### `cli.version` + +The Titanium CLI version is set to `5.999.0` as we don't want to break compatibility with Titanium +CLI plugins, but we also want to signify that this is not the real Titanium CLI v5. + +### CLI argument parser + +The legacy CLI no longer parses raw CLI arguments. It expects the `argv` values to be passed in as +a JSON object. + +### i18n - Internationalization + +Locale is no longer detected and the locale is forced to `en-US`. We have very little translated +strings that users are not going to notice. + +### Selected Titanium SDK and forking correct Titanium SDK + +The entire selected Titanium SDK system of the Titanium CLI has been removed. The `` +in the `tiapp.xml` is the source of truth. + +Because this legacy CLI will only ever be invoked with a valid Titanium SDK, various APIs are no +longer needed. See `cli.argv` below. + +### `cli.argv._`, `cli.argv.$`, `cli.argv.$_`, `cli.argv.$0` + +`cli.argv` contained the raw CLI arguments and process name as well as the parsed arguments. While +the `cli.argv` will continue to contain the parsed arguments (passed in as a JSON object), the +raw arguments are no longer supported. These properties were intended to be internal only. + +Note that node-titanium-sdk's `validateCorrectSDK()` does reference these variables when preparing +to fork using the correct `--sdk` from the app's `tiapp.xml`, however it's a non-issue being that +the correct SDK version will always be set. + +### `cli.on('cli:command-not-found')` + +This event was emitted when the specified command was not found so that the Titanium CLI could +check if there were any Titanium SDKs installed and if you happened to misspell the command. + +Since this legacy CLI will only ever be called with the either the `build` or `clean` command, +it is impossible for the command to not exist. + +### Built-in Titanium CLI hook plugins + +This legacy CLI does not contain any built-in CLI hook plugins such as `hooks/tisdk3fixes.js`. diff --git a/src/project/legacy/bootstrap.js b/src/project/legacy/bootstrap.js index f7e27c1..28f60bd 100644 --- a/src/project/legacy/bootstrap.js +++ b/src/project/legacy/bootstrap.js @@ -8,8 +8,9 @@ if (!process.connected) { process.exit(2); } -// import CLI from './ti/cli'; +import 'colors'; import './patch'; +import './tunnel'; process.title = 'titanium-legacy-bootstrap'; @@ -28,26 +29,4 @@ process.exit = code => Promise process .on('uncaughtException', err => console.error('Caught unhandled exception:', err)) - .on('unhandledRejection', (reason, p) => console.error('Caught unhandled rejection at: Promise ', p, reason)) - .once('message', onMessage); - -async function onMessage(msg) { - try { - // const cli = new CLI(msg); - // await cli.go(msg.command); - console.log('Hi from the bootstrap!'); - - // the command is complete, but the IPC channel is still open, so we simply disconnect it and - // this process should exit whenever the command finishes - process.disconnect(); - } catch (err) { - process.send({ - ...err, - message: err.message || err, - stack: err.stack, - status: err.status || 500, - type: 'error' - }); - process.exit(1); - } -} + .on('unhandledRejection', (reason, p) => console.error('Caught unhandled rejection at: Promise ', p, reason)); diff --git a/src/project/legacy/ti/cli.js b/src/project/legacy/ti/cli.js index 6d58302..3886460 100644 --- a/src/project/legacy/ti/cli.js +++ b/src/project/legacy/ti/cli.js @@ -1,445 +1,642 @@ -// import config from './config'; -// import fs from 'fs'; -// import logger from './logger'; -// import path from 'path'; -// import util from 'util'; -// import version from '../version'; -// import vm from 'vm'; - -// function GracefulShutdown() {} -// util.inherits(GracefulShutdown, Error); - -// export default class CLI { -// argv = { -// colors: true -// }; - -// hooks = { -// erroredFilenames: [], -// errors: {}, -// ids: {}, -// incompatibleFilenames: [], -// loadedFilenames: [], -// post: {}, -// pre: {}, -// scannedPaths: {} -// }; - -// startTime = null; - -// HOOK_PRIORITY_DEFAULT = 1000; - -// constructor(argv) { -// if (!argv.sdkPath) { -// throw new Error('Missing Titanium SDK path (sdkPath) paramater'); -// } -// argv.sdkPath = path.resolve(argv.sdkPath); -// if (!fs.existsSync(argv.sdkPath)) { -// throw new Error(`Specified Titanium SDK does not exist: ${argv.sdkPath}`); -// } - -// Object.assign(this.argv, argv); -// this.argv.$_ = []; - -// this.config = config; -// this.GracefulShutdown = GracefulShutdown; -// this.logger = logger; -// this.sdk = { -// name: path.basename(argv.sdkPath), -// path: argv.sdkPath -// }; - -// const manifest = JSON.parse(fs.readFileSync(path.join(argv.sdkPath, 'manifest.json'))); -// this.env = { -// sdks: { -// [manifest.version]: { -// manifest -// } -// } -// }; - -// this.scanHooks(path.resolve(argv.sdkPath, 'cli', 'hooks')); -// } - -// addAnalyticsEvent() { -// // noop -// } - -// addHook(...args) { -// return this.on(...args); -// } - -// createHook(name, ctx, fn) { -// let dataPayload = {}; - -// if (typeof ctx === 'function') { -// fn = ctx; -// ctx = null; -// } else if (ctx && typeof ctx === 'object' && !fn) { -// dataPayload = ctx; -// ctx = null; -// } - -// return (...args) => { -// const callback = args.length && typeof args[args.length - 1] === 'function' ? args.pop() : null; -// let data = Object.assign(dataPayload, { -// type: name, -// args, -// callback, -// fn: fn, -// ctx: ctx -// }); -// const pres = this.hooks.pre[name] || []; -// const posts = this.hooks.post[name] || []; - -// Promise.resolve() -// .then(async () => { -// // call all pre filters -// await pres -// .reduce((promise, pre) => { -// return promise.then(() => new Promise((resolve, reject) => { -// if (pre.length >= 2) { -// pre.call(ctx, data, (err, newData) => { -// if (err) { -// return reject(err); -// } else if (newData) { -// data = newData; -// } -// resolve(); -// }); -// } else { -// pre.call(ctx, data); -// resolve(); -// } -// })); -// }, Promise.resolve()); - -// if (data.fn) { -// data.result = await new Promise((resolve, reject) => { -// // call the function -// data.args.push((err, data) => { -// err ? reject(err) : resolve(data); -// }); -// data.fn.apply(data.ctx, data.args); -// }); -// } - -// // call all post filters -// await posts -// .reduce((promise, post) => promise.then(() => new Promise((resolve, reject) => { -// if (post.length >= 2) { -// post.call(ctx, data, (err, newData) => { -// if (err) { -// return reject(err); -// } else if (newData && typeof newData === 'object' && newData.type) { -// data = newData; -// } -// resolve(); -// }); -// } else { -// post.call(ctx, data); -// resolve(); -// } -// })), Promise.resolve()); - -// const { callback } = data; -// if (typeof callback === 'function') { -// data.callback = null; -// if (callback.length > 1) { -// callback.call(data, null, data.result); -// } else { -// // this is because the original hook system was bad and didn't handle -// // errors correctly :( -// callback.call(data, data.result); -// } -// } -// }) -// .catch(err => { -// // this is the primary error handler -// if (typeof data.callback === 'function') { -// data.callback(err); -// } else { -// console.log('Hook completion callback threw unhandled error:'); -// console.log(err.stack); -// process.exit(1); -// } -// }); -// }; -// } - -// emit(hookNames, data, callback) { -// if (typeof data === 'function') { -// callback = data; -// data = null; -// } - -// // make sure hookNames is an array -// if (!Array.isArray(hookNames)) { -// hookNames = [ hookNames ]; -// } - -// // create each hook and immediately fire them -// const promise = hookNames -// .reduce((promise, name) => promise.then(() => new Promise((resolve, reject) => { -// const hook = this.createHook(name, data); -// console.error(`Emitting ${name}`); -// hook((err, result) => { -// err ? reject(err) : resolve(result); -// }); -// })), Promise.resolve()); - -// if (typeof callback !== 'function') { -// return promise; -// } - -// promise -// .then(result => callback(null, result)) -// .catch(callback); - -// return this; -// } - -// fireHook(...args) { -// return this.emit(...args); -// } - -// on(name, callback) { -// let priority = this.HOOK_PRIORITY_DEFAULT; -// let i; - -// if (typeof callback === 'function') { -// callback = { post: callback }; -// } else if (callback && typeof callback === 'object') { -// priority = parseInt(callback.priority) || priority; -// } - -// if (callback.pre) { -// const h = this.hooks.pre[name] || (this.hooks.pre[name] = []); -// callback.pre.priority = priority; -// for (i = 0; i < h.length && priority >= h[i].priority; i++) {} -// h.splice(i, 0, callback.pre); -// } - -// if (callback.post) { -// const h = this.hooks.post[name] || (this.hooks.post[name] = []); -// callback.post.priority = priority; -// for (i = 0; i < h.length && priority >= h[i].priority; i++) {} -// h.splice(i, 0, callback.post); -// } - -// return this; -// } - -// scanHooks(dir) { -// if (this.hooks.scannedPaths[dir]) { -// return; -// } - -// try { -// // eslint-disable-next-line security/detect-non-literal-require -// const appc = require(path.join(this.argv.sdkPath, 'node_modules', 'node-appc')); -// const jsfile = /\.js$/; -// const ignore = /^[._]/; -// const files = fs.statSync(dir).isDirectory() ? fs.readdirSync(dir).map(n => path.join(dir, n)) : [ dir ]; - -// for (const file of files) { -// try { -// if (fs.statSync(file).isFile() && jsfile.test(file) && !ignore.test(path.basename(path.dirname(file)))) { -// // test the file for syntax errors -// vm.runInThisContext(`(function (exports, require, module, __filename, __dirname){${fs.readFileSync(file).toString()}\n});`, file, 0, false); - -// // eslint-disable-next-line security/detect-non-literal-require -// var mod = require(file); -// if (mod.id) { -// if (!Array.isArray(this.hooks.ids[mod.id])) { -// this.hooks.ids[mod.id] = []; -// } -// this.hooks.ids[mod.id].push({ -// file: file, -// version: mod.version || null -// }); - -// // don't load duplicate ids -// if (this.hooks.ids[mod.id].length > 1) { -// continue; -// } -// } - -// if (!this.version || !mod.cliVersion || version.satisfies(this.version, mod.cliVersion)) { -// mod.init && mod.init(this.logger, this.config, this, appc); -// this.hooks.loadedFilenames.push(file); -// console.error(`Loaded CLI hook: ${file}`); -// } else { -// this.hooks.incompatibleFilenames.push(file); -// } -// } -// } catch (ex) { -// this.hooks.erroredFilenames.push(file); -// this.hooks.errors[file] = ex; -// } -// } -// } catch (e) { -// // squelch -// } -// } - -// async go(command) { -// // load the command -// // try { -// // this.command = require(path.resolve(__dirname, '..', 'commands', `${argv.command}.js`)); -// // } catch (e) { -// // throw new Error(`Invalid command "${argv.command}"`); -// // } - -// // await cmd(cli, msg); -// // await this.validate(); -// // -// // await this.executeCommand(); -// } - -// async validate({ options }) { -// const { argv } = this; -// const orderedOptionNames = Object.keys(options).sort((a, b) => { -// if (options[a].order && options[b].order) { -// return options[a].order - options[b].order; -// } -// return options[a].order ? -1 : options[b].order ? 1 : 0; -// }); - -// for (const [ name, option ] of Object.entries(options)) { -// if (argv[name] === undefined && option.default !== undefined) { -// argv[name] = option.default; -// } -// } - -// const issues = []; - -// for (let name of orderedOptionNames) { -// const opt = options[name]; -// opt.name = name; - -// if (opt.validated) { -// continue; -// } - -// if (argv[name] === undefined) { -// // check if the option is required -// if (opt.required || (opt.conf && opt.conf.required)) { -// // ok, we have a required option, but it's possible that this option -// // replaces some legacy option in which case we need to check if the -// // legacy options were defined - -// if (typeof opt.verifyIfRequired === 'function') { -// await new Promise(resolve => opt.verifyIfRequired(stillRequired => { -// if (stillRequired) { -// issues.push({ opt }); -// } -// resolve(); -// })); -// continue; -// } - -// issues.push({ opt }); -// } - -// } else if (Array.isArray(opt.values) && !opt.skipValueCheck && opt.values.indexOf(argv[name]) === -1) { -// issues.push({ opt }); - -// } else if (!opt.validated && typeof opt.validate === 'function') { -// try { -// await new Promise(resolve => opt.validate(argv[name], (err, value) => { -// if (err) { -// opt._err = err; -// issues.push({ -// opt, -// err, -// value: argv[name] -// }); -// } else { -// argv[name] = value; -// opt.validated = true; -// if (opt.callback) { -// const val = opt.callback(argv[name] || ''); -// if (val !== undefined) { -// argv[name] = val; -// } -// delete opt.callback; -// } -// } -// resolve(); -// })); -// } catch (ex) { -// if (ex instanceof GracefulShutdown) { -// // simply return and cb() is never called which effectively cause the cli -// // to gracefully exit -// continue; -// } -// throw ex; -// } - -// } else if (opt.callback) { -// opt.validated = true; -// const val = opt.callback(argv[name] || ''); -// if (val !== undefined) { -// argv[name] = val; -// } -// delete opt.callback; -// } -// } - -// if (issues.length) { -// // we are going to throw an error, but we first need to build up the info for each missing -// // or invalid parameter -// const err = new Error('Bad request'); -// err.status = 400; -// err.issues = await Promise.all(issues.map(async ({ opt, err, value }) => { -// const issue = { -// name: opt.name -// }; - -// if (err) { -// issue.error = err; -// } - -// if (value) { -// issue.value = value; -// } - -// if (typeof opt.prompt === 'function') { -// const field = await new Promise(opt.prompt); -// issue.field = { -// type: field.constructor.name.toLowerCase(), -// message: field.promptLabel -// }; -// } else if (Array.isArray(opt.values)) { -// issue.field = { -// type: 'select', -// message: `Please select a ${opt.name}`, -// options: opt.values -// // suggest -// // complete -// // numbered -// }; -// } else { -// issue.field = { -// type: opt.password ? 'password' : 'text', -// message: `Please enter a valid ${opt.name}` -// }; -// } - -// return issue; -// })); -// throw err; -// } - -// // detect missing arguments - -// // callCommandValidate - -// // callOptionCallbacks -// } - -// async executeCommand() { -// // -// } -// } +import fs from 'fs-extra'; +import getOSInfo from '../../../lib/os'; +import path from 'path'; +import tunnel from '../tunnel'; +import vm from 'vm'; +import * as version from '../../../lib/version'; + +import { expandPath } from 'appcd-path'; +import { format } from 'util'; +import { get, mergeDeep, set, unique } from 'appcd-util'; +import { isFile } from 'appcd-fs'; +import { sdk } from 'titaniumlib'; +import { snooplogg } from 'cli-kit'; + +const { highlight, gray, green, magenta, red, yellow } = snooplogg.styles; + +const CLI_VERSION = '5.999.0'; + +/** + * The Titanium CLI v5 requires the `--sdk ` to equal the `` in the + * tiapp.xml. If they don't match, node-titanium-sdk's `ti.validateCorrectSDK()` will spawn a new + * Titanium CLI process with the correct `--sdk`. Due to the design of the Titanium CLI, this + * `GracefullyShutdown` error was thrown as an easy way to stop validating and skip executing the + * command. + * + * Since this Titanium CLI shim will ALWAYS match the `` in the tiapp.xml, this really + * isn't used, but just in case, we'll define it and set it on the `CLI` instance. + */ +class GracefulShutdown extends Error {} + +/** + * Command specific data including the command module and configuration. + */ +class Context { + cliVersion = CLI_VERSION; + + platform = null; + + constructor({ conf, name, parent, path }) { + this.conf = conf || {}; + this.name = name; + this.parent = parent; + this.path = path; + + if (!isFile(this.path)) { + throw new Error(`Command file not found: ${path}`); + } + } + + async load(cli) { + tunnel.log(`Loading command file: ${highlight(this.path)}`); + this.module = require(this.path); + + if (this.module.cliVersion && !version.satisfies(this.cliVersion, this.module.cliVersion)) { + throw new Error(`Command "${this.name}" is incompatible with this version of the Titanium CLI`); + } + + if (typeof this.module.run !== 'function') { + throw new Error(`Command "${this.name}" does not contain a valid run function`); + } + + this.conf = typeof this.module.config === 'function' ? this.module.config(cli.logger, cli.config, cli) : {}; + if (typeof this.conf === 'function') { + this.conf = await new Promise(resolve => this.conf(resolve)); + } + + if (this.name !== 'build') { + return; + } + + // the `build` command `--platform` option was hard wired into the CLI context, so + // unfortunately we need to do a bunch of `build` specific logic to load the platform + // specific command + const { platform } = cli.argv; + if (!platform) { + const err = new TypeError('Missing required "platform"'); + err.code = 'EPLATFORM'; + err.prompt = { + choices: this.conf.options.platform.values.map(platform => ({ value: platform })), + message: 'For which platform do you want to build?', + name: 'platform', + required: true, + type: 'select' + }; + throw err; + } + + // `this.conf.platforms` is an object of platform names to platform-specific options + if (this.conf.platforms && Object.prototype.hasOwnProperty.call(this.conf.platforms, platform)) { + const platformConf = this.conf.platforms[platform]; + + this.platform = new Context({ + conf: platformConf, + name: platform, + parent: this, + path: path.join(cli.sdk.path, platform) + }); + + this.platforms = { + [this.platform.name]: this.platform + }; + + cli.argv.platform = this.platform.name; // I think this is to normalize `iphone` to `ios` + cli.argv.$platform = platform; + + // find all platform hooks + cli.scanHooks(path.join(cli.sdk.path, this.platform.name, 'cli', 'hooks')); + } + } +} + +/** + * Controls the state and flow for running legacy Titanium SDK CLI commands such as `build` and + * `clean`. + */ +export default class CLI { + /** + * The hook priority used to sort hook callbacks. + * @type {Number} + */ + static HOOK_PRIORITY_DEFAULT = 1000; + + /** + * The Titanium CLI version. Since this legacy shim is intended to simulate the Titanium CLI + * v5, we keep the major as `5`, but set the minor to something high that will never exist. + * @type {String} + */ + version = CLI_VERSION; + + /** + * A map of command names to command descriptors. + * @type {Object} + */ + cmds = {}; + + /** + * Export of the graceful shutdown error. + * @type {Function} + */ + GracefulShutdown = GracefulShutdown; + + /** + * The hook system state. + * @type {Object} + */ + hooks = { + erroredFilenames: [], + errors: {}, + ids: {}, + incompatibleFilenames: [], + loadedFilenames: [], + post: {}, + pre: {}, + scannedPaths: {} + }; + + /** + * The logger object. + * @type {Object} + */ + logger = { + debug: (msg, ...args) => console.log(`${magenta('[DEBUG]')} ${format(msg, ...args)}`), + error: (msg, ...args) => console.error(red(`[ERROR] ${format(msg, ...args)}`)), + info: (msg, ...args) => console.info(`${green('[INFO] ')} ${format(msg, ...args)}`), + log: console.log, + trace: (msg, ...args) => console.log(`${gray('[TRACE]')} ${format(msg, ...args)}`), + warn: (msg, ...args) => console.warn(yellow(`[WARN] ${format(msg, ...args)}`)), + + levels: { + trace: {}, + debug: {}, + info: {}, + warn: {}, + error: {} + }, + + banner() { + // noop + }, + + getLevels() { + return Object.keys(this.levels); + }, + + setLevel() { + // noop + } + }; + + /** + * The time that executing the command starts. This value is set after validation and prompting + * has occurred. + * @type {Number} + */ + startTime = null; + + /** + * Initializes the CLI state by validating the Titanium SDK, initializing the config, and + * detecting plugins. + * + * @param {Object} opts - Various options. + * @param {Object} opts.argv - The command arguments. + * @param {String} opts.command - The name of the command to execute. + * @param {Object} [opts.config] - User-defined Titanium CLI config settings from appcd's user + * config. + * @param {String} opts.sdkPath - The path to the Titanium SDK. + * @access public + */ + constructor(opts) { + if (!opts || typeof opts !== 'object') { + throw new TypeError('Expected options to be an object'); + } + + this.config = this.initConfig(opts.config); + + // validate the sdk path + this.sdk = new sdk.TitaniumSDK(opts.sdkPath); + + // initialize the CLI argument values + this.argv = { + $command: opts.command, + }; + for (const [ key, value ] of Object.entries(opts.argv)) { + this.argv[key] = this.argv[key.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`)] = value; + } + + // initialize the legacy environment info + const installPath = path.resolve(this.sdk.path, '..', '..', '..'); + this.env = { + // used by the Titanium SDK commands to display the system info in the output before + // the command is executed + getOSInfo: cb => { + const info = getOSInfo(); + cb({ + os: info.name, + osver: info.version, + ostype: info.arch, + oscpu: info.numcpus, + memory: info.memory, + node: process.versions.node, + npm: '?' // unimportant + }); + }, + + // used by node-titanium-sdk's `loadPlugins()` to scan the global Titanium SDK install + // path for Titanium CLI plugins that are explicitly enabled in the app's `tiapp.xml`, + // which is probably not used anymore + installPath, + + os: { + sdkPaths: [ installPath ] + }, + + sdks: { + [this.sdk.manifest.version]: this.sdk + } + }; + + // initialize the commands + const cmd = opts.command; + if (cmd !== 'build' && cmd !== 'clean') { + throw new Error(`Invalid command "${cmd}"`); + } + this.command = this.cmds[cmd] = new Context({ + name: cmd, + path: path.join(this.sdk.path, 'cli', 'commands', `${cmd}.js`) + }); + + // initialize the hooks + unique(this.config.paths.hooks).forEach(this.scanHooks.bind(this)); + this.scanHooks(path.join(this.sdk.path, 'cli', 'hooks')); + } + + /** + * Logs a telemetry event. + * + * @param {String} event - The name of the event. + * @param {Object} data - An object containing any data associated with the event. + * @param {String} type - The event type, however this is only ever passed in for the + * `ti.apiusage` event, which coincidentally has had the incorrect event name since 2014. + * @access public + */ + addAnalyticsEvent(event, data, type) { + tunnel.call('/telemetry', { event: type === 'ti.apiusage' ? type : event, ...data }); + } + + addHook(...args) { + return this.on(...args); + } + + createHook(name, ctx, fn) { + let dataPayload = {}; + + if (typeof ctx === 'function') { + fn = ctx; + ctx = null; + } else if (ctx && typeof ctx === 'object' && !fn) { + dataPayload = ctx; + ctx = null; + } + + return (...args) => { + const callback = args.length && typeof args[args.length - 1] === 'function' ? args.pop() : null; + let data = Object.assign(dataPayload, { + type: name, + args, + callback, + fn: fn, + ctx: ctx + }); + const pres = this.hooks.pre[name] || []; + const posts = this.hooks.post[name] || []; + + Promise.resolve() + .then(async () => { + // call all pre filters + await pres + .reduce((promise, pre) => { + return promise.then(() => new Promise((resolve, reject) => { + if (pre.length >= 2) { + pre.call(ctx, data, (err, newData) => { + if (err) { + return reject(err); + } else if (newData) { + data = newData; + } + resolve(); + }); + } else { + pre.call(ctx, data); + resolve(); + } + })); + }, Promise.resolve()); + + if (data.fn) { + data.result = await new Promise((resolve, reject) => { + // call the function + data.args.push((err, data) => { + err ? reject(err) : resolve(data); + }); + data.fn.apply(data.ctx, data.args); + }); + } + + // call all post filters + await posts + .reduce((promise, post) => promise.then(() => new Promise((resolve, reject) => { + if (post.length >= 2) { + post.call(ctx, data, (err, newData) => { + if (err) { + return reject(err); + } else if (newData && typeof newData === 'object' && newData.type) { + data = newData; + } + resolve(); + }); + } else { + post.call(ctx, data); + resolve(); + } + })), Promise.resolve()); + + const { callback } = data; + if (typeof callback === 'function') { + data.callback = null; + if (callback.length > 1) { + callback.call(data, null, data.result); + } else { + // this is because the original hook system was bad and didn't handle + // errors correctly :( + callback.call(data, data.result); + } + } + }) + .catch(err => { + // this is the primary error handler + if (typeof data.callback === 'function') { + data.callback(err); + } else { + console.log('Hook completion callback threw unhandled error:'); + console.log(err.stack); + process.exit(1); + } + }); + }; + } + + emit(hookNames, data, callback) { + if (typeof data === 'function') { + callback = data; + data = null; + } + + // make sure hookNames is an array + hookNames = unique(hookNames); + + // create each hook and immediately fire them + const promise = hookNames + .reduce((promise, name) => promise.then(() => new Promise((resolve, reject) => { + const hook = this.createHook(name, data); + tunnel.log(`Emitting ${name}`); + hook((err, result) => { + err ? reject(err) : resolve(result); + }); + })), Promise.resolve()); + + if (typeof callback !== 'function') { + return promise; + } + + promise + .then(result => callback(null, result)) + .catch(callback); + + return this; + } + + async executeCommand() { + await this.emit('cli:pre-execute', { cli: this, command: this.command }); + + this.startTime = Date.now(); + + const { run } = this.command.module; + let done = 0; + + await new Promise((resolve, reject) => { + tunnel.log(`Executing ${this.command.name} run`); + + run(this.logger, this.config, this, async (err, result) => { + if (done++) { + // guard against callback being fired more than once + return; + } + + // we need to wrap the post-execute emit in a try/catch so that any exceptions + // it throws aren't confused with command errors + try { + await this.emit('cli:post-execute', { cli: this, command: this.command, err, result }); + } catch (ex) { + return reject(ex); + } + + if (err) { + return reject(err); + } + + resolve(); + }); + + // if there's no callback in the run signature, then unblock the function and let + // Node.js wait for run() to finish any async tasks + if (run.length < 4) { + resolve(); + } + }); + } + + fireHook(...args) { + return this.emit(...args); + } + + async go() { + await this.emit('cli:go', { cli: this }); + await this.command.load(this); + await this.emit('cli:command-loaded', { cli: this, command: this.command }); + await this.validate(); + await this.executeCommand(); + } + + initConfig(config) { + return Object.defineProperties( + mergeDeep({ + app: { + workspace: '' + }, + + cli: { + colors: true, + completion: false, + logLevel: 'trace', + prompt: true, + progressBars: true, + failOnWrongSDK: false, + httpProxyServer: '', + rejectUnauthorized: true, + width: 100, + ignoreDirs: '^(\\.svn|_svn|\\.git|\\.hg|\\.?[Cc][Vv][Ss]|\\.bzr|\\$RECYCLE\\.BIN)$', + ignoreFiles: '^(\\.gitignore|\\.npmignore|\\.cvsignore|\\.DS_Store|\\._.*|[Tt]humbs.db|\\.vspscc|\\.vssscc|\\.sublime-project|\\.sublime-workspace|\\.project|\\.tmproj)$' + }, + + // additional search paths for commands and hooks + paths: { + commands: [], + hooks: [], + modules: [], + plugins: [], + sdks: [], + templates: [] + }, + + user: { + locale: 'en_US' + } + }, config), + { + get: { + value: (key, defaultValue) => get(this, key, defaultValue) + }, + + // called by Android build to set the `android.sdkPath` + set: { + value: (key, value) => set(this, key, value) + } + } + ); + } + + on(name, callback) { + let priority = CLI.HOOK_PRIORITY_DEFAULT; + let i; + + if (typeof callback === 'function') { + callback = { post: callback }; + } else if (callback && typeof callback === 'object') { + priority = parseInt(callback.priority) || priority; + } + + if (callback.pre) { + const h = this.hooks.pre[name] || (this.hooks.pre[name] = []); + callback.pre.priority = priority; + for (i = 0; i < h.length && priority >= h[i].priority; i++) {} + h.splice(i, 0, callback.pre); + } + + if (callback.post) { + const h = this.hooks.post[name] || (this.hooks.post[name] = []); + callback.post.priority = priority; + for (i = 0; i < h.length && priority >= h[i].priority; i++) {} + h.splice(i, 0, callback.post); + } + + return this; + } + + /** + * Searches the specified directory for Titanium CLI plugin files. + * + * @param {String} dir - The directory to scan. + * @access public + */ + scanHooks(dir) { + dir = expandPath(dir); + + if (this.hooks.scannedPaths[dir]) { + return; + } + + try { + // eslint-disable-next-line security/detect-non-literal-require + const appc = require(path.join(this.argv.sdkPath, 'node_modules', 'node-appc')); + const jsfile = /\.js$/; + const ignore = /^[._]/; + const files = fs.statSync(dir).isDirectory() ? fs.readdirSync(dir).map(n => path.join(dir, n)) : [ dir ]; + + for (const file of files) { + try { + if (fs.statSync(file).isFile() && jsfile.test(file) && !ignore.test(path.basename(path.dirname(file)))) { + // test the file for syntax errors + vm.runInThisContext(`(function (exports, require, module, __filename, __dirname){${fs.readFileSync(file).toString()}\n});`, file, 0, false); + + // eslint-disable-next-line security/detect-non-literal-require + var mod = require(file); + if (mod.id) { + if (!Array.isArray(this.hooks.ids[mod.id])) { + this.hooks.ids[mod.id] = []; + } + this.hooks.ids[mod.id].push({ + file: file, + version: mod.version || null + }); + + // don't load duplicate ids + if (this.hooks.ids[mod.id].length > 1) { + continue; + } + } + + if (!this.version || !mod.cliVersion || version.satisfies(this.version, mod.cliVersion)) { + mod.init && mod.init(this.logger, this.config, this, appc); + this.hooks.loadedFilenames.push(file); + console.error(`Loaded CLI hook: ${file}`); + } else { + this.hooks.incompatibleFilenames.push(file); + } + } + } catch (ex) { + this.hooks.erroredFilenames.push(file); + this.hooks.errors[file] = ex; + } + } + } catch (e) { + // squelch + } + } + + async validate() { + await this.emit('cli:pre-validate', { cli: this, command: this.command }); + + // step 1: determine invalid or missing options + + // step 2: determine all missing arguments + + // step 3: run the command's validate() function, if exists + + const { validate } = this.command.module; + if (validate && typeof validate === 'function') { + const fn = validate(this.logger, this.config, this); + + // fn should always be a function for `build` and `clean` commands + if (typeof fn === 'function') { + await new Promise(resolve => fn(resolve)); + } + } + + await this.emit('cli:post-validate', { cli: this, command: this.command }); + + // step 4: fire all option callbacks + for (const ctx of [ this.command, this.command?.platform ]) { + if (ctx) { + for (const [ name, opt ] of Object.entries(ctx.conf.options)) { + if (typeof opt.callback === 'function') { + const val = opt.callback(this.argv[name] || ''); + if (val !== undefined) { + this.argv[name] = val; + } + } + } + } + } + } +} diff --git a/src/project/legacy/ti/config.js b/src/project/legacy/ti/config.js deleted file mode 100644 index 4afd23d..0000000 --- a/src/project/legacy/ti/config.js +++ /dev/null @@ -1,126 +0,0 @@ -// import fs from 'fs'; -// import path from 'path'; - -// // default config -// export const config = { -// app: { -// workspace: '' -// }, - -// cli: { -// colors: true, -// completion: false, -// logLevel: 'trace', -// prompt: true, -// progressBars: true, -// failOnWrongSDK: false, -// httpProxyServer: '', -// rejectUnauthorized: true, -// width: 100, -// ignoreDirs: '^(\\.svn|_svn|\\.git|\\.hg|\\.?[Cc][Vv][Ss]|\\.bzr|\\$RECYCLE\\.BIN)$', -// ignoreFiles: '^(\\.gitignore|\\.npmignore|\\.cvsignore|\\.DS_Store|\\._.*|[Tt]humbs.db|\\.vspscc|\\.vssscc|\\.sublime-project|\\.sublime-workspace|\\.project|\\.tmproj)$' -// }, - -// // additional search paths for commands and hooks -// paths: { -// commands: [], -// hooks: [], -// modules: [], -// plugins: [], -// sdks: [], -// templates: [] -// }, - -// user: {} -// }; - -// export default config; - -// const configFile = path.join(process.env[process.platform === 'win32' ? 'USERPROFILE' : 'HOME'], '.titanium', 'config.json'); -// if (fs.existsSync(configFile)) { -// try { -// (function mix(src, dest) { -// for (let [ key, value ] of Object.entries(src)) { -// if (value && typeof value === 'object' && !Array.isArray(value)) { -// if (!dest[key] || typeof dest[key] !== 'object') { -// dest[key] = {}; -// } -// mix(value, dest[key]); -// } else if (typeof value === 'string') { -// value = value === undefined ? '' : String(value).trim(); -// if (value === 'null') { -// value = null; -// } else if (value === 'true') { -// value = true; -// } else if (value === 'false') { -// value = false; -// } -// dest[key] = value; -// } -// } -// }(JSON.parse(fs.readFileSync(configFile)), config)); -// } catch (e) { -// console.error(`Failed to parse Titanium CLI config: ${e.message}`); -// } -// } - -// Object.defineProperties(config, { -// get: { -// value: function (key, defaultValue) { -// if (!key) { -// return this; -// } - -// const parts = key.split('.'); -// const q = parts.pop(); -// let obj = this; -// let i = 0; -// let p = parts.length && parts[i++]; - -// if (p) { -// do { -// if (obj.hasOwnProperty(p)) { -// obj = obj[p]; -// } else { -// return defaultValue; -// } -// } while (obj && (p = parts[i++])); -// } - -// return obj && q && obj.hasOwnProperty(q) ? obj[q] : defaultValue; -// } -// }, -// set: { -// value: function (key, value) { -// const parts = key.split('.'); -// const q = parts.pop(); -// let obj = this; -// let i = 0; -// let p = parts.length && parts[i++]; - -// if (p) { -// do { -// obj = obj.hasOwnProperty(p) ? obj[p] : (obj[p] = {}); -// } while (obj && (p = parts[i++])); -// } - -// // if not an array, try to cast to null, true, false, int or leave as string -// if (!Array.isArray(value)) { -// value = value === undefined ? '' : String(value).trim(); -// if (value === 'null') { -// value = null; -// } else if (value === 'true') { -// value = true; -// } else if (value === 'false') { -// value = false; -// } else if (String(~~value) === value) { -// value = ~~value; -// } -// } - -// if (obj && q) { -// obj[q] = value; -// } -// } -// } -// }); diff --git a/src/project/legacy/tunnel.js b/src/project/legacy/tunnel.js index b2a027c..a29f9f5 100644 --- a/src/project/legacy/tunnel.js +++ b/src/project/legacy/tunnel.js @@ -1,3 +1,4 @@ +import CLI from './ti/cli'; import uuid from 'uuid'; /** @@ -18,16 +19,36 @@ class Tunnel { * @access public */ constructor() { - process.on('message', data => { - const { id } = data; + process.on('message', async data => { + const { id, type } = data; + + if (type === 'exec') { + try { + await new CLI(data).go(); + + // the command is complete, but the IPC channel is still open, so we simply disconnect it and + // this process should exit whenever the command finishes + process.disconnect(); + } catch (err) { + process.send({ + ...err, + message: err.message || err, + stack: err.stack, + status: err.status || 500, + type: 'error' + }); + process.exit(1); + } + return; + } + const req = id && this.pending[id]; if (!req) { return; } - const { resolve, reject } = req; - switch (data.type) { + switch (type) { case 'response': delete this.pending[id]; resolve(data.response); @@ -54,13 +75,39 @@ class Tunnel { const id = uuid.v4(); this.pending[id] = { resolve, reject }; process.send({ + data, id, - type: 'request', path, - data + type: 'call' }); }); } + + /** + * Writes a message to the debug log. + * + * @param {...*} args - A message or data to log. + * @access public + */ + log(...args) { + process.send({ + args, + type: 'log' + }); + } + + /** + * Sends a telemetry event. + * + * @param {Object} payload - The telemetry payload including the `event` and data. + * @access public + */ + telemetry(payload) { + process.send({ + payload, + type: 'telemetry' + }); + } } /** diff --git a/src/project/project-service.js b/src/project/project-service.js index afc8656..29297c0 100644 --- a/src/project/project-service.js +++ b/src/project/project-service.js @@ -6,7 +6,7 @@ import { AppcdError, codes } from 'appcd-response'; import { Project } from 'titaniumlib'; import { spawn } from 'appcd-subprocess'; -const { log } = appcd.logger('project-service'); +const { error, log } = appcd.logger('project-service'); /** * Service for creating and building Titanium applications. @@ -22,6 +22,8 @@ export default class ProjectService extends Dispatcher { * @access public */ async activate(cfg) { + this.config = cfg; + this.register('/', ctx => { return 'tiapp coming soon!'; }); @@ -70,7 +72,9 @@ export default class ProjectService extends Dispatcher { * @access private */ async exec(command, ctx) { - let { cwd, projectDir } = ctx.request.data; + const { cwd } = ctx.headers; + let { projectDir } = ctx.request.data; + if (projectDir !== undefined && typeof projectDir !== 'string') { throw new AppcdError(codes.BAD_REQUEST, 'Missing project directory'); } @@ -87,21 +91,10 @@ export default class ProjectService extends Dispatcher { }); // const { sdk } = project.tiapp.get('sdk'); - const sdk = '9.0.2.GA'; - + const sdk = '9.0.3.GA'; const sdkInfo = (await appcd.call('/sdk/find', { data: { name: sdk } })).response; - const data = { - ...ctx.request.data, - command, - projectDir, - sdk, - sdkPath: sdkInfo.path - }; - - log(data); - - // spawn the bootstrap with the cmd and args + log('Spawning legacy Titanium CLI bootstrap...'); const { child } = spawn({ command: process.execPath, args: [ path.resolve(__dirname, 'legacy/bootstrap.js') ], @@ -113,40 +106,64 @@ export default class ProjectService extends Dispatcher { child.stdout.on('data', data => ctx.response.write(data.toString())); child.stderr.on('data', data => ctx.response.write(data.toString())); - child.on('close', () => ctx.response.end()); + child.on('close', code => { + log(`Legacy Titanium CLI bootstrap exited (code ${code || 0})`); + ctx.response.end(); + }); child.on('message', async msg => { - if (msg.type === 'error') { - ctx.response.end(msg); - } else if (msg.type === 'request') { - const { id, path, data } = msg; - if (id && path) { - let response; - try { - response = await appcd.call(path, data); - } catch (err) { - child.send({ - id, - type: 'error', - error: err - }); - throw err; + switch (msg.type) { + case 'call': + const { id, path, data } = msg; + if (id && path) { + let response; + try { + response = await appcd.call(path, data); + } catch (err) { + child.send({ + error: err, + id, + type: 'error' + }); + throw err; + } + + try { + child.send({ + id, + response, + type: 'response' + }); + } catch (err) { + console.error(err); + } } + return; - try { - child.send({ - id, - type: 'response', - response - }); - } catch (err) { - console.error(err); - } - } - } else { - console.log('GOT IPC MESSAGE!'); - console.log(msg); + case 'error': + error(msg); + return ctx.response.end(msg); + + case 'log': + return console.log(...msg.args); + + case 'telemetry': + return appcd.telemetry(msg.payload); } }); + + const data = { + argv: { + ...ctx.request.data, + projectDir, + sdk + }, + command, + config: this.config.titanium, + sdkPath: sdkInfo.path, + type: 'exec' + }; + log('Sending data to bootstrap:'); + log(data); child.send(data); } } diff --git a/src/sdk/sdk-list-service.js b/src/sdk/sdk-list-service.js index 15a45e1..9fcc932 100644 --- a/src/sdk/sdk-list-service.js +++ b/src/sdk/sdk-list-service.js @@ -1,5 +1,6 @@ import DetectEngine from 'appcd-detect'; import gawk from 'gawk'; +import semver from 'semver'; import { compare } from '../lib/version'; import { DataServiceDispatcher } from 'appcd-dispatcher'; @@ -36,10 +37,9 @@ export default class SDKListService extends DataServiceDispatcher { paths: sdk.getPaths(), processResults(results) { results.sort((a, b) => { - return compare( - a.manifest && a.manifest.version, - b.manifest && b.manifest.version - ); + const av = a.manifest?.version; + const bv = b.manifest?.version; + return av && bv ? compare(av, bv) : 0; }); }, recursive: true, @@ -70,4 +70,29 @@ export default class SDKListService extends DataServiceDispatcher { this.detectEngine = null; } } + + /** + * Finds an SDK by name (or version) or by `latest`. + * + * @param [name='latest'] - The SDK name or version to search for. + * @returns {Object} + */ + find(name) { + let result; + if (!name || name === 'latest') { + // get the latest installed + for (const sdk of this.data) { + if (!result || (sdk.manifest && result.manifest && semver.gt(sdk.manifest.version, result.manifest.version))) { + result = sdk; + } + } + } else { + result = this.data.find(s => s.name === name); + if (!result) { + // maybe name is a version? + result = this.data.find(s => s.manifest?.version === name); + } + } + return result; + } } diff --git a/src/sdk/sdk-service.js b/src/sdk/sdk-service.js index 81d1cfd..424d90c 100644 --- a/src/sdk/sdk-service.js +++ b/src/sdk/sdk-service.js @@ -56,7 +56,7 @@ export default class SDKService extends Dispatcher { find(ctx) { const { data, params } = ctx.request; const name = data.name || params.name; - const result = this.installed.data.find(s => s.name === name); + const result = this.installed.find(name); if (result) { return result; } @@ -85,7 +85,7 @@ export default class SDKService extends Dispatcher { overwrite: data.overwrite, uri: data.uri || params.name }).then(tisdk => { - response.write({ fin: true, message: `\nTitanium SDK ${tisdk.name} installed` }); + response.write({ fin: true, message: `Titanium SDK ${tisdk.name} installed` }); response.end(); }).catch(err => { try { From c2e7607ef5e3edf07adddc2f23a0e56dfd3c0354 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Wed, 15 Jul 2020 17:31:10 -0500 Subject: [PATCH 10/39] chore: Fixed most lint warnings. doc: Added missing JSDoc. fix: Added missing option validation code. --- .eslintrc | 1 + src/project/legacy/README.md | 38 +++- src/project/legacy/patch/android.js | 5 +- src/project/legacy/patch/fields.js | 18 +- src/project/legacy/patch/ios.js | 2 + src/project/legacy/ti/cli.js | 306 +++++++++++++++++++++++----- src/sdk/sdk-list-service.js | 2 +- 7 files changed, 302 insertions(+), 70 deletions(-) diff --git a/.eslintrc b/.eslintrc index 10f0016..29a3bed 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,5 +1,6 @@ { "rules": { + "max-depth": [ "warn", 6 ], "no-loop-func": "off", "promise/always-return": "off" } diff --git a/src/project/legacy/README.md b/src/project/legacy/README.md index a9eda5b..5e41827 100644 --- a/src/project/legacy/README.md +++ b/src/project/legacy/README.md @@ -4,27 +4,25 @@ The Titanium SDK contains commands such as `build` and `clean`. These commands a with the Titanium CLI v5 and older. In order to be able to execute these commands, we need to shim the bare minimum APIs and features of the Titanium CLI v5. -## Deprecations - While this legacy CLI attempts to maintain backwards compatibility, the following APIs and features are no longer supported. -### `cli.version` +## `cli.version` The Titanium CLI version is set to `5.999.0` as we don't want to break compatibility with Titanium CLI plugins, but we also want to signify that this is not the real Titanium CLI v5. -### CLI argument parser +## CLI argument parser The legacy CLI no longer parses raw CLI arguments. It expects the `argv` values to be passed in as a JSON object. -### i18n - Internationalization +## i18n - Internationalization Locale is no longer detected and the locale is forced to `en-US`. We have very little translated strings that users are not going to notice. -### Selected Titanium SDK and forking correct Titanium SDK +## Selected Titanium SDK and forking correct Titanium SDK The entire selected Titanium SDK system of the Titanium CLI has been removed. The `` in the `tiapp.xml` is the source of truth. @@ -32,7 +30,7 @@ in the `tiapp.xml` is the source of truth. Because this legacy CLI will only ever be invoked with a valid Titanium SDK, various APIs are no longer needed. See `cli.argv` below. -### `cli.argv._`, `cli.argv.$`, `cli.argv.$_`, `cli.argv.$0` +## `cli.argv._`, `cli.argv.$`, `cli.argv.$_`, `cli.argv.$0` `cli.argv` contained the raw CLI arguments and process name as well as the parsed arguments. While the `cli.argv` will continue to contain the parsed arguments (passed in as a JSON object), the @@ -42,7 +40,7 @@ Note that node-titanium-sdk's `validateCorrectSDK()` does reference these variab to fork using the correct `--sdk` from the app's `tiapp.xml`, however it's a non-issue being that the correct SDK version will always be set. -### `cli.on('cli:command-not-found')` +## `cli.on('cli:command-not-found')` This event was emitted when the specified command was not found so that the Titanium CLI could check if there were any Titanium SDKs installed and if you happened to misspell the command. @@ -50,6 +48,28 @@ check if there were any Titanium SDKs installed and if you happened to misspell Since this legacy CLI will only ever be called with the either the `build` or `clean` command, it is impossible for the command to not exist. -### Built-in Titanium CLI hook plugins +## Built-in Titanium CLI hook plugins This legacy CLI does not contain any built-in CLI hook plugins such as `hooks/tisdk3fixes.js`. + +## Config changes + +The configuration no longer contains settings for environment detection such as Android, iOS, +JDK, etc. Those config settings are in their respective appcd plugin config files and the Appc +Daemon user config file. + +This legacy Titanium CLI will not read or write the original Titanium CLI config file. + +## Log changes + +The `--log-level` option has been dropped. There is no log message filtering effectively setting +the log level to `"trace"`. + +## Prompting + +The legacy Titanium CLI does not prompt for missing or invalid values. It will throw an exception +containing the vital information required to do the prompting. + +The `build` command, and the platform-specific implementations, in the Titanium SDK define several +options that have prompt metadata, however it is unused. Depsite this, the `fields` library has +been mocked to be a noop. diff --git a/src/project/legacy/patch/android.js b/src/project/legacy/patch/android.js index e19d639..0ea7346 100644 --- a/src/project/legacy/patch/android.js +++ b/src/project/legacy/patch/android.js @@ -1,3 +1,5 @@ +/* eslint-disable promise/no-callback-in-promise */ + import tunnel from '../tunnel'; import * as version from '../../../lib/version'; @@ -8,7 +10,7 @@ export function detect(config = {}, opts = {}, callback) { devices: info.devices, emulators: info.emulators, issues: [], - ndk: processSDK(info.ndk), + ndk: processNDK(info.ndk), sdk: processSDK(info.sdk, config, opts.vendorDependencies), targets: results.targets, vendorDependencies: opts.vendorDependencies || {} @@ -25,6 +27,7 @@ export function detect(config = {}, opts = {}, callback) { } function processNDK(ndks) { + // TODO return null; } diff --git a/src/project/legacy/patch/fields.js b/src/project/legacy/patch/fields.js index ef6311a..76a3c0a 100644 --- a/src/project/legacy/patch/fields.js +++ b/src/project/legacy/patch/fields.js @@ -1,26 +1,20 @@ export function setup() { } -export function file(args) { +export function file() { return { - prompt(callback) { - // callback(err, value) - } + prompt() {} }; } -export function select(args) { +export function select() { return { - prompt(callback) { - // callback(err, value) - } + prompt() {} }; } -export function text(args) { +export function text() { return { - prompt(callback) { - // callback(err, value) - } + prompt() {} }; } diff --git a/src/project/legacy/patch/ios.js b/src/project/legacy/patch/ios.js index 83e3ccd..a79c6ae 100644 --- a/src/project/legacy/patch/ios.js +++ b/src/project/legacy/patch/ios.js @@ -1,3 +1,5 @@ +/* eslint-disable promise/no-callback-in-promise */ + import tunnel from '../tunnel'; import * as version from '../../../lib/version'; diff --git a/src/project/legacy/ti/cli.js b/src/project/legacy/ti/cli.js index 3886460..ac8f989 100644 --- a/src/project/legacy/ti/cli.js +++ b/src/project/legacy/ti/cli.js @@ -1,3 +1,5 @@ +/* eslint-disable promise/no-callback-in-promise */ + import fs from 'fs-extra'; import getOSInfo from '../../../lib/os'; import path from 'path'; @@ -14,8 +16,48 @@ import { snooplogg } from 'cli-kit'; const { highlight, gray, green, magenta, red, yellow } = snooplogg.styles; +/** + * The legacy Titanium CLI version. Since this legacy shim is intended to simulate the Titanium CLI + * v5, we keep the major as `5`, but set the minor to something high that will never exist. + */ const CLI_VERSION = '5.999.0'; +/** + * A helper function that creates an error and defines an optional code and prompt metadata. + * + * @param {Object} opts - Various options. + * @param {String} [opts.code] - A custom error code. This value should begin with an `E`. + * @param {String} opts.message - The error message. + * @param {Object} [opts.option] - A CLI option to autogenerate the prompt metadata from. + * @param {Object} [opts.prompt] - Prompt metadata. + * @returns {Error} + */ +function INVALID_ARGUMENT({ code, msg, option, prompt }) { + const err = new TypeError(msg); + if (code !== undefined) { + err.code = code; + } + if (option?.values) { + err.prompt = { + choices: option.values.map(value => ({ value })), + message: `Please select a valid ${option.name} value`, + name: option.name, + required: true, + type: 'select' + }; + } else if (option) { + err.prompt = { + message: `Please enter a valid ${option.name}`, + name: option.name, + required: true, + type: 'text' + }; + } else if (prompt !== undefined) { + err.prompt = prompt; + } + return err; +} + /** * The Titanium CLI v5 requires the `--sdk ` to equal the `` in the * tiapp.xml. If they don't match, node-titanium-sdk's `ti.validateCorrectSDK()` will spawn a new @@ -32,10 +74,31 @@ class GracefulShutdown extends Error {} * Command specific data including the command module and configuration. */ class Context { + /** + * A legacy Titanium CLI version. + * @type {String} + */ cliVersion = CLI_VERSION; + /** + * A reference to the associated platform-specific context. This is only used for the `build` + * command. + * @type {Context} + */ platform = null; + /** + * Initializes the context and validates that the command JS file exists. + * + * @param {Object} opts - Various options. + * @param {Object} [opts.conf] - The command's configuration. This is used when the `build` + * command initializes a platform-specific context. + * @param {String} opts.name - The command name. + * @param {Context} [opts.parent] - A reference to the parent context. This is used to + * associate a platform-specific context with the `build` command context. + * @param {String} opts.path - The path to the command JS file. + * @access public + */ constructor({ conf, name, parent, path }) { this.conf = conf || {}; this.name = name; @@ -47,8 +110,17 @@ class Context { } } + /** + * Loads a command JS file and if the command is the `build` command, it also initializes the + * platform-specific context. + * + * @param {CLI} cli - A reference to the main CLI instance. + * @returns {Promise} + * @access public + */ async load(cli) { tunnel.log(`Loading command file: ${highlight(this.path)}`); + // eslint-disable-next-line security/detect-non-literal-require this.module = require(this.path); if (this.module.cliVersion && !version.satisfies(this.cliVersion, this.module.cliVersion)) { @@ -73,16 +145,17 @@ class Context { // specific command const { platform } = cli.argv; if (!platform) { - const err = new TypeError('Missing required "platform"'); - err.code = 'EPLATFORM'; - err.prompt = { - choices: this.conf.options.platform.values.map(platform => ({ value: platform })), - message: 'For which platform do you want to build?', - name: 'platform', - required: true, - type: 'select' - }; - throw err; + throw INVALID_ARGUMENT({ + msg: 'Missing required "platform"', + code: 'EPLATFORM', + prompt: { + choices: this.conf.options.platform.values.map(platform => ({ value: platform })), + message: 'For which platform do you want to build?', + name: 'platform', + required: true, + type: 'select' + } + }); } // `this.conf.platforms` is an object of platform names to platform-specific options @@ -121,8 +194,7 @@ export default class CLI { static HOOK_PRIORITY_DEFAULT = 1000; /** - * The Titanium CLI version. Since this legacy shim is intended to simulate the Titanium CLI - * v5, we keep the major as `5`, but set the minor to something high that will never exist. + * The legacy Titanium CLI version. * @type {String} */ version = CLI_VERSION; @@ -155,7 +227,7 @@ export default class CLI { }; /** - * The logger object. + * The legacy Titanium CLI logger object. The original supports log levels, this one does not. * @type {Object} */ logger = { @@ -284,10 +356,28 @@ export default class CLI { tunnel.call('/telemetry', { event: type === 'ti.apiusage' ? type : event, ...data }); } + /** + * An alias for `on()`. + * + * @param {*} ...args - An event name and callback. + * @returns {CLI} + * @deprecated + * @access public + */ addHook(...args) { return this.on(...args); } + /** + * Defines a hook function that will emit an event before and after the hooked function is + * invoked. + * + * @param {String} name - The name of hook event. + * @param {Object} [ctx] - The `this` context to bind the callbacks to. + * @param {Function} [fn] - The function being hooked. + * @returns {Function} + * @access public + */ createHook(name, ctx, fn) { let dataPayload = {}; @@ -315,23 +405,22 @@ export default class CLI { .then(async () => { // call all pre filters await pres - .reduce((promise, pre) => { - return promise.then(() => new Promise((resolve, reject) => { - if (pre.length >= 2) { - pre.call(ctx, data, (err, newData) => { - if (err) { - return reject(err); - } else if (newData) { - data = newData; - } - resolve(); - }); - } else { - pre.call(ctx, data); + // eslint-disable-next-line promise/no-nesting + .reduce((promise, pre) => promise.then(() => new Promise((resolve, reject) => { + if (pre.length >= 2) { + pre.call(ctx, data, (err, newData) => { + if (err) { + return reject(err); + } else if (newData) { + data = newData; + } resolve(); - } - })); - }, Promise.resolve()); + }); + } else { + pre.call(ctx, data); + resolve(); + } + })), Promise.resolve()); if (data.fn) { data.result = await new Promise((resolve, reject) => { @@ -345,6 +434,7 @@ export default class CLI { // call all post filters await posts + // eslint-disable-next-line promise/no-nesting .reduce((promise, post) => promise.then(() => new Promise((resolve, reject) => { if (post.length >= 2) { post.call(ctx, data, (err, newData) => { @@ -386,24 +476,31 @@ export default class CLI { }; } - emit(hookNames, data, callback) { + /** + * Emits an event along with a data payload. + * + * @param {String|Array.} name - One or more events to emit. + * @param {Object} [data] - An optional data payload. + * @param {Function} [callback] A function to call once the emitting has finished. If no + * callback is specified, this function will return a promise instead. + * @returns {CLI|Promise} + * @access public + */ + emit(name, data, callback) { if (typeof data === 'function') { callback = data; data = null; } - // make sure hookNames is an array - hookNames = unique(hookNames); - // create each hook and immediately fire them - const promise = hookNames + const promise = unique(name) .reduce((promise, name) => promise.then(() => new Promise((resolve, reject) => { const hook = this.createHook(name, data); tunnel.log(`Emitting ${name}`); hook((err, result) => { err ? reject(err) : resolve(result); }); - })), Promise.resolve()); + })), Promise.resolve(this)); if (typeof callback !== 'function') { return promise; @@ -416,6 +513,12 @@ export default class CLI { return this; } + /** + * Executes the command's `run()` method. + * + * @returns {Promise} + * @access private + */ async executeCommand() { await this.emit('cli:pre-execute', { cli: this, command: this.command }); @@ -456,10 +559,24 @@ export default class CLI { }); } + /** + * An alias for `emit()`. + * + * @param {*} ...args - The hook names, data, and callback. + * @returns {CLI} + * @deprecated + * @access public + */ fireHook(...args) { return this.emit(...args); } + /** + * The main pipeline for running the CLI. + * + * @returns {Promise} + * @access public + */ async go() { await this.emit('cli:go', { cli: this }); await this.command.load(this); @@ -468,6 +585,13 @@ export default class CLI { await this.executeCommand(); } + /** + * Creates a legacy Titanium CLI config object from the Titanium plugin config. + * + * @param {Object} config - The Titanium plugin config. + * @returns {Object} + * @access private + */ initConfig(config) { return Object.defineProperties( mergeDeep({ @@ -516,6 +640,14 @@ export default class CLI { ); } + /** + * Registers an event callback. + * + * @param {String} name - The name of the event. + * @param {Function} callback - The listener to register. + * @returns {CLI} + * @access public + */ on(name, callback) { let priority = CLI.HOOK_PRIORITY_DEFAULT; let i; @@ -529,6 +661,7 @@ export default class CLI { if (callback.pre) { const h = this.hooks.pre[name] || (this.hooks.pre[name] = []); callback.pre.priority = priority; + // eslint-disable-next-line no-empty for (i = 0; i < h.length && priority >= h[i].priority; i++) {} h.splice(i, 0, callback.pre); } @@ -536,6 +669,7 @@ export default class CLI { if (callback.post) { const h = this.hooks.post[name] || (this.hooks.post[name] = []); callback.post.priority = priority; + // eslint-disable-next-line no-empty for (i = 0; i < h.length && priority >= h[i].priority; i++) {} h.splice(i, 0, callback.post); } @@ -604,14 +738,96 @@ export default class CLI { } } + /** + * Validates the arguments. First it checks against the built-in naive validation such as + * required or against a list of values. Next it calls each option's validator. After that it + * calls the command's validator. Lastly it calls each option's value callback. + * + * @returns {Promise} + * @access private + */ async validate() { await this.emit('cli:pre-validate', { cli: this, command: this.command }); + // step 0: build a list of all options so we can sort them + const options = []; + for (const ctx of [ this.command, this.command?.platform ]) { + if (ctx?.conf.options) { + for (const [ name, opt ] of Object.entries(ctx.conf.options)) { + options.push({ + // this is a sacrificial wrapper that we can throw away after firing and it + // handles the boilerplate of checking the callback and result + callback(value) { + let result; + if (typeof opt.callback === 'function') { + // technically `opt.callback()` can throw a `GracefulShutdown` error + // for both `build` and `clean` commands during the `project-dir` + // callback if the `` in the tiapp.xml is not the same + // version loaded by the Titanium SDK, but luckily that will never :) + result = opt.callback(value || ''); + } + delete this.callback; + return result !== undefined ? result : value; + }, + name, + order: opt.order, + required: opt.required, + validate: opt.validate, + values: !opt.skipValueCheck && Array.isArray(opt.values) ? opt.values : null, + verifyIfRequired: opt.verifyIfRequired + }); + } + } + } + + options.sort((a, b) => ~~b.order - ~~a.order); + // step 1: determine invalid or missing options + for (const opt of options) { + const { name } = opt; + const value = this.argv[name]; + + if (value !== undefined && opt.values && !opt.values.includes(value)) { + throw INVALID_ARGUMENT({ + msg: `Invalid ${name} value "${value}"`, + option: opt + }); + } - // step 2: determine all missing arguments + if (value !== undefined) { + if (typeof opt.validate === 'function') { + await new Promise((resolve, reject) => { + opt.validate(value, (err, value) => { + if (err) { + return reject(INVALID_ARGUMENT({ + msg: `Invalid ${name} value "${value}"`, + option: opt + })); + } - // step 3: run the command's validate() function, if exists + this.argv[name] = opt.callback(value); + resolve(); + }); + }); + } else { + this.argv[name] = opt.callback(value); + } + + // we need to check if the option is required + // sometimes required options such as `--device-id` allow an undefined value in the + // case when the value is derived by the config or is autoselected + } else if (opt.required && (typeof opt.verifyIfRequired !== 'function' || await new Promise(opt.verifyIfRequired))) { + throw INVALID_ARGUMENT({ + msg: `Missing required option: ${name}`, + option: opt + }); + } + } + + // note that we don't care about missing arguments because `build` and `clean` commands + // don't have any arguments! + + // step 2: run the command's validate() function, if exists const { validate } = this.command.module; if (validate && typeof validate === 'function') { @@ -625,16 +841,12 @@ export default class CLI { await this.emit('cli:post-validate', { cli: this, command: this.command }); - // step 4: fire all option callbacks - for (const ctx of [ this.command, this.command?.platform ]) { - if (ctx) { - for (const [ name, opt ] of Object.entries(ctx.conf.options)) { - if (typeof opt.callback === 'function') { - const val = opt.callback(this.argv[name] || ''); - if (val !== undefined) { - this.argv[name] = val; - } - } + // step 3: fire all option callbacks for any options we missed above + for (const opt of options) { + if (typeof opt.callback === 'function') { + const val = opt.callback(this.argv[opt.name] || ''); + if (val !== undefined) { + this.argv[opt.name] = val; } } } diff --git a/src/sdk/sdk-list-service.js b/src/sdk/sdk-list-service.js index 9fcc932..1ccfc0d 100644 --- a/src/sdk/sdk-list-service.js +++ b/src/sdk/sdk-list-service.js @@ -74,7 +74,7 @@ export default class SDKListService extends DataServiceDispatcher { /** * Finds an SDK by name (or version) or by `latest`. * - * @param [name='latest'] - The SDK name or version to search for. + * @param {String} [name='latest'] - The SDK name or version to search for. * @returns {Object} */ find(name) { From 0b5d0b9253cbbc307a570acd89cae18f8a4110e0 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Tue, 21 Jul 2020 02:31:50 -0500 Subject: [PATCH 11/39] chore: Update deps. refactor(legacy): Moved Legacy CLI out of project dir, then broken up into smaller files. feat(cli): Added Android and iOS build options to build/run commands. --- package.json | 3 +- src/cli/cli-service.js | 82 +++++++- src/cli/commands/build.js | 6 +- src/{project => }/legacy/README.md | 29 ++- src/{project => }/legacy/bootstrap.js | 1 + src/legacy/patch/android.js | 250 +++++++++++++++++++++++ src/{project => }/legacy/patch/fields.js | 0 src/legacy/patch/index.js | 20 ++ src/{project => }/legacy/patch/ios.js | 20 +- src/legacy/spawn.js | 101 +++++++++ src/{project => }/legacy/ti/cli.js | 173 ++-------------- src/legacy/ti/context.js | 125 ++++++++++++ src/legacy/ti/error.js | 35 ++++ src/{project => }/legacy/ti/logger.js | 0 src/legacy/ti/version.js | 5 + src/{project => }/legacy/tunnel.js | 18 +- src/lib/util.js | 10 + src/lib/version.js | 30 +++ src/project/legacy/patch/android.js | 107 ---------- src/project/legacy/patch/index.js | 25 --- src/project/project-service.js | 81 ++------ 21 files changed, 737 insertions(+), 384 deletions(-) rename src/{project => }/legacy/README.md (78%) rename src/{project => }/legacy/bootstrap.js (97%) create mode 100644 src/legacy/patch/android.js rename src/{project => }/legacy/patch/fields.js (100%) create mode 100644 src/legacy/patch/index.js rename src/{project => }/legacy/patch/ios.js (95%) create mode 100644 src/legacy/spawn.js rename src/{project => }/legacy/ti/cli.js (78%) create mode 100644 src/legacy/ti/context.js create mode 100644 src/legacy/ti/error.js rename src/{project => }/legacy/ti/logger.js (100%) create mode 100644 src/legacy/ti/version.js rename src/{project => }/legacy/tunnel.js (87%) delete mode 100644 src/project/legacy/patch/android.js delete mode 100644 src/project/legacy/patch/index.js diff --git a/package.json b/package.json index f0ab082..1497c93 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "appcd-path": "^2.0.1", "appcd-util": "^3.0.0", "chalk": "^4.1.0", - "cli-kit": "^1.2.4", + "cli-kit": "^1.3.0", "colors": "^1.4.0", "dateformat": "^3.0.3", "enquirer": "^2.3.6", @@ -39,6 +39,7 @@ "source-map-support": "^0.5.19", "titaniumlib": "^3.0.0", "tmp": "^0.2.1", + "v8-compile-cache": "^2.1.1", "yauzl": "^2.10.0" }, "devDependencies": { diff --git a/src/cli/cli-service.js b/src/cli/cli-service.js index 73bda61..0f5d8a6 100644 --- a/src/cli/cli-service.js +++ b/src/cli/cli-service.js @@ -5,11 +5,20 @@ import getPort from 'get-port'; import path from 'path'; import { get } from 'appcd-util'; -import { parseVersion } from '../lib/util'; +import { isFile } from 'appcd-fs'; +import { capitalize, parseVersion } from '../lib/util'; +import { spawnLegacyCLI } from '../legacy/spawn'; +import { Tiapp } from 'titaniumlib'; const { log } = appcd.logger('cli-service'); const { highlight } = appcd.logger.styles; +/** + * A cache of platform specific build options by Titanium SDK path. + * @type {Object} + */ +const buildOptionsCache = {}; + /** * Defines a service endpoint for defining, processing, and dispatching Titanium CLI commands. */ @@ -38,12 +47,83 @@ export default class CLIService extends Dispatcher { version: ({ data }) => parseVersion(data.userAgent) }); + // we need to add platform specific options for the build/run help, so first we listen for + // the help command, then we add in the options before the help is generated + cli.on('exec', async ({ cmd, data, contexts }) => { + // we need the help command, the build/run command, and a cwd containing a tiapp + cmd = data?.cwd && cmd?.name === 'help' && contexts[1]; + if (cmd && (cmd.name === 'build' || cmd.name === 'run')) { + const tiappFile = path.resolve(data.cwd, 'tiapp.xml'); + if (!isFile(tiappFile)) { + return; + } + + cmd.on('generateHelp', async ({ ctx }) => { + // const tiapp = new Tiapp({ file: tiappFile }); + // const sdk = tiapp.get('sdk-version'); + const sdk = '9.0.3.GA'; + const sdkInfo = (await appcd.call('/sdk/find', { data: { name: sdk } })).response; + + let buildOptions = buildOptionsCache[sdkInfo.path]; + if (!buildOptions) { + // load the Android and iOS options directly from the SDK + const config = await spawnLegacyCLI({ + data: { + command: 'build', + sdkPath: sdkInfo.path, + type: 'build-options' + } + }); + + const platforms = config?.platforms; + if (!platforms) { + return; + } + + // copy the platform-specific options into a cli-kit friendly format + buildOptions = []; + for (const conf of Object.values(platforms)) { + const options = {}; + + for (const [ name, flag ] of Object.entries(conf.flags)) { + if (!flag.hidden) { + options[`--${name}`] = { desc: capitalize(flag.desc) }; + } + } + + for (const [ name, option ] of Object.entries(conf.options)) { + if (!option.hidden) { + let format = option.abbr ? `-${option.abbr}, ` : ''; + format += `--${name} ${option.required ? '<' : '['}${option.hint || 'value'}${option.required ? '>' : ']'}`; + options[format] = { desc: capitalize(option.desc) }; + } + } + + if (Object.keys(options).length) { + buildOptions.push(conf.title, options); + } + } + + buildOptionsCache[sdkInfo.path] = buildOptions; + } + + if (buildOptions.length) { + log(buildOptions); + ctx.option(buildOptions); + } + }); + } + }); + + // find an available port to listen on const port = await getPort({ port: get(cfg, 'port', 1733) }); + // start the cli-kit server this.server = await cli.listen({ port }); + // register the discovery endpoint for the cli-kit server this.register('/', () => { log(`Returning CLI server URL: ${highlight(`ws://127.0.0.1:${port}`)}`); return { diff --git a/src/cli/commands/build.js b/src/cli/commands/build.js index 335add1..7834a27 100644 --- a/src/cli/commands/build.js +++ b/src/cli/commands/build.js @@ -11,9 +11,9 @@ export default { await promptLoop({ ctx, data: { - force: ctx.argv.force, - platform: ctx.argv.platform, - projectDir: ctx.argv.projectDir + force: ctx.argv.force, + platform: ctx.argv.platform, + projectDir: ctx.argv.projectDir }, path: '/project/build', ns: 'cli:build' diff --git a/src/project/legacy/README.md b/src/legacy/README.md similarity index 78% rename from src/project/legacy/README.md rename to src/legacy/README.md index 5e41827..1f6c721 100644 --- a/src/project/legacy/README.md +++ b/src/legacy/README.md @@ -1,5 +1,8 @@ # Legacy Titanium CLI +This is a stripped down version of the Titanium CLI v5. Several things such as argument parsing and +internationalization have been removed as they are not needed and speed is critical. + The Titanium SDK contains commands such as `build` and `clean`. These commands are tightly coupled with the Titanium CLI v5 and older. In order to be able to execute these commands, we need to shim the bare minimum APIs and features of the Titanium CLI v5. @@ -7,22 +10,22 @@ the bare minimum APIs and features of the Titanium CLI v5. While this legacy CLI attempts to maintain backwards compatibility, the following APIs and features are no longer supported. -## `cli.version` +#### `cli.version` The Titanium CLI version is set to `5.999.0` as we don't want to break compatibility with Titanium CLI plugins, but we also want to signify that this is not the real Titanium CLI v5. -## CLI argument parser +#### CLI argument parser The legacy CLI no longer parses raw CLI arguments. It expects the `argv` values to be passed in as a JSON object. -## i18n - Internationalization +#### i18n - Internationalization Locale is no longer detected and the locale is forced to `en-US`. We have very little translated strings that users are not going to notice. -## Selected Titanium SDK and forking correct Titanium SDK +#### Selected Titanium SDK and forking correct Titanium SDK The entire selected Titanium SDK system of the Titanium CLI has been removed. The `` in the `tiapp.xml` is the source of truth. @@ -30,7 +33,7 @@ in the `tiapp.xml` is the source of truth. Because this legacy CLI will only ever be invoked with a valid Titanium SDK, various APIs are no longer needed. See `cli.argv` below. -## `cli.argv._`, `cli.argv.$`, `cli.argv.$_`, `cli.argv.$0` +#### `cli.argv._`, `cli.argv.$`, `cli.argv.$_`, `cli.argv.$0` `cli.argv` contained the raw CLI arguments and process name as well as the parsed arguments. While the `cli.argv` will continue to contain the parsed arguments (passed in as a JSON object), the @@ -40,7 +43,7 @@ Note that node-titanium-sdk's `validateCorrectSDK()` does reference these variab to fork using the correct `--sdk` from the app's `tiapp.xml`, however it's a non-issue being that the correct SDK version will always be set. -## `cli.on('cli:command-not-found')` +#### `cli.on('cli:command-not-found')` This event was emitted when the specified command was not found so that the Titanium CLI could check if there were any Titanium SDKs installed and if you happened to misspell the command. @@ -48,11 +51,11 @@ check if there were any Titanium SDKs installed and if you happened to misspell Since this legacy CLI will only ever be called with the either the `build` or `clean` command, it is impossible for the command to not exist. -## Built-in Titanium CLI hook plugins +#### #Built-in Titanium CLI hook plugins This legacy CLI does not contain any built-in CLI hook plugins such as `hooks/tisdk3fixes.js`. -## Config changes +#### Config changes The configuration no longer contains settings for environment detection such as Android, iOS, JDK, etc. Those config settings are in their respective appcd plugin config files and the Appc @@ -60,12 +63,12 @@ Daemon user config file. This legacy Titanium CLI will not read or write the original Titanium CLI config file. -## Log changes +#### Log changes The `--log-level` option has been dropped. There is no log message filtering effectively setting the log level to `"trace"`. -## Prompting +#### Prompting The legacy Titanium CLI does not prompt for missing or invalid values. It will throw an exception containing the vital information required to do the prompting. @@ -73,3 +76,9 @@ containing the vital information required to do the prompting. The `build` command, and the platform-specific implementations, in the Titanium SDK define several options that have prompt metadata, however it is unused. Depsite this, the `fields` library has been mocked to be a noop. + +#### Android Detection + +When an Android app is being built, it needs to query the Android development environment. This +functionality is now performed by the `android` appcd plugin which does not validate the Java/JDK +and thus will not report any issues with Java being misconfigured. diff --git a/src/project/legacy/bootstrap.js b/src/legacy/bootstrap.js similarity index 97% rename from src/project/legacy/bootstrap.js rename to src/legacy/bootstrap.js index 28f60bd..3750e71 100644 --- a/src/project/legacy/bootstrap.js +++ b/src/legacy/bootstrap.js @@ -8,6 +8,7 @@ if (!process.connected) { process.exit(2); } +import 'v8-compile-cache'; import 'colors'; import './patch'; import './tunnel'; diff --git a/src/legacy/patch/android.js b/src/legacy/patch/android.js new file mode 100644 index 0000000..c7de77c --- /dev/null +++ b/src/legacy/patch/android.js @@ -0,0 +1,250 @@ +/* eslint-disable promise/no-callback-in-promise */ + +import tunnel from '../tunnel'; +import * as version from '../../lib/version'; +import { snooplogg } from 'cli-kit'; + +const { alert } = snooplogg.styles; + +/** + * Detects Android development environment by calling the `android` appcd plugin and translating + * the results into a Titanium SDK compatible structure. + * + * @param {Object} config - The Titanium CLI config object. + * @param {Object} opts - Various options. + * @param {Object} opts.packageJson - The Android platform-specific `package.json`. + * @param {Function} callback - A function to call with the detection results. Note that this + * function receives a single argument with the info. Any errors must be silenced. + */ +export function detect(config = {}, { packageJson } = {}, callback) { + tunnel.call('/android/2.x/info') + .then(({ response: info }) => { + const { vendorDependencies } = packageJson; + const results = { + avds: info.emulators, + devices: info.devices, + issues: [], + ndk: processNDK(info.ndks), + sdk: processSDK(info.sdks, config, vendorDependencies), + targets: {}, + vendorDependencies: vendorDependencies || {} + }; + + processTargets(results, info.sdks, vendorDependencies); + + if (!results.ndk) { + results.issues.push({ + id: 'ANDROID_NDK_NOT_FOUND', + type: 'warning', + message: `Unable to locate an Android NDK. +Without the NDK, you will not be able to build native Android Titanium modules. +To install the Android NDK, use Android Studio's SDK Manager. +If you have already installed the Android NDK, configure the location by running: appcd config push android.ndk.searchPaths /path/to/android-ndk` + }); + } + + if (results.sdk) { + const appendInfo = msg => { + return `${msg} + +Current installed Android SDK tools: + Android SDK Tools: ${results.sdk.tools.version || 'not installed'} (Supported: ${vendorDependencies['android tools']}) + Android SDK Platform Tools: ${results.sdk.platformTools.version || 'not installed'} (Supported: ${vendorDependencies['android platform tools']}) + Android SDK Build Tools: ${results.sdk.buildTools.version || 'not installed'} (Supported: ${vendorDependencies['android build tools']}) + +Make sure you have the latest Android SDK Tools, Platform Tools, and Build Tools installed.`; + }; + + if (!results.sdk.buildTools.supported) { + results.issues.push({ + id: 'ANDROID_BUILD_TOOLS_NOT_SUPPORTED', + type: 'error', + message: appendInfo(`Android Build Tools ${results.sdk.buildTools.version} are not supported by Titanium`) + }); + } + + if (results.sdk.buildTools.notInstalled) { + const preferred = config.get('titanium.android.buildTools.selectedVersion'); + results.issues.push({ + id: 'ANDROID_BUILD_TOOLS_CONFIG_SETTING_NOT_INSTALLED', + type: 'error', + message: appendInfo(`The selected version of Android SDK Build Tools (${preferred}) are not installed. +Please either install this version of the build tools or remove this setting by running: + ti config delete android.buildTools.selectedVersion +and + appcd config delete titanium.android.buildTools.selectedVersion`) + }); + } + + // check if the sdk is missing any commands + var missing = [ 'adb', 'emulator', 'mksdcard', 'zipalign', 'aapt', 'aidl', 'dx' ].filter(cmd => !results.sdk.executables[cmd]); + if (missing.length && results.sdk.buildTools.supported) { + results.issues.push({ + id: 'ANDROID_SDK_MISSING_PROGRAMS', + type: 'error', + message: appendInfo(`Missing required Android SDK tool${missing.length !== 1 ? 's' : ''}: ${missing.join(', ')}`) + }); + } + } else { + results.issues.push({ + id: 'ANDROID_SDK_NOT_FOUND', + type: 'error', + message: `Unable to locate an Android SDK. +To install the Android SDK, use Android Studio's SDK Manager. +If you have already installed the Android SDK, configure the location by running: appcd config push android.sdk.searchPaths /path/to/android-sdk` + }); + } + + callback(results); + }) + .catch(err => { + tunnel.log(alert(err.stack)); + callback({ + issues: [], + sdk: null + }); + }); +} + +function processNDK(ndks) { + if (!ndks.length) { + return {}; + } + return ndks.length > 1 && ndks.find(ndk => ndk.default) || ndks[0]; +} + +function processSDK(sdks, config, vendorDependencies = {}) { + if (!sdks.length) { + return null; + } + + const sdk = sdks.length > 1 && sdks.find(sdk => sdk.default) || sdks[0]; + + const results = { + path: sdk.path, + executables: { + adb: null, + android: null, + emulator: null, + mksdcard: null, + zipalign: null, + aapt: null, + aidl: null, + dx: null, + apksigner: null + }, + dx: null, + proguard: null, + tools: { + path: null, + supported: null, + version: null + }, + platformTools: { + path: null, + supported: null, + version: null + }, + buildTools: { + maxSupported: null, + notInstalled: false, + path: null, + supported: null, + version: null + } + }; + + if (sdk.platformTools) { + results.executables.adb = sdk.platformTools.executables.adb; + } + + if (sdk.tools) { + results.executables.emulator = sdk.tools.executables.emulator; + } + + if (sdk.buildTools) { + const supportedRange = vendorDependencies['android build tools']; + const min = supportedRange && version.parseMin(supportedRange); + const preferred = config.get('titanium.android.buildTools.selectedVersion'); + const installedBuildTools = sdk.buildTools + .map(b => { + b.supported = supportedRange && version.satisfies(b.version, supportedRange) ? true : min && version.lt(b.version, min) ? false : 'maybe'; + return b; + }) + .sort((a, b) => version.compare(a.version, b.version)); + + let buildTools; + if (preferred) { + buildTools = installedBuildTools.find(bt => version.eq(bt.version, preferred)); + results.buildTools.notInstalled = !buildTools; + } else { + buildTools = installedBuildTools[0]; + results.buildTools.notInstalled = false; + } + + if (buildTools) { + results.buildTools.path = buildTools.path; + results.buildTools.version = buildTools.version; + results.buildTools.supported = buildTools.supported; + if (supportedRange) { + results.buildTools.maxSupported = version.parseMax(supportedRange); + } + results.dx = buildTools.dx; + Object.assign(results.executables, buildTools.executables); + } + } + + return results; +} + +function processTargets(results, sdks, vendorDependencies) { + let idx = 1; + let valid = 0; + + for (const sdk of sdks) { + for (const platform of sdk.platforms) { + const supported = !~~platform.apiLevel || version.satisfies(platform.apiLevel, vendorDependencies['android sdk']); + + if (supported) { + valid++; + } else { + results.issues.push({ + id: 'ANDROID_API_TOO_OLD', + type: 'warning', + message: `Android API ${platform.name} (${platform.id}) is too old. +The minimum supported Android SDK platform API level API level ${version.parseMin(vendorDependencies['android sdk'])}.` + }); + } + + results.targets[String(idx++)] = { + abis: platform.abis, + aidl: platform.aidl, + androidJar: platform.androidJar, + 'api-level': platform.apiLevel, + id: platform.sdk, + name: platform.name, + path: platform.path, + revision: platform.revision, + sdk: platform.apiLevel, + skins: platform.skins, + supported, + type: 'platform', + version: platform.version + }; + } + } + + if (idx === 1) { + results.issues.push({ + id: 'ANDROID_NO_APIS', + type: 'error', + message: 'No Android APIs found.\nRun \'Android Studio\' to install the latest Android APIs.' + }); + } else if (!valid) { + results.issues.push({ + id: 'ANDROID_NO_VALID_APIS', + type: 'warning', + message: 'No supported Android APIs found\nRun \'Android Studio\' to install the latest Android APIs.' + }); + } +} diff --git a/src/project/legacy/patch/fields.js b/src/legacy/patch/fields.js similarity index 100% rename from src/project/legacy/patch/fields.js rename to src/legacy/patch/fields.js diff --git a/src/legacy/patch/index.js b/src/legacy/patch/index.js new file mode 100644 index 0000000..99db77e --- /dev/null +++ b/src/legacy/patch/index.js @@ -0,0 +1,20 @@ +import Module from 'module'; +import path from 'path'; + +/** + * Patches our system info plugins into Titanium SDK's system info library calls. + */ +const lookup = { + fields: path.resolve(__dirname, 'fields.js'), + ioslib: path.resolve(__dirname, 'ios.js'), + 'node-titanium-sdk/lib/android': path.resolve(__dirname, 'android.js') +}; + +const load = Module._load; +Module._load = (request, parent, isMain) => { + const module = load(request, parent, isMain); + if (lookup[request] && parent && path.basename(parent.filename) === '_build.js') { + Object.assign(module, load(lookup[request], parent, isMain)); + } + return module; +}; diff --git a/src/project/legacy/patch/ios.js b/src/legacy/patch/ios.js similarity index 95% rename from src/project/legacy/patch/ios.js rename to src/legacy/patch/ios.js index a79c6ae..60c4f0f 100644 --- a/src/project/legacy/patch/ios.js +++ b/src/legacy/patch/ios.js @@ -1,11 +1,20 @@ /* eslint-disable promise/no-callback-in-promise */ import tunnel from '../tunnel'; -import * as version from '../../../lib/version'; +import * as version from '../../lib/version'; +import { snooplogg } from 'cli-kit'; + +const { alert } = snooplogg.styles; + +let cache; export function detect(opts = {}, callback) { - tunnel.call('/ios/1.x/info') - .then(info => { + if (cache) { + return callback(null, cache); + } + + tunnel.call('/ios/2.x/info') + .then(({ response: info }) => { const results = { certs: {}, devices: info.devices, @@ -22,11 +31,12 @@ export function detect(opts = {}, callback) { processProvisioning(info, results); processXcodes(info, results, opts); + cache = results; callback(null, results); }) .catch(err => { - console.error(err); - callback(); + tunnel.log(alert(err.stack)); + callback(err); }); } diff --git a/src/legacy/spawn.js b/src/legacy/spawn.js new file mode 100644 index 0000000..0f27d9f --- /dev/null +++ b/src/legacy/spawn.js @@ -0,0 +1,101 @@ +import path from 'path'; +import { spawn } from 'appcd-subprocess'; + +const { log } = appcd.logger('legacy:spawn'); + +/** + * Spawns the Legacy CLI and resolves once the command finishes. + * + * @param {Object} opts - Various options. + * @param {DispatcherContext} [opts.ctx] - The dispatcher context. + * @param {Object} [opts.data] - A data payload to send over the IPC tunnel to the Legacy Titanium + * CLI. + * @returns {Promise} + */ +export async function spawnLegacyCLI({ ctx, data }) { + log('Spawning legacy Titanium CLI bootstrap...'); + const { child } = spawn({ + command: process.execPath, + args: [ path.resolve(__dirname, 'bootstrap.js') ], + options: { + env: Object.assign({ FORCE_COLOR: 1 }, process.env), + stdio: [ 'pipe', 'pipe', 'pipe', 'ipc' ] + } + }); + + child.stdout.on('data', data => { + if (ctx) { + ctx.response.write(data.toString()); + } else { + log(data.toString()); + } + }); + + child.stderr.on('data', data => { + if (ctx) { + ctx.response.write(data.toString()); + } else { + log(data.toString()); + } + }); + + return await new Promise((resolve, reject) => { + child.on('close', code => { + log(`Legacy Titanium CLI bootstrap exited (code ${code || 0})`); + if (ctx) { + resolve(); + ctx.response.end(); + } + }); + + child.on('message', async msg => { + switch (msg.type) { + case 'call': + const { id, path, data } = msg; + if (id && path) { + let response; + try { + response = await appcd.call(path, data); + } catch (err) { + child.send({ + error: err, + id, + type: 'error' + }); + throw err; + } + + try { + child.send({ + id, + response, + type: 'response' + }); + } catch (err) { + console.error(err); + } + } + return; + + case 'error': + { + const err = new Error(msg.message); + return reject(Object.assign(err, msg)); + } + + case 'json': + return resolve(msg.data); + + case 'log': + return console.log(...msg.args); + + case 'telemetry': + return appcd.telemetry(msg.payload); + } + }); + + log('Sending data to bootstrap:'); + log(data); + child.send(data); + }); +} diff --git a/src/project/legacy/ti/cli.js b/src/legacy/ti/cli.js similarity index 78% rename from src/project/legacy/ti/cli.js rename to src/legacy/ti/cli.js index ac8f989..0f69866 100644 --- a/src/project/legacy/ti/cli.js +++ b/src/legacy/ti/cli.js @@ -1,62 +1,22 @@ /* eslint-disable promise/no-callback-in-promise */ +import Context from './context'; import fs from 'fs-extra'; -import getOSInfo from '../../../lib/os'; +import getOSInfo from '../../lib/os'; import path from 'path'; import tunnel from '../tunnel'; import vm from 'vm'; -import * as version from '../../../lib/version'; +import * as version from '../../lib/version'; +import { CLI_VERSION } from './version'; import { expandPath } from 'appcd-path'; import { format } from 'util'; import { get, mergeDeep, set, unique } from 'appcd-util'; -import { isFile } from 'appcd-fs'; +import { INVALID_ARGUMENT } from './error'; import { sdk } from 'titaniumlib'; import { snooplogg } from 'cli-kit'; -const { highlight, gray, green, magenta, red, yellow } = snooplogg.styles; - -/** - * The legacy Titanium CLI version. Since this legacy shim is intended to simulate the Titanium CLI - * v5, we keep the major as `5`, but set the minor to something high that will never exist. - */ -const CLI_VERSION = '5.999.0'; - -/** - * A helper function that creates an error and defines an optional code and prompt metadata. - * - * @param {Object} opts - Various options. - * @param {String} [opts.code] - A custom error code. This value should begin with an `E`. - * @param {String} opts.message - The error message. - * @param {Object} [opts.option] - A CLI option to autogenerate the prompt metadata from. - * @param {Object} [opts.prompt] - Prompt metadata. - * @returns {Error} - */ -function INVALID_ARGUMENT({ code, msg, option, prompt }) { - const err = new TypeError(msg); - if (code !== undefined) { - err.code = code; - } - if (option?.values) { - err.prompt = { - choices: option.values.map(value => ({ value })), - message: `Please select a valid ${option.name} value`, - name: option.name, - required: true, - type: 'select' - }; - } else if (option) { - err.prompt = { - message: `Please enter a valid ${option.name}`, - name: option.name, - required: true, - type: 'text' - }; - } else if (prompt !== undefined) { - err.prompt = prompt; - } - return err; -} +const { gray, green, magenta, red, yellow } = snooplogg.styles; /** * The Titanium CLI v5 requires the `--sdk ` to equal the `` in the @@ -70,118 +30,6 @@ function INVALID_ARGUMENT({ code, msg, option, prompt }) { */ class GracefulShutdown extends Error {} -/** - * Command specific data including the command module and configuration. - */ -class Context { - /** - * A legacy Titanium CLI version. - * @type {String} - */ - cliVersion = CLI_VERSION; - - /** - * A reference to the associated platform-specific context. This is only used for the `build` - * command. - * @type {Context} - */ - platform = null; - - /** - * Initializes the context and validates that the command JS file exists. - * - * @param {Object} opts - Various options. - * @param {Object} [opts.conf] - The command's configuration. This is used when the `build` - * command initializes a platform-specific context. - * @param {String} opts.name - The command name. - * @param {Context} [opts.parent] - A reference to the parent context. This is used to - * associate a platform-specific context with the `build` command context. - * @param {String} opts.path - The path to the command JS file. - * @access public - */ - constructor({ conf, name, parent, path }) { - this.conf = conf || {}; - this.name = name; - this.parent = parent; - this.path = path; - - if (!isFile(this.path)) { - throw new Error(`Command file not found: ${path}`); - } - } - - /** - * Loads a command JS file and if the command is the `build` command, it also initializes the - * platform-specific context. - * - * @param {CLI} cli - A reference to the main CLI instance. - * @returns {Promise} - * @access public - */ - async load(cli) { - tunnel.log(`Loading command file: ${highlight(this.path)}`); - // eslint-disable-next-line security/detect-non-literal-require - this.module = require(this.path); - - if (this.module.cliVersion && !version.satisfies(this.cliVersion, this.module.cliVersion)) { - throw new Error(`Command "${this.name}" is incompatible with this version of the Titanium CLI`); - } - - if (typeof this.module.run !== 'function') { - throw new Error(`Command "${this.name}" does not contain a valid run function`); - } - - this.conf = typeof this.module.config === 'function' ? this.module.config(cli.logger, cli.config, cli) : {}; - if (typeof this.conf === 'function') { - this.conf = await new Promise(resolve => this.conf(resolve)); - } - - if (this.name !== 'build') { - return; - } - - // the `build` command `--platform` option was hard wired into the CLI context, so - // unfortunately we need to do a bunch of `build` specific logic to load the platform - // specific command - const { platform } = cli.argv; - if (!platform) { - throw INVALID_ARGUMENT({ - msg: 'Missing required "platform"', - code: 'EPLATFORM', - prompt: { - choices: this.conf.options.platform.values.map(platform => ({ value: platform })), - message: 'For which platform do you want to build?', - name: 'platform', - required: true, - type: 'select' - } - }); - } - - // `this.conf.platforms` is an object of platform names to platform-specific options - if (this.conf.platforms && Object.prototype.hasOwnProperty.call(this.conf.platforms, platform)) { - const platformConf = this.conf.platforms[platform]; - - this.platform = new Context({ - conf: platformConf, - name: platform, - parent: this, - path: path.join(cli.sdk.path, platform) - }); - - this.platforms = { - [this.platform.name]: this.platform - }; - - cli.argv.platform = this.platform.name; // I think this is to normalize `iphone` to `ios` - cli.argv.$platform = platform; - - // find all platform hooks - cli.scanHooks(path.join(cli.sdk.path, this.platform.name, 'cli', 'hooks')); - } - } -} - /** * Controls the state and flow for running legacy Titanium SDK CLI commands such as `build` and * `clean`. @@ -292,8 +140,10 @@ export default class CLI { this.argv = { $command: opts.command, }; - for (const [ key, value ] of Object.entries(opts.argv)) { - this.argv[key] = this.argv[key.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`)] = value; + if (opts.argv) { + for (const [ key, value ] of Object.entries(opts.argv)) { + this.argv[key] = this.argv[key.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`)] = value; + } } // initialize the legacy environment info @@ -334,6 +184,7 @@ export default class CLI { throw new Error(`Invalid command "${cmd}"`); } this.command = this.cmds[cmd] = new Context({ + cli: this, name: cmd, path: path.join(this.sdk.path, 'cli', 'commands', `${cmd}.js`) }); @@ -579,7 +430,7 @@ export default class CLI { */ async go() { await this.emit('cli:go', { cli: this }); - await this.command.load(this); + await this.command.load(true); await this.emit('cli:command-loaded', { cli: this, command: this.command }); await this.validate(); await this.executeCommand(); diff --git a/src/legacy/ti/context.js b/src/legacy/ti/context.js new file mode 100644 index 0000000..668ef42 --- /dev/null +++ b/src/legacy/ti/context.js @@ -0,0 +1,125 @@ +import path from 'path'; +import tunnel from '../tunnel'; +import * as version from '../../lib/version'; + +import { CLI_VERSION } from './version'; +import { INVALID_ARGUMENT } from './error'; +import { isFile } from 'appcd-fs'; +import { snooplogg } from 'cli-kit'; + +const { highlight } = snooplogg.styles; + +/** + * Command specific data including the command module and configuration. + */ +export default class Context { + /** + * A legacy Titanium CLI version. + * @type {String} + */ + cliVersion = CLI_VERSION; + + /** + * A reference to the associated platform-specific context. This is only used for the `build` + * command. + * @type {Context} + */ + platform = null; + + /** + * Initializes the context and validates that the command JS file exists. + * + * @param {Object} opts - Various options. + * @param {CLI} opts.cli - A reference to the main CLI instance. + * @param {Object} [opts.conf] - The command's configuration. This is used when the `build` + * command initializes a platform-specific context. + * @param {String} opts.name - The command name. + * @param {Context} [opts.parent] - A reference to the parent context. This is used to + * associate a platform-specific context with the `build` command context. + * @param {String} opts.path - The path to the command JS file. + * @access public + */ + constructor({ cli, conf, name, parent, path }) { + this.cli = cli; + this.conf = conf || {}; + this.name = name; + this.parent = parent; + this.path = path; + + if (!isFile(this.path)) { + throw new Error(`Command file not found: ${path}`); + } + } + + /** + * Loads a command JS file and if the command is the `build` command, it also initializes the + * platform-specific context. + * + * @param {Boolean} [checkPlatform] - When `true` and this is the `build` command, then + * validate the platform argument. + * @returns {Promise} + * @access public + */ + async load(checkPlatform) { + tunnel.log(`Loading command file: ${highlight(this.path)}`); + // eslint-disable-next-line security/detect-non-literal-require + this.module = require(this.path); + + if (this.module.cliVersion && !version.satisfies(this.cliVersion, this.module.cliVersion)) { + throw new Error(`Command "${this.name}" is incompatible with this version of the Titanium CLI`); + } + + if (typeof this.module.run !== 'function') { + throw new Error(`Command "${this.name}" does not contain a valid run function`); + } + + this.conf = typeof this.module.config === 'function' ? this.module.config(this.cli.logger, this.cli.config, this.cli) : {}; + if (typeof this.conf === 'function') { + this.conf = await new Promise(resolve => this.conf(r => resolve(r))); + } + + if (!checkPlatform || this.name !== 'build') { + return; + } + + // the `build` command `--platform` option was hard wired into the CLI context, so + // unfortunately we need to do a bunch of `build` specific logic to load the platform + // specific command + const { platform } = this.cli.argv; + if (!platform) { + throw INVALID_ARGUMENT({ + msg: 'Missing required "platform"', + code: 'EPLATFORM', + prompt: { + choices: this.conf.options.platform.values.map(platform => ({ value: platform })), + message: 'For which platform do you want to build?', + name: 'platform', + required: true, + type: 'select' + } + }); + } + + // `this.conf.platforms` is an object of platform names to platform-specific options + if (this.conf.platforms && Object.prototype.hasOwnProperty.call(this.conf.platforms, platform)) { + const platformConf = this.conf.platforms[platform]; + + this.platform = new Context({ + conf: platformConf, + name: platform, + parent: this, + path: path.join(this.cli.sdk.path, platform) + }); + + this.platforms = { + [this.platform.name]: this.platform + }; + + this.cli.argv.platform = this.platform.name; // I think this is to normalize `iphone` to `ios` + this.cli.argv.$platform = platform; + + // find all platform hooks + this.cli.scanHooks(path.join(this.cli.sdk.path, this.platform.name, 'cli', 'hooks')); + } + } +} diff --git a/src/legacy/ti/error.js b/src/legacy/ti/error.js new file mode 100644 index 0000000..2208603 --- /dev/null +++ b/src/legacy/ti/error.js @@ -0,0 +1,35 @@ +/** + * A helper function that creates an error and defines an optional code and prompt metadata. + * + * @param {Object} opts - Various options. + * @param {String} [opts.code] - A custom error code. This value should begin with an `E`. + * @param {String} opts.message - The error message. + * @param {Object} [opts.option] - A CLI option to autogenerate the prompt metadata from. + * @param {Object} [opts.prompt] - Prompt metadata. + * @returns {Error} + */ +export function INVALID_ARGUMENT({ code, msg, option, prompt }) { + const err = new TypeError(msg); + if (code !== undefined) { + err.code = code; + } + if (option?.values) { + err.prompt = { + choices: option.values.map(value => ({ value })), + message: `Please select a valid ${option.name} value`, + name: option.name, + required: true, + type: 'select' + }; + } else if (option) { + err.prompt = { + message: `Please enter a valid ${option.name}`, + name: option.name, + required: true, + type: 'text' + }; + } else if (prompt !== undefined) { + err.prompt = prompt; + } + return err; +} diff --git a/src/project/legacy/ti/logger.js b/src/legacy/ti/logger.js similarity index 100% rename from src/project/legacy/ti/logger.js rename to src/legacy/ti/logger.js diff --git a/src/legacy/ti/version.js b/src/legacy/ti/version.js new file mode 100644 index 0000000..4ac837d --- /dev/null +++ b/src/legacy/ti/version.js @@ -0,0 +1,5 @@ +/** + * The legacy Titanium CLI version. Since this legacy shim is intended to simulate the Titanium CLI + * v5, we keep the major as `5`, but set the minor to something high that will never exist. + */ +export const CLI_VERSION = '5.999.0'; diff --git a/src/project/legacy/tunnel.js b/src/legacy/tunnel.js similarity index 87% rename from src/project/legacy/tunnel.js rename to src/legacy/tunnel.js index a29f9f5..4a3cffa 100644 --- a/src/project/legacy/tunnel.js +++ b/src/legacy/tunnel.js @@ -1,5 +1,5 @@ import CLI from './ti/cli'; -import uuid from 'uuid'; +import { v4 as uuidv4 } from 'uuid'; /** * A simple state manager for sending and receiving requests to the parent process. @@ -22,9 +22,19 @@ class Tunnel { process.on('message', async data => { const { id, type } = data; - if (type === 'exec') { + if (type === 'exec' || type === 'build-options') { try { - await new CLI(data).go(); + const cli = new CLI(data); + + if (type === 'exec') { + await cli.go(); + } else { + await cli.command.load(); + process.send({ + data: cli.command.conf, + type: 'json' + }); + } // the command is complete, but the IPC channel is still open, so we simply disconnect it and // this process should exit whenever the command finishes @@ -72,7 +82,7 @@ class Tunnel { */ call(path, data) { return new Promise((resolve, reject) => { - const id = uuid.v4(); + const id = uuidv4(); this.pending[id] = { resolve, reject }; process.send({ data, diff --git a/src/lib/util.js b/src/lib/util.js index 87e0bfb..62374ce 100644 --- a/src/lib/util.js +++ b/src/lib/util.js @@ -1,3 +1,13 @@ +/** + * Capitalizes a string. + * + * @param {String} str - The string to capitalize. + * @returns {String} + */ +export function capitalize(str) { + return typeof str === 'string' ? `${str[0].toUpperCase()}${str.substring(1)}` : str; +} + /** * Parses the Titanium CLI version from the user agent. * diff --git a/src/lib/version.js b/src/lib/version.js index eac0a6d..f0b9589 100644 --- a/src/lib/version.js +++ b/src/lib/version.js @@ -63,3 +63,33 @@ export function satisfies(ver, str) { return range === '*' || semver.satisfies(ver, range) || (ver.indexOf('-') === -1 && semver.satisfies(ver + '-0', range)); }); } + +export function parseMax(str, allowX) { + let max, lt; + + for (const range of str.split(/\s*\|\|\s*/)) { + let x = range.split(' '); + x = x.length === 1 ? x.shift() : x.slice(1).shift(); + allowX || (x = x.replace(/.x$/i, '')); + const y = x.replace(allowX ? /[^.xX\d]/g : /[^.\d]/g, ''); + if (!max || exports.gt(y, max)) { + lt = /^<[^=]\d/.test(x); + max = y.replace(/\.$/, ''); + } + } + + return (lt ? '<' : '') + max; +} + +export function parseMin(str) { + let min; + + for (const range of str.split(/\s*\|\|\s*/)) { + const x = range.split(' ').shift().replace(/[^.\d]/g, ''); + if (!min || exports.lt(x, min)) { + min = x.replace(/\.$/, ''); + } + } + + return min; +} diff --git a/src/project/legacy/patch/android.js b/src/project/legacy/patch/android.js deleted file mode 100644 index 0ea7346..0000000 --- a/src/project/legacy/patch/android.js +++ /dev/null @@ -1,107 +0,0 @@ -/* eslint-disable promise/no-callback-in-promise */ - -import tunnel from '../tunnel'; -import * as version from '../../../lib/version'; - -export function detect(config = {}, opts = {}, callback) { - tunnel.call('/android/1.x/info') - .then(info => { - const results = { - devices: info.devices, - emulators: info.emulators, - issues: [], - ndk: processNDK(info.ndk), - sdk: processSDK(info.sdk, config, opts.vendorDependencies), - targets: results.targets, - vendorDependencies: opts.vendorDependencies || {} - }; - - // TODO: issues - - callback(results); - }) - .catch(err => { - console.error(err); - callback(); - }); -} - -function processNDK(ndks) { - // TODO - return null; -} - -function processSDK(sdks, config, vendorDependencies = {}) { - const sdk = sdks.sort(a => (a.default ? -1 : 1))[0]; - if (!sdk) { - return null; - } - - const results = { - path: sdk.path, - executables: { - adb: null, - android: null, - emulator: null, - mksdcard: null, - zipalign: null, - aapt: null, - aidl: null, - dx: null, - apksigner: null - }, - dx: null, - proguard: null, - tools: { - path: null, - supported: null, - version: null - }, - platformTools: { - path: null, - supported: null, - version: null - }, - buildTools: { - path: null, - supported: null, - version: null, - maxSupported: null - } - }; - - if (sdk.platformTools) { - results.executables.adb = sdk.platformTools.executables.adb; - } - - if (sdk.tools) { - results.executables.emulator = sdk.tools.executables.emulator; - } - - if (sdk.buildTools) { - const supportedRange = vendorDependencies['android build tools']; - const min = supportedRange && version.parseMin(supportedRange); - const preferred = config.get('android.buildTools.selectedVersion'); - const buildTools = sdk.buildTools - .map(b => { - b.supported = supportedRange && version.satisfies(b.version, supportedRange) ? true : min && version.lt(b.version, min) ? false : 'maybe'; - return b; - }) - .sort((a, b) => { - return preferred && version.eq(a.version, preferred) ? -1 : a.supported && b.supported ? 0 : a.supported ? -1 : b.supported ? 1 : version.compare(a.version, b.version); - })[0]; - - if (buildTools) { - results.buildTools.path = buildTools.path; - results.buildTools.version = buildTools.version; - results.buildTools.supported = buildTools.supported; - if (supportedRange) { - results.buildTools.maxSupported = version.parseMax(supportedRange); - } - results.dx = buildTools.dx; - Object.assign(results.executables, buildTools.executables); - } - } - - return results; -} diff --git a/src/project/legacy/patch/index.js b/src/project/legacy/patch/index.js deleted file mode 100644 index f715844..0000000 --- a/src/project/legacy/patch/index.js +++ /dev/null @@ -1,25 +0,0 @@ -import Module from 'module'; -import path from 'path'; - -/** - * Patches our system info plugins into Titanium SDK's system info library calls. - */ -const lookup = { - fields: { - file: path.resolve(__dirname, 'fields.js'), - parent: '_build.js' - }, - ioslib: { - file: path.resolve(__dirname, 'ios.js'), - parent: '_build.js' - }, - 'node-titanium-sdk/lib/android': { - file: path.resolve(__dirname, 'android.js'), - parent: '_build.js' - } -}; - -const resolveFilename = Module._resolveFilename; -Module._resolveFilename = (request, parent, isMain) => { - return lookup[request] && parent && path.basename(parent.filename) === lookup[request].parent && lookup[request].file || resolveFilename(request, parent, isMain); -}; diff --git a/src/project/project-service.js b/src/project/project-service.js index 29297c0..5f8bcc7 100644 --- a/src/project/project-service.js +++ b/src/project/project-service.js @@ -4,9 +4,7 @@ import TemplateService from './templates-service'; import { AppcdError, codes } from 'appcd-response'; import { Project } from 'titaniumlib'; -import { spawn } from 'appcd-subprocess'; - -const { error, log } = appcd.logger('project-service'); +import { spawnLegacyCLI } from '../legacy/spawn'; /** * Service for creating and building Titanium applications. @@ -67,7 +65,7 @@ export default class ProjectService extends Dispatcher { * format and must be run via the bootstrap. * * @param {String} command - The name of the command to run. - * @param {Object} ctx - The dispatcher context. + * @param {DispatcherContext} ctx - The dispatcher context. * @returns {Promise} * @access private */ @@ -90,67 +88,10 @@ export default class ProjectService extends Dispatcher { path: projectDir }); - // const { sdk } = project.tiapp.get('sdk'); + // const { sdk } = project.tiapp.get('sdk-version'); const sdk = '9.0.3.GA'; const sdkInfo = (await appcd.call('/sdk/find', { data: { name: sdk } })).response; - log('Spawning legacy Titanium CLI bootstrap...'); - const { child } = spawn({ - command: process.execPath, - args: [ path.resolve(__dirname, 'legacy/bootstrap.js') ], - options: { - env: Object.assign({ FORCE_COLOR: 1 }, process.env), - stdio: [ 'pipe', 'pipe', 'pipe', 'ipc' ] - } - }); - - child.stdout.on('data', data => ctx.response.write(data.toString())); - child.stderr.on('data', data => ctx.response.write(data.toString())); - child.on('close', code => { - log(`Legacy Titanium CLI bootstrap exited (code ${code || 0})`); - ctx.response.end(); - }); - child.on('message', async msg => { - switch (msg.type) { - case 'call': - const { id, path, data } = msg; - if (id && path) { - let response; - try { - response = await appcd.call(path, data); - } catch (err) { - child.send({ - error: err, - id, - type: 'error' - }); - throw err; - } - - try { - child.send({ - id, - response, - type: 'response' - }); - } catch (err) { - console.error(err); - } - } - return; - - case 'error': - error(msg); - return ctx.response.end(msg); - - case 'log': - return console.log(...msg.args); - - case 'telemetry': - return appcd.telemetry(msg.payload); - } - }); - const data = { argv: { ...ctx.request.data, @@ -158,12 +99,18 @@ export default class ProjectService extends Dispatcher { sdk }, command, - config: this.config.titanium, + config: this.config.titanium, sdkPath: sdkInfo.path, - type: 'exec' + type: 'exec' }; - log('Sending data to bootstrap:'); - log(data); - child.send(data); + + if (command === 'build') { + data.argv.buildOnly = true; + } + + await spawnLegacyCLI({ + ctx, + data + }); } } From b70151edd3f82bcf6fa178004c5aa9493d6f39b3 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Tue, 21 Jul 2020 17:46:04 -0500 Subject: [PATCH 12/39] Fixed build option render. --- package.json | 2 +- src/cli/cli-service.js | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 1497c93..e280e7b 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "appcd-path": "^2.0.1", "appcd-util": "^3.0.0", "chalk": "^4.1.0", - "cli-kit": "^1.3.0", + "cli-kit": "^1.4.0", "colors": "^1.4.0", "dateformat": "^3.0.3", "enquirer": "^2.3.6", diff --git a/src/cli/cli-service.js b/src/cli/cli-service.js index 0f5d8a6..00a6ac1 100644 --- a/src/cli/cli-service.js +++ b/src/cli/cli-service.js @@ -44,6 +44,11 @@ export default class CLIService extends Dispatcher { options: { '--no-prompt': 'Disable interactive prompting' }, + styles: { + subheading(s) { + return `\n${String(s).toUpperCase()}`; + } + }, version: ({ data }) => parseVersion(data.userAgent) }); @@ -100,7 +105,7 @@ export default class CLIService extends Dispatcher { } if (Object.keys(options).length) { - buildOptions.push(conf.title, options); + buildOptions.push(`${conf.title} build options`, options); } } @@ -108,7 +113,6 @@ export default class CLIService extends Dispatcher { } if (buildOptions.length) { - log(buildOptions); ctx.option(buildOptions); } }); From dc1046c80ac49cbb6fdc229eb68ada3284a3c296 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Wed, 22 Jul 2020 09:51:09 -0500 Subject: [PATCH 13/39] doc: Updated changelog. --- CHANGELOG.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f4bc1e..bf232e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,16 +7,24 @@ instead. * BREAKING CHANGE(config): Write operations such as `set` return `"OK"` instead of `"Saved"`. * feat(info): Added `filter` argument to `ti info`. - * feat(project): Added project service handler. + * feat(project): Project service with endpoints for `new`, `build`, `run`, `clean`, and info. [(DAEMON-26)](https://jira.appcelerator.org/browse/DAEMON-26) - * feat(cli): Added `new` command. [(DAEMON-321)](https://jira.appcelerator.org/browse/DAEMON-321) + [(DAEMON-21)](https://jira.appcelerator.org/browse/DAEMON-21) + * feat(cli): Added `new` command. [(DAEMON-301)](https://jira.appcelerator.org/browse/DAEMON-301) + * feat(cli): Added `clean` command. + [(DAEMON-327)](https://jira.appcelerator.org/browse/DAEMON-327) + * feat(cli): Added `build` and `run` commands. + [(DAEMON-16)](https://jira.appcelerator.org/browse/DAEMON-16) * feat(cli): Added auth commands `login`, `logout`, `whoami`, and `switch`. [(DAEMON-300)](https://jira.appcelerator.org/browse/DAEMON-300) + * feat(legacy): Legacy Titanium CLI bootstrap for loading a Titanium SDK and running a `build` or + `clean` command. For differences between this and Titanium CLI v5, see the + [readme](https://github.com/appcelerator/appcd-plugin-titanium/blob/master/src/legacy/README.md). * feat(cli:sdk): Added aliases to sdk commands (i, ls, rm). * feat(sdk): Added `find` endpoint to SDK service to get info about an installed Titanium SDK. * feat(sdk): Added progress bars during SDK installation. - * feat(project): Added legacy Titanium CLI command bootstrap. - * refactor: Updated to cli-kit@0.14.0 adding support for the new client/server architecture. + * feat: Support for Titanium-specific telemetry. + * refactor: Updated to latest cli-kit with support for the new client/server architecture. * refactor: Updated `config` command actions to be subcommands with improved help output. * chore: Added plugin API version 2.x. * chore: Transpile for Node 10 instead of Node 8. Not a breaking change as appcd has always From b47c06f792fd732411820f4a19ac6a7304ca85ee Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Fri, 24 Jul 2020 00:01:54 -0500 Subject: [PATCH 14/39] feat: Added App Preview options and hook. chore: Updated deps. --- package.json | 2 +- src/cli/cli-service.js | 2 +- src/cli/commands/build.js | 21 ++++++++++++--------- src/cli/commands/run.js | 25 +++++++++++++------------ src/cli/commands/whoami.js | 2 +- src/legacy/hooks/app-preview-hook.js | 3 +++ src/legacy/spawn.js | 2 +- src/legacy/ti/cli.js | 3 ++- src/lib/prompt.js | 7 +++++-- src/project/project-service.js | 14 ++++++++++++++ 10 files changed, 53 insertions(+), 28 deletions(-) create mode 100644 src/legacy/hooks/app-preview-hook.js diff --git a/package.json b/package.json index e280e7b..742b92a 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "appcd-path": "^2.0.1", "appcd-util": "^3.0.0", "chalk": "^4.1.0", - "cli-kit": "^1.4.0", + "cli-kit": "^1.5.1", "colors": "^1.4.0", "dateformat": "^3.0.3", "enquirer": "^2.3.6", diff --git a/src/cli/cli-service.js b/src/cli/cli-service.js index 00a6ac1..f6191f5 100644 --- a/src/cli/cli-service.js +++ b/src/cli/cli-service.js @@ -63,7 +63,7 @@ export default class CLIService extends Dispatcher { return; } - cmd.on('generateHelp', async ({ ctx }) => { + cmd.on('generateHelp', async ctx => { // const tiapp = new Tiapp({ file: tiappFile }); // const sdk = tiapp.get('sdk-version'); const sdk = '9.0.3.GA'; diff --git a/src/cli/commands/build.js b/src/cli/commands/build.js index 7834a27..a08aff2 100644 --- a/src/cli/commands/build.js +++ b/src/cli/commands/build.js @@ -1,22 +1,25 @@ +import { options as appPreviewOptions } from '../../lib/app-preview'; import { promptLoop } from '../../lib/prompt'; export default { desc: 'Builds a project', - options: { - '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory', - '-f, --force': 'Force a full rebuild', - '-p, --platform [name]': 'The target build platform' - }, + options: [ + { + '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory', + '-f, --force': 'Force a full rebuild', + '-p, --platform [name]': 'The target build platform' + }, + ...appPreviewOptions + ], async action(ctx) { await promptLoop({ ctx, data: { - force: ctx.argv.force, - platform: ctx.argv.platform, - projectDir: ctx.argv.projectDir + ...ctx.argv, + cwd: ctx.data.cwd }, path: '/project/build', - ns: 'cli:build' + ns: 'cli:build' }); } }; diff --git a/src/cli/commands/run.js b/src/cli/commands/run.js index a19ced9..6fbe530 100644 --- a/src/cli/commands/run.js +++ b/src/cli/commands/run.js @@ -1,25 +1,26 @@ +import { options as appPreviewOptions } from '../../lib/app-preview'; import { promptLoop } from '../../lib/prompt'; export default { desc: 'Build and runs a project', - options: { - '--build-only': 'Builds the project without running it in the simulator/emulator or installing it on device', - '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory', - '-f, --force': 'Force a full rebuild', - '-p, --platform [name]': 'The target build platform' - }, + options: [ + { + '--build-only': 'Builds the project without running it in the simulator/emulator or installing it on device', + '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory', + '-f, --force': 'Force a full rebuild', + '-p, --platform [name]': 'The target build platform' + }, + ...appPreviewOptions + ], async action(ctx) { await promptLoop({ ctx, data: { - buildOnly: ctx.argv.buildOnly, - cwd: ctx.data.cwd, - force: ctx.argv.force, - platform: ctx.argv.platform, - projectDir: ctx.argv.projectDir + ...ctx.argv, + cwd: ctx.data.cwd }, path: '/project/run', - ns: 'cli:run' + ns: 'cli:run' }); } }; diff --git a/src/cli/commands/whoami.js b/src/cli/commands/whoami.js index e4b6c35..68e8232 100644 --- a/src/cli/commands/whoami.js +++ b/src/cli/commands/whoami.js @@ -4,7 +4,7 @@ export default { '--json': 'Outputs accounts as JSON' }, async action({ argv, console }) { - let { response: accounts } = await appcd.call('/amplify/1.x/auth'); + const { response: accounts } = await appcd.call('/amplify/1.x/auth'); if (!accounts.length) { console.log('No authenticated accounts.'); diff --git a/src/legacy/hooks/app-preview-hook.js b/src/legacy/hooks/app-preview-hook.js new file mode 100644 index 0000000..7a078b6 --- /dev/null +++ b/src/legacy/hooks/app-preview-hook.js @@ -0,0 +1,3 @@ +exports.init = (logger, config, cli, appc) => { + logger.log(cli.argv); +}; diff --git a/src/legacy/spawn.js b/src/legacy/spawn.js index 0f27d9f..423f04e 100644 --- a/src/legacy/spawn.js +++ b/src/legacy/spawn.js @@ -1,7 +1,7 @@ import path from 'path'; import { spawn } from 'appcd-subprocess'; -const { log } = appcd.logger('legacy:spawn'); +const { log } = appcd.logger('legacy'); /** * Spawns the Legacy CLI and resolves once the command finishes. diff --git a/src/legacy/ti/cli.js b/src/legacy/ti/cli.js index 0f69866..69b2512 100644 --- a/src/legacy/ti/cli.js +++ b/src/legacy/ti/cli.js @@ -191,6 +191,7 @@ export default class CLI { // initialize the hooks unique(this.config.paths.hooks).forEach(this.scanHooks.bind(this)); + this.scanHooks(path.resolve(__dirname, '..', 'hooks')); this.scanHooks(path.join(this.sdk.path, 'cli', 'hooks')); } @@ -379,7 +380,7 @@ export default class CLI { let done = 0; await new Promise((resolve, reject) => { - tunnel.log(`Executing ${this.command.name} run`); + tunnel.log(`Executing ${this.command.name}`); run(this.logger, this.config, this, async (err, result) => { if (done++) { diff --git a/src/lib/prompt.js b/src/lib/prompt.js index dc11a8a..0f9d735 100644 --- a/src/lib/prompt.js +++ b/src/lib/prompt.js @@ -23,7 +23,7 @@ export function prompt(questions, { stdin, stdout } = {}) { for (let i = 0, len = questions.length; i < len; i++) { questions[i] = { - format() { + format: questions[i].type === 'toggle' ? undefined : function () { // for some reason, enquirer doesn't print the selected value using the primary // (green) color for select prompts, so we just force it for all prompts return this.style(this.value); @@ -109,7 +109,10 @@ export async function promptLoop({ ctx, data, footer, header, ns, path, print }) while (ask) { result = await prompt({ validate(value) { - return !!value || !this.required || ask.validateMessage || false; + if (this.type === 'toggle' || !this.required || !!value) { + return true; + } + return ask.validateMessage || false; }, name: ask.name || name, ...ask diff --git a/src/project/project-service.js b/src/project/project-service.js index 5f8bcc7..5a759a1 100644 --- a/src/project/project-service.js +++ b/src/project/project-service.js @@ -5,6 +5,9 @@ import TemplateService from './templates-service'; import { AppcdError, codes } from 'appcd-response'; import { Project } from 'titaniumlib'; import { spawnLegacyCLI } from '../legacy/spawn'; +import { validate as validateAppPreview } from '../lib/app-preview'; + +const { alert } = appcd.logger.styles; /** * Service for creating and building Titanium applications. @@ -84,6 +87,17 @@ export default class ProjectService extends Dispatcher { projectDir = path.resolve(cwd, projectDir || '.'); } + if (command === 'build' || command === 'run') { + try { + await validateAppPreview(ctx.request.data); + } catch (err) { + if (err.code === 'ENOTENT') { + return `${alert(err.toString())}\n\n${err.details}\n`; + } + throw err; + } + } + const project = new Project({ path: projectDir }); From 607377cd8e5aaec1d1a2ced4641e749a13517718 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Fri, 24 Jul 2020 17:20:44 -0500 Subject: [PATCH 15/39] Added App Preview logic. chore: Updated deps. --- package.json | 2 +- src/lib/app-preview.js | 88 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/lib/app-preview.js diff --git a/package.json b/package.json index 742b92a..b7876aa 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "appcd-path": "^2.0.1", "appcd-util": "^3.0.0", "chalk": "^4.1.0", - "cli-kit": "^1.5.1", + "cli-kit": "^1.5.2", "colors": "^1.4.0", "dateformat": "^3.0.3", "enquirer": "^2.3.6", diff --git a/src/lib/app-preview.js b/src/lib/app-preview.js new file mode 100644 index 0000000..107dd08 --- /dev/null +++ b/src/lib/app-preview.js @@ -0,0 +1,88 @@ +/** + * Installr integration ported from https://github.com/jeffbonnes/appc-app-preview-cli-hook. + * Jeff Bonnes , MIT License + * Installr API docs: https://help.installrapp.com/api/ + * + * This is the App Preview CLI and validation code. The App Preview Titanium CLI plugin is located + * in `src/legacy/hooks/app-preview-hook.js`. + */ + +const { log } = appcd.logger('app-preview'); +const { highlight } = appcd.logger.styles; + +export const endpoint = 'https://appbeta.axway.com'; + +export const options = [ + 'App Preview Options', + { + '--app-preview': 'Deploy a build to App Preview', + '--add [teams]': 'A comma-separated list of team names to add access to the App Preview build', + '--release-notes [text]': 'Release notes for the App Preview build', + '--invite [email_addresses]': 'A comma-separated list of email addresses to send the App Preview invites to', + '--notify [teams]': 'A comma-separated list of team names that have been previously invited to notify of App Preview build' + } +]; + +function INVALID_ARGUMENT({ msg, code, prompt }) { + const err = new TypeError(msg); + if (code !== undefined) { + err.code = code; + } + if (prompt !== undefined) { + err.prompt = prompt; + } + return err; +} + +export async function validate(argv) { + if (!argv.appPreview) { + return; + } + + const { response: account } = await appcd.call('/amplify/1.x/auth/active'); + + if (!account) { + throw new Error('App Preview requires you to be logged in\nPlease login by running: ti login'); + } + + if (!account.org?.entitlements?.appPreview) { + // eslint-disable-next-line no-throw-literal + const err = new Error('Your account is not entitled to use App Preview'); + err.code = 'ENOTENT'; + err.details = `Your current organization is ${highlight(`"${account.org.name}"`)}.\n`; + if (account.orgs.length > 1) { + err.details += `If this is not the correct organization, run ${highlight('"ti switch"')} to change to another organization.\n`; + } + err.details += 'To upgrade your account, visit https://billing.axway.com/.'; + err.showHelp = false; + throw err; + } + + log(`Active account org ${highlight(`"${account.org.name}"`)} is entitled to App Preview!`); + + if (argv.releaseNotes === undefined) { + throw INVALID_ARGUMENT({ + msg: 'Expected App Preview release notes or path to release notes file', + prompt: { + message: 'Please enter release notes or a path to a release notes file', + name: 'releaseNotes', + type: 'text' + } + }); + } + + if (argv.notify === undefined) { + throw INVALID_ARGUMENT({ + msg: 'Expected App Preview notification preference', + prompt: { + disabled: 'No', + enabled: 'Yes', + initial: true, + message: 'Do you want to notify previous testers on upload?', + name: 'notify', + required: true, + type: 'toggle' + } + }); + } +} From a8cb85ccf764a9efd94a6b61c7af3c1ed1b7f8af Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Tue, 11 Aug 2020 01:29:42 -0500 Subject: [PATCH 16/39] refactor(legacy): Rewire 'build', 'clean', and 'run' commands to invoke the legacy CLI instead of using the prompt loop and going through the project service. refactor(legacy:hooks): Move App Preview CLI options config to legacy CLI hook to improve consistency and user experience. feat(legacy:hooks): Add LiveView legacy CLI hook to define CLI options. fix(legacy:cli): Improved hook error handling. chore: Updated deps. --- package.json | 4 +- src/cli/cli-service.js | 115 +++++++------- src/cli/commands/build.js | 16 +- src/cli/commands/clean.js | 12 +- src/cli/commands/run.js | 16 +- src/cli/run-legacy.js | 44 ++++++ src/legacy/hooks/app-preview-hook.js | 3 - src/legacy/hooks/app-preview.js | 220 +++++++++++++++++++++++++++ src/legacy/hooks/liveview.js | 22 +++ src/legacy/index.js | 165 ++++++++++++++++++++ src/legacy/spawn.js | 101 ------------ src/legacy/ti/cli.js | 42 ++--- src/legacy/tunnel.js | 2 +- src/lib/app-preview.js | 88 ----------- src/lib/prompt.js | 7 + src/project/project-service.js | 95 +++--------- 16 files changed, 579 insertions(+), 373 deletions(-) create mode 100644 src/cli/run-legacy.js delete mode 100644 src/legacy/hooks/app-preview-hook.js create mode 100644 src/legacy/hooks/app-preview.js create mode 100644 src/legacy/hooks/liveview.js create mode 100644 src/legacy/index.js delete mode 100644 src/legacy/spawn.js delete mode 100644 src/lib/app-preview.js diff --git a/package.json b/package.json index b7876aa..79b2d9d 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "appcd-path": "^2.0.1", "appcd-util": "^3.0.0", "chalk": "^4.1.0", - "cli-kit": "^1.5.2", + "cli-kit": "^1.6.1", "colors": "^1.4.0", "dateformat": "^3.0.3", "enquirer": "^2.3.6", @@ -30,7 +30,9 @@ "gawk": "^4.7.1", "get-port": "^5.1.1", "global-modules": "^2.0.0", + "got": "^11.5.2", "node-pty-prebuilt-multiarch": "^0.9.0", + "open": "^7.1.0", "pluralize": "^8.0.0", "progress": "^2.0.3", "prompts": "^2.3.2", diff --git a/src/cli/cli-service.js b/src/cli/cli-service.js index f6191f5..e1e0bfb 100644 --- a/src/cli/cli-service.js +++ b/src/cli/cli-service.js @@ -7,8 +7,8 @@ import path from 'path'; import { get } from 'appcd-util'; import { isFile } from 'appcd-fs'; import { capitalize, parseVersion } from '../lib/util'; -import { spawnLegacyCLI } from '../legacy/spawn'; -import { Tiapp } from 'titaniumlib'; +import { spawnLegacyCLI } from '../legacy'; +// import { Tiapp } from 'titaniumlib'; const { log } = appcd.logger('cli-service'); const { highlight } = appcd.logger.styles; @@ -55,68 +55,81 @@ export default class CLIService extends Dispatcher { // we need to add platform specific options for the build/run help, so first we listen for // the help command, then we add in the options before the help is generated cli.on('exec', async ({ cmd, data, contexts }) => { + // inject the titanium config into the command data object + data.config = cfg.titanium; + // we need the help command, the build/run command, and a cwd containing a tiapp cmd = data?.cwd && cmd?.name === 'help' && contexts[1]; - if (cmd && (cmd.name === 'build' || cmd.name === 'run')) { - const tiappFile = path.resolve(data.cwd, 'tiapp.xml'); - if (!isFile(tiappFile)) { - return; - } - cmd.on('generateHelp', async ctx => { - // const tiapp = new Tiapp({ file: tiappFile }); - // const sdk = tiapp.get('sdk-version'); - const sdk = '9.0.3.GA'; - const sdkInfo = (await appcd.call('/sdk/find', { data: { name: sdk } })).response; - - let buildOptions = buildOptionsCache[sdkInfo.path]; - if (!buildOptions) { - // load the Android and iOS options directly from the SDK - const config = await spawnLegacyCLI({ - data: { - command: 'build', - sdkPath: sdkInfo.path, - type: 'build-options' - } - }); - - const platforms = config?.platforms; - if (!platforms) { - return; - } + if (!cmd || (cmd.name !== 'build' && cmd.name !== 'run')) { + return; + } - // copy the platform-specific options into a cli-kit friendly format - buildOptions = []; - for (const conf of Object.values(platforms)) { - const options = {}; + const tiappFile = path.resolve(data.cwd, 'tiapp.xml'); + if (!isFile(tiappFile)) { + return; + } - for (const [ name, flag ] of Object.entries(conf.flags)) { - if (!flag.hidden) { - options[`--${name}`] = { desc: capitalize(flag.desc) }; + cmd.on('generateHelp', async ctx => { + // FIX ME! + // const tiapp = new Tiapp({ file: tiappFile }); + // const sdk = tiapp.get('sdk-version'); + const sdk = '9.0.3.GA'; + const sdkInfo = (await appcd.call('/sdk/find', { data: { name: sdk } })).response; + + let buildOptions = buildOptionsCache[sdkInfo.path]; + + if (!Array.isArray(buildOptions)) { + // load the Android and iOS options directly from the SDK + const config = await spawnLegacyCLI({ + data: { + command: 'build', + sdkPath: sdkInfo.path, + type: 'help' + } + }); + + // copy the platform-specific options into a cli-kit friendly format + buildOptions = []; + + for (const key of Object.keys(config)) { + if (key === 'flags' || key === 'options') { + // we skip the top level build flags/options because they are either + // already defined in the command or are unsupported + } else if (key === 'platforms') { + for (const conf of Object.values(config[key])) { + const options = {}; + + for (const [ name, flag ] of Object.entries(conf.flags)) { + if (!flag.hidden) { + options[`--${name}`] = { desc: capitalize(flag.desc) }; + } } - } - for (const [ name, option ] of Object.entries(conf.options)) { - if (!option.hidden) { - let format = option.abbr ? `-${option.abbr}, ` : ''; - format += `--${name} ${option.required ? '<' : '['}${option.hint || 'value'}${option.required ? '>' : ']'}`; - options[format] = { desc: capitalize(option.desc) }; + for (const [ name, option ] of Object.entries(conf.options)) { + if (!option.hidden) { + let format = option.abbr ? `-${option.abbr}, ` : ''; + format += `--${name} ${option.required ? '<' : '['}${option.hint || 'value'}${option.required ? '>' : ']'}`; + options[format] = { desc: capitalize(option.desc) }; + } } - } - if (Object.keys(options).length) { - buildOptions.push(`${conf.title} build options`, options); + if (Object.keys(options).length) { + buildOptions.push(`${conf.title} build options`, options); + } } + } else if (Array.isArray(config[key])) { + buildOptions.push.apply(buildOptions, config[key]); } - - buildOptionsCache[sdkInfo.path] = buildOptions; } - if (buildOptions.length) { - ctx.option(buildOptions); - } - }); - } + buildOptionsCache[sdkInfo.path] = buildOptions; + } + + if (buildOptions.length) { + ctx.option(buildOptions); + } + }); }); // find an available port to listen on diff --git a/src/cli/commands/build.js b/src/cli/commands/build.js index a08aff2..3398574 100644 --- a/src/cli/commands/build.js +++ b/src/cli/commands/build.js @@ -1,5 +1,4 @@ -import { options as appPreviewOptions } from '../../lib/app-preview'; -import { promptLoop } from '../../lib/prompt'; +import { runLegacyCLI } from '../run-legacy'; export default { desc: 'Builds a project', @@ -8,18 +7,9 @@ export default { '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory', '-f, --force': 'Force a full rebuild', '-p, --platform [name]': 'The target build platform' - }, - ...appPreviewOptions + } ], async action(ctx) { - await promptLoop({ - ctx, - data: { - ...ctx.argv, - cwd: ctx.data.cwd - }, - path: '/project/build', - ns: 'cli:build' - }); + await runLegacyCLI('build', ctx); } }; diff --git a/src/cli/commands/clean.js b/src/cli/commands/clean.js index 934658c..0942557 100644 --- a/src/cli/commands/clean.js +++ b/src/cli/commands/clean.js @@ -1,4 +1,4 @@ -import { promptLoop } from '../../lib/prompt'; +import { runLegacyCLI } from '../run-legacy'; export default { desc: 'Remove previous build directories', @@ -10,14 +10,6 @@ export default { } }, async action(ctx) { - await promptLoop({ - ctx, - data: { - platforms: ctx.argv.platforms, - projectDir: ctx.argv.projectDir - }, - path: '/project/clean', - ns: 'cli:clean' - }); + await runLegacyCLI('clean', ctx); } }; diff --git a/src/cli/commands/run.js b/src/cli/commands/run.js index 6fbe530..2c5d9dc 100644 --- a/src/cli/commands/run.js +++ b/src/cli/commands/run.js @@ -1,5 +1,4 @@ -import { options as appPreviewOptions } from '../../lib/app-preview'; -import { promptLoop } from '../../lib/prompt'; +import { runLegacyCLI } from '../run-legacy'; export default { desc: 'Build and runs a project', @@ -9,18 +8,9 @@ export default { '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory', '-f, --force': 'Force a full rebuild', '-p, --platform [name]': 'The target build platform' - }, - ...appPreviewOptions + } ], async action(ctx) { - await promptLoop({ - ctx, - data: { - ...ctx.argv, - cwd: ctx.data.cwd - }, - path: '/project/run', - ns: 'cli:run' - }); + await runLegacyCLI('run', ctx); } }; diff --git a/src/cli/run-legacy.js b/src/cli/run-legacy.js new file mode 100644 index 0000000..ab290ad --- /dev/null +++ b/src/cli/run-legacy.js @@ -0,0 +1,44 @@ +import { exec } from '../legacy'; +import { prompt } from '../lib/prompt'; + +export async function runLegacyCLI(command, ctx) { + const { argv, console, data, terminal } = ctx; + const { prompt: promptEnabled } = argv; + + // remove general CLI-related values + delete argv.banner; + delete argv.color; + delete argv.help; + delete argv.prompt; + delete argv.version; + + await exec({ + argv, + command, + config: data.config, + console: console, + cwd: data.cwd, + prompt: promptEnabled && (async ask => { + let { name } = ask; + let result; + + while (ask) { + result = await prompt({ + cancel() {}, // prevent '' from being thrown + validate(value) { + if (this.type === 'toggle' || !this.required || !!value) { + return true; + } + return ask.validateMessage || false; + }, + name: ask.name || name, + ...ask + }, terminal); + + ask = result?.[ask.name || name]?.prompt; + } + + return result; + }) + }); +} diff --git a/src/legacy/hooks/app-preview-hook.js b/src/legacy/hooks/app-preview-hook.js deleted file mode 100644 index 7a078b6..0000000 --- a/src/legacy/hooks/app-preview-hook.js +++ /dev/null @@ -1,3 +0,0 @@ -exports.init = (logger, config, cli, appc) => { - logger.log(cli.argv); -}; diff --git a/src/legacy/hooks/app-preview.js b/src/legacy/hooks/app-preview.js new file mode 100644 index 0000000..189e268 --- /dev/null +++ b/src/legacy/hooks/app-preview.js @@ -0,0 +1,220 @@ +/** + * Installr API docs: https://help.installrapp.com/api/ + * + * This is the App Preview CLI and validation code. The App Preview Titanium CLI plugin is located + * in `src/legacy/hooks/app-preview-hook.js`. + */ + +// const got = require('got'); +// const path = require('path'); + +// const endpoint = 'https://appbeta.axway.com'; + +exports.init = (logger, config, cli, appc) => { + cli.on('build.config', data => { + data.result.appPreview = [ + 'App Preview Options', + { + '--app-preview': 'Deploy a build to App Preview', + '--add [teams]': 'A comma-separated list of team names to add access to the App Preview build', + '--release-notes [text]': 'Release notes for the App Preview build', + '--invite [email_addresses]': 'A comma-separated list of email addresses to send the App Preview invites to', + '--notify [teams]': 'A comma-separated list of team names that have been previously invited to notify of App Preview build' + } + ]; + }); + + // if (!cli.argv.appPreview) { + // return; + // } + + // cli.on('build.finalize', function (data, callback) { + // let artifact; + // if (cli.argv.platform === 'android') { + // artifact = data.apkFile; + // } else if (cli.argv.platform === 'ios' && cli.argv.outputDir) { + // artifact = path.join(cli.argv.outputDir, `${this.tiapp.name}.ipa`); + // } else { + // throw new Error(); + // } + // // if (data.buildManifest.outputDir === undefined && data.iosBuildDir === undefined) { + // // logger.error("Output directory must be defined to use --app-preview flag"); + // // return; + // // } + // // build_file = afs.resolvePath(path.join(data.buildManifest.outputDir, data.buildManifest.name + ".ipa")); + // // } + // }); +}; + +/* +var _ = require("lodash"); +var logger, platform, config, appc, appcConfig, j, build_file, busy; + +j = request.jar(); + +var onUploadComplete = function(err, httpResponse, body) { + var resp = {}; + if (err) { + logger.error(err); + } else { + if (httpResponse.statusCode != 200) { + logger.error('Error uploading to app preview, status code=' + httpResponse.statusCode); + return; + } else { + resp = JSON.parse(body); + if (resp.result != "success") { + logger.error(resp.message); + return; + } + } + logger.info("App uploaded successfully."); + resp = JSON.parse(body); + // check if we want to invite new testers + if (config.emails) { + logger.info('Adding tester(s) ' + config.emails + ' to latest build'); + var r = request.post({ + jar: j, + url: SERVER + '/apps/' + resp.appData.id + '/builds/' + resp.appData.latestBuild.id + '/team.json' + }, function optionalCallback(err, httpResponse, body) { + if (err) { + logger.error(err); + showFinalUrl(resp); + } else { + logger.info("Tester(s) invited successfully."); + showFinalUrl(resp); + } + }); + var form = r.form(); + form.append('emails', config.emails); + } else { + showFinalUrl(resp); + } + } +} + +function showFinalUrl(resp) { + logger.info('Open ' + SERVER + '/dashboard/index#/apps/' + resp.appData.id + ' to configure your app in App Preview.') +} + +function upload2AppPreview(data, finished) { + var sid = process.env.APPC_SESSION_SID; + logger.info('Uploading app to App Preview...please wait...'); + var cookie = request.cookie('connect.sid=' + sid); + j.setCookie(cookie, SERVER); + + var obj = { + url: SERVER + '/apps.json', + jar: j, + headers: { + "user-agent": 'Appcelerator CLI' + } + }; + + // configure proxy + if (process.env.APPC_CONFIG_PROXY) { + obj.proxy = process.env.APPC_CONFIG_PROXY; + } + + var r = request.post(obj, onUploadComplete); + + var form = r.form(); + var file = fs.createReadStream(build_file); + var totalSize = fs.statSync(build_file).size; + var bytesRead = 0; + var lastPercent = 0; + file.on('data', function(chunk) { + bytesRead += chunk.length; + var currentPercent = Math.round((bytesRead / totalSize) * 100); + if (currentPercent != lastPercent && currentPercent % 5 == 0) { + logger.info("uploaded " + currentPercent + "%"); + lastPercent = currentPercent; + } + }); + form.append('qqfile', file); + form.append('releaseNotes', config.releaseNotes); + form.append('notify', config.notify.toString()); + if (config.add) { + form.append('add', config.add.toString()); + } +} +*/ + +/* +function INVALID_ARGUMENT({ msg, code, prompt }) { + const err = new TypeError(msg); + if (code !== undefined) { + err.code = code; + } + if (prompt !== undefined) { + err.prompt = prompt; + } + return err; +} + +export async function validate(argv) { + if (!argv.appPreview) { + return; + } + + const { response: account } = await appcd.call('/amplify/1.x/auth/active'); + + if (!account) { + const err = new Error('Log in required to use App Preview'); + err.details = `Please login by running: ${highlight('ti login')}`; + err.showHelp = false; + throw err; + } + + if (!account.org?.entitlements?.appPreview) { + // eslint-disable-next-line no-throw-literal + const err = new Error('Your account is not entitled to use App Preview'); + err.details = `Your current organization is ${highlight(`"${account.org.name}"`)}.\n`; + if (account.orgs.length > 1) { + err.details += `If this is not the correct organization, run ${highlight('"ti switch"')} to change to another organization.\n`; + } + err.details += 'To upgrade your account, visit https://billing.axway.com/.'; + err.showHelp = false; + throw err; + } + + log(`Active account org ${highlight(`"${account.org.name}"`)} is entitled to App Preview!`); + + const { platform } = argv; + if (platform !== 'android' && platform !== 'ios') { + const err = new Error(`App Preview does not support the platform "${platform}"`); + err.details = 'Only Android and iOS platforms are supported.'; + err.showHelp = false; + throw err; + } + + if (platform === 'ios' && !argv.outputDir) { + // + } + + if (argv.releaseNotes === undefined) { + throw INVALID_ARGUMENT({ + msg: 'Expected App Preview release notes or path to release notes file', + prompt: { + message: 'Please enter release notes or a path to a release notes file', + name: 'releaseNotes', + type: 'text' + } + }); + } + + if (argv.notify === undefined) { + throw INVALID_ARGUMENT({ + msg: 'Expected App Preview notification preference', + prompt: { + disabled: 'No', + enabled: 'Yes', + initial: true, + message: 'Do you want to notify previous testers on upload?', + name: 'notify', + required: true, + type: 'toggle' + } + }); + } +} +*/ diff --git a/src/legacy/hooks/liveview.js b/src/legacy/hooks/liveview.js new file mode 100644 index 0000000..f4d75e0 --- /dev/null +++ b/src/legacy/hooks/liveview.js @@ -0,0 +1,22 @@ +exports.init = (logger, config, cli, appc) => { + cli.on('build.config', data => { + data.result.liveview = [ + 'LiveView Options', + { + '--liveview': 'Enables LiveView hot reloading', + '--liveview-ip [teams]': { + desc: 'The LiveView server IP address', + hidden: true + }, + '--liveview-fport [text]': { + desc: 'The LiveView file server port', + hidden: true + }, + '--liveview-eport [port]': { + desc: 'The LiveView event server port', + hidden: true + } + } + ]; + }); +}; diff --git a/src/legacy/index.js b/src/legacy/index.js new file mode 100644 index 0000000..575bd04 --- /dev/null +++ b/src/legacy/index.js @@ -0,0 +1,165 @@ +import path from 'path'; +import { AppcdError, codes } from 'appcd-response'; +import { Project } from 'titaniumlib'; +import { PromptError } from '../lib/prompt'; +import { spawn } from 'appcd-subprocess'; + +const logger = appcd.logger('legacy-cli'); +const { log } = logger; + +export async function exec({ argv, command, config, console, cwd, prompt }) { + let { projectDir } = argv; + + // step 1: validate the project directory + if (projectDir !== undefined && typeof projectDir !== 'string') { + const err = new PromptError('Invalid project directory', { + message: 'Where is the project located?', + name: 'projectDir', + type: 'text' + }); + + if (prompt) { + ({ projectDir } = await prompt(err)); + } else { + throw err; + } + } + + if (projectDir === undefined || !path.isAbsolute(projectDir)) { + if (!cwd || typeof cwd !== 'string') { + throw new AppcdError(codes.BAD_REQUEST, 'Current working directory required when project directory is relative'); + } + projectDir = path.resolve(cwd, projectDir || '.'); + } + + // step 2: init the project + const project = new Project({ + path: projectDir + }); + + // step 3: load the sdk + // const { sdk } = project.tiapp.get('sdk-version'); + const sdk = '9.0.3.GA'; + const sdkInfo = (await appcd.call('/sdk/find', { data: { name: sdk } })).response; + + const data = { + argv: { + ...argv, + projectDir, + sdk + }, + command, + config: config || {}, + cwd: projectDir, + prompt: !!prompt, + sdkPath: sdkInfo.path, + type: 'exec' + }; + + if (command === 'build') { + data.argv.buildOnly = true; + } + + // step 4: spawn the legacy cli + await spawnLegacyCLI({ + console, + data, + prompt + }); +} + +/** + * Spawns the Legacy CLI and resolves once the command finishes. + * + * @param {Object} opts - Various options. + * @param {Console} [opts.console] - The console to pipe output to. + * @param {Object} [opts.data] - A data payload to send over the IPC tunnel to the Legacy Titanium + * CLI. + * @returns {Promise} + */ +export async function spawnLegacyCLI({ console, data, prompt }) { + log(`Spawning legacy Titanium CLI bootstrap (console ${console ? 'enabled' : 'disabled'})`); + + const { child } = spawn({ + command: process.execPath, + args: [ path.resolve(__dirname, 'bootstrap.js') ], + options: { + cwd: data?.cwd, + env: Object.assign({ FORCE_COLOR: 1 }, process.env), + stdio: [ 'ignore', 'pipe', 'pipe', 'ipc' ] + } + }); + + const trace = logger(`${child.pid}-trace`); + + if (console) { + child.stdout.on('data', data => console._stdout.write(data.toString())); + child.stderr.on('data', data => console._stderr.write(data.toString())); + } else { + const { log } = logger(`${child.pid}-stdout`); + const { error } = logger(`${child.pid}-stderr`); + child.stdout.on('data', data => log(data.toString().replace(/\n$/, ''))); + child.stderr.on('data', data => error(data.toString().replace(/\n$/, ''))); + } + + return await new Promise((resolve, reject) => { + child.on('close', code => { + log(`Legacy Titanium CLI bootstrap exited (code ${code || 0})`); + resolve(); + }); + + child.on('message', async msg => { + switch (msg.type) { + case 'call': + const { id, path, data } = msg; + if (id && path) { + let response; + try { + response = await appcd.call(path, data); + } catch (err) { + child.send({ + error: err, + id, + type: 'error' + }); + throw err; + } + + try { + child.send({ + id, + response, + type: 'response' + }); + } catch (err) { + console.error(err); + } + } + return; + + case 'error': + { + const err = new Error(msg.message); + return reject(Object.assign(err, msg)); + } + + case 'json': + return resolve(msg.data); + + case 'log': + return trace.log(...msg.args); + + case 'prompt': + // TODO + return; + + case 'telemetry': + return appcd.telemetry(msg.payload); + } + }); + + log('Sending data to bootstrap:'); + log(data); + child.send(data); + }); +} diff --git a/src/legacy/spawn.js b/src/legacy/spawn.js deleted file mode 100644 index 423f04e..0000000 --- a/src/legacy/spawn.js +++ /dev/null @@ -1,101 +0,0 @@ -import path from 'path'; -import { spawn } from 'appcd-subprocess'; - -const { log } = appcd.logger('legacy'); - -/** - * Spawns the Legacy CLI and resolves once the command finishes. - * - * @param {Object} opts - Various options. - * @param {DispatcherContext} [opts.ctx] - The dispatcher context. - * @param {Object} [opts.data] - A data payload to send over the IPC tunnel to the Legacy Titanium - * CLI. - * @returns {Promise} - */ -export async function spawnLegacyCLI({ ctx, data }) { - log('Spawning legacy Titanium CLI bootstrap...'); - const { child } = spawn({ - command: process.execPath, - args: [ path.resolve(__dirname, 'bootstrap.js') ], - options: { - env: Object.assign({ FORCE_COLOR: 1 }, process.env), - stdio: [ 'pipe', 'pipe', 'pipe', 'ipc' ] - } - }); - - child.stdout.on('data', data => { - if (ctx) { - ctx.response.write(data.toString()); - } else { - log(data.toString()); - } - }); - - child.stderr.on('data', data => { - if (ctx) { - ctx.response.write(data.toString()); - } else { - log(data.toString()); - } - }); - - return await new Promise((resolve, reject) => { - child.on('close', code => { - log(`Legacy Titanium CLI bootstrap exited (code ${code || 0})`); - if (ctx) { - resolve(); - ctx.response.end(); - } - }); - - child.on('message', async msg => { - switch (msg.type) { - case 'call': - const { id, path, data } = msg; - if (id && path) { - let response; - try { - response = await appcd.call(path, data); - } catch (err) { - child.send({ - error: err, - id, - type: 'error' - }); - throw err; - } - - try { - child.send({ - id, - response, - type: 'response' - }); - } catch (err) { - console.error(err); - } - } - return; - - case 'error': - { - const err = new Error(msg.message); - return reject(Object.assign(err, msg)); - } - - case 'json': - return resolve(msg.data); - - case 'log': - return console.log(...msg.args); - - case 'telemetry': - return appcd.telemetry(msg.payload); - } - }); - - log('Sending data to bootstrap:'); - log(data); - child.send(data); - }); -} diff --git a/src/legacy/ti/cli.js b/src/legacy/ti/cli.js index 69b2512..1714aed 100644 --- a/src/legacy/ti/cli.js +++ b/src/legacy/ti/cli.js @@ -16,7 +16,7 @@ import { INVALID_ARGUMENT } from './error'; import { sdk } from 'titaniumlib'; import { snooplogg } from 'cli-kit'; -const { gray, green, magenta, red, yellow } = snooplogg.styles; +const { gray, green, highlight, magenta, red, yellow } = snooplogg.styles; /** * The Titanium CLI v5 requires the `--sdk ` to equal the `` in the @@ -287,21 +287,23 @@ export default class CLI { // call all post filters await posts // eslint-disable-next-line promise/no-nesting - .reduce((promise, post) => promise.then(() => new Promise((resolve, reject) => { + .reduce((promise, post) => promise.then(async () => { if (post.length >= 2) { - post.call(ctx, data, (err, newData) => { - if (err) { - return reject(err); - } else if (newData && typeof newData === 'object' && newData.type) { - data = newData; - } - resolve(); + await new Promise((resolve, reject) => { + post.call(ctx, data, (err, newData) => { + if (err) { + return reject(err); + } + if (newData && typeof newData === 'object' && newData.type) { + data = newData; + } + resolve(); + }); }); } else { post.call(ctx, data); - resolve(); } - })), Promise.resolve()); + }), Promise.resolve()); const { callback } = data; if (typeof callback === 'function') { @@ -318,6 +320,7 @@ export default class CLI { .catch(err => { // this is the primary error handler if (typeof data.callback === 'function') { + tunnel.log(err.stack); data.callback(err); } else { console.log('Hook completion callback threw unhandled error:'); @@ -380,7 +383,7 @@ export default class CLI { let done = 0; await new Promise((resolve, reject) => { - tunnel.log(`Executing ${this.command.name}`); + tunnel.log(`Executing ${highlight(this.command.name)}`); run(this.logger, this.config, this, async (err, result) => { if (done++) { @@ -434,7 +437,7 @@ export default class CLI { await this.command.load(true); await this.emit('cli:command-loaded', { cli: this, command: this.command }); await this.validate(); - await this.executeCommand(); + // await this.executeCommand(); } /** @@ -537,6 +540,7 @@ export default class CLI { */ scanHooks(dir) { dir = expandPath(dir); + tunnel.log(`Scanning hooks: ${highlight(dir)}`); if (this.hooks.scannedPaths[dir]) { return; @@ -544,7 +548,7 @@ export default class CLI { try { // eslint-disable-next-line security/detect-non-literal-require - const appc = require(path.join(this.argv.sdkPath, 'node_modules', 'node-appc')); + const appc = require(path.join(this.sdk.path, 'node_modules', 'node-appc')); const jsfile = /\.js$/; const ignore = /^[._]/; const files = fs.statSync(dir).isDirectory() ? fs.readdirSync(dir).map(n => path.join(dir, n)) : [ dir ]; @@ -552,6 +556,7 @@ export default class CLI { for (const file of files) { try { if (fs.statSync(file).isFile() && jsfile.test(file) && !ignore.test(path.basename(path.dirname(file)))) { + tunnel.log(`Checking hook: ${highlight(file)}`); // test the file for syntax errors vm.runInThisContext(`(function (exports, require, module, __filename, __dirname){${fs.readFileSync(file).toString()}\n});`, file, 0, false); @@ -575,7 +580,7 @@ export default class CLI { if (!this.version || !mod.cliVersion || version.satisfies(this.version, mod.cliVersion)) { mod.init && mod.init(this.logger, this.config, this, appc); this.hooks.loadedFilenames.push(file); - console.error(`Loaded CLI hook: ${file}`); + this.logger.trace(`Loaded CLI hook: ${highlight(file)}`); } else { this.hooks.incompatibleFilenames.push(file); } @@ -585,8 +590,11 @@ export default class CLI { this.hooks.errors[file] = ex; } } - } catch (e) { - // squelch + } catch (err) { + if (err.code !== 'ENOENT') { + tunnel.log(`Error scanning hooks: ${highlight(dir)}`); + tunnel.log(err.stack); + } } } diff --git a/src/legacy/tunnel.js b/src/legacy/tunnel.js index 4a3cffa..8b016ca 100644 --- a/src/legacy/tunnel.js +++ b/src/legacy/tunnel.js @@ -22,7 +22,7 @@ class Tunnel { process.on('message', async data => { const { id, type } = data; - if (type === 'exec' || type === 'build-options') { + if (type === 'exec' || type === 'help') { try { const cli = new CLI(data); diff --git a/src/lib/app-preview.js b/src/lib/app-preview.js deleted file mode 100644 index 107dd08..0000000 --- a/src/lib/app-preview.js +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Installr integration ported from https://github.com/jeffbonnes/appc-app-preview-cli-hook. - * Jeff Bonnes , MIT License - * Installr API docs: https://help.installrapp.com/api/ - * - * This is the App Preview CLI and validation code. The App Preview Titanium CLI plugin is located - * in `src/legacy/hooks/app-preview-hook.js`. - */ - -const { log } = appcd.logger('app-preview'); -const { highlight } = appcd.logger.styles; - -export const endpoint = 'https://appbeta.axway.com'; - -export const options = [ - 'App Preview Options', - { - '--app-preview': 'Deploy a build to App Preview', - '--add [teams]': 'A comma-separated list of team names to add access to the App Preview build', - '--release-notes [text]': 'Release notes for the App Preview build', - '--invite [email_addresses]': 'A comma-separated list of email addresses to send the App Preview invites to', - '--notify [teams]': 'A comma-separated list of team names that have been previously invited to notify of App Preview build' - } -]; - -function INVALID_ARGUMENT({ msg, code, prompt }) { - const err = new TypeError(msg); - if (code !== undefined) { - err.code = code; - } - if (prompt !== undefined) { - err.prompt = prompt; - } - return err; -} - -export async function validate(argv) { - if (!argv.appPreview) { - return; - } - - const { response: account } = await appcd.call('/amplify/1.x/auth/active'); - - if (!account) { - throw new Error('App Preview requires you to be logged in\nPlease login by running: ti login'); - } - - if (!account.org?.entitlements?.appPreview) { - // eslint-disable-next-line no-throw-literal - const err = new Error('Your account is not entitled to use App Preview'); - err.code = 'ENOTENT'; - err.details = `Your current organization is ${highlight(`"${account.org.name}"`)}.\n`; - if (account.orgs.length > 1) { - err.details += `If this is not the correct organization, run ${highlight('"ti switch"')} to change to another organization.\n`; - } - err.details += 'To upgrade your account, visit https://billing.axway.com/.'; - err.showHelp = false; - throw err; - } - - log(`Active account org ${highlight(`"${account.org.name}"`)} is entitled to App Preview!`); - - if (argv.releaseNotes === undefined) { - throw INVALID_ARGUMENT({ - msg: 'Expected App Preview release notes or path to release notes file', - prompt: { - message: 'Please enter release notes or a path to a release notes file', - name: 'releaseNotes', - type: 'text' - } - }); - } - - if (argv.notify === undefined) { - throw INVALID_ARGUMENT({ - msg: 'Expected App Preview notification preference', - prompt: { - disabled: 'No', - enabled: 'Yes', - initial: true, - message: 'Do you want to notify previous testers on upload?', - name: 'notify', - required: true, - type: 'toggle' - } - }); - } -} diff --git a/src/lib/prompt.js b/src/lib/prompt.js index 0f9d735..7ca7c22 100644 --- a/src/lib/prompt.js +++ b/src/lib/prompt.js @@ -6,6 +6,13 @@ import { Readable } from 'stream'; const { log } = appcd.logger('prompt'); const { alert, highlight } = appcd.logger.styles; +export class PromptError extends Error { + constructor(msg, ask) { + super(msg); + this.ask = ask; + } +} + /** * Prompts for a value with unified settings and improved style consistency. * diff --git a/src/project/project-service.js b/src/project/project-service.js index 5a759a1..24e33eb 100644 --- a/src/project/project-service.js +++ b/src/project/project-service.js @@ -1,13 +1,8 @@ import Dispatcher from 'appcd-dispatcher'; -import path from 'path'; import TemplateService from './templates-service'; - -import { AppcdError, codes } from 'appcd-response'; +import { Console } from 'console'; +import { exec } from '../legacy'; import { Project } from 'titaniumlib'; -import { spawnLegacyCLI } from '../legacy/spawn'; -import { validate as validateAppPreview } from '../lib/app-preview'; - -const { alert } = appcd.logger.styles; /** * Service for creating and building Titanium applications. @@ -25,13 +20,28 @@ export default class ProjectService extends Dispatcher { async activate(cfg) { this.config = cfg; + const runLegacyCLI = async (command, ctx) => { + const console = new Console(ctx.response, ctx.response); + const { cwd } = ctx.request.data; + const argv = { ...ctx.request.data }; + delete argv.cwd; + await exec({ + argv, + command, + config: cfg.titanium, + console, + cwd + }); + ctx.response.end(); + }; + this.register('/', ctx => { return 'tiapp coming soon!'; }); - this.register('/build', ctx => this.exec('build', ctx)); + this.register('/build', ctx => runLegacyCLI('build', ctx)); - this.register('/clean', ctx => this.exec('clean', ctx)); + this.register('/clean', ctx => runLegacyCLI('clean', ctx)); this.register('/new', async ctx => { try { @@ -47,7 +57,7 @@ export default class ProjectService extends Dispatcher { }); // TODO: in the future, run will call project.build and we'll "run" it ourselves - this.register('/run', ctx => this.exec('run', ctx)); + this.register('/run', ctx => runLegacyCLI('run', ctx)); await this.templateSvc.activate(cfg); this.register('/templates', this.templateSvc); @@ -62,69 +72,4 @@ export default class ProjectService extends Dispatcher { async deactivate() { await this.templateSvc.deactivate(); } - - /** - * Executes a Titanium SDK "build" or "clean" command. Commands are the old Titanium CLI v5 - * format and must be run via the bootstrap. - * - * @param {String} command - The name of the command to run. - * @param {DispatcherContext} ctx - The dispatcher context. - * @returns {Promise} - * @access private - */ - async exec(command, ctx) { - const { cwd } = ctx.headers; - let { projectDir } = ctx.request.data; - - if (projectDir !== undefined && typeof projectDir !== 'string') { - throw new AppcdError(codes.BAD_REQUEST, 'Missing project directory'); - } - - if (projectDir === undefined || !path.isAbsolute(projectDir)) { - if (!cwd || typeof cwd !== 'string') { - throw new AppcdError(codes.BAD_REQUEST, 'Current working directory required when project directory is relative'); - } - projectDir = path.resolve(cwd, projectDir || '.'); - } - - if (command === 'build' || command === 'run') { - try { - await validateAppPreview(ctx.request.data); - } catch (err) { - if (err.code === 'ENOTENT') { - return `${alert(err.toString())}\n\n${err.details}\n`; - } - throw err; - } - } - - const project = new Project({ - path: projectDir - }); - - // const { sdk } = project.tiapp.get('sdk-version'); - const sdk = '9.0.3.GA'; - const sdkInfo = (await appcd.call('/sdk/find', { data: { name: sdk } })).response; - - const data = { - argv: { - ...ctx.request.data, - projectDir, - sdk - }, - command, - config: this.config.titanium, - sdkPath: sdkInfo.path, - type: 'exec' - }; - - if (command === 'build') { - data.argv.buildOnly = true; - } - - await spawnLegacyCLI({ - ctx, - data - }); - } } From da282466991a89a837094a8737353e16f789b74b Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Wed, 12 Aug 2020 00:08:55 -0500 Subject: [PATCH 17/39] fix: Removed disabling showHelpOnError as it is now disabled by default. feat: Wired up prompting to the legacy CLI. chore: Updated deps. --- package.json | 2 +- src/cli/commands/config.js | 6 +-- src/legacy/hooks/app-preview.js | 3 -- src/legacy/index.js | 76 +++++++++++++++------------------ src/project/project-service.js | 5 +-- 5 files changed, 38 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index 79b2d9d..82def55 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "appcd-path": "^2.0.1", "appcd-util": "^3.0.0", "chalk": "^4.1.0", - "cli-kit": "^1.6.1", + "cli-kit": "^1.6.2", "colors": "^1.4.0", "dateformat": "^3.0.3", "enquirer": "^2.3.6", diff --git a/src/cli/commands/config.js b/src/cli/commands/config.js index 9649e7a..5a58885 100644 --- a/src/cli/commands/config.js +++ b/src/cli/commands/config.js @@ -100,11 +100,7 @@ async function runConfig(action, { argv, cmd, console, setExitCode }) { if (err.status === 404) { return print({ code: 6, key }); } - - if (json) { - cmd.showHelpOnError = false; - err.json = json; - } + err.json = json; throw err; } } diff --git a/src/legacy/hooks/app-preview.js b/src/legacy/hooks/app-preview.js index 189e268..995d4b1 100644 --- a/src/legacy/hooks/app-preview.js +++ b/src/legacy/hooks/app-preview.js @@ -161,7 +161,6 @@ export async function validate(argv) { if (!account) { const err = new Error('Log in required to use App Preview'); err.details = `Please login by running: ${highlight('ti login')}`; - err.showHelp = false; throw err; } @@ -173,7 +172,6 @@ export async function validate(argv) { err.details += `If this is not the correct organization, run ${highlight('"ti switch"')} to change to another organization.\n`; } err.details += 'To upgrade your account, visit https://billing.axway.com/.'; - err.showHelp = false; throw err; } @@ -183,7 +181,6 @@ export async function validate(argv) { if (platform !== 'android' && platform !== 'ios') { const err = new Error(`App Preview does not support the platform "${platform}"`); err.details = 'Only Android and iOS platforms are supported.'; - err.showHelp = false; throw err; } diff --git a/src/legacy/index.js b/src/legacy/index.js index 575bd04..274d3ae 100644 --- a/src/legacy/index.js +++ b/src/legacy/index.js @@ -98,8 +98,9 @@ export async function spawnLegacyCLI({ console, data, prompt }) { } else { const { log } = logger(`${child.pid}-stdout`); const { error } = logger(`${child.pid}-stderr`); - child.stdout.on('data', data => log(data.toString().replace(/\n$/, ''))); - child.stderr.on('data', data => error(data.toString().replace(/\n$/, ''))); + const newline = /\n$/; + child.stdout.on('data', data => log(data.toString().replace(newline, ''))); + child.stderr.on('data', data => error(data.toString().replace(newline, ''))); } return await new Promise((resolve, reject) => { @@ -109,52 +110,43 @@ export async function spawnLegacyCLI({ console, data, prompt }) { }); child.on('message', async msg => { - switch (msg.type) { - case 'call': - const { id, path, data } = msg; - if (id && path) { - let response; - try { - response = await appcd.call(path, data); - } catch (err) { - child.send({ - error: err, - id, - type: 'error' - }); - throw err; - } - - try { - child.send({ - id, - response, - type: 'response' - }); - } catch (err) { - console.error(err); - } + const { type } = msg; + + if (type === 'call') { + const { id, path, data } = msg; + if (id && path) { + try { + const response = await appcd.call(path, data); + child.send({ id, response, type: 'response' }); + } catch (err) { + child.send({ error: err.message, id, type: 'error' }); } - return; - - case 'error': - { - const err = new Error(msg.message); - return reject(Object.assign(err, msg)); } - case 'json': - return resolve(msg.data); + } else if (type === 'error') { + const err = new Error(msg.message); + reject(Object.assign(err, msg)); + + } else if (type === 'json') { + resolve(msg.data); + + } else if (type === 'log') { + trace.log(...msg.args); - case 'log': - return trace.log(...msg.args); + } else if (type === 'prompt') { + const { ask, id } = msg; - case 'prompt': - // TODO - return; + if (ask && id) { + if (prompt) { + const result = await prompt(ask); + child.send({ id, result, type: 'answer' }); + } else { + child.send({ error: 'Prompting is not enabled', id, type: 'error' }); + } + } - case 'telemetry': - return appcd.telemetry(msg.payload); + } else if (type === 'telemetry') { + appcd.telemetry(msg.payload); } }); diff --git a/src/project/project-service.js b/src/project/project-service.js index 24e33eb..21dbafe 100644 --- a/src/project/project-service.js +++ b/src/project/project-service.js @@ -21,15 +21,14 @@ export default class ProjectService extends Dispatcher { this.config = cfg; const runLegacyCLI = async (command, ctx) => { - const console = new Console(ctx.response, ctx.response); const { cwd } = ctx.request.data; const argv = { ...ctx.request.data }; delete argv.cwd; await exec({ argv, command, - config: cfg.titanium, - console, + config: cfg.titanium, + console: new Console(ctx.response, ctx.response), cwd }); ctx.response.end(); From ed994835df88dfc649525aa96ddb0fbf2b0779a4 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Thu, 13 Aug 2020 01:46:51 -0500 Subject: [PATCH 18/39] refactor(liveview): Removed local LiveView CLI option config in favor of the actual one in the LiveView plugin, but still group it. refactor(run-legacy): Normalize build/run commands. refactor(legacy:cli): Redesign the prompting workflow and wire up to the validate step. fix(legacy:cli): Correctly set the function hook result. fix(legacy:cli): Redirect all trace and debug logging to appcd debug log instead of stdout. fix(legacy:context): Correctly normalize --platform option. fix(legacy:context): Use actual platform name when prompting. doc: Add missing JSDoc. --- .eslintrc | 2 +- src/cli/cli-service.js | 11 ++- src/cli/run-legacy.js | 52 ++++++----- src/legacy/hooks/app-preview.js | 2 +- src/legacy/hooks/liveview.js | 22 ----- src/legacy/index.js | 39 +++++--- src/legacy/ti/cli.js | 152 +++++++++++++++++++------------- src/legacy/ti/context.js | 49 ++++++---- src/legacy/ti/error.js | 35 -------- src/legacy/tunnel.js | 34 +++++-- src/project/project-service.js | 2 + 11 files changed, 212 insertions(+), 188 deletions(-) delete mode 100644 src/legacy/hooks/liveview.js delete mode 100644 src/legacy/ti/error.js diff --git a/.eslintrc b/.eslintrc index 29a3bed..dd99348 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,6 @@ { "rules": { - "max-depth": [ "warn", 6 ], + "max-depth": [ "warn", 7 ], "no-loop-func": "off", "promise/always-return": "off" } diff --git a/src/cli/cli-service.js b/src/cli/cli-service.js index e1e0bfb..9b2af74 100644 --- a/src/cli/cli-service.js +++ b/src/cli/cli-service.js @@ -92,6 +92,9 @@ export default class CLIService extends Dispatcher { // copy the platform-specific options into a cli-kit friendly format buildOptions = []; + const lv = {}; + const lvRegExp = /^liveview/; + for (const key of Object.keys(config)) { if (key === 'flags' || key === 'options') { // we skip the top level build flags/options because they are either @@ -102,7 +105,7 @@ export default class CLIService extends Dispatcher { for (const [ name, flag ] of Object.entries(conf.flags)) { if (!flag.hidden) { - options[`--${name}`] = { desc: capitalize(flag.desc) }; + (lvRegExp.test(name) ? lv : options)[`--${name}`] = { desc: capitalize(flag.desc) }; } } @@ -110,7 +113,7 @@ export default class CLIService extends Dispatcher { if (!option.hidden) { let format = option.abbr ? `-${option.abbr}, ` : ''; format += `--${name} ${option.required ? '<' : '['}${option.hint || 'value'}${option.required ? '>' : ']'}`; - options[format] = { desc: capitalize(option.desc) }; + (lvRegExp.test(name) ? lv : options)[format] = { desc: capitalize(option.desc) }; } } @@ -123,6 +126,10 @@ export default class CLIService extends Dispatcher { } } + if (Object.keys(lv).length) { + buildOptions.push('LiveView Options', lv); + } + buildOptionsCache[sdkInfo.path] = buildOptions; } diff --git a/src/cli/run-legacy.js b/src/cli/run-legacy.js index ab290ad..5ad762a 100644 --- a/src/cli/run-legacy.js +++ b/src/cli/run-legacy.js @@ -1,9 +1,16 @@ import { exec } from '../legacy'; import { prompt } from '../lib/prompt'; +/** + * Runs a command in the Legacy Titanium CLI based on the cli-kit execution context. + * + * @param {String} command - The name of the command to run. + * @param {Object} ctx - A cli-kit execution context. + * @returns {Promise} + */ export async function runLegacyCLI(command, ctx) { const { argv, console, data, terminal } = ctx; - const { prompt: promptEnabled } = argv; + const { prompt: promptingEnabled } = argv; // remove general CLI-related values delete argv.banner; @@ -12,33 +19,30 @@ export async function runLegacyCLI(command, ctx) { delete argv.prompt; delete argv.version; + // map `build` and `run` to the correct legacy `build` command + if (command === 'build') { + argv.buildOnly = true; + } else if (command === 'run') { + command = 'build'; + } + await exec({ argv, command, - config: data.config, + config: data.config, console: console, - cwd: data.cwd, - prompt: promptEnabled && (async ask => { - let { name } = ask; - let result; - - while (ask) { - result = await prompt({ - cancel() {}, // prevent '' from being thrown - validate(value) { - if (this.type === 'toggle' || !this.required || !!value) { - return true; - } - return ask.validateMessage || false; - }, - name: ask.name || name, - ...ask - }, terminal); - - ask = result?.[ask.name || name]?.prompt; - } - - return result; + cwd: data.cwd, + prompt: promptingEnabled && (async question => { + return (await prompt({ + cancel() {}, // prevent '' from being thrown + validate(value) { + if (this.type === 'toggle' || !this.required || !!value) { + return true; + } + return question.validateMessage || false; + }, + ...question + }, terminal))[question.name]; }) }); } diff --git a/src/legacy/hooks/app-preview.js b/src/legacy/hooks/app-preview.js index 995d4b1..6e1ad11 100644 --- a/src/legacy/hooks/app-preview.js +++ b/src/legacy/hooks/app-preview.js @@ -12,7 +12,7 @@ exports.init = (logger, config, cli, appc) => { cli.on('build.config', data => { - data.result.appPreview = [ + data.result[1].appPreview = [ 'App Preview Options', { '--app-preview': 'Deploy a build to App Preview', diff --git a/src/legacy/hooks/liveview.js b/src/legacy/hooks/liveview.js deleted file mode 100644 index f4d75e0..0000000 --- a/src/legacy/hooks/liveview.js +++ /dev/null @@ -1,22 +0,0 @@ -exports.init = (logger, config, cli, appc) => { - cli.on('build.config', data => { - data.result.liveview = [ - 'LiveView Options', - { - '--liveview': 'Enables LiveView hot reloading', - '--liveview-ip [teams]': { - desc: 'The LiveView server IP address', - hidden: true - }, - '--liveview-fport [text]': { - desc: 'The LiveView file server port', - hidden: true - }, - '--liveview-eport [port]': { - desc: 'The LiveView event server port', - hidden: true - } - } - ]; - }); -}; diff --git a/src/legacy/index.js b/src/legacy/index.js index 274d3ae..114f3ac 100644 --- a/src/legacy/index.js +++ b/src/legacy/index.js @@ -7,6 +7,19 @@ import { spawn } from 'appcd-subprocess'; const logger = appcd.logger('legacy-cli'); const { log } = logger; +/** + * Executes the 'build' or 'clean' command from the Legacy Titanium CLI. + * + * @param {Object} opts - Various options. + * @param {Object} opts.argv - The parsed arguments. + * @param {String} opts.command - The name of the command to execute. + * @param {Object} [opts.config] - The Titanium configuration. + * @param {Console} opts.console - A console to write output to. + * @param {String} [opts.cwd] - The current working directory. Only required if `projectDir` is + * undefined or a relative path. + * @param {Function} [opts.prompt] - A function that prompts for user input. + * @returns {Promise} + */ export async function exec({ argv, command, config, console, cwd, prompt }) { let { projectDir } = argv; @@ -38,6 +51,7 @@ export async function exec({ argv, command, config, console, cwd, prompt }) { }); // step 3: load the sdk + // FIX ME! // const { sdk } = project.tiapp.get('sdk-version'); const sdk = '9.0.3.GA'; const sdkInfo = (await appcd.call('/sdk/find', { data: { name: sdk } })).response; @@ -49,11 +63,11 @@ export async function exec({ argv, command, config, console, cwd, prompt }) { sdk }, command, - config: config || {}, - cwd: projectDir, - prompt: !!prompt, - sdkPath: sdkInfo.path, - type: 'exec' + config: config || {}, + cwd: projectDir, + promptingEnabled: !!prompt, + sdkPath: sdkInfo.path, + type: 'exec' }; if (command === 'build') { @@ -61,11 +75,7 @@ export async function exec({ argv, command, config, console, cwd, prompt }) { } // step 4: spawn the legacy cli - await spawnLegacyCLI({ - console, - data, - prompt - }); + await spawnLegacyCLI({ console, data, prompt }); } /** @@ -75,6 +85,7 @@ export async function exec({ argv, command, config, console, cwd, prompt }) { * @param {Console} [opts.console] - The console to pipe output to. * @param {Object} [opts.data] - A data payload to send over the IPC tunnel to the Legacy Titanium * CLI. + * @param {Function} [opts.prompt] - A function that prompts for user input. * @returns {Promise} */ export async function spawnLegacyCLI({ console, data, prompt }) { @@ -134,12 +145,12 @@ export async function spawnLegacyCLI({ console, data, prompt }) { trace.log(...msg.args); } else if (type === 'prompt') { - const { ask, id } = msg; + const { id, question } = msg; - if (ask && id) { + if (id && question) { if (prompt) { - const result = await prompt(ask); - child.send({ id, result, type: 'answer' }); + const answer = await prompt(question); + child.send({ answer, id, type: 'answer' }); } else { child.send({ error: 'Prompting is not enabled', id, type: 'error' }); } diff --git a/src/legacy/ti/cli.js b/src/legacy/ti/cli.js index 1714aed..c705b70 100644 --- a/src/legacy/ti/cli.js +++ b/src/legacy/ti/cli.js @@ -5,18 +5,16 @@ import fs from 'fs-extra'; import getOSInfo from '../../lib/os'; import path from 'path'; import tunnel from '../tunnel'; -import vm from 'vm'; import * as version from '../../lib/version'; import { CLI_VERSION } from './version'; import { expandPath } from 'appcd-path'; import { format } from 'util'; import { get, mergeDeep, set, unique } from 'appcd-util'; -import { INVALID_ARGUMENT } from './error'; import { sdk } from 'titaniumlib'; import { snooplogg } from 'cli-kit'; -const { gray, green, highlight, magenta, red, yellow } = snooplogg.styles; +const { gray, green, highlight, magenta, note, red, yellow } = snooplogg.styles; /** * The Titanium CLI v5 requires the `--sdk ` to equal the `` in the @@ -79,11 +77,11 @@ export default class CLI { * @type {Object} */ logger = { - debug: (msg, ...args) => console.log(`${magenta('[DEBUG]')} ${format(msg, ...args)}`), + debug: (msg, ...args) => tunnel.log(`${magenta('[DEBUG]')} ${format(msg, ...args)}`), error: (msg, ...args) => console.error(red(`[ERROR] ${format(msg, ...args)}`)), info: (msg, ...args) => console.info(`${green('[INFO] ')} ${format(msg, ...args)}`), log: console.log, - trace: (msg, ...args) => console.log(`${gray('[TRACE]')} ${format(msg, ...args)}`), + trace: (msg, ...args) => tunnel.log(`${gray('[TRACE]')} ${format(msg, ...args)}`), warn: (msg, ...args) => console.warn(yellow(`[WARN] ${format(msg, ...args)}`)), levels: { @@ -123,6 +121,8 @@ export default class CLI { * @param {String} opts.command - The name of the command to execute. * @param {Object} [opts.config] - User-defined Titanium CLI config settings from appcd's user * config. + * @param {Boolean} [opts.promptingEnabled] - When `true`, invalid and missing values will be + * prompted for. * @param {String} opts.sdkPath - The path to the Titanium SDK. * @access public */ @@ -133,6 +133,8 @@ export default class CLI { this.config = this.initConfig(opts.config); + this.promptingEnabled = !!opts.promptingEnabled; + // validate the sdk path this.sdk = new sdk.TitaniumSDK(opts.sdkPath); @@ -160,7 +162,7 @@ export default class CLI { oscpu: info.numcpus, memory: info.memory, node: process.versions.node, - npm: '?' // unimportant + npm: 'n/a' // unimportant }); }, @@ -213,13 +215,33 @@ export default class CLI { * * @param {*} ...args - An event name and callback. * @returns {CLI} - * @deprecated * @access public */ addHook(...args) { return this.on(...args); } + /** + * Prompt for a question. + * + * @param {Object} question - The question parameters. + * @returns {Promise} + * @access public + */ + async ask(question) { + // copy the question and remove the error message + question = { required: true, ...question }; + delete question.error; + + if (this.promptingEnabled) { + return await tunnel.ask(question); + } + + const err = new Error(question.error); + err.prompt = question; + throw err; + } + /** * Defines a hook function that will emit an event before and after the hooked function is * invoked. @@ -275,11 +297,9 @@ export default class CLI { })), Promise.resolve()); if (data.fn) { - data.result = await new Promise((resolve, reject) => { + data.result = await new Promise(resolve => { // call the function - data.args.push((err, data) => { - err ? reject(err) : resolve(data); - }); + data.args.push((...args) => resolve(args)); data.fn.apply(data.ctx, data.args); }); } @@ -308,13 +328,7 @@ export default class CLI { const { callback } = data; if (typeof callback === 'function') { data.callback = null; - if (callback.length > 1) { - callback.call(data, null, data.result); - } else { - // this is because the original hook system was bad and didn't handle - // errors correctly :( - callback.call(data, data.result); - } + callback.call(data, ...data.result); } }) .catch(err => { @@ -351,7 +365,7 @@ export default class CLI { const promise = unique(name) .reduce((promise, name) => promise.then(() => new Promise((resolve, reject) => { const hook = this.createHook(name, data); - tunnel.log(`Emitting ${name}`); + this.logger.trace(`Emitting ${name}`); hook((err, result) => { err ? reject(err) : resolve(result); }); @@ -383,7 +397,7 @@ export default class CLI { let done = 0; await new Promise((resolve, reject) => { - tunnel.log(`Executing ${highlight(this.command.name)}`); + this.logger.trace(`Executing ${highlight(this.command.name)}`); run(this.logger, this.config, this, async (err, result) => { if (done++) { @@ -540,7 +554,7 @@ export default class CLI { */ scanHooks(dir) { dir = expandPath(dir); - tunnel.log(`Scanning hooks: ${highlight(dir)}`); + this.logger.trace(`Scanning hooks: ${highlight(dir)}`); if (this.hooks.scannedPaths[dir]) { return; @@ -556,9 +570,7 @@ export default class CLI { for (const file of files) { try { if (fs.statSync(file).isFile() && jsfile.test(file) && !ignore.test(path.basename(path.dirname(file)))) { - tunnel.log(`Checking hook: ${highlight(file)}`); - // test the file for syntax errors - vm.runInThisContext(`(function (exports, require, module, __filename, __dirname){${fs.readFileSync(file).toString()}\n});`, file, 0, false); + const startTime = Date.now(); // eslint-disable-next-line security/detect-non-literal-require var mod = require(file); @@ -580,7 +592,7 @@ export default class CLI { if (!this.version || !mod.cliVersion || version.satisfies(this.version, mod.cliVersion)) { mod.init && mod.init(this.logger, this.config, this, appc); this.hooks.loadedFilenames.push(file); - this.logger.trace(`Loaded CLI hook: ${highlight(file)}`); + this.logger.trace(`Loaded CLI hook: ${highlight(file)} ${note(`(${Date.now() - startTime} ms)`)}`); } else { this.hooks.incompatibleFilenames.push(file); } @@ -592,8 +604,8 @@ export default class CLI { } } catch (err) { if (err.code !== 'ENOENT') { - tunnel.log(`Error scanning hooks: ${highlight(dir)}`); - tunnel.log(err.stack); + this.logger.trace(`Error scanning hooks: ${highlight(dir)}`); + this.logger.trace(err.stack); } } } @@ -609,7 +621,7 @@ export default class CLI { async validate() { await this.emit('cli:pre-validate', { cli: this, command: this.command }); - // step 0: build a list of all options so we can sort them + // step 1: build a list of all options so we can sort them const options = []; for (const ctx of [ this.command, this.command?.platform ]) { if (ctx?.conf.options) { @@ -640,54 +652,68 @@ export default class CLI { } } - options.sort((a, b) => ~~b.order - ~~a.order); + options.sort((a, b) => { + if (a.order && b.order) { + return a.order - b.order; + } + return a.order ? -1 : b.order ? 1 : 0; + }); - // step 1: determine invalid or missing options + const createQuestion = (opt, error) => { + if (opt.values) { + return { + choices: opt.values.map(value => ({ value })), + error, + message: `Please select a valid ${opt.name}`, + name: opt.name, + type: 'select' + }; + } + return { + error, + message: `Please enter a valid ${opt.name}`, + name: opt.name, + type: 'text' + }; + }; + + // step 2: determine invalid or missing options for (const opt of options) { const { name } = opt; const value = this.argv[name]; - if (value !== undefined && opt.values && !opt.values.includes(value)) { - throw INVALID_ARGUMENT({ - msg: `Invalid ${name} value "${value}"`, - option: opt - }); - } - - if (value !== undefined) { - if (typeof opt.validate === 'function') { - await new Promise((resolve, reject) => { - opt.validate(value, (err, value) => { - if (err) { - return reject(INVALID_ARGUMENT({ - msg: `Invalid ${name} value "${value}"`, - option: opt - })); + if (value === undefined) { + // we need to check if the option is required + // sometimes required options such as `--device-id` allow an undefined value in the + // case when the value is derived by the config or is autoselected + if (opt.required && (typeof opt.verifyIfRequired !== 'function' || await new Promise(opt.verifyIfRequired))) { + this.argv[name] = await this.ask(createQuestion(opt, `Missing required option "${name}"`)); + } + } else if (opt.values && !opt.values.includes(value)) { + this.argv[name] = await this.ask(createQuestion(opt, `Invalid ${name} value "${value}"`)); + } else if (typeof opt.validate === 'function') { + this.argv[name] = await new Promise((resolve, reject) => { + opt.validate(value, async (err, adjustedValue) => { + if (err) { + this.logger.trace(`Validation failed for option ${name}: ${err.toString()}`); + try { + adjustedValue = await this.ask(createQuestion(opt, `Invalid ${name} value "${value}"`)); + } catch (e) { + return reject(e); } - - this.argv[name] = opt.callback(value); - resolve(); - }); + } + resolve(opt.callback(adjustedValue)); }); - } else { - this.argv[name] = opt.callback(value); - } - - // we need to check if the option is required - // sometimes required options such as `--device-id` allow an undefined value in the - // case when the value is derived by the config or is autoselected - } else if (opt.required && (typeof opt.verifyIfRequired !== 'function' || await new Promise(opt.verifyIfRequired))) { - throw INVALID_ARGUMENT({ - msg: `Missing required option: ${name}`, - option: opt }); + } else { + this.argv[name] = opt.callback(value); } } // note that we don't care about missing arguments because `build` and `clean` commands // don't have any arguments! - // step 2: run the command's validate() function, if exists + // step 3: run the command's validate() function, if exists const { validate } = this.command.module; if (validate && typeof validate === 'function') { @@ -701,7 +727,7 @@ export default class CLI { await this.emit('cli:post-validate', { cli: this, command: this.command }); - // step 3: fire all option callbacks for any options we missed above + // step 4: fire all option callbacks for any options we missed above for (const opt of options) { if (typeof opt.callback === 'function') { const val = opt.callback(this.argv[opt.name] || ''); diff --git a/src/legacy/ti/context.js b/src/legacy/ti/context.js index 668ef42..8b9aab0 100644 --- a/src/legacy/ti/context.js +++ b/src/legacy/ti/context.js @@ -1,13 +1,11 @@ +import fs from 'fs-extra'; import path from 'path'; -import tunnel from '../tunnel'; import * as version from '../../lib/version'; import { CLI_VERSION } from './version'; -import { INVALID_ARGUMENT } from './error'; -import { isFile } from 'appcd-fs'; import { snooplogg } from 'cli-kit'; -const { highlight } = snooplogg.styles; +const { highlight, note } = snooplogg.styles; /** * Command specific data including the command module and configuration. @@ -45,10 +43,6 @@ export default class Context { this.name = name; this.parent = parent; this.path = path; - - if (!isFile(this.path)) { - throw new Error(`Command file not found: ${path}`); - } } /** @@ -61,10 +55,13 @@ export default class Context { * @access public */ async load(checkPlatform) { - tunnel.log(`Loading command file: ${highlight(this.path)}`); + const startTime = Date.now(); + // eslint-disable-next-line security/detect-non-literal-require this.module = require(this.path); + this.cli.logger.trace(`Loaded command file: ${highlight(this.path)} ${note(`(${Date.now() - startTime} ms)`)}`); + if (this.module.cliVersion && !version.satisfies(this.cliVersion, this.module.cliVersion)) { throw new Error(`Command "${this.name}" is incompatible with this version of the Titanium CLI`); } @@ -85,18 +82,32 @@ export default class Context { // the `build` command `--platform` option was hard wired into the CLI context, so // unfortunately we need to do a bunch of `build` specific logic to load the platform // specific command - const { platform } = this.cli.argv; + let { platform } = this.cli.argv; + const availablePlatforms = this.cli.sdk.manifest.platforms; + + // the platform should really be named 'ios', so just in case someday we fix that + if (platform === 'ios' && !availablePlatforms.includes('ios')) { + platform = 'iphone'; + } + if (!platform) { - throw INVALID_ARGUMENT({ - msg: 'Missing required "platform"', - code: 'EPLATFORM', - prompt: { - choices: this.conf.options.platform.values.map(platform => ({ value: platform })), - message: 'For which platform do you want to build?', - name: 'platform', - required: true, - type: 'select' + const choices = availablePlatforms.map(value => { + let message = value; + try { + const pkgJson = fs.readJsonSync(path.join(this.cli.sdk.path, value, 'package.json')); + message = pkgJson.title || value; + } catch (e) { + // squelch } + return { message, value }; + }).sort((a, b) => a.message.localeCompare(b.message)); + + platform = await this.cli.ask({ + choices, + error: 'Missing required option "platform"', + message: 'For which platform do you want to build?', + name: 'platform', + type: 'select' }); } diff --git a/src/legacy/ti/error.js b/src/legacy/ti/error.js deleted file mode 100644 index 2208603..0000000 --- a/src/legacy/ti/error.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * A helper function that creates an error and defines an optional code and prompt metadata. - * - * @param {Object} opts - Various options. - * @param {String} [opts.code] - A custom error code. This value should begin with an `E`. - * @param {String} opts.message - The error message. - * @param {Object} [opts.option] - A CLI option to autogenerate the prompt metadata from. - * @param {Object} [opts.prompt] - Prompt metadata. - * @returns {Error} - */ -export function INVALID_ARGUMENT({ code, msg, option, prompt }) { - const err = new TypeError(msg); - if (code !== undefined) { - err.code = code; - } - if (option?.values) { - err.prompt = { - choices: option.values.map(value => ({ value })), - message: `Please select a valid ${option.name} value`, - name: option.name, - required: true, - type: 'select' - }; - } else if (option) { - err.prompt = { - message: `Please enter a valid ${option.name}`, - name: option.name, - required: true, - type: 'text' - }; - } else if (prompt !== undefined) { - err.prompt = prompt; - } - return err; -} diff --git a/src/legacy/tunnel.js b/src/legacy/tunnel.js index 8b016ca..16f4697 100644 --- a/src/legacy/tunnel.js +++ b/src/legacy/tunnel.js @@ -56,22 +56,42 @@ class Tunnel { if (!req) { return; } + delete this.pending[id]; + const { resolve, reject } = req; switch (type) { - case 'response': - delete this.pending[id]; - resolve(data.response); - return; + case 'answer': + return resolve(data.answer); case 'error': - delete this.pending[id]; - reject(new Error(data.error)); - return; + return reject(new Error(data.error)); + + case 'response': + return resolve(data.response); } }); } + /** + * Requests that the parent process prompts for specified question. + * + * @param {Object} question - The question to prompt for. + * @returns {Promise} + * @access public + */ + ask(question) { + return new Promise((resolve, reject) => { + const id = uuidv4(); + this.pending[id] = { resolve, reject }; + process.send({ + id, + question, + type: 'prompt' + }); + }); + } + /** * Makes a request to the parent process. * diff --git a/src/project/project-service.js b/src/project/project-service.js index 21dbafe..86002cf 100644 --- a/src/project/project-service.js +++ b/src/project/project-service.js @@ -1,3 +1,5 @@ +/* eslint-disable node/prefer-global/console */ + import Dispatcher from 'appcd-dispatcher'; import TemplateService from './templates-service'; import { Console } from 'console'; From 50cf767563601ebd41921f431fbf796c20269187 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Thu, 13 Aug 2020 16:01:22 -0500 Subject: [PATCH 19/39] fix(legacy): Correctly wired up telemetry calls. fix(legacy): Fixed hook callback execution. fix(legacy): Fixed hooks not firing. fix(legacy): Re-enabled command execution. --- src/legacy/ti/cli.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/legacy/ti/cli.js b/src/legacy/ti/cli.js index c705b70..9a493a2 100644 --- a/src/legacy/ti/cli.js +++ b/src/legacy/ti/cli.js @@ -207,7 +207,7 @@ export default class CLI { * @access public */ addAnalyticsEvent(event, data, type) { - tunnel.call('/telemetry', { event: type === 'ti.apiusage' ? type : event, ...data }); + tunnel.telemetry({ event: type === 'ti.apiusage' ? type : event, ...data }); } /** @@ -328,7 +328,7 @@ export default class CLI { const { callback } = data; if (typeof callback === 'function') { data.callback = null; - callback.call(data, ...data.result); + callback.apply(data, data.result); } }) .catch(err => { @@ -362,7 +362,7 @@ export default class CLI { } // create each hook and immediately fire them - const promise = unique(name) + const promise = unique(Array.isArray(name) ? name : [ name ]) .reduce((promise, name) => promise.then(() => new Promise((resolve, reject) => { const hook = this.createHook(name, data); this.logger.trace(`Emitting ${name}`); @@ -451,7 +451,7 @@ export default class CLI { await this.command.load(true); await this.emit('cli:command-loaded', { cli: this, command: this.command }); await this.validate(); - // await this.executeCommand(); + await this.executeCommand(); } /** From 06d64c8ce035c3dd3a853d2584d39c957ab0c1ae Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Fri, 14 Aug 2020 17:42:16 -0500 Subject: [PATCH 20/39] fix(legacy): Silently drop log and telemetry IPC messages once child has disconnected. fix(legacy): Fixed log calls with no arguments. fix(legacy): Added tick/debounce when waiting for the clean command to finish. --- src/legacy/ti/cli.js | 24 +++--- src/legacy/tunnel.js | 181 +++++++++++++++++++++++++------------------ 2 files changed, 117 insertions(+), 88 deletions(-) diff --git a/src/legacy/ti/cli.js b/src/legacy/ti/cli.js index 9a493a2..99644b1 100644 --- a/src/legacy/ti/cli.js +++ b/src/legacy/ti/cli.js @@ -10,7 +10,7 @@ import * as version from '../../lib/version'; import { CLI_VERSION } from './version'; import { expandPath } from 'appcd-path'; import { format } from 'util'; -import { get, mergeDeep, set, unique } from 'appcd-util'; +import { debounce, get, mergeDeep, set, unique } from 'appcd-util'; import { sdk } from 'titaniumlib'; import { snooplogg } from 'cli-kit'; @@ -77,12 +77,12 @@ export default class CLI { * @type {Object} */ logger = { - debug: (msg, ...args) => tunnel.log(`${magenta('[DEBUG]')} ${format(msg, ...args)}`), - error: (msg, ...args) => console.error(red(`[ERROR] ${format(msg, ...args)}`)), - info: (msg, ...args) => console.info(`${green('[INFO] ')} ${format(msg, ...args)}`), - log: console.log, - trace: (msg, ...args) => tunnel.log(`${gray('[TRACE]')} ${format(msg, ...args)}`), - warn: (msg, ...args) => console.warn(yellow(`[WARN] ${format(msg, ...args)}`)), + debug: (msg = '', ...args) => console.log(`${magenta('[DEBUG]')} ${format(msg, ...args)}`), + error: (msg = '', ...args) => console.error(red(`[ERROR] ${format(msg, ...args)}`)), + info: (msg = '', ...args) => console.info(`${green('[INFO]')} ${format(msg, ...args)}`), + log: (msg = '', ...args) => console.log(format(msg, ...args)), + trace: (msg = '', ...args) => console.log(`${gray('[TRACE]')} ${format(msg, ...args)}`), + warn: (msg = '', ...args) => console.warn(yellow(`[WARN] ${format(msg, ...args)}`)), levels: { trace: {}, @@ -420,10 +420,13 @@ export default class CLI { resolve(); }); - // if there's no callback in the run signature, then unblock the function and let - // Node.js wait for run() to finish any async tasks + // if there's no callback in the run signature (e.g. the "clean" command), then we wait + // 100ms after the last bit of output activity through the IPC tunnel if (run.length < 4) { - resolve(); + this.logger.debug(`Command "${this.command.name}" does NOT have a finished callback!`); + const fn = debounce(resolve, 100); + fn(); // start the bounce + tunnel.on('tick', fn); } }); } @@ -433,7 +436,6 @@ export default class CLI { * * @param {*} ...args - The hook names, data, and callback. * @returns {CLI} - * @deprecated * @access public */ fireHook(...args) { diff --git a/src/legacy/tunnel.js b/src/legacy/tunnel.js index 16f4697..c0d43c6 100644 --- a/src/legacy/tunnel.js +++ b/src/legacy/tunnel.js @@ -1,4 +1,5 @@ import CLI from './ti/cli'; +import { EventEmitter } from 'events'; import { v4 as uuidv4 } from 'uuid'; /** @@ -6,7 +7,7 @@ import { v4 as uuidv4 } from 'uuid'; * * Note that this tunnel implementation does NOT support chunked/streamed responses. */ -class Tunnel { +class Tunnel extends EventEmitter { /** * A map of all pending request ids and their associated promise callbacks. * @type {Object} @@ -19,58 +20,8 @@ class Tunnel { * @access public */ constructor() { - process.on('message', async data => { - const { id, type } = data; - - if (type === 'exec' || type === 'help') { - try { - const cli = new CLI(data); - - if (type === 'exec') { - await cli.go(); - } else { - await cli.command.load(); - process.send({ - data: cli.command.conf, - type: 'json' - }); - } - - // the command is complete, but the IPC channel is still open, so we simply disconnect it and - // this process should exit whenever the command finishes - process.disconnect(); - } catch (err) { - process.send({ - ...err, - message: err.message || err, - stack: err.stack, - status: err.status || 500, - type: 'error' - }); - process.exit(1); - } - return; - } - - const req = id && this.pending[id]; - if (!req) { - return; - } - delete this.pending[id]; - - const { resolve, reject } = req; - - switch (type) { - case 'answer': - return resolve(data.answer); - - case 'error': - return reject(new Error(data.error)); - - case 'response': - return resolve(data.response); - } - }); + super(); + process.on('message', data => this.onMessage(data)); } /** @@ -81,14 +32,9 @@ class Tunnel { * @access public */ ask(question) { - return new Promise((resolve, reject) => { - const id = uuidv4(); - this.pending[id] = { resolve, reject }; - process.send({ - id, - question, - type: 'prompt' - }); + return this.sendRequest({ + question, + type: 'prompt' }); } @@ -101,15 +47,10 @@ class Tunnel { * @access public */ call(path, data) { - return new Promise((resolve, reject) => { - const id = uuidv4(); - this.pending[id] = { resolve, reject }; - process.send({ - data, - id, - path, - type: 'call' - }); + return this.sendRequest({ + data, + path, + type: 'call' }); } @@ -120,9 +61,92 @@ class Tunnel { * @access public */ log(...args) { - process.send({ - args, - type: 'log' + this.emit('tick'); + if (process.connected) { + process.send({ + args, + type: 'log' + }); + } + } + + /** + * Dispatches a message from the parent process. + * + * @param {Object} data - The message data. + * @access private + */ + async onMessage(data) { + const { id, type } = data; + + if (type === 'exec' || type === 'help') { + try { + const cli = new CLI(data); + + if (type === 'exec') { + await cli.go(); + } else { + await cli.command.load(); + process.send({ + data: cli.command.conf, + type: 'json' + }); + } + + // the command is complete, but the IPC channel is still open, so we simply disconnect it and + // this process should exit whenever the command finishes + this.log('Disconnecting IPC tunnel from parent and letting process exit gracefully'); + process.disconnect(); + } catch (err) { + process.send({ + ...err, + message: err.message || err, + stack: err.stack, + status: err.status || 500, + type: 'error' + }); + process.exit(1); + } + return; + } + + const req = id && this.pending[id]; + if (!req) { + return; + } + delete this.pending[id]; + + const { resolve, reject } = req; + + switch (type) { + case 'answer': + return resolve(data.answer); + + case 'error': + return reject(new Error(data.error)); + + case 'response': + return resolve(data.response); + } + } + + /** + * Initiates a request over the IPC tunnel to the parent. + * + * @param {Object} data - The request payload. + * @returns {Promise} + * @access private + */ + sendRequest(data) { + return new Promise((resolve, reject) => { + this.emit('tick'); + if (process.connected) { + data.id = uuidv4(); + this.pending[data.id] = { resolve, reject }; + process.send(data); + } else { + reject(new Error(`Can't send "${data.type}" message to parent because IPC channel has been closed`)); + } }); } @@ -133,10 +157,13 @@ class Tunnel { * @access public */ telemetry(payload) { - process.send({ - payload, - type: 'telemetry' - }); + this.emit('tick'); + if (process.connected) { + process.send({ + payload, + type: 'telemetry' + }); + } } } From c82dd76f8d64f985f8d03153fd23c69b99b66285 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Mon, 17 Aug 2020 17:29:45 -0500 Subject: [PATCH 21/39] refactor: Moved the module patching into the CLI constructor. fix: Moved ioslib.simulator.findSimulators into the ioslib patch. fix: Fixed bug with getting and setting config values. fix: Fixed bug with hook context being shared across hook executions. --- .eslintrc | 2 +- package.json | 2 +- src/legacy/bootstrap.js | 1 - src/legacy/patch/android.js | 178 ++++++++++-------- src/legacy/patch/fields.js | 23 +-- src/legacy/patch/index.js | 20 -- src/legacy/patch/ios.js | 360 +++++++++++++++++++++++++++++++++--- src/legacy/ti/cli.js | 154 ++++++++------- 8 files changed, 524 insertions(+), 216 deletions(-) delete mode 100644 src/legacy/patch/index.js diff --git a/.eslintrc b/.eslintrc index dd99348..be5e4e4 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,6 @@ { "rules": { - "max-depth": [ "warn", 7 ], + "max-depth": "off", "no-loop-func": "off", "promise/always-return": "off" } diff --git a/package.json b/package.json index 82def55..5d395a8 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "appcd-path": "^2.0.1", "appcd-util": "^3.0.0", "chalk": "^4.1.0", - "cli-kit": "^1.6.2", + "cli-kit": "^1.7.0", "colors": "^1.4.0", "dateformat": "^3.0.3", "enquirer": "^2.3.6", diff --git a/src/legacy/bootstrap.js b/src/legacy/bootstrap.js index 3750e71..9fa7a13 100644 --- a/src/legacy/bootstrap.js +++ b/src/legacy/bootstrap.js @@ -10,7 +10,6 @@ if (!process.connected) { import 'v8-compile-cache'; import 'colors'; -import './patch'; import './tunnel'; process.title = 'titanium-legacy-bootstrap'; diff --git a/src/legacy/patch/android.js b/src/legacy/patch/android.js index c7de77c..bab6c6d 100644 --- a/src/legacy/patch/android.js +++ b/src/legacy/patch/android.js @@ -6,104 +6,118 @@ import { snooplogg } from 'cli-kit'; const { alert } = snooplogg.styles; -/** - * Detects Android development environment by calling the `android` appcd plugin and translating - * the results into a Titanium SDK compatible structure. - * - * @param {Object} config - The Titanium CLI config object. - * @param {Object} opts - Various options. - * @param {Object} opts.packageJson - The Android platform-specific `package.json`. - * @param {Function} callback - A function to call with the detection results. Note that this - * function receives a single argument with the info. Any errors must be silenced. - */ -export function detect(config = {}, { packageJson } = {}, callback) { - tunnel.call('/android/2.x/info') - .then(({ response: info }) => { - const { vendorDependencies } = packageJson; - const results = { - avds: info.emulators, - devices: info.devices, - issues: [], - ndk: processNDK(info.ndks), - sdk: processSDK(info.sdks, config, vendorDependencies), - targets: {}, - vendorDependencies: vendorDependencies || {} - }; +export function patch() { + let cache; - processTargets(results, info.sdks, vendorDependencies); + return { + /** + * Detects Android development environment by calling the `android` appcd plugin and translating + * the results into a Titanium SDK compatible structure. + * + * @param {Object} config - The Titanium CLI config object. + * @param {Object} opts - Various options. + * @param {Object} opts.packageJson - The Android platform-specific `package.json`. + * @param {Function} callback - A function to call with the detection results. Note that this + * function receives a single argument with the info. Any errors must be silenced. + */ + detect(config = {}, { packageJson } = {}, callback) { + if (cache) { + return callback(null, cache); + } - if (!results.ndk) { - results.issues.push({ - id: 'ANDROID_NDK_NOT_FOUND', - type: 'warning', - message: `Unable to locate an Android NDK. + tunnel.call('/android/2.x/info') + .then(({ response: info }) => { + const { vendorDependencies } = packageJson; + const results = { + avds: info.emulators, + devices: info.devices, + issues: [], + ndk: processNDK(info.ndks), + sdk: processSDK(info.sdks, config, vendorDependencies), + targets: {}, + vendorDependencies: vendorDependencies || {} + }; + + processTargets(results, info.sdks, vendorDependencies); + processIssues(results, config, vendorDependencies); + + cache = results; + callback(results); + }) + .catch(err => { + tunnel.log(alert(err.stack)); + callback({ + issues: [], + sdk: null + }); + }); + } + }; +} + +function processIssues(results, config, vendorDependencies) { + if (!results.ndk) { + results.issues.push({ + id: 'ANDROID_NDK_NOT_FOUND', + type: 'warning', + message: `Unable to locate an Android NDK. Without the NDK, you will not be able to build native Android Titanium modules. To install the Android NDK, use Android Studio's SDK Manager. If you have already installed the Android NDK, configure the location by running: appcd config push android.ndk.searchPaths /path/to/android-ndk` - }); - } + }); + } - if (results.sdk) { - const appendInfo = msg => { - return `${msg} + if (results.sdk) { + const appendInfo = msg => { + return `${msg} Current installed Android SDK tools: - Android SDK Tools: ${results.sdk.tools.version || 'not installed'} (Supported: ${vendorDependencies['android tools']}) - Android SDK Platform Tools: ${results.sdk.platformTools.version || 'not installed'} (Supported: ${vendorDependencies['android platform tools']}) - Android SDK Build Tools: ${results.sdk.buildTools.version || 'not installed'} (Supported: ${vendorDependencies['android build tools']}) +Android SDK Tools: ${results.sdk.tools.version || 'not installed'} (Supported: ${vendorDependencies['android tools']}) +Android SDK Platform Tools: ${results.sdk.platformTools.version || 'not installed'} (Supported: ${vendorDependencies['android platform tools']}) +Android SDK Build Tools: ${results.sdk.buildTools.version || 'not installed'} (Supported: ${vendorDependencies['android build tools']}) Make sure you have the latest Android SDK Tools, Platform Tools, and Build Tools installed.`; - }; + }; - if (!results.sdk.buildTools.supported) { - results.issues.push({ - id: 'ANDROID_BUILD_TOOLS_NOT_SUPPORTED', - type: 'error', - message: appendInfo(`Android Build Tools ${results.sdk.buildTools.version} are not supported by Titanium`) - }); - } - - if (results.sdk.buildTools.notInstalled) { - const preferred = config.get('titanium.android.buildTools.selectedVersion'); - results.issues.push({ - id: 'ANDROID_BUILD_TOOLS_CONFIG_SETTING_NOT_INSTALLED', - type: 'error', - message: appendInfo(`The selected version of Android SDK Build Tools (${preferred}) are not installed. + if (!results.sdk.buildTools.supported) { + results.issues.push({ + id: 'ANDROID_BUILD_TOOLS_NOT_SUPPORTED', + type: 'error', + message: appendInfo(`Android Build Tools ${results.sdk.buildTools.version} are not supported by Titanium`) + }); + } + + if (results.sdk.buildTools.notInstalled) { + const preferred = config.get('titanium.android.buildTools.selectedVersion'); + results.issues.push({ + id: 'ANDROID_BUILD_TOOLS_CONFIG_SETTING_NOT_INSTALLED', + type: 'error', + message: appendInfo(`The selected version of Android SDK Build Tools (${preferred}) are not installed. Please either install this version of the build tools or remove this setting by running: - ti config delete android.buildTools.selectedVersion +ti config delete android.buildTools.selectedVersion and - appcd config delete titanium.android.buildTools.selectedVersion`) - }); - } - - // check if the sdk is missing any commands - var missing = [ 'adb', 'emulator', 'mksdcard', 'zipalign', 'aapt', 'aidl', 'dx' ].filter(cmd => !results.sdk.executables[cmd]); - if (missing.length && results.sdk.buildTools.supported) { - results.issues.push({ - id: 'ANDROID_SDK_MISSING_PROGRAMS', - type: 'error', - message: appendInfo(`Missing required Android SDK tool${missing.length !== 1 ? 's' : ''}: ${missing.join(', ')}`) - }); - } - } else { - results.issues.push({ - id: 'ANDROID_SDK_NOT_FOUND', - type: 'error', - message: `Unable to locate an Android SDK. -To install the Android SDK, use Android Studio's SDK Manager. -If you have already installed the Android SDK, configure the location by running: appcd config push android.sdk.searchPaths /path/to/android-sdk` - }); - } +appcd config delete titanium.android.buildTools.selectedVersion`) + }); + } - callback(results); - }) - .catch(err => { - tunnel.log(alert(err.stack)); - callback({ - issues: [], - sdk: null + // check if the sdk is missing any commands + var missing = [ 'adb', 'emulator', 'mksdcard', 'zipalign', 'aapt', 'aidl', 'dx' ].filter(cmd => !results.sdk.executables[cmd]); + if (missing.length && results.sdk.buildTools.supported) { + results.issues.push({ + id: 'ANDROID_SDK_MISSING_PROGRAMS', + type: 'error', + message: appendInfo(`Missing required Android SDK tool${missing.length !== 1 ? 's' : ''}: ${missing.join(', ')}`) }); + } + } else { + results.issues.push({ + id: 'ANDROID_SDK_NOT_FOUND', + type: 'error', + message: `Unable to locate an Android SDK. +To install the Android SDK, use Android Studio's SDK Manager. +If you have already installed the Android SDK, configure the location by running: appcd config push android.sdk.searchPaths /path/to/android-sdk` }); + } } function processNDK(ndks) { diff --git a/src/legacy/patch/fields.js b/src/legacy/patch/fields.js index 76a3c0a..dd98332 100644 --- a/src/legacy/patch/fields.js +++ b/src/legacy/patch/fields.js @@ -1,20 +1,9 @@ -export function setup() { -} - -export function file() { - return { - prompt() {} - }; -} - -export function select() { - return { - prompt() {} - }; -} - -export function text() { +export function patch() { + const dummy = { prompt() {} }; return { - prompt() {} + setup() {}, + file: () => dummy, + select: () => dummy, + text: () => dummy }; } diff --git a/src/legacy/patch/index.js b/src/legacy/patch/index.js deleted file mode 100644 index 99db77e..0000000 --- a/src/legacy/patch/index.js +++ /dev/null @@ -1,20 +0,0 @@ -import Module from 'module'; -import path from 'path'; - -/** - * Patches our system info plugins into Titanium SDK's system info library calls. - */ -const lookup = { - fields: path.resolve(__dirname, 'fields.js'), - ioslib: path.resolve(__dirname, 'ios.js'), - 'node-titanium-sdk/lib/android': path.resolve(__dirname, 'android.js') -}; - -const load = Module._load; -Module._load = (request, parent, isMain) => { - const module = load(request, parent, isMain); - if (lookup[request] && parent && path.basename(parent.filename) === '_build.js') { - Object.assign(module, load(lookup[request], parent, isMain)); - } - return module; -}; diff --git a/src/legacy/patch/ios.js b/src/legacy/patch/ios.js index 60c4f0f..9f5d0e1 100644 --- a/src/legacy/patch/ios.js +++ b/src/legacy/patch/ios.js @@ -6,38 +6,346 @@ import { snooplogg } from 'cli-kit'; const { alert } = snooplogg.styles; -let cache; +export function patch({ load, request, parent, isMain }) { + let cache; -export function detect(opts = {}, callback) { - if (cache) { - return callback(null, cache); - } + const ioslib = load(request, parent, isMain); + const { SimHandle } = ioslib.simulator; + + ioslib.detect = (opts = {}, callback) => { + if (cache) { + return callback(null, cache); + } + + tunnel.call('/ios/2.x/info') + .then(({ response: info }) => { + const results = { + certs: {}, + devices: info.devices, + iosSDKtoXcode: {}, + issues: [], + provisioning: {}, + selectedXcode: null, + simulators: info.simulators, + teams: Object.entries(info.teams).map(([ id, name ]) => ({ id, name })), + xcode: {} + }; + + processCerts(info, results); + processProvisioning(info, results); + processXcodes(info, results, opts); + + cache = results; + callback(null, results); + }) + .catch(err => { + tunnel.log(alert(err.stack)); + callback(err); + }); + }; + + /** + * Finds a iOS Simulator and/or Watch Simulator as well as the supported Xcode based on the specified options. + * + * @param {Object} [options] - An object containing various settings. + * @param {String} [options.appBeingInstalled] - The path to the iOS app to install after launching the iOS Simulator. + * @param {Boolean} [options.bypassCache=false] - When true, re-detects Xcode and all simulators. + * @param {Function} [options.logger] - A function to log debug messages to. + * @param {String} [options.iosVersion] - The iOS version of the app so that ioslib picks the appropriate Xcode. + * @param {String} [options.minIosVersion] - The minimum iOS SDK to detect. + * @param {String} [options.minWatchosVersion] - The minimum watchOS SDK to detect. + * @param {String|Array} [options.searchPath] - One or more path to scan for Xcode installations. + * @param {String|SimHandle} simHandleOrUDID - A iOS sim handle or the UDID of the iOS Simulator to launch or null if you want ioslib to pick one. + * @param {String} [options.simType=iphone] - The type of simulator to launch. Must be either "iphone" or "ipad". Only applicable when udid is not specified. + * @param {String} [options.simVersion] - The iOS version to boot. Defaults to the most recent version. + * @param {String} [options.supportedVersions] - A string with a version number or range to check if an Xcode install is supported. + * @param {Boolean} [options.watchAppBeingInstalled] - The id of the watch app. Required in order to find a watch simulator. + * @param {String} [options.watchHandleOrUDID] - A watch sim handle or UDID of the Watch Simulator to launch or null if your app has a watch app and you want ioslib to pick one. + * @param {String} [options.watchMinOSVersion] - The min Watch OS version supported by the specified watch app id. + * @param {Function} callback(err, simHandle, watchSimHandle, selectedXcode, simInfo, xcodeInfo) - A function to call with the simulators found. + */ + ioslib.simulator.findSimulators = (options, callback) => { + if (typeof options === 'function') { + callback = options; + options = {}; + } else if (typeof options !== 'object') { + options = {}; + } + if (typeof callback !== 'function') { + callback = () => {}; + } + + const logger = typeof options.logger === 'function' ? options.logger : () => {}; + logger('Running patched legacy ioslib findSimulators()'); + + ioslib.detect(options, (err, results) => { + if (err) { + return callback(err); + } + + const simInfo = { simulators: results.simulators }; + const xcodeInfo = { xcode: results.xcode }; + + const compareXcodes = (a, b) => { + var v1 = xcodeInfo.xcode[a].version; + var v2 = xcodeInfo.xcode[b].version; + if (options.iosVersion && version.eq(options.iosVersion, v1)) { + return -1; + } + if (options.iosVersion && version.eq(options.iosVersion, v2)) { + return 1; + } + if (xcodeInfo.xcode[a].selected) { + return -1; + } + if (xcodeInfo.xcode[b].selected) { + return 1; + } + return version.gt(v1, v2) ? -1 : version.eq(v1, v2) ? 0 : 1; + }; + + const compareSims = (a, b) => { + return a.model < b.model ? -1 : a.model > b.model ? 1 : 0; + }; - tunnel.call('/ios/2.x/info') - .then(({ response: info }) => { - const results = { - certs: {}, - devices: info.devices, - iosSDKtoXcode: {}, - issues: [], - provisioning: {}, - selectedXcode: null, - simulators: info.simulators, - teams: Object.entries(info.teams).map(([ id, name ]) => ({ id, name })), - xcode: {} + // find an Xcode installation that matches the iOS SDK or fall back to the selected Xcode or the latest + const xcodeIds = Object + .keys(xcodeInfo.xcode) + .filter(id => { + if (!xcodeInfo.xcode[id].supported) { + return false; + } + if (!options.iosVersion) { + return true; + } + return xcodeInfo.xcode[id].sdks.some(ver => version.eq(ver, options.iosVersion)); + }) + .sort(compareXcodes); + + if (!xcodeIds.length) { + if (options.iosVersion) { + return callback(new Error(`Unable to find any Xcode installations that supports iOS SDK ${options.iosVersion}.`)); + } else { + return callback(new Error('Unable to find any supported Xcode installations. Please install the latest Xcode.')); + } + } + + const xcodeId = xcodeIds[0]; + let selectedXcode = xcodeInfo.xcode[xcodeId]; + let simHandle = options.simHandleOrUDID instanceof SimHandle ? options.simHandleOrUDID : null; + let watchSimHandle = options.watchHandleOrUDID instanceof SimHandle ? options.watchHandleOrUDID : null; + const findWatchSimHandle = watchUDID => { + const sim = Object.values(simInfo.simulators.watchos).find(({ udid }) => udid === watchUDID); + if (sim) { + logger(`Found Watch Simulator UDID ${watchUDID}`); + return new SimHandle(sim); + } }; - processCerts(info, results); - processProvisioning(info, results); - processXcodes(info, results, opts); + if (options.simHandleOrUDID) { + // validate the udid + if (!(options.simHandleOrUDID instanceof SimHandle)) { + const vers = Object.keys(simInfo.simulators.ios); + + logger(`Validating iOS Simulator UDID ${options.simHandleOrUDID}`); + + for (let i = 0, l = vers.length; !simHandle && i < l; i++) { + const sims = simInfo.simulators.ios[vers[i]]; + for (var j = 0, k = sims.length; j < k; j++) { + if (sims[j].udid === options.simHandleOrUDID) { + logger(`Found iOS Simulator UDID ${options.simHandleOrUDID}`); + simHandle = new SimHandle(sims[j]); + break; + } + } + } + + if (!simHandle) { + return callback(new Error(`Unable to find an iOS Simulator with the UDID "${options.simHandleOrUDID}".`)); + } + } + + if (options.minIosVersion && version.lt(simHandle.version, options.minIosVersion)) { + return callback(new Error(`The selected iOS ${simHandle.version} Simulator is less than the minimum iOS version ${options.minIosVersion}.`)); + } + + if (options.watchAppBeingInstalled) { + const watchXcodeId = Object + .keys(simHandle.watchCompanion) + .filter(xcodeId => xcodeInfo.xcode[xcodeId].supported) + .sort(compareXcodes) + .pop(); + + if (!watchXcodeId) { + return callback(new Error(`Unable to find any Watch Simulators that can be paired with the specified iOS Simulator ${simHandle.udid}.`)); + } + + if (!options.watchHandleOrUDID) { + logger('Watch app present, autoselecting a Watch Simulator'); + + const companions = simHandle.watchCompanion[watchXcodeId]; + const companionUDID = Object.keys(companions) + .sort((a, b) => companions[a].model.localeCompare(companions[b].model)) + .pop(); + + watchSimHandle = new SimHandle(companions[companionUDID]); + + if (!watchSimHandle) { + return callback(new Error(`Specified iOS Simulator "${options.simHandleOrUDID}" does not support Watch apps.`)); + } + } else if (!(options.watchHandleOrUDID instanceof SimHandle)) { + logger(`Watch app present, validating Watch Simulator UDID ${options.watchHandleOrUDID}`); + watchSimHandle = findWatchSimHandle(options.watchHandleOrUDID); + if (!watchSimHandle) { + return callback(new Error(`Unable to find a Watch Simulator with the UDID "${options.watchHandleOrUDID}".`)); + } + } + + // double check + if (watchSimHandle && !simHandle.watchCompanion[watchXcodeId][watchSimHandle.udid]) { + return callback(new Error(`Specified Watch Simulator "${watchSimHandle.udid}" is not compatible with iOS Simulator "${simHandle.udid}".`)); + } + } + + if (options.watchAppBeingInstalled && !options.watchHandleOrUDID && !watchSimHandle) { + if (options.watchMinOSVersion) { + return callback(new Error(`Unable to find a Watch Simulator that supports watchOS ${options.watchMinOSVersion}.`)); + } + return callback(new Error('Unable to find a Watch Simulator.')); + } + + logger(`Selected iOS Simulator: ${simHandle.name}`); + logger(` UDID = ${simHandle.udid}`); + logger(` iOS = ${simHandle.version}`); + if (watchSimHandle) { + if (options.watchAppBeingInstalled && options.watchHandleOrUDID) { + logger(`Selected watchOS Simulator: ${watchSimHandle.name}`); + } else { + logger(`Autoselected watchOS Simulator: ${watchSimHandle.name}`); + } + logger(` UDID = ${watchSimHandle.udid}`); + logger(` watchOS = ${watchSimHandle.version}`); + } + logger(`Autoselected Xcode: ${selectedXcode.version}`); + } else { + logger('No iOS Simulator UDID specified, searching for best match'); + + if (options.watchAppBeingInstalled && options.watchHandleOrUDID) { + logger(`Validating Watch Simulator UDID ${options.watchHandleOrUDID}`); + watchSimHandle = findWatchSimHandle(options.watchHandleOrUDID); + if (!watchSimHandle) { + return callback(new Error(`Unable to find a Watch Simulator with the UDID "${options.watchHandleOrUDID}".`)); + } + } + + // pick one + logger(`Scanning Xcodes: ${xcodeIds.join(' ')}`); + + // loop through xcodes + for (let i = 0; !simHandle && i < xcodeIds.length; i++) { + const xc = xcodeInfo.xcode[xcodeIds[i]]; + const simVersMap = {}; + for (const ver of Object.keys(simInfo.simulators.ios)) { + for (const iosRange of Object.keys(xc.simDevicePairs)) { + if (version.satisfies(ver, iosRange)) { + simVersMap[ver] = xc.simDevicePairs[iosRange]; + break; + } + } + } + const simVers = Object.keys(simVersMap).sort(version.rcompare); + + logger(`Scanning Xcode ${xcodeIds[i]} sims: ${simVers.join(', ')}`); + + // loop through each xcode simulators + for (let j = 0; !simHandle && j < simVers.length; j++) { + if (!options.minIosVersion || version.gte(simVers[j], options.minIosVersion)) { + const sims = simInfo.simulators.ios[simVers[j]]; + + sims.sort(compareSims).reverse(); + + // loop through each simulator + for (let k = 0; !simHandle && k < sims.length; k++) { + if (options.simType && sims[k].family !== options.simType) { + continue; + } + + // if we're installing a watch extension, make sure we pick a simulator that supports the watch + if (options.watchAppBeingInstalled) { + if (watchSimHandle) { + for (const xcodeVer of Object.keys(sims[k].supportsWatch)) { + if (watchSimHandle.supportsXcode[xcodeVer]) { + selectedXcode = xcodeInfo.xcode[xcodeVer]; + simHandle = new SimHandle(sims[k]); + break; + } + } + } else if (sims[k].supportsWatch[xcodeIds[i]]) { + // make sure this version of Xcode has a watch simulator that supports the watch app version + for (const watchosVer of Object.keys(simInfo.simulators.watchos)) { + for (const watchosRange of Object.keys(simVersMap[simVers[j]])) { // 4.x, 5.x, etc + if (version.satisfies(watchosVer, watchosRange) && version.gte(watchosVer, options.watchMinOSVersion)) { + simHandle = new SimHandle(sims[k]); + selectedXcode = xcodeInfo.xcode[xcodeIds[i]]; + const watchSim = simInfo.simulators.watchos[watchosVer].sort(compareSims).reverse()[0]; + watchSimHandle = new SimHandle(watchSim); + break; + } + } + if (watchSimHandle) { + break; + } + } + } + } else { + // no watch app + logger('No watch app being installed, so picking first Simulator'); + simHandle = new SimHandle(sims[k]); + + // fallback to the newest supported Xcode version + for (const id of xcodeIds) { + if (simHandle.supportsXcode[id]) { + selectedXcode = xcodeInfo.xcode[id]; + break; + } + } + } + } + } + } + } + + if (!simHandle) { + // user experience! + if (options.simVersion) { + return callback(new Error(`Unable to find an iOS Simulator running iOS ${options.simVersion}`)); + } else { + return callback(new Error('Unable to find an iOS Simulator.')); + } + } else if (options.watchAppBeingInstalled && !watchSimHandle) { + return callback(new Error(`Unable to find a watchOS Simulator that supports watchOS ${options.watchMinOSVersion}.`)); + } + + logger(`Autoselected iOS Simulator: ${simHandle.name}`); + logger(` UDID = ${simHandle.udid}`); + logger(` iOS = ${simHandle.version}`); + if (watchSimHandle) { + if (options.watchAppBeingInstalled && options.watchHandleOrUDID) { + logger(`Selected watchOS Simulator: ${watchSimHandle.name}`); + } else { + logger(`Autoselected watchOS Simulator: ${watchSimHandle.name}`); + } + logger(` UDID = ${watchSimHandle.udid}`); + logger(` watchOS = ${watchSimHandle.version}`); + } + logger(`Autoselected Xcode: ${selectedXcode.version}`); + } - cache = results; - callback(null, results); - }) - .catch(err => { - tunnel.log(alert(err.stack)); - callback(err); + callback(null, simHandle, watchSimHandle, selectedXcode, simInfo, xcodeInfo); }); + }; + + return ioslib; } function processCerts(info, results) { diff --git a/src/legacy/ti/cli.js b/src/legacy/ti/cli.js index 99644b1..2d7482d 100644 --- a/src/legacy/ti/cli.js +++ b/src/legacy/ti/cli.js @@ -3,6 +3,7 @@ import Context from './context'; import fs from 'fs-extra'; import getOSInfo from '../../lib/os'; +import Module from 'module'; import path from 'path'; import tunnel from '../tunnel'; import * as version from '../../lib/version'; @@ -180,6 +181,21 @@ export default class CLI { } }; + // patch modules + const load = Module._load; + const patchDir = path.resolve(__dirname, '..', 'patch'); + const lookup = { + fields: path.join(patchDir, 'fields.js'), + ioslib: path.join(patchDir, 'ios.js'), + 'node-titanium-sdk/lib/android': path.join(patchDir, 'android.js') + }; + Module._load = (request, parent, isMain) => { + if (lookup[request] && parent && path.basename(parent.filename) === '_build.js') { + return require(lookup[request]).patch({ load, request, parent, isMain }); + } + return load(request, parent, isMain); + }; + // initialize the commands const cmd = opts.command; if (cmd !== 'build' && cmd !== 'clean') { @@ -264,84 +280,79 @@ export default class CLI { } return (...args) => { - const callback = args.length && typeof args[args.length - 1] === 'function' ? args.pop() : null; - let data = Object.assign(dataPayload, { + let data = Object.assign({}, dataPayload, { type: name, args, - callback, fn: fn, ctx: ctx }); + const callback = data.args.pop(); const pres = this.hooks.pre[name] || []; const posts = this.hooks.post[name] || []; - Promise.resolve() - .then(async () => { - // call all pre filters - await pres - // eslint-disable-next-line promise/no-nesting - .reduce((promise, pre) => promise.then(() => new Promise((resolve, reject) => { - if (pre.length >= 2) { - pre.call(ctx, data, (err, newData) => { + (async () => { + // call all pre filters + await pres + // eslint-disable-next-line promise/no-nesting + .reduce((promise, pre) => promise.then(() => new Promise((resolve, reject) => { + if (pre.length >= 2) { + pre.call(ctx, data, (err, newData) => { + if (err) { + return reject(err); + } else if (newData) { + data = newData; + } + resolve(); + }); + } else { + pre.call(ctx, data); + resolve(); + } + })), Promise.resolve()); + + if (data.fn) { + data.result = await new Promise(resolve => { + // call the function + data.args.push((...args) => resolve(args)); + data.fn.apply(data.ctx, data.args); + }); + } + + // call all post filters + await posts + // eslint-disable-next-line promise/no-nesting + .reduce((promise, post) => promise.then(async () => { + if (post.length >= 2) { + await new Promise((resolve, reject) => { + post.call(ctx, data, (err, newData) => { if (err) { return reject(err); - } else if (newData) { + } + if (newData && typeof newData === 'object' && newData.type) { data = newData; } resolve(); }); - } else { - pre.call(ctx, data); - resolve(); - } - })), Promise.resolve()); - - if (data.fn) { - data.result = await new Promise(resolve => { - // call the function - data.args.push((...args) => resolve(args)); - data.fn.apply(data.ctx, data.args); - }); - } - - // call all post filters - await posts - // eslint-disable-next-line promise/no-nesting - .reduce((promise, post) => promise.then(async () => { - if (post.length >= 2) { - await new Promise((resolve, reject) => { - post.call(ctx, data, (err, newData) => { - if (err) { - return reject(err); - } - if (newData && typeof newData === 'object' && newData.type) { - data = newData; - } - resolve(); - }); - }); - } else { - post.call(ctx, data); - } - }), Promise.resolve()); + }); + } else { + post.call(ctx, data); + } + }), Promise.resolve()); - const { callback } = data; - if (typeof callback === 'function') { - data.callback = null; - callback.apply(data, data.result); - } - }) - .catch(err => { - // this is the primary error handler - if (typeof data.callback === 'function') { - tunnel.log(err.stack); - data.callback(err); - } else { - console.log('Hook completion callback threw unhandled error:'); - console.log(err.stack); - process.exit(1); - } - }); + if (typeof callback === 'function') { + callback.apply(data, data.result); + } + })().catch(err => { + // this is the primary error handler + if (typeof callback === 'function') { + tunnel.log(err.stack); + callback(err); + } else { + this.logger.error('Hook completion callback threw unhandled error:'); + this.logger.error(err.stack); + process.exit(1); + } + }); }; } @@ -365,7 +376,7 @@ export default class CLI { const promise = unique(Array.isArray(name) ? name : [ name ]) .reduce((promise, name) => promise.then(() => new Promise((resolve, reject) => { const hook = this.createHook(name, data); - this.logger.trace(`Emitting ${name}`); + this.logger.trace(`Emitting ${highlight(name)}`); hook((err, result) => { err ? reject(err) : resolve(result); }); @@ -500,12 +511,16 @@ export default class CLI { }, config), { get: { - value: (key, defaultValue) => get(this, key, defaultValue) + value(key, defaultValue) { + return get(this, key, defaultValue); + } }, // called by Android build to set the `android.sdkPath` set: { - value: (key, value) => set(this, key, value) + value(key, value) { + set(this, key, value); + } } } ); @@ -563,11 +578,10 @@ export default class CLI { } try { - // eslint-disable-next-line security/detect-non-literal-require - const appc = require(path.join(this.sdk.path, 'node_modules', 'node-appc')); const jsfile = /\.js$/; const ignore = /^[._]/; const files = fs.statSync(dir).isDirectory() ? fs.readdirSync(dir).map(n => path.join(dir, n)) : [ dir ]; + let appc; for (const file of files) { try { @@ -592,6 +606,10 @@ export default class CLI { } if (!this.version || !mod.cliVersion || version.satisfies(this.version, mod.cliVersion)) { + // eslint-disable-next-line security/detect-non-literal-require + if (!appc) { + appc = require(path.join(this.sdk.path, 'node_modules', 'node-appc')); + } mod.init && mod.init(this.logger, this.config, this, appc); this.hooks.loadedFilenames.push(file); this.logger.trace(`Loaded CLI hook: ${highlight(file)} ${note(`(${Date.now() - startTime} ms)`)}`); From 69c78b5db581d2b3ba1f66c96edbe4a960265e60 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Wed, 2 Sep 2020 00:48:54 -0500 Subject: [PATCH 22/39] chore: Added support files. feat: Remove ciruclar refs from tunnel payloads. fix(legacy): Fixed hook firing. fix(ios): Fixed iOS simulator handling and Xcode EULA check. refactor(build): Centralized loading build command options from SDK to be used by help and parsing. feat(legacy:hook): Added appc hook. chore: Updated deps. --- CHANGELOG.md | 1 + conf/config.json | 1 + package.json | 6 +- src/cli/cli-service.js | 88 ++------- src/cli/commands/build.js | 8 +- src/cli/commands/run.js | 10 +- src/cli/run-legacy.js | 154 ++++++++++++++- src/legacy/bootstrap.js | 4 +- src/legacy/hooks/app-preview.js | 179 +++++++++--------- src/legacy/hooks/appc.js | 218 ++++++++++++++++++++++ src/legacy/index.js | 6 +- src/legacy/patch/android.js | 1 + src/legacy/patch/ios.js | 16 +- src/legacy/ti/cli.js | 43 ++--- src/legacy/tunnel.js | 36 +++- support/android/appcelerator-security.jar | Bin 0 -> 3425 bytes support/android/appcelerator-verify.jar | Bin 0 -> 11795 bytes support/ios/ApplicationRouting.m | 125 +++++++++++++ support/ios/libappcverify.a | Bin 0 -> 1445224 bytes 19 files changed, 669 insertions(+), 227 deletions(-) create mode 100644 src/legacy/hooks/appc.js create mode 100644 support/android/appcelerator-security.jar create mode 100644 support/android/appcelerator-verify.jar create mode 100644 support/ios/ApplicationRouting.m create mode 100644 support/ios/libappcverify.a diff --git a/CHANGELOG.md b/CHANGELOG.md index bf232e1..772357a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ * feat: Support for Titanium-specific telemetry. * refactor: Updated to latest cli-kit with support for the new client/server architecture. * refactor: Updated `config` command actions to be subcommands with improved help output. + * fix(legacy): Improved logging of uncaught exceptions and rejections. * chore: Added plugin API version 2.x. * chore: Transpile for Node 10 instead of Node 8. Not a breaking change as appcd has always guaranteed Node 10 or newer. diff --git a/conf/config.json b/conf/config.json index 7d553c3..a97e7ed 100644 --- a/conf/config.json +++ b/conf/config.json @@ -1,4 +1,5 @@ { + "home": "~/.axway/titanium", "searchPaths": [], "telemetry": { "app": "de93eb9e-e53c-4f3a-9222-9e0d124950e5" diff --git a/package.json b/package.json index 5d395a8..34f3d68 100644 --- a/package.json +++ b/package.json @@ -16,11 +16,12 @@ "test": "gulp test" }, "dependencies": { + "@titanium-sdk/node-is-platform-guid": "^1.0.2", "appcd-fs": "^2.0.0", "appcd-path": "^2.0.1", "appcd-util": "^3.0.0", "chalk": "^4.1.0", - "cli-kit": "^1.7.0", + "cli-kit": "^1.8.2", "colors": "^1.4.0", "dateformat": "^3.0.3", "enquirer": "^2.3.6", @@ -31,8 +32,9 @@ "get-port": "^5.1.1", "global-modules": "^2.0.0", "got": "^11.5.2", + "node-forge": "^0.9.1", "node-pty-prebuilt-multiarch": "^0.9.0", - "open": "^7.1.0", + "open": "^7.2.0", "pluralize": "^8.0.0", "progress": "^2.0.3", "prompts": "^2.3.2", diff --git a/src/cli/cli-service.js b/src/cli/cli-service.js index 9b2af74..83040b0 100644 --- a/src/cli/cli-service.js +++ b/src/cli/cli-service.js @@ -6,19 +6,13 @@ import path from 'path'; import { get } from 'appcd-util'; import { isFile } from 'appcd-fs'; -import { capitalize, parseVersion } from '../lib/util'; -import { spawnLegacyCLI } from '../legacy'; +import { loadOptions } from './run-legacy'; +import { parseVersion } from '../lib/util'; // import { Tiapp } from 'titaniumlib'; const { log } = appcd.logger('cli-service'); const { highlight } = appcd.logger.styles; -/** - * A cache of platform specific build options by Titanium SDK path. - * @type {Object} - */ -const buildOptionsCache = {}; - /** * Defines a service endpoint for defining, processing, and dispatching Titanium CLI commands. */ @@ -52,12 +46,15 @@ export default class CLIService extends Dispatcher { version: ({ data }) => parseVersion(data.userAgent) }); - // we need to add platform specific options for the build/run help, so first we listen for - // the help command, then we add in the options before the help is generated - cli.on('exec', async ({ cmd, data, contexts }) => { - // inject the titanium config into the command data object + // inject the titanium config into the command data object before parsing starts so that + // it's available to the command callback + cli.on('parse', ({ data }) => { data.config = cfg.titanium; + }); + // we need to add platform specific options for the build/run help, so first we listen for + // the help command, then we add in the options before the help is generated + cli.on('exec', async ({ cmd, contexts, data }) => { // we need the help command, the build/run command, and a cwd containing a tiapp cmd = data?.cwd && cmd?.name === 'help' && contexts[1]; @@ -71,71 +68,8 @@ export default class CLIService extends Dispatcher { } cmd.on('generateHelp', async ctx => { - // FIX ME! - // const tiapp = new Tiapp({ file: tiappFile }); - // const sdk = tiapp.get('sdk-version'); - const sdk = '9.0.3.GA'; - const sdkInfo = (await appcd.call('/sdk/find', { data: { name: sdk } })).response; - - let buildOptions = buildOptionsCache[sdkInfo.path]; - - if (!Array.isArray(buildOptions)) { - // load the Android and iOS options directly from the SDK - const config = await spawnLegacyCLI({ - data: { - command: 'build', - sdkPath: sdkInfo.path, - type: 'help' - } - }); - - // copy the platform-specific options into a cli-kit friendly format - buildOptions = []; - - const lv = {}; - const lvRegExp = /^liveview/; - - for (const key of Object.keys(config)) { - if (key === 'flags' || key === 'options') { - // we skip the top level build flags/options because they are either - // already defined in the command or are unsupported - } else if (key === 'platforms') { - for (const conf of Object.values(config[key])) { - const options = {}; - - for (const [ name, flag ] of Object.entries(conf.flags)) { - if (!flag.hidden) { - (lvRegExp.test(name) ? lv : options)[`--${name}`] = { desc: capitalize(flag.desc) }; - } - } - - for (const [ name, option ] of Object.entries(conf.options)) { - if (!option.hidden) { - let format = option.abbr ? `-${option.abbr}, ` : ''; - format += `--${name} ${option.required ? '<' : '['}${option.hint || 'value'}${option.required ? '>' : ']'}`; - (lvRegExp.test(name) ? lv : options)[format] = { desc: capitalize(option.desc) }; - } - } - - if (Object.keys(options).length) { - buildOptions.push(`${conf.title} build options`, options); - } - } - } else if (Array.isArray(config[key])) { - buildOptions.push.apply(buildOptions, config[key]); - } - } - - if (Object.keys(lv).length) { - buildOptions.push('LiveView Options', lv); - } - - buildOptionsCache[sdkInfo.path] = buildOptions; - } - - if (buildOptions.length) { - ctx.option(buildOptions); - } + const sdk = '9.0.3.GA'; // data.tiapp.get('sdk-version'); + await loadOptions({ config: data.config, ctx, sdk }); }); }); diff --git a/src/cli/commands/build.js b/src/cli/commands/build.js index 3398574..88436fa 100644 --- a/src/cli/commands/build.js +++ b/src/cli/commands/build.js @@ -1,12 +1,12 @@ -import { runLegacyCLI } from '../run-legacy'; +import { callback, options, runLegacyCLI } from '../run-legacy'; export default { + callback, desc: 'Builds a project', options: [ { - '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory', - '-f, --force': 'Force a full rebuild', - '-p, --platform [name]': 'The target build platform' + ...options, + '-f, --force': 'Force a full rebuild' } ], async action(ctx) { diff --git a/src/cli/commands/run.js b/src/cli/commands/run.js index 2c5d9dc..00fac5f 100644 --- a/src/cli/commands/run.js +++ b/src/cli/commands/run.js @@ -1,13 +1,13 @@ -import { runLegacyCLI } from '../run-legacy'; +import { callback, options, runLegacyCLI } from '../run-legacy'; export default { + callback, desc: 'Build and runs a project', options: [ { - '--build-only': 'Builds the project without running it in the simulator/emulator or installing it on device', - '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory', - '-f, --force': 'Force a full rebuild', - '-p, --platform [name]': 'The target build platform' + ...options, + '--build-only': 'Builds the project without running it in the simulator/emulator or installing it on device', + '-f, --force': 'Force a full rebuild' } ], async action(ctx) { diff --git a/src/cli/run-legacy.js b/src/cli/run-legacy.js index 5ad762a..bece802 100644 --- a/src/cli/run-legacy.js +++ b/src/cli/run-legacy.js @@ -1,5 +1,150 @@ -import { exec } from '../legacy'; +import path from 'path'; +import { capitalize } from '../lib/util'; +import { exec, spawnLegacyCLI } from '../legacy'; +import { isFile } from 'appcd-fs'; import { prompt } from '../lib/prompt'; +// import { Tiapp } from 'titaniumlib'; + +const { log } = appcd.logger('run-legacy'); +const { highlight } = appcd.logger.styles; + +/** + * A cache of build options. The key is a combination of the platform and the Titanium SDK path. + * @type {Object} + */ +const buildOptionCache = {}; + +/** + * The build/run command callback that is executed when the parser finds the command. + * + * @param {Object} opts - Various options. + * @param {Object} opts.data - The CLI data object. + * @param {Parser} opts.parser - The cli-kit parser instance. + */ +export function callback({ data, parser }) { + parser.on('finalize', async ({ ctx }) => { + let platform; + let projectDir = data?.cwd; + + for (const arg of parser.args) { + if (arg.type === 'option') { + if (arg.option.name === 'project-dir') { + projectDir = arg.value; + } else if (arg.option.name === 'platform') { + platform = arg.value; + if (platform === 'ios') { + platform = 'iphone'; + } + } + } + } + + if (!projectDir) { + throw new Error('Expected project directory or current working directory'); + } + + const tiappFile = path.resolve(projectDir, 'tiapp.xml'); + if (!isFile(tiappFile)) { + throw new Error('Invalid project directory'); + } + + // FIX ME! + // data.tiapp = new Tiapp({ file: tiappFile }); + const sdk = '9.0.3.GA'; // data.tiapp.get('sdk-version'); + await loadOptions({ config: data.config, ctx, platform, sdk }); + }); +} + +/** + * Loads the CLI options for the given Titanium SDK into a cli-kit context. + * + * @param {Object} opts - Various options. + * @param {Context} opts.ctx - A cli-kit Context. + * @param {String} [opts.platform] - The platform name or falsey for all platforms. + * @param {String} opts.sdk - The name of the Titanium SDK. + * @returns {Promise} + */ +export async function loadOptions({ config, ctx, platform, sdk }) { + const sdkInfo = (await appcd.call('/sdk/find', { data: { name: sdk } })).response; + const cacheKey = `${platform || ''}|${sdkInfo.path}`; + let buildOptions = buildOptionCache[cacheKey]; + + if (!Array.isArray(buildOptions)) { + log(`Fetching "build" help for ${platform ? `platform "${platform}"` : 'all platforms'}: ${highlight(sdkInfo.path)}`); + + // load the Android and iOS options directly from the SDK + const buildConfig = await spawnLegacyCLI({ + data: { + command: 'build', + config, + sdkPath: sdkInfo.path, + type: 'help' + } + }); + + // copy the platform-specific options into a cli-kit friendly format + const lv = {}; + const lvRegExp = /^liveview/; + + buildOptions = []; + + for (const key of Object.keys(buildConfig)) { + if (key === 'flags' || key === 'options') { + // we skip the top level build flags/options because they are either + // already defined in the command or are unsupported + } else if (key === 'platforms') { + for (const [ platformName, conf ] of Object.entries(buildConfig[key])) { + if (platform && platform !== platformName) { + continue; + } + + const options = {}; + + for (const [ name, flag ] of Object.entries(conf.flags)) { + if (!flag.hidden) { + (lvRegExp.test(name) ? lv : options)[`--${name}`] = { desc: capitalize(flag.desc) }; + } + } + + for (const [ name, option ] of Object.entries(conf.options)) { + if (!option.hidden) { + let format = option.abbr ? `-${option.abbr}, ` : ''; + format += `--${name} [${option.hint || 'value'}]`; + (lvRegExp.test(name) ? lv : options)[format] = { + desc: capitalize(option.desc) + }; + } + } + + if (Object.keys(options).length) { + buildOptions.push(`${conf.title} build options`, options); + } + } + } else if (Array.isArray(buildConfig[key])) { + buildOptions.push.apply(buildOptions, buildConfig[key]); + } + } + + if (Object.keys(lv).length) { + buildOptions.push('LiveView Options', lv); + } + + buildOptionCache[cacheKey] = buildOptions; + } + + if (buildOptions.length) { + ctx.option(buildOptions); + } +} + +/** + * Common options for the `build` and `run` commands. + * @type {Object} + */ +export const options = { + '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory', + '-p, --platform [name]': 'The target build platform' +}; /** * Runs a command in the Legacy Titanium CLI based on the cli-kit execution context. @@ -19,13 +164,6 @@ export async function runLegacyCLI(command, ctx) { delete argv.prompt; delete argv.version; - // map `build` and `run` to the correct legacy `build` command - if (command === 'build') { - argv.buildOnly = true; - } else if (command === 'run') { - command = 'build'; - } - await exec({ argv, command, diff --git a/src/legacy/bootstrap.js b/src/legacy/bootstrap.js index 9fa7a13..27714a1 100644 --- a/src/legacy/bootstrap.js +++ b/src/legacy/bootstrap.js @@ -28,5 +28,5 @@ process.exit = code => Promise .then(() => exit(code)); process - .on('uncaughtException', err => console.error('Caught unhandled exception:', err)) - .on('unhandledRejection', (reason, p) => console.error('Caught unhandled rejection at: Promise ', p, reason)); + .on('uncaughtException', err => console.error(`Caught exception: ${err.stack || err.toString()}`)) + .on('unhandledRejection', err => console.error(`Unhandled rejection: ${err.stack || err.toString()}`)); diff --git a/src/legacy/hooks/app-preview.js b/src/legacy/hooks/app-preview.js index 6e1ad11..ec5233b 100644 --- a/src/legacy/hooks/app-preview.js +++ b/src/legacy/hooks/app-preview.js @@ -1,14 +1,17 @@ /** - * Installr API docs: https://help.installrapp.com/api/ + * This code is based on https://github.com/jeffbonnes/appc-app-preview-cli-hook by Jeff Bonnes and + * licensed under the MIT license. * - * This is the App Preview CLI and validation code. The App Preview Titanium CLI plugin is located - * in `src/legacy/hooks/app-preview-hook.js`. + * Installr API docs: https://help.installrapp.com/api/ */ -// const got = require('got'); -// const path = require('path'); +import fs from 'fs'; +import got from 'got'; +import path from 'path'; +import tunnel from '../tunnel'; +import { expandPath } from 'appcd-path'; -// const endpoint = 'https://appbeta.axway.com'; +const endpoint = 'https://appbeta.axway.com'; exports.init = (logger, config, cli, appc) => { cli.on('build.config', data => { @@ -24,34 +27,75 @@ exports.init = (logger, config, cli, appc) => { ]; }); - // if (!cli.argv.appPreview) { - // return; - // } - - // cli.on('build.finalize', function (data, callback) { - // let artifact; - // if (cli.argv.platform === 'android') { - // artifact = data.apkFile; - // } else if (cli.argv.platform === 'ios' && cli.argv.outputDir) { - // artifact = path.join(cli.argv.outputDir, `${this.tiapp.name}.ipa`); - // } else { - // throw new Error(); - // } - // // if (data.buildManifest.outputDir === undefined && data.iosBuildDir === undefined) { - // // logger.error("Output directory must be defined to use --app-preview flag"); - // // return; - // // } - // // build_file = afs.resolvePath(path.join(data.buildManifest.outputDir, data.buildManifest.name + ".ipa")); - // // } - // }); + cli.on('build.pre.compile', async data => { + // + }); + + cli.on('build.finalize', async data => { + // + }); }; /* -var _ = require("lodash"); var logger, platform, config, appc, appcConfig, j, build_file, busy; j = request.jar(); +exports.init = function(_logger, _config, cli, _appc) { + if (process.argv.indexOf('--app-preview') !== -1) { + cli.addHook('build.pre.compile', configure); + cli.addHook('build.finalize', upload2AppPreview); + } + logger = _logger; + appcConfig = _config; + appc = _appc; +}; + +function configure(data, finished) { + config = {}; + config.releaseNotes = data.cli.argv['release-notes']; + config.add = data.cli.argv['add']; + config.notify = data.cli.argv['notify']; + config.emails = data.cli.argv['invite']; + + if (!config.releaseNotes || !config.notify) { + doPrompt(finished); + } else { + finished(); + } +} + +function doPrompt(finishedFunction) { + var f = {}; + + if (config.releaseNotes === undefined) { + f.releaseNotes = fields.text({ + title: "Release Notes", + desc: "Enter release notes.", + validate: function(value, callback) { + callback(!value.length, value); + } + }) + } + if (config.notify === undefined) { + f.notify = fields.select({ + title: "Notify", + desc: "Notify previous testers on upload.", + promptLabel: "(y,n)", + options: ['__y__es', '__n__o'] + }); + } + + var prompt = fields.set(f); + + prompt.prompt(function(err, result) { + _.each(_.keys(result), function(key) { + config[key] = result[key]; + }); + finishedFunction(); + }); +} + var onUploadComplete = function(err, httpResponse, body) { var resp = {}; if (err) { @@ -97,6 +141,7 @@ function showFinalUrl(resp) { } function upload2AppPreview(data, finished) { + validate(data); var sid = process.env.APPC_SESSION_SID; logger.info('Uploading app to App Preview...please wait...'); var cookie = request.cookie('connect.sid=' + sid); @@ -137,81 +182,25 @@ function upload2AppPreview(data, finished) { form.append('add', config.add.toString()); } } -*/ -/* -function INVALID_ARGUMENT({ msg, code, prompt }) { - const err = new TypeError(msg); - if (code !== undefined) { - err.code = code; - } - if (prompt !== undefined) { - err.prompt = prompt; - } - return err; -} +function validate(data) { -export async function validate(argv) { - if (!argv.appPreview) { - return; - } - - const { response: account } = await appcd.call('/amplify/1.x/auth/active'); + platform = data.cli.argv.platform; - if (!account) { - const err = new Error('Log in required to use App Preview'); - err.details = `Please login by running: ${highlight('ti login')}`; - throw err; + if (['android', 'ios'].indexOf(platform) === -1) { + logger.error("Only android and ios support with --app-preview flag"); + return; } - if (!account.org?.entitlements?.appPreview) { - // eslint-disable-next-line no-throw-literal - const err = new Error('Your account is not entitled to use App Preview'); - err.details = `Your current organization is ${highlight(`"${account.org.name}"`)}.\n`; - if (account.orgs.length > 1) { - err.details += `If this is not the correct organization, run ${highlight('"ti switch"')} to change to another organization.\n`; + if (data.cli.argv.platform === "android") { + build_file = data.apkFile + } else { + if (data.buildManifest.outputDir === undefined && data.iosBuildDir === undefined) { + logger.error("Output directory must be defined to use --app-preview flag"); + return; } - err.details += 'To upgrade your account, visit https://billing.axway.com/.'; - throw err; + build_file = afs.resolvePath(path.join(data.buildManifest.outputDir, data.buildManifest.name + ".ipa")); } - log(`Active account org ${highlight(`"${account.org.name}"`)} is entitled to App Preview!`); - - const { platform } = argv; - if (platform !== 'android' && platform !== 'ios') { - const err = new Error(`App Preview does not support the platform "${platform}"`); - err.details = 'Only Android and iOS platforms are supported.'; - throw err; - } - - if (platform === 'ios' && !argv.outputDir) { - // - } - - if (argv.releaseNotes === undefined) { - throw INVALID_ARGUMENT({ - msg: 'Expected App Preview release notes or path to release notes file', - prompt: { - message: 'Please enter release notes or a path to a release notes file', - name: 'releaseNotes', - type: 'text' - } - }); - } - - if (argv.notify === undefined) { - throw INVALID_ARGUMENT({ - msg: 'Expected App Preview notification preference', - prompt: { - disabled: 'No', - enabled: 'Yes', - initial: true, - message: 'Do you want to notify previous testers on upload?', - name: 'notify', - required: true, - type: 'toggle' - } - }); - } } */ diff --git a/src/legacy/hooks/appc.js b/src/legacy/hooks/appc.js new file mode 100644 index 0000000..47a9a6d --- /dev/null +++ b/src/legacy/hooks/appc.js @@ -0,0 +1,218 @@ +import fs from 'fs-extra'; +import path from 'path'; +import isPlatformGuid from '@titanium-sdk/node-is-platform-guid'; +import tunnel from '../tunnel'; + +import { expandPath } from 'appcd-path'; +import { isFile } from 'appcd-fs'; +import { sha1 } from 'appcd-util'; + +exports.init = (logger, config, cli, appc) => { + const homeDir = expandPath(config.get('home')); + + async function generateDevCert({ account }) { + logger.info('Generating developer certificate and private/public keys'); + + const filename = path.join(homeDir, `.${sha1(`${account.name}${account.org.id}`)}`); + const certFile = `${filename}.pem`; + const keyFile = `${filename}.pk`; + const { pki } = require('node-forge'); + const keys = pki.rsa.generateKeyPair(1024); + const publicKey = pki.publicKeyToPem(keys.publicKey); + + logger.info('Registering developer certificate'); + const certificate = await tunnel.call('/amplify/1.x/ti/enroll', { + data: { + accountName: account.name, + fingerprint: cli.fingerprint, + publicKey + } + }); + + await fs.mkdirs(homeDir); + await fs.writeFile(certFile, certificate); + await fs.writeFile(keyFile, pki.privateKeyToPem(keys.privateKey)); + + if (process.platform === 'darwin' || process.platform === 'linux') { + await fs.chmod(certFile, '400'); + await fs.chmod(keyFile, '400'); + } + } + + async function verifyBuild({ account, deployType, modules, projectDir, tiapp }) { + const buildFile = path.join(homeDir, 'builds', `${sha1([ + account.name, + tiapp.guid, + tiapp.id, + deployType, + cli.fingerprint, + account.org.id + ])}.json`); + + let lastBuild; + try { + lastBuild = await fs.readJson(buildFile); + } catch (e) { + // squelch + } + + // how long since we last verified this build configuration + const age = lastBuild ? (Date.now() - lastBuild.timestamp) / 3600000 : null; + + if (lastBuild && deployType !== 'production' && age < 24) { + lastBuild.skipped = true; + return lastBuild; + } + + // we are going to verify the build because it's either there was no previous build, this + // is a production build, or it is a development/test build that was last verified over + // 24 hours ago + + const verify = async data => { + try { + return await tunnel.call('/amplify/1.x/ti/build-verify', { data }); + } catch (err) { + if (err.code === 'com.appcelerator.platform.app.notregistered') { + // this is ok, just means the app isn't registered with the platform yet + } else if (/^com\.appcelerator\.platform\.developercertificate\.(notfound|invalid)$/.test(err.code)) { + logger.warn('Developer certs need to be regenerated'); + await generateDevCert({ account }); + + try { + // try again + return await tunnel.call('/amplify/1.x/ti/build-verify', { data }); + } catch (err2) { + if (err2.code !== 'com.appcelerator.platform.app.notregistered') { + throw err2; + } + } + } else { + throw err; + } + } + }; + + try { + logger.info('Verifying build'); + const result = await verify({ + accountName: account.name, + appGuid: tiapp.guid, + appId: tiapp.id, + deployType, + fingerprint: cli.fingerprint, + name: tiapp.name, + modules, + tiapp: await fs.readFile(path.join(projectDir, 'tiapp.xml'), 'utf-8') + }); + + // write last build + if (result) { + result.skipped = false; + result.timestamp = Date.now(); + await fs.mkdirs(path.join(homeDir, 'builds')); + await fs.writeJson(buildFile, result); + } + + return result; + } catch (err) { + // probably offline, fail + tunnel.log(`Build verify failed: ${err.toString()} (${err.code})`); + if (deployType === 'production') { + throw new Error('You must be online in order to build this application for production'); + } else { + throw new Error(`You must be online to build this application${lastBuild ? ' again' : ''}`); + } + } + } + + cli.on('cli:post-validate', { + priority: 10000, + post(data) { + const policy = data.cli.tiapp?.properties?.['appc-sourcecode-encryption-policy']?.value; + + if (policy === 'embed') { + throw new Error('The source code encryption policy "embed" is no longer supported'); + } + + if (policy === 'remote') { + throw new Error('The source code encryption policy "remote" is unsupported'); + } + } + }); + + cli.on('build.pre.compile', { + post: async function (builder) { + let account; + let result; + + if (builder.deployType === 'production') { + account = await tunnel.getAccount(); + if (!account) { + throw new Error('You must be authenticated to perform production builds'); + } + if (!account.org.entitlements.allowProduction) { + throw new Error(`Your current organization "${account.org.name}" is not entitled to production builds\nPlease upgrade your plan by visiting https://www.appcelerator.com/pricing/`); + } + } + + if (isPlatformGuid(builder.tiapp.guid)) { + if (!account) { + account = await tunnel.getAccount(); + if (!account) { + throw new Error('You must be authenticated to build registered applications'); + } + } + + result = await verifyBuild({ + account, + deployType: builder.deployType, + modules: builder.modules, + projectDir: builder.projectDir, + tiapp: builder.tiapp + }); + + // check to see if we need to force a rebuild + if (builder.platformName === 'android') { + try { + const applicationJava = fs.readFileSync(path.join(builder.buildDir, 'java-sources.txt'), 'utf-8').match(/^"?(.+Application\.java)"?$/m); + builder.forceRebuild = !fs.readFileSync(applicationJava[1], 'utf-8').includes('new AssetCryptImpl'); + } catch (e) { + builder.forceRebuild = true; + } + } else if (builder.platformName === 'iphone') { + try { + const src = fs.readFileSync(path.join(__dirname, '..', '..', '..', 'support', 'ios', 'ApplicationRouting.m'), 'utf-8'); + const dest = fs.readFileSync(path.join(builder.buildDir, 'Classes', 'ApplicationRouting.m'), 'utf-8'); + builder.forceRebuild = src !== dest; + } catch (e) { + builder.forceRebuild = true; + } + } + } + }, + priority: 0 + }); + + // cli.on('build.post.compile', { + // priority: 10000, + // post: postcompileHook + // }); + + const mutator = { + pre: function (data) { + const orig = data.fn; + data.fn = function (...args) { + const data = args[1]; + [ null, arguments['1'].length - 1 ].forEach(function () { + if (data[arguments['0'] || arguments['1']].length === 36) { + data[arguments['0'] || arguments['1']] = data[arguments['0'] || arguments['1']].split('').reverse().join(''); + } + }); + orig.apply(this, args); + }; + }, + priority: 10000 + }; + cli.on('build.android.titaniumprep', mutator); + cli.on('build.ios.titaniumprep', mutator); +}; diff --git a/src/legacy/index.js b/src/legacy/index.js index 114f3ac..6fb9569 100644 --- a/src/legacy/index.js +++ b/src/legacy/index.js @@ -65,13 +65,17 @@ export async function exec({ argv, command, config, console, cwd, prompt }) { command, config: config || {}, cwd: projectDir, + fingerprint: (await appcd.call('/appcd/status/system/mid')).response, promptingEnabled: !!prompt, sdkPath: sdkInfo.path, type: 'exec' }; + // map `build` and `run` to the correct legacy `build` command if (command === 'build') { data.argv.buildOnly = true; + } else if (command === 'run') { + data.command = 'build'; } // step 4: spawn the legacy cli @@ -130,7 +134,7 @@ export async function spawnLegacyCLI({ console, data, prompt }) { const response = await appcd.call(path, data); child.send({ id, response, type: 'response' }); } catch (err) { - child.send({ error: err.message, id, type: 'error' }); + child.send({ code: err.code, error: err.message, id, stack: err.stack, type: 'error' }); } } diff --git a/src/legacy/patch/android.js b/src/legacy/patch/android.js index bab6c6d..eae4917 100644 --- a/src/legacy/patch/android.js +++ b/src/legacy/patch/android.js @@ -19,6 +19,7 @@ export function patch() { * @param {Object} opts.packageJson - The Android platform-specific `package.json`. * @param {Function} callback - A function to call with the detection results. Note that this * function receives a single argument with the info. Any errors must be silenced. + * @returns {undefined} */ detect(config = {}, { packageJson } = {}, callback) { if (cache) { diff --git a/src/legacy/patch/ios.js b/src/legacy/patch/ios.js index 9f5d0e1..e26f35b 100644 --- a/src/legacy/patch/ios.js +++ b/src/legacy/patch/ios.js @@ -1,5 +1,6 @@ /* eslint-disable promise/no-callback-in-promise */ +import path from 'path'; import tunnel from '../tunnel'; import * as version from '../../lib/version'; import { snooplogg } from 'cli-kit'; @@ -26,7 +27,7 @@ export function patch({ load, request, parent, isMain }) { issues: [], provisioning: {}, selectedXcode: null, - simulators: info.simulators, + simulators: processSimulators(info.simulators), teams: Object.entries(info.teams).map(([ id, name ]) => ({ id, name })), xcode: {} }; @@ -55,7 +56,7 @@ export function patch({ load, request, parent, isMain }) { * @param {String} [options.minIosVersion] - The minimum iOS SDK to detect. * @param {String} [options.minWatchosVersion] - The minimum watchOS SDK to detect. * @param {String|Array} [options.searchPath] - One or more path to scan for Xcode installations. - * @param {String|SimHandle} simHandleOrUDID - A iOS sim handle or the UDID of the iOS Simulator to launch or null if you want ioslib to pick one. + * @param {String|SimHandle} [options.simHandleOrUDID] - A iOS sim handle or the UDID of the iOS Simulator to launch or null if you want ioslib to pick one. * @param {String} [options.simType=iphone] - The type of simulator to launch. Must be either "iphone" or "ipad". Only applicable when udid is not specified. * @param {String} [options.simVersion] - The iOS version to boot. Defaults to the most recent version. * @param {String} [options.supportedVersions] - A string with a version number or range to check if an Xcode install is supported. @@ -533,3 +534,14 @@ function processXcodes(info, results, opts) { }); } } + +function processSimulators(simulators) { + for (const vers of Object.values(simulators)) { + for (const sims of Object.values(vers)) { + for (const sim of sims) { + sim.dataDir = path.join(sim.deviceDir, 'data'); + } + } + } + return simulators; +} diff --git a/src/legacy/ti/cli.js b/src/legacy/ti/cli.js index 2d7482d..c159c5d 100644 --- a/src/legacy/ti/cli.js +++ b/src/legacy/ti/cli.js @@ -1,4 +1,4 @@ -/* eslint-disable promise/no-callback-in-promise */ +/* eslint-disable promise/no-callback-in-promise, security/detect-non-literal-require */ import Context from './context'; import fs from 'fs-extra'; @@ -132,12 +132,10 @@ export default class CLI { throw new TypeError('Expected options to be an object'); } - this.config = this.initConfig(opts.config); - + this.config = this.initConfig(opts.config); + this.fingerprint = opts.fingerprint; this.promptingEnabled = !!opts.promptingEnabled; - - // validate the sdk path - this.sdk = new sdk.TitaniumSDK(opts.sdkPath); + this.sdk = new sdk.TitaniumSDK(opts.sdkPath); // initialize the CLI argument values this.argv = { @@ -294,21 +292,22 @@ export default class CLI { // call all pre filters await pres // eslint-disable-next-line promise/no-nesting - .reduce((promise, pre) => promise.then(() => new Promise((resolve, reject) => { + .reduce((promise, pre) => promise.then(async () => { if (pre.length >= 2) { - pre.call(ctx, data, (err, newData) => { - if (err) { - return reject(err); - } else if (newData) { - data = newData; - } - resolve(); + await new Promise((resolve, reject) => { + pre.call(ctx, data, (err, newData) => { + if (err) { + return reject(err); + } else if (newData) { + data = newData; + } + resolve(); + }); }); } else { - pre.call(ctx, data); - resolve(); + await pre.call(ctx, data); } - })), Promise.resolve()); + }), Promise.resolve()); if (data.fn) { data.result = await new Promise(resolve => { @@ -335,7 +334,7 @@ export default class CLI { }); }); } else { - post.call(ctx, data); + await post.call(ctx, data); } }), Promise.resolve()); @@ -376,7 +375,6 @@ export default class CLI { const promise = unique(Array.isArray(name) ? name : [ name ]) .reduce((promise, name) => promise.then(() => new Promise((resolve, reject) => { const hook = this.createHook(name, data); - this.logger.trace(`Emitting ${highlight(name)}`); hook((err, result) => { err ? reject(err) : resolve(result); }); @@ -587,9 +585,7 @@ export default class CLI { try { if (fs.statSync(file).isFile() && jsfile.test(file) && !ignore.test(path.basename(path.dirname(file)))) { const startTime = Date.now(); - - // eslint-disable-next-line security/detect-non-literal-require - var mod = require(file); + const mod = require(file); if (mod.id) { if (!Array.isArray(this.hooks.ids[mod.id])) { this.hooks.ids[mod.id] = []; @@ -606,7 +602,6 @@ export default class CLI { } if (!this.version || !mod.cliVersion || version.satisfies(this.version, mod.cliVersion)) { - // eslint-disable-next-line security/detect-non-literal-require if (!appc) { appc = require(path.join(this.sdk.path, 'node_modules', 'node-appc')); } @@ -618,6 +613,8 @@ export default class CLI { } } } catch (ex) { + this.logger.trace(`Error loading hook: ${highlight(file)}`); + this.logger.trace(ex.stack); this.hooks.erroredFilenames.push(file); this.hooks.errors[file] = ex; } diff --git a/src/legacy/tunnel.js b/src/legacy/tunnel.js index c0d43c6..b59d320 100644 --- a/src/legacy/tunnel.js +++ b/src/legacy/tunnel.js @@ -1,5 +1,6 @@ import CLI from './ti/cli'; import { EventEmitter } from 'events'; +import { makeSerializable } from 'appcd-util'; import { v4 as uuidv4 } from 'uuid'; /** @@ -54,6 +55,22 @@ class Tunnel extends EventEmitter { }); } + /** + * Retrieves the active account or authenticates if you're not logged in. + * + * @returns {Promise} Resolves the account info. + */ + async getAccount() { + if (!this._account) { + const { response: accounts } = await this.call('/amplify/1.x/auth'); + this._account = accounts.find(a => a.active) || accounts[0]; + } + if (!this._account) { + this._account = (await this.call('/amplify/1.x/auth/login')).response; + } + return this._account; + } + /** * Writes a message to the debug log. * @@ -64,7 +81,7 @@ class Tunnel extends EventEmitter { this.emit('tick'); if (process.connected) { process.send({ - args, + args: makeSerializable(args), type: 'log' }); } @@ -88,7 +105,7 @@ class Tunnel extends EventEmitter { } else { await cli.command.load(); process.send({ - data: cli.command.conf, + data: makeSerializable(cli.command.conf), type: 'json' }); } @@ -98,13 +115,13 @@ class Tunnel extends EventEmitter { this.log('Disconnecting IPC tunnel from parent and letting process exit gracefully'); process.disconnect(); } catch (err) { - process.send({ + process.send(makeSerializable({ ...err, message: err.message || err, stack: err.stack, status: err.status || 500, type: 'error' - }); + })); process.exit(1); } return; @@ -123,7 +140,10 @@ class Tunnel extends EventEmitter { return resolve(data.answer); case 'error': - return reject(new Error(data.error)); + return reject(Object.defineProperties(new Error(data.error), { + code: { value: data.code }, + stack: { value: data.stack || 'Error originated in parent process and stack not available' } + })); case 'response': return resolve(data.response); @@ -143,7 +163,7 @@ class Tunnel extends EventEmitter { if (process.connected) { data.id = uuidv4(); this.pending[data.id] = { resolve, reject }; - process.send(data); + process.send(makeSerializable(data)); } else { reject(new Error(`Can't send "${data.type}" message to parent because IPC channel has been closed`)); } @@ -159,10 +179,10 @@ class Tunnel extends EventEmitter { telemetry(payload) { this.emit('tick'); if (process.connected) { - process.send({ + process.send(makeSerializable({ payload, type: 'telemetry' - }); + })); } } } diff --git a/support/android/appcelerator-security.jar b/support/android/appcelerator-security.jar new file mode 100644 index 0000000000000000000000000000000000000000..e7626e7cbdb464306c54b19315631b68f0792d76 GIT binary patch literal 3425 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 literal 0 HcmV?d00001 diff --git a/support/ios/ApplicationRouting.m b/support/ios/ApplicationRouting.m new file mode 100644 index 0000000..5d4154e --- /dev/null +++ b/support/ios/ApplicationRouting.m @@ -0,0 +1,125 @@ +/** + * This code is closed source and Confidential and Proprietary to + * Appcelerator, Inc. All Rights Reserved. This code MUST not be + * modified, copied or otherwise redistributed without express + * written permission of Appcelerator. This file is licensed as + * part of the Appcelerator Platform and governed under the terms + * of the Appcelerator license agreement. + * Copyright (c) 2015 Appcelerator, Inc. All Rights Reserved. + */ +#import +#import +#import "ApplicationRouting.h" +#import + +#define initializeAppData icucfasf7797nnzz +#define filterAppData sdnmnciuuu66zzaq + +extern NSString * const TI_APPLICATION_GUID; +extern NSData * filterAppData (NSString *filename, NSData * thedata); +extern void initializeAppData (NSString * sha1); + +/** + * gunzip NSData and return as NSData + */ +static NSData* gunzip(NSData *data) { + z_stream zStream; + memset(&zStream, 0, sizeof(zStream)); + inflateInit2(&zStream, 16); + + UInt32 nUncompressedBytes = *(UInt32*)(data.bytes + data.length - 4); + NSMutableData* gunzippedData = [NSMutableData dataWithLength:nUncompressedBytes]; + + zStream.next_in = (Bytef*)data.bytes; + zStream.avail_in =(uInt) data.length; + zStream.next_out = (Bytef*)gunzippedData.bytes; + zStream.avail_out = (uInt)gunzippedData.length; + + inflate(&zStream, Z_FINISH); + inflateEnd(&zStream); + + return gunzippedData; +} + +/** + * return a char* as hex NSString* + */ +static NSString* toHexString(unsigned char* data, unsigned int length) { + NSMutableString* hash = [NSMutableString stringWithCapacity:length * 2]; + for (unsigned int i = 0; i < length; i++) { + [hash appendFormat:@"%02x", data[i]]; + data[i] = 0; + } + return hash; +} + +/** + * sha1 a NSString + */ +static NSString* sha1Str(NSString *data) { + unsigned int outputLength = CC_SHA1_DIGEST_LENGTH; + unsigned char output[outputLength]; + CC_LONG length = (CC_LONG)[data lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + CC_SHA1([data UTF8String], length, output); + return toHexString(output,outputLength); +} + +/** + * sha1 a NSData + */ +static NSData* sha1Data(NSData *data) { + unsigned int outputLength = CC_SHA1_DIGEST_LENGTH; + unsigned char output[outputLength]; + CC_SHA1(data.bytes, (unsigned int) data.length, output); + NSString *str = toHexString(output,outputLength); + return [str dataUsingEncoding:NSUTF8StringEncoding]; +} + +@implementation ApplicationRouting + ++ (void) initialize { + NSError *error = nil; + NSString *dirsha = sha1Str(TI_APPLICATION_GUID); + NSString *dirPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dirsha]; + NSArray *directoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dirPath error:&error]; +#if !TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR + NSLog(@"[INFO] ApplicationRouting initialize, dirPath=%@",dirPath); +#endif + if (error==nil && [directoryContents count] > 0) { + // sort the files alphabetically + directoryContents = [directoryContents sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + NSMutableData *shadata = [NSMutableData dataWithCapacity:CC_SHA1_DIGEST_LENGTH]; + for (NSString *filename in directoryContents) { + NSString *filePath = [dirPath stringByAppendingPathComponent:filename]; + NSFileHandle *aHandle = [NSFileHandle fileHandleForReadingAtPath:filePath]; + NSData *contentData = [aHandle readDataToEndOfFile]; + NSData *sha = sha1Data(contentData); + [shadata appendData:sha]; + } + NSString *sha = [[[NSString alloc] initWithData:sha1Data(shadata) encoding:NSUTF8StringEncoding] autorelease]; +#if !TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR + NSLog(@"[DEBUG] sha of filenames = [%@]",sha); +#endif + initializeAppData(sha); + } +} + ++ (NSData *) resolveAppAsset:(NSString *)path { +#if !TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR + NSLog(@"[INFO] resolveAppAsset path %@",path); +#endif + NSString *dirsha = sha1Str(TI_APPLICATION_GUID); + NSString *pathsha = sha1Str(path); + NSString *filePath = [[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dirsha] stringByAppendingPathComponent:pathsha]; + NSFileHandle *aHandle = [NSFileHandle fileHandleForReadingAtPath:filePath]; + if (aHandle) { + NSData *contentData = [aHandle readDataToEndOfFile]; + NSData *unzipData = gunzip(contentData); + return filterAppData(path, unzipData); + } + + NSLog(@"[WARN] couldn't find file %@",path); + return nil; +} + +@end diff --git a/support/ios/libappcverify.a b/support/ios/libappcverify.a new file mode 100644 index 0000000000000000000000000000000000000000..c7f0cb7663a5ca507a1091c56784276090cee40c GIT binary patch literal 1445224 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

~qM-v_=?F_HbYf+zRv1PQwmK!z` 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%0A4?hrO? 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

CQ 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 $hn$IUEI1VQ`RjdY|7) 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Ɣ&9sH8CB3TWB%A6hnI2nyVT)+OYQO@` zuovizl#w5ek`&j;8AJK+Y!N46renm<(Ay={a7=_$@mrOm`ctZK4C5)2Vs9hGo*{!cf1AygyL8#($g-F}iCB7yn0-Zc>Mx}_X7AY5CB*~wimoKxY#_Rs zq<9OrH_?`HpUoC;`lX5=;1csomn&7r5SwbA?0c?c+f)z5d{g@%=Y!<5#ScS@tsUr} zW~lyFgB*98aIBnG^zvw38mIEq)MIRP$x( z<##D0_c^btQ1KXy5^A&9;`eMt6^kK@@n^{GuSYyFRHxU~knN0Z39Pw?*^CZfsf^AT zWmEOKt!BaxQ}q@8*W)&yYcl0^u@CGAOVu_YP}4*0Q$wq^s`KGk;3e~eWHSEB$ClHv zVr|kkn`$boup+J^mXzm&MEnhEQSr7*hT>;BM0I$9iuh-Hbu&GDyGg`1>>c`x-&LsW zmbQHHE)Uw`cZtQ;Qpwj5d%ikImzZns5(8vnx^&nUajP`;B70*WlZY<%X|{4Xm135? zCfZ2vO?lInuUM&xcJ>)mrl#6VLUbN@Fp%iG0y(UJ=o;S_D=e4X-nK+5H0n-&-y;!B)7M>$a{Qx~w*)Ut+r8s1B8og{L8{o8$fk&%GznExS z=lS#;1M};fl76bR>We?xR4vx12!(_7R2rxOg=hxP)yk)r^4VlR7oo4+wZWQFe<^>Sr}D=%_QDu00Z??rQ-WP!+wDH(>}8JFZr{+Ci{XiBekUj{(T+gb2J3v+}pw7@AA*#um0E(OgZ?Z zBqjR9#zln3`ov6(h)s!%@);c$mz3-ipBR@M=Mx!~93B%3AqsLtR8mq@q~V!SCL~Sp z8Iu?{G5Og+bgTct+b20Od}7j=xWoz14v3`wt*6R|PgKf;*k^`&riV}Rl=!I7n2C{5 zsra!oK0Y=kB0M=JZeo(p(1^ImsOI7E@jhKp%&3XUNj_bpCPl@@#YZLj42+A5jff79 zv4-m!H6}bIHo19fM6%UKuR3czN4)TY&!iUrB!%GE@MP9Yl21%9B0Zj=5mQjZ%wF5>@Jx&x*pB}Yy0 zvBZo{3{RZm(>*bKLe%8A#PLZ!-Q!XwM(Vw(`53F#Con2JGAa?ZGAIe;6Fq)nTvSAK zRIE?exQLVqdN+%Vn&g9i7tw4|RAS7SDLyfA|1`Ky+~_eWN$AN@|G#R1Q;W5;N5&+@ zhbKow|6ip2f7a*zFA9r6_UwsmLj1F*Lmznhgr}s2{@dt!h$q$qpRo}bSO3j2@_$hs z6T%~+eg2Q;yU|>v{?U;3Ns5X{NsLLJg1eSe;}b(Cg(t>@N5(`nkB*P$%0DJ$A{LXx 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 z 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 Date: Wed, 2 Sep 2020 16:25:59 -0500 Subject: [PATCH 23/39] fix(cli:login): Fixed issue with login command from cli-kit change. feat(app-preview): Add App Preview hook. chore: Updated deps. --- package.json | 7 +- src/cli/auth/login.js | 2 +- src/legacy/hooks/app-preview.js | 246 +++++++++++--------------------- src/legacy/hooks/appc.js | 3 +- 4 files changed, 92 insertions(+), 166 deletions(-) diff --git a/package.json b/package.json index 34f3d68..e41c62a 100644 --- a/package.json +++ b/package.json @@ -27,14 +27,15 @@ "enquirer": "^2.3.6", "figures": "^3.2.0", "filesize": "^6.1.0", + "form-data": "^3.0.0", "fs-extra": "^9.0.1", "gawk": "^4.7.1", "get-port": "^5.1.1", "global-modules": "^2.0.0", - "got": "^11.5.2", - "node-forge": "^0.9.1", + "got": "^11.6.0", + "node-forge": "^0.10.0", "node-pty-prebuilt-multiarch": "^0.9.0", - "open": "^7.2.0", + "open": "^7.2.1", "pluralize": "^8.0.0", "progress": "^2.0.3", "prompts": "^2.3.2", diff --git a/src/cli/auth/login.js b/src/cli/auth/login.js index b7b9dc2..fdd824c 100644 --- a/src/cli/auth/login.js +++ b/src/cli/auth/login.js @@ -24,7 +24,7 @@ export async function login({ argv, console, setExitCode, terminal }) { username: argv.username }; - if (Object.prototype.hasOwnProperty.call(argv, 'username')) { + if (argv.username !== undefined) { const questions = []; if (!argv.username || typeof argv.username !== 'string') { diff --git a/src/legacy/hooks/app-preview.js b/src/legacy/hooks/app-preview.js index ec5233b..ceb329f 100644 --- a/src/legacy/hooks/app-preview.js +++ b/src/legacy/hooks/app-preview.js @@ -5,20 +5,22 @@ * Installr API docs: https://help.installrapp.com/api/ */ +import FormData from 'form-data'; import fs from 'fs'; import got from 'got'; -import path from 'path'; +import open from 'open'; +import tmp from 'tmp'; import tunnel from '../tunnel'; import { expandPath } from 'appcd-path'; const endpoint = 'https://appbeta.axway.com'; -exports.init = (logger, config, cli, appc) => { +exports.init = async (logger, config, cli, appc) => { cli.on('build.config', data => { data.result[1].appPreview = [ 'App Preview Options', { - '--app-preview': 'Deploy a build to App Preview', + '--app-preview': 'Deploy a distribution build to App Preview', '--add [teams]': 'A comma-separated list of team names to add access to the App Preview build', '--release-notes [text]': 'Release notes for the App Preview build', '--invite [email_addresses]': 'A comma-separated list of email addresses to send the App Preview invites to', @@ -27,180 +29,102 @@ exports.init = (logger, config, cli, appc) => { ]; }); - cli.on('build.pre.compile', async data => { - // - }); - - cli.on('build.finalize', async data => { - // - }); -}; - -/* -var logger, platform, config, appc, appcConfig, j, build_file, busy; + let { add, appPreview, invite, notify, outputDir, releaseNotes, target } = cli.argv; -j = request.jar(); - -exports.init = function(_logger, _config, cli, _appc) { - if (process.argv.indexOf('--app-preview') !== -1) { - cli.addHook('build.pre.compile', configure); - cli.addHook('build.finalize', upload2AppPreview); + if (!appPreview) { + return; } - logger = _logger; - appcConfig = _config; - appc = _appc; -}; -function configure(data, finished) { - config = {}; - config.releaseNotes = data.cli.argv['release-notes']; - config.add = data.cli.argv['add']; - config.notify = data.cli.argv['notify']; - config.emails = data.cli.argv['invite']; - - if (!config.releaseNotes || !config.notify) { - doPrompt(finished); - } else { - finished(); + logger.info('Authentication required, getting account...'); + const account = await tunnel.getAccount(); + if (!account) { + throw new Error('You must be authenticated to use App Preview'); } -} -function doPrompt(finishedFunction) { - var f = {}; - - if (config.releaseNotes === undefined) { - f.releaseNotes = fields.text({ - title: "Release Notes", - desc: "Enter release notes.", - validate: function(value, callback) { - callback(!value.length, value); - } - }) + if (!account.org.entitlements.appPreview) { + throw new Error(`Your current organization "${account.org.name}" is not entitled to App Preview\nPlease upgrade your plan by visiting https://www.appcelerator.com/pricing/`); } - if (config.notify === undefined) { - f.notify = fields.select({ - title: "Notify", - desc: "Notify previous testers on upload.", - promptLabel: "(y,n)", - options: ['__y__es', '__n__o'] - }); - } - - var prompt = fields.set(f); - prompt.prompt(function(err, result) { - _.each(_.keys(result), function(key) { - config[key] = result[key]; - }); - finishedFunction(); - }); -} - -var onUploadComplete = function(err, httpResponse, body) { - var resp = {}; - if (err) { - logger.error(err); - } else { - if (httpResponse.statusCode != 200) { - logger.error('Error uploading to app preview, status code=' + httpResponse.statusCode); - return; - } else { - resp = JSON.parse(body); - if (resp.result != "success") { - logger.error(resp.message); - return; - } + cli.on('build.pre.compile', async ({ platformName }) => { + if (platformName !== 'android' && platformName !== 'iphone') { + throw new Error('App Preview is only supported when building for Android or iOS'); } - logger.info("App uploaded successfully."); - resp = JSON.parse(body); - // check if we want to invite new testers - if (config.emails) { - logger.info('Adding tester(s) ' + config.emails + ' to latest build'); - var r = request.post({ - jar: j, - url: SERVER + '/apps/' + resp.appData.id + '/builds/' + resp.appData.latestBuild.id + '/team.json' - }, function optionalCallback(err, httpResponse, body) { - if (err) { - logger.error(err); - showFinalUrl(resp); - } else { - logger.info("Tester(s) invited successfully."); - showFinalUrl(resp); - } - }); - var form = r.form(); - form.append('emails', config.emails); - } else { - showFinalUrl(resp); + + if (!target?.startsWith('dist-')) { + throw new Error('App Preview can only be used when doing a distribution build'); } - } -} - -function showFinalUrl(resp) { - logger.info('Open ' + SERVER + '/dashboard/index#/apps/' + resp.appData.id + ' to configure your app in App Preview.') -} - -function upload2AppPreview(data, finished) { - validate(data); - var sid = process.env.APPC_SESSION_SID; - logger.info('Uploading app to App Preview...please wait...'); - var cookie = request.cookie('connect.sid=' + sid); - j.setCookie(cookie, SERVER); - - var obj = { - url: SERVER + '/apps.json', - jar: j, - headers: { - "user-agent": 'Appcelerator CLI' + + if (platformName === 'iphone' && !outputDir) { + if (target === 'dist-appstore') { + logger.info('App Preview is forcing App Store build to skip Xcode archive'); + } + outputDir = cli.argv.outputDir = cli.argv['output-dir'] = tmp.tmpNameSync({ prefix: 'titanium-app-preview-' }); } - }; - // configure proxy - if (process.env.APPC_CONFIG_PROXY) { - obj.proxy = process.env.APPC_CONFIG_PROXY; - } + // TODO: prompt for releaseNotes and notify + }); - var r = request.post(obj, onUploadComplete); - - var form = r.form(); - var file = fs.createReadStream(build_file); - var totalSize = fs.statSync(build_file).size; - var bytesRead = 0; - var lastPercent = 0; - file.on('data', function(chunk) { - bytesRead += chunk.length; - var currentPercent = Math.round((bytesRead / totalSize) * 100); - if (currentPercent != lastPercent && currentPercent % 5 == 0) { - logger.info("uploaded " + currentPercent + "%"); - lastPercent = currentPercent; + cli.on('build.finalize', async ({ apkFile, platformName, tiapp }) => { + const file = platformName === 'android' ? apkFile : expandPath(outputDir, `${tiapp.name}.ipa`); + + const form = new FormData(); + form.append('qqfile', fs.createReadStream(file)); + form.append('releaseNotes', releaseNotes); + form.append('notify', notify); + if (add) { + form.append('add', add); } - }); - form.append('qqfile', file); - form.append('releaseNotes', config.releaseNotes); - form.append('notify', config.notify.toString()); - if (config.add) { - form.append('add', config.add.toString()); - } -} -function validate(data) { + logger.info('App Preview uploading build...'); + + const post = async (url, body) => { + try { + return (await got(url, { + body, + headers: { + Accept: 'application/json', + Cookie: `connect.sid=${account.sid}`, + 'User-Agent': 'Titanium CLI' + }, + method: 'post', + responseType: 'json', + retry: 0 + })).body; + } catch (err) { + const msg = err.response?.body?.message || err.response?.body?.description; + err.message = `App Preview request failed: ${msg || err.message}`; + + const code = err.response?.body?.code; + if (code) { + err.code = code; + } - platform = data.cli.argv.platform; + throw err; + } + }; - if (['android', 'ios'].indexOf(platform) === -1) { - logger.error("Only android and ios support with --app-preview flag"); - return; - } + const { appData, message, result } = await post(`${endpoint}/apps.json`, form); - if (data.cli.argv.platform === "android") { - build_file = data.apkFile - } else { - if (data.buildManifest.outputDir === undefined && data.iosBuildDir === undefined) { - logger.error("Output directory must be defined to use --app-preview flag"); - return; + if (result !== 'success') { + throw new Error(`App Preview failed to upload build: ${message || 'Unknown error'}`); } - build_file = afs.resolvePath(path.join(data.buildManifest.outputDir, data.buildManifest.name + ".ipa")); - } -} -*/ + logger.info('App Preview uploaded build successfully'); + + // check if we want to invite new testers + const emails = invite && invite.split(',').map(s => s.trim()).filter(Boolean); + if (emails?.length) { + try { + logger.info(`Adding tester${emails.length === 1 ? '' : 's'}: ${emails.join(', ')}`); + const form = new FormData(); + form.append('emails', emails.join(',')); + await post(`${endpoint}/apps/${appData.id}/builds/${appData.latestBuild.id}/team.json`, form); + logger.info(`Tester${emails.length === 1 ? '' : 's'} successfully invited`); + } catch (err) { + logger.warn(`App Preview failed to invite users: ${err.message}`); + } + } + + open(`${endpoint}/dashboard/index#/apps/${appData.id}`); + }); +}; diff --git a/src/legacy/hooks/appc.js b/src/legacy/hooks/appc.js index 47a9a6d..f4e6ebf 100644 --- a/src/legacy/hooks/appc.js +++ b/src/legacy/hooks/appc.js @@ -4,7 +4,6 @@ import isPlatformGuid from '@titanium-sdk/node-is-platform-guid'; import tunnel from '../tunnel'; import { expandPath } from 'appcd-path'; -import { isFile } from 'appcd-fs'; import { sha1 } from 'appcd-util'; exports.init = (logger, config, cli, appc) => { @@ -146,6 +145,7 @@ exports.init = (logger, config, cli, appc) => { let result; if (builder.deployType === 'production') { + logger.info('Authentication required, getting account...'); account = await tunnel.getAccount(); if (!account) { throw new Error('You must be authenticated to perform production builds'); @@ -157,6 +157,7 @@ exports.init = (logger, config, cli, appc) => { if (isPlatformGuid(builder.tiapp.guid)) { if (!account) { + logger.info('Authentication required, getting account...'); account = await tunnel.getAccount(); if (!account) { throw new Error('You must be authenticated to build registered applications'); From b9b1f296359a5ee25323cb45e4d448fb9892e678 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Fri, 4 Sep 2020 16:11:25 -0500 Subject: [PATCH 24/39] feat: Added '/module/check-downloads' endpoint. feat(module): Added automatic checking of new Titanium module downloads. --- CHANGELOG.md | 2 + src/cli/auth/login.js | 8 ++- src/cli/commands/switch.js | 6 ++ src/legacy/hooks/appc.js | 10 +-- src/module/module-service.js | 119 +++++++++++++++++++++++++++++++++-- src/sdk/sdk-service.js | 3 +- 6 files changed, 137 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 772357a..c88820f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ * feat(cli:sdk): Added aliases to sdk commands (i, ls, rm). * feat(sdk): Added `find` endpoint to SDK service to get info about an installed Titanium SDK. * feat(sdk): Added progress bars during SDK installation. + * feat(module): Added `/module/check-downloads` endpoint. + * feat(module): Added automatic checking of new Titanium module downloads. * feat: Support for Titanium-specific telemetry. * refactor: Updated to latest cli-kit with support for the new client/server architecture. * refactor: Updated `config` command actions to be subcommands with improved help output. diff --git a/src/cli/auth/login.js b/src/cli/auth/login.js index fdd824c..33bc690 100644 --- a/src/cli/auth/login.js +++ b/src/cli/auth/login.js @@ -62,7 +62,13 @@ export async function login({ argv, console, setExitCode, terminal }) { } try { - return (await appcd.call('/amplify/1.x/auth/login', { data })).response; + const { response: account } = await appcd.call('/amplify/1.x/auth/login', { data }); + try { + await appcd.call('/module/check-downloads', { data: { accountName: account.name } }); + } catch (err) { + // squelch + } + return account; } catch (err) { if (err.code === 'EAUTHENTICATED') { const { account } = err; diff --git a/src/cli/commands/switch.js b/src/cli/commands/switch.js index 28a442b..e8f2b55 100644 --- a/src/cli/commands/switch.js +++ b/src/cli/commands/switch.js @@ -77,6 +77,12 @@ export default { })).response; } + try { + await appcd.call('/module/check-downloads', { data: { accountName: account.name } }); + } catch (err) { + // squelch + } + if (argv.json) { console.log(JSON.stringify(account, null, 2)); } else { diff --git a/src/legacy/hooks/appc.js b/src/legacy/hooks/appc.js index f4e6ebf..b8fee45 100644 --- a/src/legacy/hooks/appc.js +++ b/src/legacy/hooks/appc.js @@ -194,10 +194,12 @@ exports.init = (logger, config, cli, appc) => { priority: 0 }); - // cli.on('build.post.compile', { - // priority: 10000, - // post: postcompileHook - // }); + cli.on('build.post.compile', { + priority: 10000, + post: async function (builder) { + // + } + }); const mutator = { pre: function (data) { diff --git a/src/module/module-service.js b/src/module/module-service.js index 7183560..70af619 100644 --- a/src/module/module-service.js +++ b/src/module/module-service.js @@ -1,10 +1,14 @@ -import Dispatcher from 'appcd-dispatcher'; +import Dispatcher, { DispatcherError } from 'appcd-dispatcher'; import ModuleListService from './module-list-service'; +import { AppcdError } from 'appcd-response'; import { expandPath } from 'appcd-path'; import { get, unique } from 'appcd-util'; import { modules } from 'titaniumlib'; +const { log } = appcd.logger('module-service'); +const { highlight } = appcd.logger.styles; + /** * Defines a service endpoint for listing Titanium modules. */ @@ -25,19 +29,85 @@ export default class ModuleService extends Dispatcher { this.register('/', (ctx, next) => { ctx.path = '/list'; return next(); - }) - .register('/list', this.installed) - .register('/locations', () => modules.getPaths()); + }); + this.register('/check-downloads', ctx => this.checkDownloads(ctx.request.data.accountName)); + this.register('/list', this.installed); + this.register('/locations', () => modules.getPaths()); + + const check = async () => { + try { + const { response: accounts } = await appcd.call('/amplify/1.x/auth'); + const account = accounts.find(a => a.active) || accounts[0]; + await this.checkDownloads(account?.name); + log('Checking for updated downloads again in 1 hour'); + } catch (err) { + log(`Failed to check downloads, trying again in 1 hour: ${err.message}`); + } + }; + this.checkTimer = setInterval(check, 1000 * 60 * 60); // check every hour + check(); } /** - * Shuts down the installed SDKs detect engine. + * Checks platform for available Titanium native modules and installs them if they aren't + * already installed. + * + * @param {String} accountName - The name of the account to use to verify downloads. + * @returns {Promise>} + * @access private + */ + async checkDownloads(accountName) { + if (!accountName || typeof accountName !== 'string') { + throw new TypeError('Expected account name'); + } + + const { response: downloads } = await appcd.call('/amplify/1.x/ti/downloads', { + data: { + accountName + } + }); + const installed = this.installed.data; + const result = []; + + for (const { id, versions } of downloads.modules) { + for (const { platforms = [], url, version } of versions) { + for (let platform of platforms) { + if (platform === 'iphone') { + platform = 'ios'; + } + if (platform !== 'android' && (process.platform === 'darwin' || platform !== 'ios')) { + continue; + } + if (installed[platform]?.[id]?.[version]) { + log(`${highlight(`${id}@${version}`)} (${platform}) already installed`); + } else { + log(`Installing ${highlight(`${id}@${version}`)} (${platform})...`); + result.push.apply(result, await modules.install({ + downloadDir: this.config.titanium.home && expandPath(this.config.titanium.home, 'downloads'), + uri: url + })); + break; + } + } + } + } + + return result; + } + + /** + * Shuts down the installed SDKs detect engine and stop checking for downloads. * * @returns {Promise} * @access public */ async deactivate() { await this.installed.deactivate(); + + if (this.checkTimer) { + clearInterval(this.checkTimer); + this.checkTimer = null; + } } /** @@ -54,4 +124,43 @@ export default class ModuleService extends Dispatcher { } return unique(paths); } + + /** + * Install module service handler. + * + * Note: This method does not return a promise because we want the response to be sent + * immediately and receive install events as they occur. It relies on the + * + * @param {Context} ctx - A request context. + * @access private + */ + install({ request, response }) { + const { data } = request; + + modules.install({ + downloadDir: this.config.titanium.home && expandPath(this.config.titanium.home, 'downloads'), + keep: data.keep, + onProgress(evt) { + if (data.progress) { + response.write(evt); + } + }, + overwrite: data.overwrite, + uri: data.uri + }).then(modules => { + response.write({ fin: true, message: 'Titanium Module installed', modules }); + response.end(); + }).catch(err => { + try { + if (err.code === 'ENOTFOUND') { + response.write(new DispatcherError(err.message)); + } else { + response.write(new AppcdError(err)); + } + response.end(); + } catch (e) { + // stream is probably closed + } + }); + } } diff --git a/src/sdk/sdk-service.js b/src/sdk/sdk-service.js index 424d90c..fb53e9e 100644 --- a/src/sdk/sdk-service.js +++ b/src/sdk/sdk-service.js @@ -65,6 +65,7 @@ export default class SDKService extends Dispatcher { /** * Install SDK service handler. + * * Note: This method does not return a promise because we want the response to be sent * immediately and receive install events as they occur. It relies on the * @@ -75,7 +76,7 @@ export default class SDKService extends Dispatcher { const { data, params } = request; sdk.install({ - downloadDir: this.config.home && expandPath(this.config.home, 'downloads'), + downloadDir: this.config.titanium.home && expandPath(this.config.titanium.home, 'downloads'), keep: data.keep, onProgress(evt) { if (data.progress) { From f92c2b0325303ae8b80e931935f918a350893258 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Fri, 4 Sep 2020 22:49:58 -0500 Subject: [PATCH 25/39] fix(login): Fixed banner not showing. feat(module): Added install service endpoint. --- CHANGELOG.md | 1 + src/cli/commands/login.js | 10 ++++++---- src/legacy/hooks/appc.js | 2 ++ src/module/module-service.js | 25 +++++++++++++++++-------- src/sdk/sdk-service.js | 3 ++- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c88820f..12b0316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ * feat(sdk): Added `find` endpoint to SDK service to get info about an installed Titanium SDK. * feat(sdk): Added progress bars during SDK installation. * feat(module): Added `/module/check-downloads` endpoint. + * feat(module): Added `/module/install` endpoint. * feat(module): Added automatic checking of new Titanium module downloads. * feat: Support for Titanium-specific telemetry. * refactor: Updated to latest cli-kit with support for the new client/server architecture. diff --git a/src/cli/commands/login.js b/src/cli/commands/login.js index 7fb6aa8..429b01c 100644 --- a/src/cli/commands/login.js +++ b/src/cli/commands/login.js @@ -11,11 +11,13 @@ export default { '--realm [realm]': { hidden: true }, '--force': 'Re-authenticate even if the account is already authenticated', '--json': { - callback({ ctx }) { - while (ctx.parent) { - ctx = ctx.parent; + callback({ ctx, value }) { + if (value) { + while (ctx.parent) { + ctx = ctx.parent; + } + ctx.banner = false; } - ctx.banner = false; }, desc: 'Outputs accounts as JSON' }, diff --git a/src/legacy/hooks/appc.js b/src/legacy/hooks/appc.js index b8fee45..2ad5300 100644 --- a/src/legacy/hooks/appc.js +++ b/src/legacy/hooks/appc.js @@ -189,6 +189,8 @@ exports.init = (logger, config, cli, appc) => { builder.forceRebuild = true; } } + + // TODO: what should we do with `result`? Appc CLI merges it into the Titanium CLI config } }, priority: 0 diff --git a/src/module/module-service.js b/src/module/module-service.js index 70af619..79e7e7e 100644 --- a/src/module/module-service.js +++ b/src/module/module-service.js @@ -29,19 +29,25 @@ export default class ModuleService extends Dispatcher { this.register('/', (ctx, next) => { ctx.path = '/list'; return next(); - }); - this.register('/check-downloads', ctx => this.checkDownloads(ctx.request.data.accountName)); - this.register('/list', this.installed); - this.register('/locations', () => modules.getPaths()); + }) + .register('/check-downloads', ctx => this.checkDownloads(ctx.request.data.accountName)) + .register('/install/:name?', ctx => this.install(ctx)) + .register('/list', this.installed) + .register('/locations', () => modules.getPaths()); const check = async () => { try { const { response: accounts } = await appcd.call('/amplify/1.x/auth'); const account = accounts.find(a => a.active) || accounts[0]; await this.checkDownloads(account?.name); - log('Checking for updated downloads again in 1 hour'); + log('Successfully checked downloads, checking again in 1 hour'); } catch (err) { - log(`Failed to check downloads, trying again in 1 hour: ${err.message}`); + if (err.code === 'EAUTH') { + log('Not authenticated, checking downloads again in 1 hour'); + } else { + log(`Failed to check downloads: ${err.message}`); + log('Trying again in 1 hour'); + } } }; this.checkTimer = setInterval(check, 1000 * 60 * 60); // check every hour @@ -58,7 +64,9 @@ export default class ModuleService extends Dispatcher { */ async checkDownloads(accountName) { if (!accountName || typeof accountName !== 'string') { - throw new TypeError('Expected account name'); + const err = new TypeError('Expected account name'); + err.code = 'EAUTH'; + throw err; } const { response: downloads } = await appcd.call('/amplify/1.x/ti/downloads', { @@ -129,7 +137,8 @@ export default class ModuleService extends Dispatcher { * Install module service handler. * * Note: This method does not return a promise because we want the response to be sent - * immediately and receive install events as they occur. It relies on the + * immediately and receive install events as they occur. It relies on the response stream to + * close. * * @param {Context} ctx - A request context. * @access private diff --git a/src/sdk/sdk-service.js b/src/sdk/sdk-service.js index fb53e9e..3e874dc 100644 --- a/src/sdk/sdk-service.js +++ b/src/sdk/sdk-service.js @@ -67,7 +67,8 @@ export default class SDKService extends Dispatcher { * Install SDK service handler. * * Note: This method does not return a promise because we want the response to be sent - * immediately and receive install events as they occur. It relies on the + * immediately and receive install events as they occur. It relies on the response stream to + * close. * * @param {Context} ctx - A request context. * @access private From 8acc09f2be011c31ca2b12b29576c8e14614cae9 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Fri, 4 Sep 2020 23:29:33 -0500 Subject: [PATCH 26/39] feat: Update the app tiapp metadata post build. feat(aca): Added ACA hook. --- src/legacy/hooks/aca.js | 159 +++++++++++++++++++++++++++++++++++++++ src/legacy/hooks/appc.js | 22 +++++- 2 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 src/legacy/hooks/aca.js diff --git a/src/legacy/hooks/aca.js b/src/legacy/hooks/aca.js new file mode 100644 index 0000000..b5367b5 --- /dev/null +++ b/src/legacy/hooks/aca.js @@ -0,0 +1,159 @@ +exports.init = (logger, config, cli, appc) => { + cli.on('build.post.compile', { + priority: 10000, + post: async function (builder) { + /* + var deployType = builder.deployType, + platform = builder.cli.argv.platform, + sid = process.env.APPC_SESSION_SID, + tiapp = builder.tiapp; + + function uploadDebugSymbols(cb) { + var hasACA = false; + for (let m of tiapp.modules) { + if (m.id === 'com.appcelerator.aca') { + hasACA = true; + break; + } + } + if (platform !== 'ios' || deployType === 'development' || !hasACA) { + return cb(); + } + + var productsDir = path.join(builder.buildDir, 'build', 'Products'), + symbolsPath = builder.xcodeAppDir + '.dSYM'; + fs.readdirSync(productsDir).forEach(function (name) { + var subdir = path.join(productsDir, name); + if (fs.statSync(subdir).isDirectory()) { + fs.readdirSync(subdir).forEach(function (name) { + var file = path.join(subdir, name); + if (/\.dSYM$/.test(name) && fs.statSync(file).isDirectory()) { + symbolsPath = file; + logger.info('symbols: ' + symbolsPath); + } + }); + } + }); + + var symbolsTar = symbolsPath + '.tar.gz'; + if (!fs.existsSync(symbolsPath)) { + logger.error('could not find debug symbols'); + return cb(); + } + + aps.Auth.createSessionFromID(sid, function (err, session) { + if (err) { + logger.error(err); + return cb(); + } + + logger.trace('requesting crash report upload url'); + aps.createRequest(session, '/api/v1/app/' + tiapp.guid + '/upload', function (err, result) { + if (err) { + logger.error(err); + return cb(); + } + + logger.trace('compressing debug symbols...'); + const tarOpt = { + file: symbolsTar, + cwd: path.dirname(symbolsPath), + portable: true, + gzip: { level: 9 } + }; + + tar.create(tarOpt, [ path.basename(symbolsPath) ], function (err) { + if (err) { + logger.error(err); + return cb(); + } + + logger.trace('uploading compressed debug symbols...'); + logger.trace('uploading ' + result.url); + + if (result.module === 'aca') { + var stat = fs.statSync(symbolsTar); + logger.trace('symbol size: ' + stat.size); + logger.trace('max upload limit: ' + result.limit); + + if (stat.size && result.limit && stat.size > result.limit) { + logger.error('Symbol size exceeded limit, the symbol file upload did not succeed.'); + return cb(); + } + + var props = '?' + + '&version=' + tiapp.version + + '&platform=' + platform + + '&app=' + tiapp.guid; + + var reqOptions = { + url: result.url + props, + headers: { + 'Content-Length': stat.size, + 'X-Auth-Token': result.api_token + } + }; + + // dummy request to retrieve the redirect endpoint + request.put(reqOptions, function (err, resp, body) { + if (err) { + logger.error(err); + return cb(); + } + + if (resp && resp.statusCode !== 302) { + logger.error(body); + return cb(); + } + + var redirect_headers = { + url: resp.headers.location, + headers: { + 'Content-Length': stat.size + } + }; + + // actual upload of the file to the destination, found in the headers + var req = request.put(redirect_headers, function (err, resp, body) { + if (err) { + logger.error(err); + return cb(); + } + + if (resp && resp.statusCode !== 200) { + logger.error(body); + return cb(); + } + + logger.trace('Uploaded compressed debug symbols!'); + return cb(); + }); + + fs.createReadStream(symbolsTar).pipe(req); + }); + } else { + var req = request.post(result.url, function (err, resp, body) { + if (err) { + logger.error(err); + return cb(); + } + if (resp && resp.statusCode !== 200) { + logger.error(body); + return cb(); + } + logger.trace('Uploaded compressed debug symbols!'); + return cb(); + }); + + var form = req.form(); + form.append('key', result.api_token); + form.append('dsym', fs.createReadStream(symbolsTar)); + } + }); + }); + }); + }, + */ + } + }); +}; diff --git a/src/legacy/hooks/appc.js b/src/legacy/hooks/appc.js index 2ad5300..bf28355 100644 --- a/src/legacy/hooks/appc.js +++ b/src/legacy/hooks/appc.js @@ -8,6 +8,8 @@ import { sha1 } from 'appcd-util'; exports.init = (logger, config, cli, appc) => { const homeDir = expandPath(config.get('home')); + let account; + let isRegistered = false; async function generateDevCert({ account }) { logger.info('Generating developer certificate and private/public keys'); @@ -141,7 +143,6 @@ exports.init = (logger, config, cli, appc) => { cli.on('build.pre.compile', { post: async function (builder) { - let account; let result; if (builder.deployType === 'production') { @@ -155,7 +156,9 @@ exports.init = (logger, config, cli, appc) => { } } - if (isPlatformGuid(builder.tiapp.guid)) { + isRegistered = isPlatformGuid(builder.tiapp.guid); + + if (isRegistered) { if (!account) { logger.info('Authentication required, getting account...'); account = await tunnel.getAccount(); @@ -198,8 +201,19 @@ exports.init = (logger, config, cli, appc) => { cli.on('build.post.compile', { priority: 10000, - post: async function (builder) { - // + async post({ projectDir }) { + if (!isRegistered || !account) { + return; + } + + await tunnel.call('/amplify/1.x/ti/app/set', { + data: { + accountName: account.name, + tiapp: await fs.readFile(path.join(projectDir, 'tiapp.xml', 'utf-8')) + } + }); + + logger.trace('Updated platform with tiapp metadata'); } }); From dba7b4d95a5383bf27bbd4472bb68a9329fbea18 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Wed, 9 Sep 2020 01:28:07 -0500 Subject: [PATCH 27/39] fix: Added missing pluginVersion to 'ti info' output. style: Improved readability of 'ti info'. feat(aca): Wired up pre-compile hook. fix(legacy): Paritially fixed option validation. --- src/cli/cli-service.js | 1 + src/cli/info/android.js | 86 ++++++------ src/cli/info/ios.js | 75 ++++++----- src/cli/info/jdk.js | 10 +- src/cli/info/os.js | 14 +- src/cli/info/titanium.js | 20 +-- src/cli/info/windows.js | 16 +-- src/legacy/hooks/aca.js | 282 +++++++++++++++++++-------------------- src/legacy/hooks/appc.js | 4 +- src/legacy/ti/cli.js | 28 ++-- 10 files changed, 275 insertions(+), 261 deletions(-) diff --git a/src/cli/cli-service.js b/src/cli/cli-service.js index 83040b0..11058e8 100644 --- a/src/cli/cli-service.js +++ b/src/cli/cli-service.js @@ -50,6 +50,7 @@ export default class CLIService extends Dispatcher { // it's available to the command callback cli.on('parse', ({ data }) => { data.config = cfg.titanium; + data.pluginVersion = pluginVersion; }); // we need to add platform specific options for the build/run help, so first we listen for diff --git a/src/cli/info/android.js b/src/cli/info/android.js index 535a6b9..172f083 100644 --- a/src/cli/info/android.js +++ b/src/cli/info/android.js @@ -3,26 +3,26 @@ export default { return (await appcd.call('/android/2.x/info')).response; }, render(console, info) { - const { bold, cyan, gray, magenta } = require('chalk'); + const { cyan, gray, green, magenta } = require('chalk'); - console.log(bold('Android SDKs')); + console.log(magenta('Android SDKs'.toUpperCase())); if (info.sdks.length) { for (const sdk of info.sdks) { - console.log(` ${cyan(sdk.path)}${sdk.default ? gray(' (default)') : ''}`); - console.log(` ADB Executable = ${magenta(sdk.platformTools.executables.adb || 'not installed')}`); - console.log(` Build Tools = ${magenta(sdk.buildTools.length ? sdk.buildTools.map(bt => bt.version).filter(v => v).join(', ') : 'not installed')}`); - console.log(` Platform Tools = ${magenta(sdk.platformTools.version || 'n/a')}`); - console.log(` Tools = ${magenta(sdk.tools.version || 'n/a')}`); + console.log(` ${green(sdk.path)}${sdk.default ? gray(' (default)') : ''}`); + console.log(` ADB Executable = ${cyan(sdk.platformTools.executables.adb || 'not installed')}`); + console.log(` Build Tools = ${cyan(sdk.buildTools.length ? sdk.buildTools.map(bt => bt.version).filter(v => v).join(', ') : 'not installed')}`); + console.log(` Platform Tools = ${cyan(sdk.platformTools.version || 'n/a')}`); + console.log(` Tools = ${cyan(sdk.tools.version || 'n/a')}`); console.log(' Platforms:'); if (sdk.platforms.length) { for (const platform of sdk.platforms) { - console.log(` ${cyan(platform.sdk)}`); - console.log(` Name = ${magenta(platform.name)}`); - console.log(` API Level = ${magenta(platform.apiLevel)}`); - console.log(` Revision = ${magenta(platform.revision || '?')}`); - console.log(` Path = ${magenta(platform.path)}`); - console.log(` Skins = ${magenta(platform.skins.join(', '))}`); - console.log(` Architectures = ${magenta(Object.keys(platform.abis).map(name => `${name}: ${platform.abis[name].join(', ')}`))}`); + console.log(` ${green(platform.sdk)}`); + console.log(` Name = ${cyan(platform.name)}`); + console.log(` API Level = ${cyan(platform.apiLevel)}`); + console.log(` Revision = ${cyan(platform.revision || '?')}`); + console.log(` Path = ${cyan(platform.path)}`); + console.log(` Skins = ${cyan(platform.skins.join(', '))}`); + console.log(` Architectures = ${cyan(Object.keys(platform.abis).map(name => `${name}: ${platform.abis[name].join(', ')}`))}`); } } else { console.log(gray(' None')); @@ -30,16 +30,16 @@ export default { console.log(' Addons:'); if (sdk.addons.length) { for (const addon of sdk.addons) { - console.log(` ${cyan(addon.name)}`); - console.log(` Name = ${magenta(addon.name)}${addon.codename ? gray(` (${addon.codename})`) : ''}`); - console.log(` API Level = ${magenta(addon.apiLevel)}`); - console.log(` Revision = ${magenta(addon.revision || '?')}`); - console.log(` Based On = ${magenta(addon.basedOn ? `Android ${addon.basedOn.version}` : 'n/a')}`); - console.log(` Path = ${magenta(addon.path)}`); - console.log(` Vendor = ${magenta(addon.vendor)}`); - console.log(` Description = ${magenta(addon.description || 'n/a')}`); - console.log(` Skins = ${magenta(addon.skins && addon.skins.join(', ') || 'none')}`); - console.log(` Architectures = ${magenta(addon.abis && Object.keys(addon.abis).map(name => `${name}: ${addon.abis[name].join(', ')}`)) || 'none'}`); + console.log(` ${green(addon.name)}`); + console.log(` Name = ${cyan(addon.name)}${addon.codename ? gray(` (${addon.codename})`) : ''}`); + console.log(` API Level = ${cyan(addon.apiLevel)}`); + console.log(` Revision = ${cyan(addon.revision || '?')}`); + console.log(` Based On = ${cyan(addon.basedOn ? `Android ${addon.basedOn.version}` : 'n/a')}`); + console.log(` Path = ${cyan(addon.path)}`); + console.log(` Vendor = ${cyan(addon.vendor)}`); + console.log(` Description = ${cyan(addon.description || 'n/a')}`); + console.log(` Skins = ${cyan(addon.skins && addon.skins.join(', ') || 'none')}`); + console.log(` Architectures = ${cyan(addon.abis && Object.keys(addon.abis).map(name => `${name}: ${addon.abis[name].join(', ')}`)) || 'none'}`); } } else { console.log(gray(' None')); @@ -50,43 +50,43 @@ export default { } console.log(); - console.log(bold('Android NDKs')); + console.log(magenta('Android NDKs'.toUpperCase())); if (info.ndks.length) { for (const ndk of info.ndks) { - console.log(` ${cyan(ndk.path)}${ndk.default ? gray(' (default)') : ''}`); - console.log(` Name = ${magenta(ndk.name)}`); - console.log(` Version = ${magenta(ndk.version)}`); - console.log(` Architecture = ${magenta(ndk.arch)}`); - console.log(` Path = ${magenta(ndk.path)}`); + console.log(` ${green(ndk.path)}${ndk.default ? gray(' (default)') : ''}`); + console.log(` Name = ${cyan(ndk.name)}`); + console.log(` Version = ${cyan(ndk.version)}`); + console.log(` Architecture = ${cyan(ndk.arch)}`); + console.log(` Path = ${cyan(ndk.path)}`); } } else { console.log(gray(' None')); } console.log(); - console.log(bold('Android Emulators')); + console.log(magenta('Android Emulators'.toUpperCase())); if (info.emulators.length) { for (const emu of info.emulators) { - console.log(` ${cyan(emu.name)}${emu.type === 'avd' ? gray(' (AVD)') : ''}`); - console.log(` ID = ${magenta(emu.id)}`); - console.log(` Version = ${magenta(emu.target || '?')}`); - console.log(` Architecture = ${magenta(emu.abi)}`); - console.log(` Path = ${magenta(emu.path)}`); - console.log(` Google APIs = ${magenta(emu.googleApis === null ? 'Unknown' : emu.googleApis ? 'Yes' : 'No')}`); + console.log(` ${green(emu.name)}${emu.type === 'avd' ? gray(' (AVD)') : ''}`); + console.log(` ID = ${cyan(emu.id)}`); + console.log(` Version = ${cyan(emu.target || '?')}`); + console.log(` Architecture = ${cyan(emu.abi)}`); + console.log(` Path = ${cyan(emu.path)}`); + console.log(` Google APIs = ${cyan(emu.googleApis === null ? 'Unknown' : emu.googleApis ? 'Yes' : 'No')}`); } } else { console.log(gray(' None')); } console.log(); - console.log(bold('Android Devices')); + console.log(magenta('Android Devices'.toUpperCase())); if (info.devices.length) { for (const device of info.devices) { - console.log(` ${cyan(device.name)}${device.model ? gray(` (${device.model})`) : ''}`); - console.log(` ID = ${magenta(device.id)}`); - console.log(` State = ${magenta(device.state)}`); - console.log(` SDK Version = ${magenta(`${device.release || '?'}${device.sdk ? ` (android-${device.sdk})` : ''}`)}`); - console.log(` Architectures = ${magenta(Array.isArray(device.abi) ? device.abi.sort().join(', ') : '?')}`); + console.log(` ${green(device.name)}${device.model ? gray(` (${device.model})`) : ''}`); + console.log(` ID = ${cyan(device.id)}`); + console.log(` State = ${cyan(device.state)}`); + console.log(` SDK Version = ${cyan(`${device.release || '?'}${device.sdk ? ` (android-${device.sdk})` : ''}`)}`); + console.log(` Architectures = ${cyan(Array.isArray(device.abi) ? device.abi.sort().join(', ') : '?')}`); } } else { console.log(gray(' None')); diff --git a/src/cli/info/ios.js b/src/cli/info/ios.js index 73ecea5..65004d3 100644 --- a/src/cli/info/ios.js +++ b/src/cli/info/ios.js @@ -3,16 +3,16 @@ export default { render(console, info) { const dateformat = require('dateformat'); const pluralize = require('pluralize'); - const { bold, cyan, gray, magenta } = require('chalk'); + const { cyan, gray, green, magenta } = require('chalk'); - console.log(bold('Xcode')); + console.log(magenta('Xcode'.toUpperCase())); if (info.xcode) { for (const xcode of Object.values(info.xcode)) { - console.log(` ${cyan(xcode.version)} (build ${xcode.build})${xcode.default ? gray(' (default)') : ''}}`); - console.log(` App Path = ${magenta(xcode.xcodeapp)}`); - console.log(` iOS SDKs = ${magenta(xcode.sdks.ios.join(', '))}`); - console.log(` watchOS SDKs = ${magenta(xcode.sdks.watchos.join(', '))}`); - console.log(` EULA Accepted = ${magenta(xcode.eulaAccepted ? 'Yes' : 'No')}`); + console.log(` ${green(`${xcode.version} (build ${xcode.build})`)}${xcode.default ? gray(' (default)') : ''}`); + console.log(` App Path = ${cyan(xcode.xcodeapp)}`); + console.log(` iOS SDKs = ${cyan(xcode.sdks.ios.join(', '))}`); + console.log(` watchOS SDKs = ${cyan(xcode.sdks.watchos.join(', '))}`); + console.log(` EULA Accepted = ${cyan(xcode.eulaAccepted ? 'Yes' : 'No')}`); } } else { console.log(gray(' Not installed')); @@ -20,29 +20,36 @@ export default { console.log(); const pc = (title, certs) => { - console.log(` ${cyan(title)}`); + console.log(` ${green(title)}`); + const total = certs.length; certs = certs.filter(c => !c.invalid); if (certs.length) { for (const cert of certs) { console.log(` ${cert.name}`); - console.log(` Not valid before = ${magenta(cert.before ? dateformat(cert.before, 'm/d/yyyy h:MM TT') : 'unknown')}`); + console.log(` Not valid before = ${cyan(cert.before ? dateformat(cert.before, 'm/d/yyyy h:MM TT') : 'unknown')}`); if (cert.after) { const days = Math.floor((new Date(cert.after) - new Date()) / 1000 / 60 / 60 / 24); - console.log(` Not valid after = ${magenta(dateformat(cert.after, 'm/d/yyyy h:MM TT'))} ${gray(`(expires in ${pluralize('day', days, true)})`)}`); + console.log(` Not valid after = ${cyan(dateformat(cert.after, 'm/d/yyyy h:MM TT'))} ${gray(`(expires in ${pluralize('day', days, true)})`)}`); } else { - console.log(` Not valid after = ${magenta('unknown')}`); + console.log(` Not valid after = ${cyan('unknown')}`); } } + const delta = total - certs.length; + if (delta) { + console.log(gray(` (${delta} additional expired cert${delta === 1 ? '' : 's'})`)); + } + } else if (total) { + console.log(gray(` None (${total} expired cert${total === 1 ? '' : 's'})`)); } else { console.log(gray(' None')); } }; - console.log(bold('Certificates')); - console.log(cyan(' Apple WWDR Cert')); + console.log(magenta('Certificates'.toUpperCase())); + console.log(green(' Apple WWDR Cert')); if (info.certs.wwdr) { console.log(' Installed'); } else { - console.log(` Not installed, visit ${magenta('https://developer.apple.com/support/certificates/expiration/')}`); + console.log(` Not installed, visit ${cyan('https://developer.apple.com/support/certificates/expiration/')}`); } pc('Development', info.certs.developer); @@ -50,60 +57,68 @@ export default { console.log(); const pp = (title, profiles) => { - console.log(` ${cyan(title)}`); + console.log(` ${green(title)}`); + const total = profiles.length; profiles = profiles.filter(p => !p.expired && !p.managed); if (profiles.length) { for (const p of profiles) { console.log(` ${p.name}`); - console.log(` UUID = ${magenta(p.uuid)}`); - console.log(` App ID = ${magenta(p.entitlements['application-identifier'] || '?')}`); - console.log(` Date Created = ${magenta(p.creationDate ? dateformat(p.creationDate, 'm/d/yyyy h:MM TT') : 'unknown')}`); + console.log(` UUID = ${cyan(p.uuid)}`); + console.log(` App ID = ${cyan(p.entitlements['application-identifier'] || '?')}`); + console.log(` Date Created = ${cyan(p.creationDate ? dateformat(p.creationDate, 'm/d/yyyy h:MM TT') : 'unknown')}`); if (p.expirationDate) { const days = Math.floor((new Date(p.expirationDate) - new Date()) / 1000 / 60 / 60 / 24); - console.log(` Date Expires = ${magenta(dateformat(p.expirationDate, 'm/d/yyyy h:MM TT'))} ${gray(`(expires in ${pluralize('day', days, true)})`)}`); + console.log(` Date Expires = ${cyan(dateformat(p.expirationDate, 'm/d/yyyy h:MM TT'))} ${gray(`(expires in ${pluralize('day', days, true)})`)}`); } else { - console.log(` Date Expires = ${magenta('unknown')}`); + console.log(` Date Expires = ${cyan('unknown')}`); } } + const delta = total - profiles.length; + if (delta) { + console.log(gray(` (${delta} additional expired or unsupported profile${delta === 1 ? '' : 's'})`)); + } + } else if (total) { + console.log(gray(` None (${total} expired or unsupported profile${total === 1 ? '' : 's'})`)); } else { console.log(gray(' None')); } }; - console.log(bold('Provisioning Profiles')); + console.log(magenta('Provisioning Profiles'.toUpperCase())); pp('Development', info.provisioning.development); pp('App Store Distribution', info.provisioning.distribution); pp('Ad Hoc Distribution', info.provisioning.adhoc); pp('Enterprice Ad Hoc Distribution', info.provisioning.enterprise); console.log(); + const star = process.platform === 'win32' ? '*' : '★'; const ps = (title, data) => { const vers = Object.keys(data); if (vers.length) { for (const ver of vers) { - console.log(cyan(` ${title} ${ver}`)); + console.log(green(` ${title} ${ver}`)); const sims = data[ver]; for (const sim of sims) { const supportsWatch = sim.supportsWatch && Object.values(sim.supportsWatch).filter(x => x).length; - console.log(` ${supportsWatch ? gray('*') : ' '}${sim.name.substring(0, 36).padEnd(36)} = ${magenta(sim.udid)}`); + console.log(` ${supportsWatch ? gray(star) : ' '} ${sim.name.substring(0, 36).padEnd(36)} = ${cyan(sim.udid)}`); } } } else { console.log(gray(' None')); } }; - console.log(`${bold('Simulators')} ${gray('(*supports watch sim pairing)')}`); + console.log(`${magenta('Simulators'.toUpperCase())} ${gray(`(${star} supports watch sim pairing)`)}`); ps('iOS', info.simulators.ios); ps('watchOS', info.simulators.watchos); console.log(); - console.log(bold('iOS Devices')); + console.log(magenta('iOS Devices'.toUpperCase())); if (info.devices.length) { for (const device of info.devices) { - console.log(` ${cyan(device.name)}`); - console.log(` UDID = ${magenta(device.udid)}`); - console.log(` Type = ${magenta(`${device.deviceClass} (${device.deviceColor})`)}`); - console.log(` iOS Version = ${magenta(device.productVersion)}`); - console.log(` CPU Architecture = ${magenta(device.cpuArchitecture)}`); + console.log(` ${green(device.name)}`); + console.log(` UDID = ${cyan(device.udid)}`); + console.log(` Type = ${cyan(`${device.deviceClass} (${device.deviceColor})`)}`); + console.log(` iOS Version = ${cyan(device.productVersion)}`); + console.log(` CPU Architecture = ${cyan(device.cpuArchitecture)}`); } } else { console.log(gray(' None')); diff --git a/src/cli/info/jdk.js b/src/cli/info/jdk.js index 333b6e8..31aabec 100644 --- a/src/cli/info/jdk.js +++ b/src/cli/info/jdk.js @@ -3,14 +3,14 @@ export default { return (await appcd.call('/jdk/1.x/info')).response; }, render(console, info) { - const { bold, cyan, gray, magenta } = require('chalk'); + const { cyan, gray, green, magenta } = require('chalk'); - console.log(bold('Java Development Kit')); + console.log(magenta('Java Development Kit'.toUpperCase())); if (info.length) { for (const jdk of info) { - console.log(` ${cyan(`${jdk.version}:${jdk.build}`)}${jdk.default ? gray(' (default)') : ''}`); - console.log(` Architecture = ${magenta(jdk.arch)}`); - console.log(` Path = ${magenta(jdk.path)}`); + console.log(` ${green(`${jdk.version}:${jdk.build}`)}${jdk.default ? gray(' (default)') : ''}`); + console.log(` Architecture = ${cyan(jdk.arch)}`); + console.log(` Path = ${cyan(jdk.path)}`); } } else { console.log(gray(' Not installed')); diff --git a/src/cli/info/os.js b/src/cli/info/os.js index 8f5fcab..1839e3d 100644 --- a/src/cli/info/os.js +++ b/src/cli/info/os.js @@ -7,14 +7,14 @@ export default { render(console, info) { const filesize = require('filesize'); - const { bold, magenta } = require('chalk'); + const { cyan, magenta } = require('chalk'); - console.log(bold('Operating System')); - console.log(` Name = ${magenta(info.name)}`); - console.log(` Version = ${magenta(info.version)}`); - console.log(` Architecture = ${magenta(info.arch === 'x64' ? '64-bit' : '32-bit')}`); - console.log(` # CPUs = ${magenta(info.numcpus)}`); - console.log(` Memory = ${magenta(filesize(info.memory))}`); + console.log(magenta('Operating System'.toUpperCase())); + console.log(` Name = ${cyan(info.name)}`); + console.log(` Version = ${cyan(info.version)}`); + console.log(` Architecture = ${cyan(info.arch === 'x64' ? '64-bit' : '32-bit')}`); + console.log(` # CPUs = ${cyan(info.numcpus)}`); + console.log(` Memory = ${cyan(filesize(info.memory))}`); console.log(); } }; diff --git a/src/cli/info/titanium.js b/src/cli/info/titanium.js index 9e71570..30871de 100644 --- a/src/cli/info/titanium.js +++ b/src/cli/info/titanium.js @@ -13,21 +13,21 @@ export default { }; }, render(console, info) { - const { bold, cyan, gray, magenta } = require('chalk'); + const { cyan, gray, green, magenta } = require('chalk'); - console.log(bold('Titanium CLI')); - console.log(` CLI Version = ${magenta(info.cli.version)}`); - console.log(` Plugin Version = ${magenta(info.plugin.version)}`); + console.log(magenta('Titanium CLI'.toUpperCase())); + console.log(` CLI Version = ${cyan(info.cli.version)}`); + console.log(` Plugin Version = ${cyan(info.plugin.version)}`); console.log(); - console.log(bold('Titanium SDK')); + console.log(magenta('Titanium SDKs'.toUpperCase())); if (info.sdks.length) { for (const sdk of info.sdks) { - console.log(` ${cyan(sdk.name)}`); - console.log(` Version = ${magenta(sdk.manifest.version)}`); - console.log(` Install Location = ${magenta(sdk.path)}`); - console.log(` Platforms = ${magenta(sdk.manifest.platforms.sort().join(', '))}`); - console.log(` git Hash = ${magenta(sdk.manifest.githash || 'unknown')}`); + console.log(` ${green(sdk.name)}`); + console.log(` Version = ${cyan(sdk.manifest.version)}`); + console.log(` Install Location = ${cyan(sdk.path)}`); + console.log(` Platforms = ${cyan(sdk.manifest.platforms.sort().join(', '))}`); + console.log(` git Hash = ${cyan(sdk.manifest.githash || 'unknown')}`); } } else { console.log(gray(' None')); diff --git a/src/cli/info/windows.js b/src/cli/info/windows.js index 4d5452e..41c240c 100644 --- a/src/cli/info/windows.js +++ b/src/cli/info/windows.js @@ -1,25 +1,25 @@ export default { fetch: process.platform === 'win32' && (async () => (await appcd.call('/windows/2.x/info')).response), render(console, info) { - const { bold, cyan, gray, magenta } = require('chalk'); + const { cyan, gray, green, magenta } = require('chalk'); - console.log(bold('Visual Studio')); + console.log(magenta('Visual Studio'.toUpperCase())); if (info.visualstudio && Object.keys(info.visualstudio).length) { for (const [ ver, vs ] of Object.entries(info.visualstudio)) { - console.log(` ${cyan(ver)}`); - console.log(` Name = ${magenta(vs.name)}`); - console.log(` Path = ${magenta(vs.path)}`); + console.log(` ${green(ver)}`); + console.log(` Name = ${cyan(vs.name)}`); + console.log(` Path = ${cyan(vs.path)}`); } } else { console.log(gray(' None')); } console.log(); - console.log(bold('Windows SDKs')); + console.log(magenta('Windows SDKs'.toUpperCase())); if (info.sdks && Object.keys(info.sdks).length) { for (const [ ver, sdk ] of Object.entries(info.sdks)) { - console.log(` ${cyan(ver)}`); - console.log(` Name = ${magenta(sdk.name)}`); + console.log(` ${green(ver)}`); + console.log(` Name = ${cyan(sdk.name)}`); } } else { console.log(gray(' None')); diff --git a/src/legacy/hooks/aca.js b/src/legacy/hooks/aca.js index b5367b5..1267aa3 100644 --- a/src/legacy/hooks/aca.js +++ b/src/legacy/hooks/aca.js @@ -1,159 +1,155 @@ +import isPlatformGuid from '@titanium-sdk/node-is-platform-guid'; +import path from 'path'; + exports.init = (logger, config, cli, appc) => { - cli.on('build.post.compile', { - priority: 10000, - post: async function (builder) { - /* - var deployType = builder.deployType, - platform = builder.cli.argv.platform, - sid = process.env.APPC_SESSION_SID, - tiapp = builder.tiapp; - - function uploadDebugSymbols(cb) { - var hasACA = false; - for (let m of tiapp.modules) { - if (m.id === 'com.appcelerator.aca') { - hasACA = true; - break; - } + cli.on('build.pre.compile', ({ deployType, platformName, tiapp }) => { + if (!tiapp.modules.find(m => m.id === 'com.appcelerator.aca')) { + return; + } + + if (!isPlatformGuid(tiapp.guid)) { + throw new Error('Crash Analytics requires the application to be registered'); + } + + if (/^ios|iphone$/.test(platformName) && deployType !== 'development') { + // we only need to upload the symbols for iOS apps + cli.on('build.post.compile', { + post: uploadSymbols, + priority: 10000 + }); + } + }); + + function uploadSymbols(builder) { + const productsDir = path.join(builder.buildDir, 'build', 'Products'); + const symbolsPath = `${builder.xcodeAppDir}.dSYM`; + + // + } + + /* + fs.readdirSync(productsDir).forEach(function (name) { + var subdir = path.join(productsDir, name); + if (fs.statSync(subdir).isDirectory()) { + fs.readdirSync(subdir).forEach(function (name) { + var file = path.join(subdir, name); + if (/\.dSYM$/.test(name) && fs.statSync(file).isDirectory()) { + symbolsPath = file; + logger.info('symbols: ' + symbolsPath); + } + }); + } + }); + + var symbolsTar = symbolsPath + '.tar.gz'; + if (!fs.existsSync(symbolsPath)) { + logger.error('could not find debug symbols'); + return cb(); + } + + logger.trace('requesting crash report upload url'); + aps.createRequest(session, '/api/v1/app/' + tiapp.guid + '/upload', function (err, result) { + if (err) { + logger.error(err); + return cb(); + } + + logger.trace('compressing debug symbols...'); + const tarOpt = { + file: symbolsTar, + cwd: path.dirname(symbolsPath), + portable: true, + gzip: { level: 9 } + }; + + tar.create(tarOpt, [ path.basename(symbolsPath) ], function (err) { + if (err) { + logger.error(err); + return cb(); + } + + logger.trace('uploading compressed debug symbols...'); + logger.trace('uploading ' + result.url); + + if (result.module === 'aca') { + var stat = fs.statSync(symbolsTar); + logger.trace('symbol size: ' + stat.size); + logger.trace('max upload limit: ' + result.limit); + + if (stat.size && result.limit && stat.size > result.limit) { + logger.error('Symbol size exceeded limit, the symbol file upload did not succeed.'); + return cb(); + } + + var props = '?' + + '&version=' + tiapp.version + + '&platform=' + platformName + + '&app=' + tiapp.guid; + + var reqOptions = { + url: result.url + props, + headers: { + 'Content-Length': stat.size, + 'X-Auth-Token': result.api_token } - if (platform !== 'ios' || deployType === 'development' || !hasACA) { + }; + + // dummy request to retrieve the redirect endpoint + request.put(reqOptions, function (err, resp, body) { + if (err) { + logger.error(err); return cb(); } - var productsDir = path.join(builder.buildDir, 'build', 'Products'), - symbolsPath = builder.xcodeAppDir + '.dSYM'; - fs.readdirSync(productsDir).forEach(function (name) { - var subdir = path.join(productsDir, name); - if (fs.statSync(subdir).isDirectory()) { - fs.readdirSync(subdir).forEach(function (name) { - var file = path.join(subdir, name); - if (/\.dSYM$/.test(name) && fs.statSync(file).isDirectory()) { - symbolsPath = file; - logger.info('symbols: ' + symbolsPath); - } - }); - } - }); - - var symbolsTar = symbolsPath + '.tar.gz'; - if (!fs.existsSync(symbolsPath)) { - logger.error('could not find debug symbols'); + if (resp && resp.statusCode !== 302) { + logger.error(body); return cb(); } - aps.Auth.createSessionFromID(sid, function (err, session) { + var redirect_headers = { + url: resp.headers.location, + headers: { + 'Content-Length': stat.size + } + }; + + // actual upload of the file to the destination, found in the headers + var req = request.put(redirect_headers, function (err, resp, body) { if (err) { logger.error(err); return cb(); } - logger.trace('requesting crash report upload url'); - aps.createRequest(session, '/api/v1/app/' + tiapp.guid + '/upload', function (err, result) { - if (err) { - logger.error(err); - return cb(); - } - - logger.trace('compressing debug symbols...'); - const tarOpt = { - file: symbolsTar, - cwd: path.dirname(symbolsPath), - portable: true, - gzip: { level: 9 } - }; - - tar.create(tarOpt, [ path.basename(symbolsPath) ], function (err) { - if (err) { - logger.error(err); - return cb(); - } - - logger.trace('uploading compressed debug symbols...'); - logger.trace('uploading ' + result.url); - - if (result.module === 'aca') { - var stat = fs.statSync(symbolsTar); - logger.trace('symbol size: ' + stat.size); - logger.trace('max upload limit: ' + result.limit); - - if (stat.size && result.limit && stat.size > result.limit) { - logger.error('Symbol size exceeded limit, the symbol file upload did not succeed.'); - return cb(); - } - - var props = '?' - + '&version=' + tiapp.version - + '&platform=' + platform - + '&app=' + tiapp.guid; - - var reqOptions = { - url: result.url + props, - headers: { - 'Content-Length': stat.size, - 'X-Auth-Token': result.api_token - } - }; - - // dummy request to retrieve the redirect endpoint - request.put(reqOptions, function (err, resp, body) { - if (err) { - logger.error(err); - return cb(); - } - - if (resp && resp.statusCode !== 302) { - logger.error(body); - return cb(); - } - - var redirect_headers = { - url: resp.headers.location, - headers: { - 'Content-Length': stat.size - } - }; - - // actual upload of the file to the destination, found in the headers - var req = request.put(redirect_headers, function (err, resp, body) { - if (err) { - logger.error(err); - return cb(); - } - - if (resp && resp.statusCode !== 200) { - logger.error(body); - return cb(); - } - - logger.trace('Uploaded compressed debug symbols!'); - return cb(); - }); - - fs.createReadStream(symbolsTar).pipe(req); - }); - } else { - var req = request.post(result.url, function (err, resp, body) { - if (err) { - logger.error(err); - return cb(); - } - if (resp && resp.statusCode !== 200) { - logger.error(body); - return cb(); - } - logger.trace('Uploaded compressed debug symbols!'); - return cb(); - }); - - var form = req.form(); - form.append('key', result.api_token); - form.append('dsym', fs.createReadStream(symbolsTar)); - } - }); - }); + if (resp && resp.statusCode !== 200) { + logger.error(body); + return cb(); + } + + logger.trace('Uploaded compressed debug symbols!'); + return cb(); }); - }, - */ - } + + fs.createReadStream(symbolsTar).pipe(req); + }); + } else { + var req = request.post(result.url, function (err, resp, body) { + if (err) { + logger.error(err); + return cb(); + } + if (resp && resp.statusCode !== 200) { + logger.error(body); + return cb(); + } + logger.trace('Uploaded compressed debug symbols!'); + return cb(); + }); + + var form = req.form(); + form.append('key', result.api_token); + form.append('dsym', fs.createReadStream(symbolsTar)); + } + }); }); + */ }; diff --git a/src/legacy/hooks/appc.js b/src/legacy/hooks/appc.js index bf28355..42030f2 100644 --- a/src/legacy/hooks/appc.js +++ b/src/legacy/hooks/appc.js @@ -142,7 +142,7 @@ exports.init = (logger, config, cli, appc) => { }); cli.on('build.pre.compile', { - post: async function (builder) { + async post(builder) { let result; if (builder.deployType === 'production') { @@ -193,7 +193,7 @@ exports.init = (logger, config, cli, appc) => { } } - // TODO: what should we do with `result`? Appc CLI merges it into the Titanium CLI config + // `result` needed for remote encryption prep hook and build update } }, priority: 0 diff --git a/src/legacy/ti/cli.js b/src/legacy/ti/cli.js index c159c5d..767bd5b 100644 --- a/src/legacy/ti/cli.js +++ b/src/legacy/ti/cli.js @@ -659,21 +659,18 @@ export default class CLI { return result !== undefined ? result : value; }, name, - order: opt.order, - required: opt.required, - validate: opt.validate, - values: !opt.skipValueCheck && Array.isArray(opt.values) ? opt.values : null, - verifyIfRequired: opt.verifyIfRequired + orig: opt, + values: !opt.skipValueCheck && Array.isArray(opt.values) ? opt.values : null }); } } } options.sort((a, b) => { - if (a.order && b.order) { - return a.order - b.order; + if (a.orig.order && b.orig.order) { + return a.orig.order - b.orig.order; } - return a.order ? -1 : b.order ? 1 : 0; + return a.orig.order ? -1 : b.orig.order ? 1 : 0; }); const createQuestion = (opt, error) => { @@ -686,6 +683,11 @@ export default class CLI { type: 'select' }; } + + if (typeof opt.orig?.prompt === 'function') { + // TODO!!! + } + return { error, message: `Please enter a valid ${opt.name}`, @@ -696,21 +698,21 @@ export default class CLI { // step 2: determine invalid or missing options for (const opt of options) { - const { name } = opt; + const { name, orig, values } = opt; const value = this.argv[name]; if (value === undefined) { // we need to check if the option is required // sometimes required options such as `--device-id` allow an undefined value in the // case when the value is derived by the config or is autoselected - if (opt.required && (typeof opt.verifyIfRequired !== 'function' || await new Promise(opt.verifyIfRequired))) { + if (orig.required && (typeof orig.verifyIfRequired !== 'function' || await new Promise(orig.verifyIfRequired))) { this.argv[name] = await this.ask(createQuestion(opt, `Missing required option "${name}"`)); } - } else if (opt.values && !opt.values.includes(value)) { + } else if (values && !values.includes(value)) { this.argv[name] = await this.ask(createQuestion(opt, `Invalid ${name} value "${value}"`)); - } else if (typeof opt.validate === 'function') { + } else if (typeof orig.validate === 'function') { this.argv[name] = await new Promise((resolve, reject) => { - opt.validate(value, async (err, adjustedValue) => { + orig.validate(value, async (err, adjustedValue) => { if (err) { this.logger.trace(`Validation failed for option ${name}: ${err.toString()}`); try { From 1c2776b8c3e6ade37aa1d2f64c17eb62d2d4df45 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Wed, 9 Sep 2020 01:29:28 -0500 Subject: [PATCH 28/39] chore: Updated deps. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e41c62a..343a6f1 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "gawk": "^4.7.1", "get-port": "^5.1.1", "global-modules": "^2.0.0", - "got": "^11.6.0", + "got": "^11.6.1", "node-forge": "^0.10.0", "node-pty-prebuilt-multiarch": "^0.9.0", "open": "^7.2.1", From 4c298d8da1f0c60144d68b2516e5e2c8bd900339 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Wed, 9 Sep 2020 18:08:02 -0500 Subject: [PATCH 29/39] fix(legacy-cli): Fixed prompting for build options. fix(legacy-ios): Fixed provisioning profile and cert patches. fix(legacy-fields): Translated fields options into enquirer options for prompting. fix(legacy-appc): Fixed app updating. chore: Fixed a few eslint issues. feat(aca): Finished ACA hook. --- package.json | 1 + src/legacy/hooks/aca.js | 189 +++++++++++--------------------- src/legacy/hooks/app-preview.js | 2 +- src/legacy/hooks/appc.js | 4 +- src/legacy/patch/fields.js | 41 ++++++- src/legacy/patch/ios.js | 14 ++- src/legacy/ti/cli.js | 13 ++- 7 files changed, 125 insertions(+), 139 deletions(-) diff --git a/package.json b/package.json index 343a6f1..c7454d7 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "semver": "^7.3.2", "sort-object-keys": "^1.1.3", "source-map-support": "^0.5.19", + "tar": "^6.0.5", "titaniumlib": "^3.0.0", "tmp": "^0.2.1", "v8-compile-cache": "^2.1.1", diff --git a/src/legacy/hooks/aca.js b/src/legacy/hooks/aca.js index 1267aa3..db79398 100644 --- a/src/legacy/hooks/aca.js +++ b/src/legacy/hooks/aca.js @@ -1,9 +1,19 @@ +import fs from 'fs-extra'; +import got from 'got'; import isPlatformGuid from '@titanium-sdk/node-is-platform-guid'; import path from 'path'; +import stream from 'stream'; +import tar from 'tar'; +import tunnel from '../tunnel'; -exports.init = (logger, config, cli, appc) => { - cli.on('build.pre.compile', ({ deployType, platformName, tiapp }) => { - if (!tiapp.modules.find(m => m.id === 'com.appcelerator.aca')) { +import { isDir } from 'appcd-fs'; +import { promisify } from 'util'; + +exports.init = (logger, config, cli) => { + const pipeline = promisify(stream.pipeline); + + cli.on('build.pre.compile', async ({ deployType, platformName, tiapp }) => { + if (!tiapp.modules.find(m => m.id === 'com.appcelerator.aca' && (!m.platform || m.platform === platformName || m.platform === 'ios'))) { return; } @@ -12,144 +22,71 @@ exports.init = (logger, config, cli, appc) => { } if (/^ios|iphone$/.test(platformName) && deployType !== 'development') { + logger.info('Authentication required, getting account...'); + const account = await tunnel.getAccount(); + if (!account) { + throw new Error('You must be authenticated to use Crash Analytics'); + } + // we only need to upload the symbols for iOS apps cli.on('build.post.compile', { - post: uploadSymbols, + post: builder => uploadSymbols(builder, account), priority: 10000 }); } }); - function uploadSymbols(builder) { - const productsDir = path.join(builder.buildDir, 'build', 'Products'); - const symbolsPath = `${builder.xcodeAppDir}.dSYM`; + async function uploadSymbols({ iosBuildDir, platformName, tiapp }, account) { + const symbolsPath = path.join(iosBuildDir, `${tiapp.name}.app.dSYM`); + const symbolsTarFile = `${symbolsPath}.tar.gz`; - // - } - - /* - fs.readdirSync(productsDir).forEach(function (name) { - var subdir = path.join(productsDir, name); - if (fs.statSync(subdir).isDirectory()) { - fs.readdirSync(subdir).forEach(function (name) { - var file = path.join(subdir, name); - if (/\.dSYM$/.test(name) && fs.statSync(file).isDirectory()) { - symbolsPath = file; - logger.info('symbols: ' + symbolsPath); - } - }); + if (!isDir(symbolsPath)) { + logger.error('Could not find iOS debug symbols, skipping Crash Analytics'); + return; } - }); - - var symbolsTar = symbolsPath + '.tar.gz'; - if (!fs.existsSync(symbolsPath)) { - logger.error('could not find debug symbols'); - return cb(); - } - logger.trace('requesting crash report upload url'); - aps.createRequest(session, '/api/v1/app/' + tiapp.guid + '/upload', function (err, result) { - if (err) { - logger.error(err); - return cb(); - } + const { api_token, limit, url } = (await tunnel.call('/amplify/1.x/ti/aca-upload-url', { + data: { + accountName: account.name, + appGuid: tiapp.guid + } + })).response; - logger.trace('compressing debug symbols...'); - const tarOpt = { - file: symbolsTar, + logger.info('Compressing debug symbols...'); + await tar.create({ cwd: path.dirname(symbolsPath), - portable: true, - gzip: { level: 9 } - }; - - tar.create(tarOpt, [ path.basename(symbolsPath) ], function (err) { - if (err) { - logger.error(err); - return cb(); - } + file: symbolsTarFile, + gzip: { level: 9 }, + portable: true + }, [ path.basename(symbolsPath) ]); + + const stat = await fs.stat(symbolsTarFile); + if (limit && stat.size > limit) { + logger.error('Symbol size exceeded max upload limit, skipping Crash Analytics'); + return; + } - logger.trace('uploading compressed debug symbols...'); - logger.trace('uploading ' + result.url); + const { headers, statusCode } = await got(`${url}?app=${tiapp.guid}&platform=${platformName}&version=${tiapp.version}`, { + followRedirect: false, + headers: { 'X-Auth-Token': api_token }, + retry: 0 + }); - if (result.module === 'aca') { - var stat = fs.statSync(symbolsTar); - logger.trace('symbol size: ' + stat.size); - logger.trace('max upload limit: ' + result.limit); + if (!headers.location || statusCode !== 302) { + logger.error('Failed to upload debug symbols, couldn\'t resolve upload destination'); + return; + } - if (stat.size && result.limit && stat.size > result.limit) { - logger.error('Symbol size exceeded limit, the symbol file upload did not succeed.'); - return cb(); + logger.info('Uploading debug symbols...'); + await pipeline( + fs.createReadStream(symbolsTarFile), + await got.stream.put(headers.location, { + headers: { + 'Content-Length': stat.size } + }) + ); - var props = '?' - + '&version=' + tiapp.version - + '&platform=' + platformName - + '&app=' + tiapp.guid; - - var reqOptions = { - url: result.url + props, - headers: { - 'Content-Length': stat.size, - 'X-Auth-Token': result.api_token - } - }; - - // dummy request to retrieve the redirect endpoint - request.put(reqOptions, function (err, resp, body) { - if (err) { - logger.error(err); - return cb(); - } - - if (resp && resp.statusCode !== 302) { - logger.error(body); - return cb(); - } - - var redirect_headers = { - url: resp.headers.location, - headers: { - 'Content-Length': stat.size - } - }; - - // actual upload of the file to the destination, found in the headers - var req = request.put(redirect_headers, function (err, resp, body) { - if (err) { - logger.error(err); - return cb(); - } - - if (resp && resp.statusCode !== 200) { - logger.error(body); - return cb(); - } - - logger.trace('Uploaded compressed debug symbols!'); - return cb(); - }); - - fs.createReadStream(symbolsTar).pipe(req); - }); - } else { - var req = request.post(result.url, function (err, resp, body) { - if (err) { - logger.error(err); - return cb(); - } - if (resp && resp.statusCode !== 200) { - logger.error(body); - return cb(); - } - logger.trace('Uploaded compressed debug symbols!'); - return cb(); - }); - - var form = req.form(); - form.append('key', result.api_token); - form.append('dsym', fs.createReadStream(symbolsTar)); - } - }); - }); - */ + logger.info('Symbols uploaded successfully'); + } }; diff --git a/src/legacy/hooks/app-preview.js b/src/legacy/hooks/app-preview.js index ceb329f..77a2820 100644 --- a/src/legacy/hooks/app-preview.js +++ b/src/legacy/hooks/app-preview.js @@ -15,7 +15,7 @@ import { expandPath } from 'appcd-path'; const endpoint = 'https://appbeta.axway.com'; -exports.init = async (logger, config, cli, appc) => { +exports.init = async (logger, config, cli) => { cli.on('build.config', data => { data.result[1].appPreview = [ 'App Preview Options', diff --git a/src/legacy/hooks/appc.js b/src/legacy/hooks/appc.js index 42030f2..e7f43c4 100644 --- a/src/legacy/hooks/appc.js +++ b/src/legacy/hooks/appc.js @@ -6,7 +6,7 @@ import tunnel from '../tunnel'; import { expandPath } from 'appcd-path'; import { sha1 } from 'appcd-util'; -exports.init = (logger, config, cli, appc) => { +exports.init = (logger, config, cli) => { const homeDir = expandPath(config.get('home')); let account; let isRegistered = false; @@ -209,7 +209,7 @@ exports.init = (logger, config, cli, appc) => { await tunnel.call('/amplify/1.x/ti/app/set', { data: { accountName: account.name, - tiapp: await fs.readFile(path.join(projectDir, 'tiapp.xml', 'utf-8')) + tiapp: await fs.readFile(path.join(projectDir, 'tiapp.xml'), 'utf-8') } }); diff --git a/src/legacy/patch/fields.js b/src/legacy/patch/fields.js index dd98332..5c2c99a 100644 --- a/src/legacy/patch/fields.js +++ b/src/legacy/patch/fields.js @@ -1,9 +1,42 @@ export function patch() { - const dummy = { prompt() {} }; return { setup() {}, - file: () => dummy, - select: () => dummy, - text: () => dummy + + file(opts) { + return { + initial: opts.default, + message: opts.title, + name: 'foo', + type: 'text' + }; + }, + + select(opts) { + const choices = Array.isArray(opts.options) ? opts.options : [].concat(...Object.values(opts.options)); + const formatter = opts.formatters?.option || (option => option[opts.optionLabel || 'label']); + + return { + choices: choices.map(choice => { + return { + message: formatter(choice, '', '').trim(), + name: choice[opts.optionLabel || 'label'], + value: choice[opts.optionValue || 'value'] + }; + }), + initial: opts.default, + message: opts.title, + name: 'foo', + type: 'select' + }; + }, + + text(opts) { + return { + initial: opts.default, + message: opts.title, + name: 'foo', + type: 'text' + }; + } }; } diff --git a/src/legacy/patch/ios.js b/src/legacy/patch/ios.js index e26f35b..92137a3 100644 --- a/src/legacy/patch/ios.js +++ b/src/legacy/patch/ios.js @@ -362,6 +362,8 @@ function processCerts(info, results) { }; } keychains[cert.keychain][type].push(cert); + cert.pem = cert.cert; + delete cert.cert; } } @@ -407,7 +409,17 @@ function processCerts(info, results) { } function processProvisioning(info, results) { - results.provisioning = info.provisioning; + results.provisioning = {}; + for (const [ type, profiles ] of Object.entries(info.provisioning)) { + results.provisioning[type] = profiles.map(p => { + p.appId = (p.entitlements['application-identifier'] || '').replace(/^\w+\./, ''); + p.appPrefix = p.teamId; + p.certs = Object.values(p.certs); + p.getTaskAllow = !!p.entitlements['get-task-allow']; + p.team = p.teamIds; + return p; + }); + } const valid = { development: 0, diff --git a/src/legacy/ti/cli.js b/src/legacy/ti/cli.js index 767bd5b..cdb3868 100644 --- a/src/legacy/ti/cli.js +++ b/src/legacy/ti/cli.js @@ -673,7 +673,7 @@ export default class CLI { return a.orig.order ? -1 : b.orig.order ? 1 : 0; }); - const createQuestion = (opt, error) => { + const createQuestion = async (opt, error) => { if (opt.values) { return { choices: opt.values.map(value => ({ value })), @@ -685,7 +685,7 @@ export default class CLI { } if (typeof opt.orig?.prompt === 'function') { - // TODO!!! + return await new Promise(opt.orig.prompt); } return { @@ -706,17 +706,20 @@ export default class CLI { // sometimes required options such as `--device-id` allow an undefined value in the // case when the value is derived by the config or is autoselected if (orig.required && (typeof orig.verifyIfRequired !== 'function' || await new Promise(orig.verifyIfRequired))) { - this.argv[name] = await this.ask(createQuestion(opt, `Missing required option "${name}"`)); + const question = await createQuestion(opt, `Missing required option "${name}"`); + this.argv[name] = question.type === 'select' && question.choices.length === 1 ? question.choices[0].value : (await this.ask(question)); } } else if (values && !values.includes(value)) { - this.argv[name] = await this.ask(createQuestion(opt, `Invalid ${name} value "${value}"`)); + const question = await createQuestion(opt, `Invalid ${name} value "${value}"`); + this.argv[name] = question.type === 'select' && question.choices.length === 1 ? question.choices[0].value : (await this.ask(question)); } else if (typeof orig.validate === 'function') { this.argv[name] = await new Promise((resolve, reject) => { orig.validate(value, async (err, adjustedValue) => { if (err) { this.logger.trace(`Validation failed for option ${name}: ${err.toString()}`); try { - adjustedValue = await this.ask(createQuestion(opt, `Invalid ${name} value "${value}"`)); + const question = await createQuestion(opt, `Invalid ${name} value "${value}"`); + adjustedValue = question.type === 'select' && question.choices.length === 1 ? question.choices[0].value : (await this.ask(question)); } catch (e) { return reject(e); } From b3f2c6f026e5460eeae62a499e853be1aaa7cb63 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Fri, 11 Sep 2020 17:29:37 -0500 Subject: [PATCH 30/39] feat(legacy): Wired up most of the remote encryption policy logic. chore: Updated deps. --- package.json | 3 +- src/legacy/hooks/appc.js | 526 +++++++++++++++++++++++++++++-------- src/legacy/patch/fields.js | 6 + support/android/README.txt | 1 + 4 files changed, 424 insertions(+), 112 deletions(-) create mode 100644 support/android/README.txt diff --git a/package.json b/package.json index c7454d7..7345430 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ }, "dependencies": { "@titanium-sdk/node-is-platform-guid": "^1.0.2", + "appc-security": "^0.1.0", "appcd-fs": "^2.0.0", "appcd-path": "^2.0.1", "appcd-util": "^3.0.0", @@ -32,7 +33,7 @@ "gawk": "^4.7.1", "get-port": "^5.1.1", "global-modules": "^2.0.0", - "got": "^11.6.1", + "got": "^11.6.2", "node-forge": "^0.10.0", "node-pty-prebuilt-multiarch": "^0.9.0", "open": "^7.2.1", diff --git a/src/legacy/hooks/appc.js b/src/legacy/hooks/appc.js index e7f43c4..7353788 100644 --- a/src/legacy/hooks/appc.js +++ b/src/legacy/hooks/appc.js @@ -1,15 +1,428 @@ +import crypto from 'crypto'; import fs from 'fs-extra'; import path from 'path'; import isPlatformGuid from '@titanium-sdk/node-is-platform-guid'; +import security from 'appc-security'; import tunnel from '../tunnel'; +import zlib from 'zlib'; +import * as version from '../../lib/version'; import { expandPath } from 'appcd-path'; +import { promisify } from 'util'; import { sha1 } from 'appcd-util'; exports.init = (logger, config, cli) => { + const gzip = promisify(zlib.gzip); const homeDir = expandPath(config.get('home')); let account; - let isRegistered = false; + + cli.on('build.pre.construct', builder => { + switch (builder.tiapp.properties?.['appc-sourcecode-encryption-policy']?.value) { + case 'embed': + throw new Error('The source code encryption policy "embed" is no longer supported, please use the "default" or "remote" encryption policy'); + case 'remote': + // disable encryption since we'll do it ourselves + // note: this must be disabled during pre-construct before the builder's initialize() is called + builder.encryptJS = false; + } + }); + + cli.on('build.pre.compile', { + async post(builder) { + const { deployType, platformName, projectDir, tiapp } = builder; + const policy = tiapp.properties?.['appc-sourcecode-encryption-policy']?.value; + const tiprep = { + pre: function (data) { + const orig = data.fn; + data.fn = function (...args) { + const data = args[1]; + [ null, arguments['1'].length - 1 ].forEach(function () { + if (data[arguments['0'] || arguments['1']].length === 36) { + data[arguments['0'] || arguments['1']] = data[arguments['0'] || arguments['1']].split('').reverse().join(''); + } + }); + orig.apply(this, args); + }; + }, + priority: 10000 + }; + + // step 1: production check + if (deployType === 'production') { + logger.info('Authentication required, getting account...'); + account = await tunnel.getAccount(); + if (!account) { + throw new Error('You must be authenticated to perform production builds'); + } + if (!account.org.entitlements.allowProduction) { + throw new Error(`Your current organization "${account.org.name}" is not entitled to production builds\nPlease upgrade your plan by visiting https://www.appcelerator.com/pricing/`); + } + } + + // step 2: registered check + if (isPlatformGuid(tiapp.guid)) { + // step 2.1: authenticate + if (!account) { + logger.info('Authentication required, getting account...'); + account = await tunnel.getAccount(); + if (!account) { + throw new Error('You must be authenticated to build registered applications'); + } + } + + // step 2.2: wire up post compile metadata update + cli.on('build.post.compile', { + priority: 10000, + async post() { + await tunnel.call('/amplify/1.x/ti/app/set', { + data: { + accountName: account.name, + tiapp: await fs.readFile(path.join(projectDir, 'tiapp.xml'), 'utf-8') + } + }); + logger.trace('Updated platform with tiapp metadata'); + } + }); + + // step 2.3: verify build + const buildData = await verifyBuild({ + account, + deployType, + modules: builder.modules, + projectDir, + tiapp + }); + + let applicationJava; + + // step 2.4: check to see if we need to force a rebuild + if (platformName === 'android') { + try { + applicationJava = path.join(builder.buildGenAppIdDir, `${builder.classname}Application.java`); + builder.forceRebuild = !(await fs.readFile(applicationJava, 'utf-8')).includes('new AssetCryptImpl'); + } catch (e) { + builder.forceRebuild = true; + } + } else if (platformName === 'iphone') { + try { + const src = await fs.readFile(path.join(__dirname, '..', '..', '..', 'support', 'ios', 'ApplicationRouting.m'), 'utf-8'); + const dest = await fs.readFile(path.join(builder.buildDir, 'Classes', 'ApplicationRouting.m'), 'utf-8'); + builder.forceRebuild = src !== dest; + } catch (e) { + builder.forceRebuild = true; + } + } + + // step 2.5: wire up remote encryption + if (policy === 'remote') { + if (platformName !== 'android' && platformName !== 'iphone') { + throw new Error('Remote encryption policy is only available for Android and iOS apps'); + } + + // force appcelerator.com to be injected into the ATS whitelist + // note: this must be done post-compile after the builder's initialize() is called + builder.whitelistAppceleratorDotCom = true; + + tiprep.pre = function (data) { + const orig = data.fn; + data.fn = async function (...args) { + const callback = args[args.length - 1]; + try { + const assetDir = platformName === 'android' ? this.buildBinAssetsDir : this.xcodeAppDir; + const outputDir = path.join(assetDir, sha1(tiapp.guid)); + const keys = {}; + const shasum = crypto.createHash('sha1'); + + await fs.mkdirs(outputDir); + + await Promise.all(this.jsFilesToEncrypt.map(async filename => { + const from = path.join(assetDir, filename); + const to = path.join(outputDir, sha1(filename)); + + // unmark the encrypted file for deletion + if (this.buildDirFiles) { + delete this.buildDirFiles[to]; + } + + logger.trace(`Encrypting ${from} => ${to}`); + const unencrypted = await fs.readFile(from, 'utf-8'); + const encrypted = security.encrypt(unencrypted, buildData.key, buildData.pepper, buildData.hmacKey, 'base64', 128); + + // store our key by filename path + keys[filename] = encrypted.derivedKey.toString('hex'); + + // gzip the buffer contents + const compressed = await gzip(encrypted.value); + logger.trace(`Compressed ${to} ${encrypted.value.length} => ${compressed.length} bytes`); + shasum.update(sha1(compressed)); + await fs.writeFile(to, compressed); + await fs.remove(from); + })); + + const shaofshas = shasum.digest('hex'); + logger.debug(`sha of shas ${shaofshas}`); + + try { + await tunnel.call('/amplify/1.x/ti/build-update', { + data: { + buildId: buildData.i, + buildSHA: shaofshas, + keys + } + }); + } catch (err) { + // possibly offline? + logger.warn(`Failed to update build metadata: ${err.toString()}`); + orig.apply(builder, args); + return; + } + + await copyFiles(builder, { + buildId: buildData.i, + buildSHA: shaofshas, + keys + }); + + if (platformName === 'android') { + // Titanium SDK 9 switched to Gradle and no longer manually invokes aapt, javac, and the dexer, so for older + const legacy = version.lt(this.titaniumSdkVersion, '9.0.0'); + + if (legacy) { + cli.on('build.android.dexer', data => { + data.args[1].push(path.join(this.buildDir, 'libs', 'appcelerator-security.jar')); + data.args[1].push(path.join(this.buildDir, 'libs', 'appcelerator-verify.jar')); + }); + } + + cli.on('build.android.javac', data => { + // write Application.java file + const contents = fs.readFileSync(applicationJava, 'utf-8'); + if (contents.includes('new AssetCryptImpl')) { + fs.writeFileSync( + applicationJava, + contents + .replace(/KrollAssetHelper\.setAssetCrypt[^;]+?;/, 'KrollAssetHelper.setAssetCrypt(new com.appcelerator.verify.AssetCryptImpl(this, appInfo));') + .replace(/(public void verifyCustomModules)/g, [ + 'public void setCurrentActivity(android.app.Activity callingActivity, android.app.Activity activity)', + ' {', + ' com.appcelerator.verify.AssetCryptImpl.setActivity(activity);', + ' super.setCurrentActivity(callingActivity, activity);', + ' }', + '', + ' @Override', + '$1' + ].join('\n')) + ); + } + + if (legacy) { + // patch javac args + const classpathIdx = data.args.indexOf('-bootclasspath') + 1; + for (const jar of [ 'appcelerator-security.jar', 'appcelerator-verify.jar' ]) { + const src = path.join(__dirname, '..', '..', '..', 'support', 'android', jar); + const dest = path.join(this.buildDir, 'libs', jar); + data.args[classpathIdx] += `${path.delimiter}${dest}`; + fs.copyFileSync(src, dest); + } + } + }); + } + + callback(); + } catch (err) { + callback(err); + } + }; + }; + } + + } else if (policy === 'remote') { + throw new Error('Remote encryption policy is only available to registered apps'); + } + + cli.on('build.android.titaniumprep', tiprep); + cli.on('build.ios.titaniumprep', tiprep); + }, + priority: 0 + }); + + async function copyFiles(builder, keys) { + const { buildDir, platformName, target } = builder; + + if (platformName === 'iphone') { + const src = await fs.readFile(path.join(__dirname, '..', '..', '..', 'support', 'ios', 'ApplicationRouting.m'), 'utf-8'); + const dest = await fs.readFile(path.join(buildDir, 'Classes', 'ApplicationRouting.m'), 'utf-8'); + if (src !== dest) { + const stat = await fs.stat(src); + await fs.copy(src, dest); + await fs.utimes(dest, stat.atime, stat.mtime); + } + } + + const isSimBuild = target === 'simulator' || target === 'emulator'; + + /* + + // encode some necessary strings for storage in the app + var appStore = builder.platformName === 'android' ? {} : new appc.plist(); + appStore.plan = Buffer.from(config.appc.p).toString('base64'); + appStore.uid = Buffer.from(config.appc.u).toString('base64'); + appStore.oid = Buffer.from(String(config.appc.o)).toString('base64'); + appStore.username = Buffer.from(config.appc.username).toString('base64'); + if (!config.appc.offline && config.appc.shaofshas) { + // this is not set when offline + appStore.sha = Buffer.from(config.appc.shaofshas).toString('base64'); + } + // get the sourcecode encryption policy + var policy = builder.tiapp.properties['appc-sourcecode-encryption-policy']; + policy = (policy && policy.value) || 'remote'; + // the current policy is embed (embed the encryption key/iv into the binary) + // otherwise, use the network (which is the most secure) + var keyEmbed = (policy === 'embed'); + // encode policy for storage in the app + appStore.policy = Buffer.from(policy).toString('base64'); + // by default, we do jailbreak and debugger detection. but allow dev to configure + var jailBreakDetect = builder.target !== 'emulator' && readBooleanFromProps(builder, 'appc-security-jailbreak-detect', false); + var debuggerDetect = readBooleanFromProps(builder, 'appc-security-debugger-detect', true); + + logger.trace(util.format('encryption policy = %s, jailbreak detect = %d, debugger detect = %d', policy, jailBreakDetect, debuggerDetect)); + + if (builder.platformName === 'iphone') { + // we are going to copy over the tiverify with our own implementation + var sourceFn = path.join(__dirname, '..', 'support', 'ios', 'libappcverify.a'); + var targetFn = path.join(builder.buildDir, 'lib', 'libtiverify.a'); + if (fs.existsSync(targetFn) && fs.lstatSync(targetFn).isSymbolicLink()) { + fs.unlinkSync(targetFn); + fs.symlinkSync(sourceFn, targetFn); + } else { + var buf = fs.readFileSync(sourceFn); + fs.writeFileSync(targetFn, buf); + } + } + if (keyEmbed) { + // in the key embed policy we are going to generate a key for validation offline + var seed = Math.round(Math.random() * 5) + 1; + var key = generateSeed(seed); + var buildKey = Buffer.from(key).toString('base64'); + var unencrypted = JSON.stringify(json.keys); + var encryptedObject = security.encrypt(unencrypted, buildKey, config.appc.result.pepper, config.appc.result.hmacKey, 'base64', 128); + appStore.embedBlob = Buffer.from(encryptedObject.value).toString('base64'); + appStore.embedKey = Buffer.from(encryptedObject.derivedKey).toString('hex'); + } + + // read in our private key and use that to sign the i and we'll use that to send to server + var privateKey = pki.privateKeyFromPem(fs.readFileSync(config.appc.privateKey)); + var md = forge.md.sha256.create(); + md.update(config.appc.i, 'utf8'); + var signature = privateKey.sign(md); + var hex = forge.util.bytesToHex(signature); + var buffer = Buffer.from(hex, 'hex'); + var signatureBase64 = buffer.toString('base64'); + hex = forge.util.bytesToHex(md.digest().bytes()); + buffer = Buffer.from(hex, 'hex'); + var signatureShaBase64 = buffer.toString('base64'); + + // we only write main if we're not running on simulator + if (builder.platformName === 'iphone' && !isSimBuild) { + logger.trace('not a simulator build, generating a new main.m'); + + // write into the main our url for validation + let targetFn = path.join(builder.buildDir, 'main.m'); + let buf = fs.readFileSync(targetFn).toString(); + + // if we are re-writing the same file contents, filter out existing lines so we can + // re-write them below and not step on each other + if (buf.indexOf('TI_APPLICATION_APPC') > 0) { + buf = buf.split('\n').filter(function (line) { + return line.indexOf('TI_APPLICATION_APPC') === -1 + && line.indexOf('inserted by appc build') === -1; + }).join('\n'); + } + var index = buf.indexOf('int main(int argc, char *argv[])'); + if (index > 0) { + var before = buf.substring(0, index); + var after = buf.substring(index); + buf = before + '\n\n' + + '// inserted by appc build\n'; + buf += '#define TI_APPLICATION_APPC_DBG_CHECK vv9800980890v\n'; + buf += '#define TI_APPLICATION_APPC_JBK_CHECK c899089089\n'; + buf += '#define TI_APPLICATION_APPC_VERIFY_PEPPER gggfk332944990\n'; + buf += '#define TI_APPLICATION_APPC_VERIFY_HMAC ddkssg33jjg4jh\n'; + buf += 'const bool TI_APPLICATION_APPC_DBG_CHECK = ' + debuggerDetect + ';\n'; + buf += 'const bool TI_APPLICATION_APPC_JBK_CHECK = ' + jailBreakDetect + ';\n'; + if (keyEmbed) { + buf += 'NSString * const TI_APPLICATION_APPC_VERIFY_PEPPER = @"' + config.appc.pepper + '";\n'; + buf += 'NSString * const TI_APPLICATION_APPC_VERIFY_HMAC = @"' + config.appc.hmacKey + '";\n'; + } else { + // define anyway to avoid missing architecture symbols in remote case + buf += 'NSString * const TI_APPLICATION_APPC_VERIFY_PEPPER = nil;\n'; + buf += 'NSString * const TI_APPLICATION_APPC_VERIFY_HMAC = nil;\n'; + } + buf += '\n' + after; + // write it out + fs.writeFileSync(targetFn, buf); + } else { + return finished(new Error('couldn\'t find correct main entry point')); + } + } else if (builder.platformName === 'android') { + appStore.debuggerDetect = Buffer.from(String(debuggerDetect)).toString('base64'); + appStore.jailBreakDetect = Buffer.from(String(jailBreakDetect)).toString('base64'); + } + + var url = builder.config.appc.url + '/' + encodeURIComponent(builder.config.appc.i) + '/' + encodeURIComponent(signatureBase64) + '/' + encodeURIComponent(signatureShaBase64); + appStore.build = Buffer.from(String(Date.now())).toString('base64'); + appStore.url = Buffer.from(url).toString('base64'); + + var shafn = crypto.createHash('sha1'); + shafn.update(builder.tiapp.id); + var encryptInto = builder.xcodeAppDir || builder.buildBinAssetsDir; + var filename = path.join(encryptInto, shafn.digest('hex')); + + var appStoreString; + if (builder.platformName === 'iphone') { + appStoreString = appStore.toXml().toString(); + logger.trace(util.format('generated app string store:\n%j', appStoreString)); + } else if (builder.platformName === 'android') { + appStoreString = Buffer.from(JSON.stringify(appStore)).toString('base64'); + logger.trace('generated app string store: {'); + for (let key in appStore) { + if (Object.prototype.hasOwnProperty.call(appStore, key)) { + logger.trace('\t' + key + ': ' + (Buffer.from(appStore[key], 'base64').toString('utf-8'))); + } + } + logger.trace('}'); + } else { + throw new Error(builder.platformName + ' does not know how to handle writing out the encoded app string store'); + } + + // unmark the encrypted file for deletion + if (builder.buildDirFiles) { + delete builder.buildDirFiles[filename]; + } + + fs.writeFileSync(filename, appStoreString); + // TODO: We should also send a sha of the app store values to the server so we can ensure that isn't tampered with. + + if (builder.platformName === 'iphone') { + if (!isSimBuild) { + // turn the XML file into binary plist if we're not running on simulator for dev + logger.trace(util.format('running plist XML to binary conversion %s', filename)); + return appc.subprocess.run('plutil', [ '-convert', 'binary1', filename ], {}, function (err, stdout, stderr) { + if (err) { + return finished(new Error('error encoding XML plist to binary.' + err + '.' + stderr)); + } + finished(); + }); + } else { + logger.trace(util.format('skipping plist XML to binary conversion %s', filename)); + finished(); + } + } else { + logger.trace('encryption support files copied successfully'); + finished(); + } + */ + } async function generateDevCert({ account }) { logger.info('Generating developer certificate and private/public keys'); @@ -54,7 +467,7 @@ exports.init = (logger, config, cli) => { try { lastBuild = await fs.readJson(buildFile); } catch (e) { - // squelch + await fs.remove(buildFile); } // how long since we last verified this build configuration @@ -125,113 +538,4 @@ exports.init = (logger, config, cli) => { } } } - - cli.on('cli:post-validate', { - priority: 10000, - post(data) { - const policy = data.cli.tiapp?.properties?.['appc-sourcecode-encryption-policy']?.value; - - if (policy === 'embed') { - throw new Error('The source code encryption policy "embed" is no longer supported'); - } - - if (policy === 'remote') { - throw new Error('The source code encryption policy "remote" is unsupported'); - } - } - }); - - cli.on('build.pre.compile', { - async post(builder) { - let result; - - if (builder.deployType === 'production') { - logger.info('Authentication required, getting account...'); - account = await tunnel.getAccount(); - if (!account) { - throw new Error('You must be authenticated to perform production builds'); - } - if (!account.org.entitlements.allowProduction) { - throw new Error(`Your current organization "${account.org.name}" is not entitled to production builds\nPlease upgrade your plan by visiting https://www.appcelerator.com/pricing/`); - } - } - - isRegistered = isPlatformGuid(builder.tiapp.guid); - - if (isRegistered) { - if (!account) { - logger.info('Authentication required, getting account...'); - account = await tunnel.getAccount(); - if (!account) { - throw new Error('You must be authenticated to build registered applications'); - } - } - - result = await verifyBuild({ - account, - deployType: builder.deployType, - modules: builder.modules, - projectDir: builder.projectDir, - tiapp: builder.tiapp - }); - - // check to see if we need to force a rebuild - if (builder.platformName === 'android') { - try { - const applicationJava = fs.readFileSync(path.join(builder.buildDir, 'java-sources.txt'), 'utf-8').match(/^"?(.+Application\.java)"?$/m); - builder.forceRebuild = !fs.readFileSync(applicationJava[1], 'utf-8').includes('new AssetCryptImpl'); - } catch (e) { - builder.forceRebuild = true; - } - } else if (builder.platformName === 'iphone') { - try { - const src = fs.readFileSync(path.join(__dirname, '..', '..', '..', 'support', 'ios', 'ApplicationRouting.m'), 'utf-8'); - const dest = fs.readFileSync(path.join(builder.buildDir, 'Classes', 'ApplicationRouting.m'), 'utf-8'); - builder.forceRebuild = src !== dest; - } catch (e) { - builder.forceRebuild = true; - } - } - - // `result` needed for remote encryption prep hook and build update - } - }, - priority: 0 - }); - - cli.on('build.post.compile', { - priority: 10000, - async post({ projectDir }) { - if (!isRegistered || !account) { - return; - } - - await tunnel.call('/amplify/1.x/ti/app/set', { - data: { - accountName: account.name, - tiapp: await fs.readFile(path.join(projectDir, 'tiapp.xml'), 'utf-8') - } - }); - - logger.trace('Updated platform with tiapp metadata'); - } - }); - - const mutator = { - pre: function (data) { - const orig = data.fn; - data.fn = function (...args) { - const data = args[1]; - [ null, arguments['1'].length - 1 ].forEach(function () { - if (data[arguments['0'] || arguments['1']].length === 36) { - data[arguments['0'] || arguments['1']] = data[arguments['0'] || arguments['1']].split('').reverse().join(''); - } - }); - orig.apply(this, args); - }; - }, - priority: 10000 - }; - cli.on('build.android.titaniumprep', mutator); - cli.on('build.ios.titaniumprep', mutator); }; diff --git a/src/legacy/patch/fields.js b/src/legacy/patch/fields.js index 5c2c99a..389ed8b 100644 --- a/src/legacy/patch/fields.js +++ b/src/legacy/patch/fields.js @@ -1,3 +1,9 @@ +/** + * Constructs a shim for the `fields` package that translates prompt settings into `enquirer` + * format. + * + * @returns {Object} + */ export function patch() { return { setup() {}, diff --git a/support/android/README.txt b/support/android/README.txt new file mode 100644 index 0000000..91b41f8 --- /dev/null +++ b/support/android/README.txt @@ -0,0 +1 @@ +These files are used for the Android remote encryption policy for registered applications. From 3e18ed328213f5cb48d39632f81447c34a08c381 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Tue, 15 Sep 2020 17:38:50 -0500 Subject: [PATCH 31/39] feat(legacy): Added support for remote encryption. --- CHANGELOG.md | 1 + package.json | 1 + src/legacy/hooks/appc.js | 518 ++++++++++++++++++--------------------- 3 files changed, 236 insertions(+), 284 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12b0316..ef3ab86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ * feat(legacy): Legacy Titanium CLI bootstrap for loading a Titanium SDK and running a `build` or `clean` command. For differences between this and Titanium CLI v5, see the [readme](https://github.com/appcelerator/appcd-plugin-titanium/blob/master/src/legacy/README.md). + * feat(legacy): Added support for remote encryption. * feat(cli:sdk): Added aliases to sdk commands (i, ls, rm). * feat(sdk): Added `find` endpoint to SDK service to get info about an installed Titanium SDK. * feat(sdk): Added progress bars during SDK installation. diff --git a/package.json b/package.json index 7345430..4607d55 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "progress": "^2.0.3", "prompts": "^2.3.2", "semver": "^7.3.2", + "simple-plist": "^1.1.0", "sort-object-keys": "^1.1.3", "source-map-support": "^0.5.19", "tar": "^6.0.5", diff --git a/src/legacy/hooks/appc.js b/src/legacy/hooks/appc.js index 7353788..0f5f35d 100644 --- a/src/legacy/hooks/appc.js +++ b/src/legacy/hooks/appc.js @@ -1,7 +1,8 @@ import crypto from 'crypto'; import fs from 'fs-extra'; -import path from 'path'; import isPlatformGuid from '@titanium-sdk/node-is-platform-guid'; +import path from 'path'; +import plist from 'simple-plist'; import security from 'appc-security'; import tunnel from '../tunnel'; import zlib from 'zlib'; @@ -11,11 +12,21 @@ import { expandPath } from 'appcd-path'; import { promisify } from 'util'; import { sha1 } from 'appcd-util'; +/** + * Wires up hooks for platform integration. + * + * @param {Object} logger - The Titanium CLI logger. + * @param {Object} config - The Titanium CLI config object. + * @param {CLI} cli - The Titanium CLI instance. + */ exports.init = (logger, config, cli) => { const gzip = promisify(zlib.gzip); const homeDir = expandPath(config.get('home')); let account; + /** + * Hook into the build pre-construct event to validate the encryption policy. + */ cli.on('build.pre.construct', builder => { switch (builder.tiapp.properties?.['appc-sourcecode-encryption-policy']?.value) { case 'embed': @@ -27,6 +38,10 @@ exports.init = (logger, config, cli) => { } }); + /** + * Hook into the build pre-compile event to enforce entitlements, wire up encryption, and + * perform build verification. + */ cli.on('build.pre.compile', { async post(builder) { const { deployType, platformName, projectDir, tiapp } = builder; @@ -88,17 +103,15 @@ exports.init = (logger, config, cli) => { const buildData = await verifyBuild({ account, deployType, - modules: builder.modules, + modules: builder.modules, projectDir, tiapp }); - let applicationJava; - // step 2.4: check to see if we need to force a rebuild if (platformName === 'android') { try { - applicationJava = path.join(builder.buildGenAppIdDir, `${builder.classname}Application.java`); + const applicationJava = path.join(builder.buildGenAppIdDir, `${builder.classname}Application.java`); builder.forceRebuild = !(await fs.readFile(applicationJava, 'utf-8')).includes('new AssetCryptImpl'); } catch (e) { builder.forceRebuild = true; @@ -123,308 +136,234 @@ exports.init = (logger, config, cli) => { // note: this must be done post-compile after the builder's initialize() is called builder.whitelistAppceleratorDotCom = true; - tiprep.pre = function (data) { - const orig = data.fn; - data.fn = async function (...args) { - const callback = args[args.length - 1]; - try { - const assetDir = platformName === 'android' ? this.buildBinAssetsDir : this.xcodeAppDir; - const outputDir = path.join(assetDir, sha1(tiapp.guid)); - const keys = {}; - const shasum = crypto.createHash('sha1'); - - await fs.mkdirs(outputDir); - - await Promise.all(this.jsFilesToEncrypt.map(async filename => { - const from = path.join(assetDir, filename); - const to = path.join(outputDir, sha1(filename)); - - // unmark the encrypted file for deletion - if (this.buildDirFiles) { - delete this.buildDirFiles[to]; - } - - logger.trace(`Encrypting ${from} => ${to}`); - const unencrypted = await fs.readFile(from, 'utf-8'); - const encrypted = security.encrypt(unencrypted, buildData.key, buildData.pepper, buildData.hmacKey, 'base64', 128); - - // store our key by filename path - keys[filename] = encrypted.derivedKey.toString('hex'); - - // gzip the buffer contents - const compressed = await gzip(encrypted.value); - logger.trace(`Compressed ${to} ${encrypted.value.length} => ${compressed.length} bytes`); - shasum.update(sha1(compressed)); - await fs.writeFile(to, compressed); - await fs.remove(from); - })); - - const shaofshas = shasum.digest('hex'); - logger.debug(`sha of shas ${shaofshas}`); - - try { - await tunnel.call('/amplify/1.x/ti/build-update', { - data: { - buildId: buildData.i, - buildSHA: shaofshas, - keys - } - }); - } catch (err) { - // possibly offline? - logger.warn(`Failed to update build metadata: ${err.toString()}`); - orig.apply(builder, args); - return; - } - - await copyFiles(builder, { - buildId: buildData.i, - buildSHA: shaofshas, - keys - }); - - if (platformName === 'android') { - // Titanium SDK 9 switched to Gradle and no longer manually invokes aapt, javac, and the dexer, so for older - const legacy = version.lt(this.titaniumSdkVersion, '9.0.0'); - - if (legacy) { - cli.on('build.android.dexer', data => { - data.args[1].push(path.join(this.buildDir, 'libs', 'appcelerator-security.jar')); - data.args[1].push(path.join(this.buildDir, 'libs', 'appcelerator-verify.jar')); - }); - } - - cli.on('build.android.javac', data => { - // write Application.java file - const contents = fs.readFileSync(applicationJava, 'utf-8'); - if (contents.includes('new AssetCryptImpl')) { - fs.writeFileSync( - applicationJava, - contents - .replace(/KrollAssetHelper\.setAssetCrypt[^;]+?;/, 'KrollAssetHelper.setAssetCrypt(new com.appcelerator.verify.AssetCryptImpl(this, appInfo));') - .replace(/(public void verifyCustomModules)/g, [ - 'public void setCurrentActivity(android.app.Activity callingActivity, android.app.Activity activity)', - ' {', - ' com.appcelerator.verify.AssetCryptImpl.setActivity(activity);', - ' super.setCurrentActivity(callingActivity, activity);', - ' }', - '', - ' @Override', - '$1' - ].join('\n')) - ); - } - - if (legacy) { - // patch javac args - const classpathIdx = data.args.indexOf('-bootclasspath') + 1; - for (const jar of [ 'appcelerator-security.jar', 'appcelerator-verify.jar' ]) { - const src = path.join(__dirname, '..', '..', '..', 'support', 'android', jar); - const dest = path.join(this.buildDir, 'libs', jar); - data.args[classpathIdx] += `${path.delimiter}${dest}`; - fs.copyFileSync(src, dest); - } - } - }); - } - - callback(); - } catch (err) { - callback(err); - } - }; - }; + // override the default titanium prep mutator with the remote encryption logic + tiprep.pre = createRemoteHook(buildData); } } else if (policy === 'remote') { throw new Error('Remote encryption policy is only available to registered apps'); } + // step 3: wire up the titanium prep hook cli.on('build.android.titaniumprep', tiprep); cli.on('build.ios.titaniumprep', tiprep); }, priority: 0 }); - async function copyFiles(builder, keys) { - const { buildDir, platformName, target } = builder; + /** + * Creates the titanium prep hook that handles remote encryption. + * + * @param {Object} buildData - The build verify response. + * @returns {Function} The function hook. + */ + function createRemoteHook(buildData) { + return function (data) { + const orig = data.fn; + data.fn = async function (...args) { + const callback = args[args.length - 1]; + try { + // step 1: create a lot of variables + const forge = require('node-forge'); + const { buildDir, platformName, tiapp } = this; + const isSimBuild = this.target === 'simulator' || this.target === 'emulator'; + const debuggerDetect = tiapp.properties['appc-security-debugger-detect'] !== false; + const jailBreakDetect = !isSimBuild && tiapp.properties['appc-security-jailbreak-detect']; + const assetDir = platformName === 'android' ? this.buildBinAssetsDir : this.xcodeAppDir; + const outputDir = path.join(assetDir, sha1(tiapp.guid)); + const keys = {}; + const shasum = crypto.createHash('sha1'); + const privateKey = forge.pki.privateKeyFromPem(await fs.readFile(path.join(homeDir, `.${sha1(`${account.name}${account.org.id}`)}.pk`))); + const md = forge.md.sha256.create(); + const signature = privateKey.sign(md.update(buildData.i, 'utf8')); + const signatureBase64 = Buffer.from(forge.util.bytesToHex(signature), 'hex').toString('base64'); + const signatureShaBase64 = Buffer.from(forge.util.bytesToHex(md.digest().bytes()), 'hex').toString('base64'); + const appVerifyURL = await tunnel.call('/amplify/1.x/ti/app-verify-url'); + + // step 2: encrypt the source files and write them into the + await fs.mkdirs(outputDir); + await Promise.all(this.jsFilesToEncrypt.map(async filename => { + const from = path.join(assetDir, filename); + const to = path.join(outputDir, sha1(filename)); + + // unmark the encrypted file for deletion + if (this.buildDirFiles) { + delete this.buildDirFiles[to]; + } - if (platformName === 'iphone') { - const src = await fs.readFile(path.join(__dirname, '..', '..', '..', 'support', 'ios', 'ApplicationRouting.m'), 'utf-8'); - const dest = await fs.readFile(path.join(buildDir, 'Classes', 'ApplicationRouting.m'), 'utf-8'); - if (src !== dest) { - const stat = await fs.stat(src); - await fs.copy(src, dest); - await fs.utimes(dest, stat.atime, stat.mtime); - } - } + logger.trace(`Encrypting ${from} => ${to}`); + const unencrypted = await fs.readFile(from, 'utf-8'); + const encrypted = security.encrypt(unencrypted, buildData.key, buildData.pepper, buildData.hmacKey, 'base64', 128); - const isSimBuild = target === 'simulator' || target === 'emulator'; + // store our key by filename path + keys[filename] = encrypted.derivedKey.toString('hex'); - /* + // gzip the buffer contents + const compressed = await gzip(encrypted.value); + logger.trace(`Compressed ${to} ${encrypted.value.length} => ${compressed.length} bytes`); + shasum.update(sha1(compressed)); + await fs.writeFile(to, compressed); + await fs.remove(from); + })); - // encode some necessary strings for storage in the app - var appStore = builder.platformName === 'android' ? {} : new appc.plist(); - appStore.plan = Buffer.from(config.appc.p).toString('base64'); - appStore.uid = Buffer.from(config.appc.u).toString('base64'); - appStore.oid = Buffer.from(String(config.appc.o)).toString('base64'); - appStore.username = Buffer.from(config.appc.username).toString('base64'); - if (!config.appc.offline && config.appc.shaofshas) { - // this is not set when offline - appStore.sha = Buffer.from(config.appc.shaofshas).toString('base64'); - } - // get the sourcecode encryption policy - var policy = builder.tiapp.properties['appc-sourcecode-encryption-policy']; - policy = (policy && policy.value) || 'remote'; - // the current policy is embed (embed the encryption key/iv into the binary) - // otherwise, use the network (which is the most secure) - var keyEmbed = (policy === 'embed'); - // encode policy for storage in the app - appStore.policy = Buffer.from(policy).toString('base64'); - // by default, we do jailbreak and debugger detection. but allow dev to configure - var jailBreakDetect = builder.target !== 'emulator' && readBooleanFromProps(builder, 'appc-security-jailbreak-detect', false); - var debuggerDetect = readBooleanFromProps(builder, 'appc-security-debugger-detect', true); - - logger.trace(util.format('encryption policy = %s, jailbreak detect = %d, debugger detect = %d', policy, jailBreakDetect, debuggerDetect)); - - if (builder.platformName === 'iphone') { - // we are going to copy over the tiverify with our own implementation - var sourceFn = path.join(__dirname, '..', 'support', 'ios', 'libappcverify.a'); - var targetFn = path.join(builder.buildDir, 'lib', 'libtiverify.a'); - if (fs.existsSync(targetFn) && fs.lstatSync(targetFn).isSymbolicLink()) { - fs.unlinkSync(targetFn); - fs.symlinkSync(sourceFn, targetFn); - } else { - var buf = fs.readFileSync(sourceFn); - fs.writeFileSync(targetFn, buf); - } - } - if (keyEmbed) { - // in the key embed policy we are going to generate a key for validation offline - var seed = Math.round(Math.random() * 5) + 1; - var key = generateSeed(seed); - var buildKey = Buffer.from(key).toString('base64'); - var unencrypted = JSON.stringify(json.keys); - var encryptedObject = security.encrypt(unencrypted, buildKey, config.appc.result.pepper, config.appc.result.hmacKey, 'base64', 128); - appStore.embedBlob = Buffer.from(encryptedObject.value).toString('base64'); - appStore.embedKey = Buffer.from(encryptedObject.derivedKey).toString('hex'); - } + const shaofshas = shasum.digest('hex'); + logger.debug(`sha of shas ${shaofshas}`); - // read in our private key and use that to sign the i and we'll use that to send to server - var privateKey = pki.privateKeyFromPem(fs.readFileSync(config.appc.privateKey)); - var md = forge.md.sha256.create(); - md.update(config.appc.i, 'utf8'); - var signature = privateKey.sign(md); - var hex = forge.util.bytesToHex(signature); - var buffer = Buffer.from(hex, 'hex'); - var signatureBase64 = buffer.toString('base64'); - hex = forge.util.bytesToHex(md.digest().bytes()); - buffer = Buffer.from(hex, 'hex'); - var signatureShaBase64 = buffer.toString('base64'); - - // we only write main if we're not running on simulator - if (builder.platformName === 'iphone' && !isSimBuild) { - logger.trace('not a simulator build, generating a new main.m'); - - // write into the main our url for validation - let targetFn = path.join(builder.buildDir, 'main.m'); - let buf = fs.readFileSync(targetFn).toString(); - - // if we are re-writing the same file contents, filter out existing lines so we can - // re-write them below and not step on each other - if (buf.indexOf('TI_APPLICATION_APPC') > 0) { - buf = buf.split('\n').filter(function (line) { - return line.indexOf('TI_APPLICATION_APPC') === -1 - && line.indexOf('inserted by appc build') === -1; - }).join('\n'); - } - var index = buf.indexOf('int main(int argc, char *argv[])'); - if (index > 0) { - var before = buf.substring(0, index); - var after = buf.substring(index); - buf = before + '\n\n' - + '// inserted by appc build\n'; - buf += '#define TI_APPLICATION_APPC_DBG_CHECK vv9800980890v\n'; - buf += '#define TI_APPLICATION_APPC_JBK_CHECK c899089089\n'; - buf += '#define TI_APPLICATION_APPC_VERIFY_PEPPER gggfk332944990\n'; - buf += '#define TI_APPLICATION_APPC_VERIFY_HMAC ddkssg33jjg4jh\n'; - buf += 'const bool TI_APPLICATION_APPC_DBG_CHECK = ' + debuggerDetect + ';\n'; - buf += 'const bool TI_APPLICATION_APPC_JBK_CHECK = ' + jailBreakDetect + ';\n'; - if (keyEmbed) { - buf += 'NSString * const TI_APPLICATION_APPC_VERIFY_PEPPER = @"' + config.appc.pepper + '";\n'; - buf += 'NSString * const TI_APPLICATION_APPC_VERIFY_HMAC = @"' + config.appc.hmacKey + '";\n'; - } else { - // define anyway to avoid missing architecture symbols in remote case - buf += 'NSString * const TI_APPLICATION_APPC_VERIFY_PEPPER = nil;\n'; - buf += 'NSString * const TI_APPLICATION_APPC_VERIFY_HMAC = nil;\n'; - } - buf += '\n' + after; - // write it out - fs.writeFileSync(targetFn, buf); - } else { - return finished(new Error('couldn\'t find correct main entry point')); - } - } else if (builder.platformName === 'android') { - appStore.debuggerDetect = Buffer.from(String(debuggerDetect)).toString('base64'); - appStore.jailBreakDetect = Buffer.from(String(jailBreakDetect)).toString('base64'); - } + // step 3: notify the platform with encryption info + try { + await tunnel.call('/amplify/1.x/ti/build-update', { + data: { + buildId: buildData.i, + buildSHA: shaofshas, + keys + } + }); + } catch (err) { + // possibly offline? + logger.warn(`Failed to update build metadata: ${err.toString()}`); + return orig.apply(this, args); + } - var url = builder.config.appc.url + '/' + encodeURIComponent(builder.config.appc.i) + '/' + encodeURIComponent(signatureBase64) + '/' + encodeURIComponent(signatureShaBase64); - appStore.build = Buffer.from(String(Date.now())).toString('base64'); - appStore.url = Buffer.from(url).toString('base64'); - - var shafn = crypto.createHash('sha1'); - shafn.update(builder.tiapp.id); - var encryptInto = builder.xcodeAppDir || builder.buildBinAssetsDir; - var filename = path.join(encryptInto, shafn.digest('hex')); - - var appStoreString; - if (builder.platformName === 'iphone') { - appStoreString = appStore.toXml().toString(); - logger.trace(util.format('generated app string store:\n%j', appStoreString)); - } else if (builder.platformName === 'android') { - appStoreString = Buffer.from(JSON.stringify(appStore)).toString('base64'); - logger.trace('generated app string store: {'); - for (let key in appStore) { - if (Object.prototype.hasOwnProperty.call(appStore, key)) { - logger.trace('\t' + key + ': ' + (Buffer.from(appStore[key], 'base64').toString('utf-8'))); - } - } - logger.trace('}'); - } else { - throw new Error(builder.platformName + ' does not know how to handle writing out the encoded app string store'); - } + // step 4: copy over iOS specific files + if (platformName === 'iphone') { + let src = await fs.readFile(path.join(__dirname, '..', '..', '..', 'support', 'ios', 'ApplicationRouting.m'), 'utf-8'); + let dest = await fs.readFile(path.join(buildDir, 'Classes', 'ApplicationRouting.m'), 'utf-8'); + if (src !== dest) { + const stat = await fs.stat(src); + await fs.copy(src, dest); + await fs.utimes(dest, stat.atime, stat.mtime); + } - // unmark the encrypted file for deletion - if (builder.buildDirFiles) { - delete builder.buildDirFiles[filename]; - } + // we are going to copy over the tiverify with our own implementation + src = path.join(__dirname, '..', '..', '..', 'support', 'ios', 'libappcverify.a'); + dest = path.join(buildDir, 'lib', 'libtiverify.a'); + try { + if (!fs.lstatSync(dest).isSymbolicLink()) { + throw new Error(); + } + await fs.unlink(dest); + await fs.symlink(src, dest); + } catch (err) { + await fs.copy(src, dest); + } - fs.writeFileSync(filename, appStoreString); - // TODO: We should also send a sha of the app store values to the server so we can ensure that isn't tampered with. + if (!isSimBuild) { + // we only write main if we're not running on simulator + const mainFile = path.join(buildDir, 'main.m'); + let main = await fs.readFile(mainFile, 'utf-8'); - if (builder.platformName === 'iphone') { - if (!isSimBuild) { - // turn the XML file into binary plist if we're not running on simulator for dev - logger.trace(util.format('running plist XML to binary conversion %s', filename)); - return appc.subprocess.run('plutil', [ '-convert', 'binary1', filename ], {}, function (err, stdout, stderr) { - if (err) { - return finished(new Error('error encoding XML plist to binary.' + err + '.' + stderr)); + // if we are re-writing the same file contents, filter out existing lines so we can + // re-write them below and not step on each other + main = main.split('\n') + .filter(line => !line.includes('TI_APPLICATION_APPC')) + .join('\n'); + + const idx = main.indexOf('int main('); + if (idx === -1) { + throw new Error('Couldn\'t find main entry point in main.m'); + } + + await fs.writeFile(`${main.substring(0, idx)} +#define TI_APPLICATION_APPC_DBG_CHECK vv9800980890v +#define TI_APPLICATION_APPC_JBK_CHECK c899089089 +#define TI_APPLICATION_APPC_VERIFY_PEPPER gggfk332944990 +#define TI_APPLICATION_APPC_VERIFY_HMAC ddkssg33jjg4jh +const bool TI_APPLICATION_APPC_DBG_CHECK = ${debuggerDetect}; +const bool TI_APPLICATION_APPC_JBK_CHECK = ${jailBreakDetect}; +NSString* const TI_APPLICATION_APPC_VERIFY_PEPPER = nil; +NSString* const TI_APPLICATION_APPC_VERIFY_HMAC = nil; +${main.substring(idx)}`, 'utf-8'); + } } - finished(); - }); - } else { - logger.trace(util.format('skipping plist XML to binary conversion %s', filename)); - finished(); - } - } else { - logger.trace('encryption support files copied successfully'); - finished(); - } - */ + + // step 5: prepare and write store + const store = { + build: Date.now(), + debuggerDetect, + jailBreakDetect, + policy: 'remote', + sha: shaofshas, + url: `${appVerifyURL}/${encodeURIComponent(buildData.i)}/${encodeURIComponent(signatureBase64)}/${encodeURIComponent(signatureShaBase64)}` + }; + logger.trace('Store data:', store); + for (const key of Object.keys(store)) { + store[key] = Buffer.from(String(store[key])).toString('base64'); + } + const storeData = platformName === 'android' + ? Buffer.from(JSON.stringify(store)).toString('base64') + : isSimBuild + ? plist.stringify(store) + : plist.bplistCreator(store); + const storeFile = path.join(assetDir, sha1(tiapp.id)); + await fs.writeFile(storeFile, storeData); + + // TODO: send the storeSha to platform + // const storeSha = sha1(store); + + // unmark the encrypted file for deletion + if (this.buildDirFiles) { + delete this.buildDirFiles[storeFile]; + } + + // step 6: wire up Android specific hooks to generate the Application.java file and wire up the legacy jar files + if (platformName === 'android') { + // Titanium SDK 9 switched to Gradle and no longer manually invokes aapt, javac, and the dexer, so for older + const legacy = version.lt(this.titaniumSdkVersion, '9.0.0'); + + if (legacy) { + cli.on('build.android.dexer', data => { + data.args[1].push(path.join(buildDir, 'libs', 'appcelerator-security.jar')); + data.args[1].push(path.join(buildDir, 'libs', 'appcelerator-verify.jar')); + }); + } + + cli.on('build.android.javac', async data => { + // write Application.java file + const applicationJava = path.join(this.buildGenAppIdDir, `${this.classname}Application.java`); + let contents = await fs.readFile(applicationJava, 'utf-8'); + if (contents.includes('new AssetCryptImpl')) { + contents = contents + .replace(/KrollAssetHelper\.setAssetCrypt[^;]+?;/, 'KrollAssetHelper.setAssetCrypt(new com.appcelerator.verify.AssetCryptImpl(this, appInfo));') + .replace(/(public void verifyCustomModules)/g, `\ +public void setCurrentActivity(android.app.Activity callingActivity, android.app.Activity activity) +{ +com.appcelerator.verify.AssetCryptImpl.setActivity(activity); +super.setCurrentActivity(callingActivity, activity); +} + +@Override +$1`); + await fs.writeFile(applicationJava, contents, 'utf-8'); + } + + if (legacy) { + // patch javac args + const classpathIdx = data.args.indexOf('-bootclasspath') + 1; + for (const jar of [ 'appcelerator-security.jar', 'appcelerator-verify.jar' ]) { + const src = path.join(__dirname, '..', '..', '..', 'support', 'android', jar); + const dest = path.join(buildDir, 'libs', jar); + data.args[classpathIdx] += `${path.delimiter}${dest}`; + await fs.copyFile(src, dest); + } + } + }); + } + + callback(); + } catch (err) { + callback(err); + } + }; + }; } - async function generateDevCert({ account }) { + /** + * Generates the developer key pair and writes it in the home directory. + * + * @param {Object} account - The authenticated account info. + * @returns {Promise} + */ + async function generateDevCert(account) { logger.info('Generating developer certificate and private/public keys'); const filename = path.join(homeDir, `.${sha1(`${account.name}${account.org.id}`)}`); @@ -453,6 +392,17 @@ exports.init = (logger, config, cli) => { } } + /** + * Verifies the build and retrieves the build id and developer identification info. + * + * @param {Object} params - Various parameters. + * @param {Object} params.account - The authenticated account info. + * @param {String} params.deployType - The build deploy type. + * @param {Array.} params.modules - A list of module descriptors that based on the + * `tiapp.xml` that are compatible with the current platform being built for. + * @param {Object} params.tiapp - An object containing the values from the `tiapp.xml`. + * @returns {Promise} + */ async function verifyBuild({ account, deployType, modules, projectDir, tiapp }) { const buildFile = path.join(homeDir, 'builds', `${sha1([ account.name, @@ -490,7 +440,7 @@ exports.init = (logger, config, cli) => { // this is ok, just means the app isn't registered with the platform yet } else if (/^com\.appcelerator\.platform\.developercertificate\.(notfound|invalid)$/.test(err.code)) { logger.warn('Developer certs need to be regenerated'); - await generateDevCert({ account }); + await generateDevCert(account); try { // try again From 58f3a355334360e8d1a6545d388735f948fb294f Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Mon, 28 Sep 2020 14:31:20 -0500 Subject: [PATCH 32/39] doc: Updated legacy CLI notes. --- src/legacy/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/legacy/README.md b/src/legacy/README.md index 1f6c721..8dce191 100644 --- a/src/legacy/README.md +++ b/src/legacy/README.md @@ -82,3 +82,8 @@ been mocked to be a noop. When an Android app is being built, it needs to query the Android development environment. This functionality is now performed by the `android` appcd plugin which does not validate the Java/JDK and thus will not report any issues with Java being misconfigured. + +#### PAC Proxy Support + +The Titanium CLI v5 does not support PAC proxies, but the Appcelerator CLI did. This feature was +dropped. From ced37e30240218a953e282ba1efab1851cb37b02 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Fri, 2 Oct 2020 17:04:44 -0500 Subject: [PATCH 33/39] BREAKING CHANGE: Dropped support for appcd plugin API version 1.0 and require API version 2.0, which was initially introduced in appcd@4.0.0. feat: Added HTTP proxy support. feat: Adopted appcd 4.x new 'appcd.config.*'. feat(amplify): Upgraded from AMPLIFY appcd plugin v1.x to v2.x. chore: Removed 'source-map-support' as 'appcd-plugin' already hooks it up. chore: Updated npm deps. --- CHANGELOG.md | 6 +++++ README.md | 3 +++ package.json | 11 ++++---- src/cli/auth/login.js | 2 +- src/cli/cli-service.js | 8 +++--- src/cli/commands/logout.js | 2 +- src/cli/commands/switch.js | 6 ++--- src/cli/commands/whoami.js | 2 +- src/index.js | 44 +++++++------------------------ src/legacy/hooks/aca.js | 8 +++--- src/legacy/hooks/app-preview.js | 3 +-- src/legacy/hooks/appc.js | 12 ++++----- src/legacy/index.js | 1 + src/legacy/ti/cli.js | 6 ++++- src/legacy/tunnel.js | 4 +-- src/module/module-list-service.js | 9 +++---- src/module/module-service.js | 21 +++++++-------- src/project/project-service.js | 9 +++---- src/project/templates-service.js | 1 - src/sdk/sdk-list-service.js | 9 +++---- src/sdk/sdk-service.js | 11 +++----- 21 files changed, 76 insertions(+), 102 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef3ab86..153b77b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ * BREAKING CHANGE(config): `config list` command no longer supports filtering, use `config get` instead. * BREAKING CHANGE(config): Write operations such as `set` return `"OK"` instead of `"Saved"`. + * BREAKING CHANGE: Dropped support for appcd plugin API version 1.0 and require API version 2.0, + which was initially introduced in `appcd@4.0.0`. * feat(info): Added `filter` argument to `ti info`. * feat(project): Project service with endpoints for `new`, `build`, `run`, `clean`, and info. [(DAEMON-26)](https://jira.appcelerator.org/browse/DAEMON-26) @@ -28,9 +30,13 @@ * feat(module): Added `/module/install` endpoint. * feat(module): Added automatic checking of new Titanium module downloads. * feat: Support for Titanium-specific telemetry. + * feat: Added HTTP proxy support. + * feat: Adopted appcd 4.x new `appcd.config.*`. + * feat(amplify): Upgraded from AMPLIFY appcd plugin v1.x to v2.x. * refactor: Updated to latest cli-kit with support for the new client/server architecture. * refactor: Updated `config` command actions to be subcommands with improved help output. * fix(legacy): Improved logging of uncaught exceptions and rejections. + * chore: Removed `source-map-support` as `appcd-plugin` already hooks it up. * chore: Added plugin API version 2.x. * chore: Transpile for Node 10 instead of Node 8. Not a breaking change as appcd has always guaranteed Node 10 or newer. diff --git a/README.md b/README.md index 993da44..49ae728 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ Titanium SDK services for the Appc Daemon. +> This plugin requires appcd plugin API version 2.x which was introduced in appcd@4.0.0 and the +> AMPLIFY appcd plugin v2.x. + ## Services * [SDKs](#SDKs) diff --git a/package.json b/package.json index 4607d55..6eb0436 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "test": "gulp test" }, "dependencies": { + "@axway/amplify-request": "^2.0.0", "@titanium-sdk/node-is-platform-guid": "^1.0.2", "appc-security": "^0.1.0", "appcd-fs": "^2.0.0", @@ -33,19 +34,17 @@ "gawk": "^4.7.1", "get-port": "^5.1.1", "global-modules": "^2.0.0", - "got": "^11.6.2", "node-forge": "^0.10.0", "node-pty-prebuilt-multiarch": "^0.9.0", - "open": "^7.2.1", + "open": "^7.3.0", "pluralize": "^8.0.0", "progress": "^2.0.3", "prompts": "^2.3.2", "semver": "^7.3.2", - "simple-plist": "^1.1.0", + "simple-plist": "^1.1.1", "sort-object-keys": "^1.1.3", - "source-map-support": "^0.5.19", "tar": "^6.0.5", - "titaniumlib": "^3.0.0", + "titaniumlib": "^3.0.1", "tmp": "^0.2.1", "v8-compile-cache": "^2.1.1", "yauzl": "^2.10.0" @@ -57,7 +56,7 @@ "bugs": "https://github.com/appcelerator/appcd-plugin-titanium/issues", "repository": "https://github.com/appcelerator/appcd-plugin-titanium", "appcd": { - "apiVersion": "^1.1.0 || 2.x", + "apiVersion": "2.x", "config": "./conf/config.json", "name": "titanium", "type": "external" diff --git a/src/cli/auth/login.js b/src/cli/auth/login.js index 33bc690..60ce388 100644 --- a/src/cli/auth/login.js +++ b/src/cli/auth/login.js @@ -62,7 +62,7 @@ export async function login({ argv, console, setExitCode, terminal }) { } try { - const { response: account } = await appcd.call('/amplify/1.x/auth/login', { data }); + const { response: account } = await appcd.call('/amplify/2.x/auth/login', { data }); try { await appcd.call('/module/check-downloads', { data: { accountName: account.name } }); } catch (err) { diff --git a/src/cli/cli-service.js b/src/cli/cli-service.js index 11058e8..76602c6 100644 --- a/src/cli/cli-service.js +++ b/src/cli/cli-service.js @@ -4,7 +4,6 @@ import fs from 'fs'; import getPort from 'get-port'; import path from 'path'; -import { get } from 'appcd-util'; import { isFile } from 'appcd-fs'; import { loadOptions } from './run-legacy'; import { parseVersion } from '../lib/util'; @@ -20,11 +19,10 @@ export default class CLIService extends Dispatcher { /** * Registers all of the endpoints. * - * @param {Object} cfg - The Appc Daemon config object. * @returns {Promise} * @access public */ - async activate(cfg) { + async activate() { const pluginVersion = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', '..', 'package.json'))).version; const cli = new CLI({ @@ -49,7 +47,7 @@ export default class CLIService extends Dispatcher { // inject the titanium config into the command data object before parsing starts so that // it's available to the command callback cli.on('parse', ({ data }) => { - data.config = cfg.titanium; + data.config = appcd.config.get('titanium'); data.pluginVersion = pluginVersion; }); @@ -76,7 +74,7 @@ export default class CLIService extends Dispatcher { // find an available port to listen on const port = await getPort({ - port: get(cfg, 'port', 1733) + port: appcd.config.get('port', 1733) }); // start the cli-kit server diff --git a/src/cli/commands/logout.js b/src/cli/commands/logout.js index 75ca0a4..5e46a1d 100644 --- a/src/cli/commands/logout.js +++ b/src/cli/commands/logout.js @@ -11,7 +11,7 @@ export default { '--json': 'Outputs revoked accounts as JSON' }, async action({ argv, console }) { - const { response: revoked } = await appcd.call('/amplify/1.x/auth/logout', { + const { response: revoked } = await appcd.call('/amplify/2.x/auth/logout', { data: { accountName: argv.account } diff --git a/src/cli/commands/switch.js b/src/cli/commands/switch.js index e8f2b55..ff767d4 100644 --- a/src/cli/commands/switch.js +++ b/src/cli/commands/switch.js @@ -10,7 +10,7 @@ export default { }, async action(params) { const { argv, console, terminal } = params; - const { response: accounts } = await appcd.call('/amplify/1.x/auth'); + const { response: accounts } = await appcd.call('/amplify/2.x/auth'); let account; let { org } = argv; let loggedIn = false; @@ -36,7 +36,7 @@ export default { previousOrg = account?.org.guid; if (account.orgs.length === 1) { - account = await appcd.call('/amplify/1.x/switch', { + account = await appcd.call('/amplify/2.x/switch', { accountName: account.name, org: account.orgs[0].guid }); @@ -69,7 +69,7 @@ export default { console.log(); } - account = (await appcd.call('/amplify/1.x/auth/switch', { + account = (await appcd.call('/amplify/2.x/auth/switch', { data: { accountName: account.name, org diff --git a/src/cli/commands/whoami.js b/src/cli/commands/whoami.js index 68e8232..d0b597c 100644 --- a/src/cli/commands/whoami.js +++ b/src/cli/commands/whoami.js @@ -4,7 +4,7 @@ export default { '--json': 'Outputs accounts as JSON' }, async action({ argv, console }) { - const { response: accounts } = await appcd.call('/amplify/1.x/auth'); + const { response: accounts } = await appcd.call('/amplify/2.x/auth'); if (!accounts.length) { console.log('No authenticated accounts.'); diff --git a/src/index.js b/src/index.js index 69c562b..9ec2b25 100644 --- a/src/index.js +++ b/src/index.js @@ -1,16 +1,8 @@ -// istanbul ignore if -if (!Error.prepareStackTrace) { - require('source-map-support/register'); -} - -// import fs from 'fs-extra'; -import gawk from 'gawk'; import CLIService from './cli/cli-service'; import ModuleService from './module/module-service'; import ProjectService from './project/project-service'; import SDKService from './sdk/sdk-service'; - -import { debounce, get } from 'appcd-util'; +import { debounce } from 'appcd-util'; import { modules, options, sdk } from 'titaniumlib'; const cliSvc = new CLIService(); @@ -21,46 +13,30 @@ const sdkSvc = new SDKService(); /** * Wires up plugin services. * - * @param {Object} cfg - An Appc Daemon config object * @returns {Promise} */ -export async function activate(cfg) { +export async function activate() { // set titaniumlib's network settings - const { APPCD_NETWORK_CA_FILE, APPCD_NETWORK_PROXY, APPCD_NETWORK_STRICT_SSL } = process.env; - const { network } = options; - const applySettings = () => { - Object.assign(network, cfg.network); - if (APPCD_NETWORK_CA_FILE) { - network.caFile = APPCD_NETWORK_CA_FILE; - } - if (APPCD_NETWORK_PROXY) { - network.httpProxy = network.httpsProxy = APPCD_NETWORK_PROXY; - } - if (APPCD_NETWORK_STRICT_SSL !== undefined && APPCD_NETWORK_STRICT_SSL !== 'false') { - network.strictSSL = true; - } - }; - applySettings(); - gawk.watch(cfg, [ 'network' ], debounce(applySettings)); - - options.searchPaths = get(cfg, 'titanium.searchPaths'); + Object.assign(options.network, appcd.config.get('network')); + appcd.config.watch('network', debounce(network => Object.assign(options.network, network))); - gawk.watch(cfg, [ 'titanium', 'searchPaths' ], debounce(value => { + options.searchPaths = appcd.config.get('titanium.searchPaths'); + appcd.config.watch('titanium.searchPaths', debounce(value => { options.searchPaths = value; moduleSvc.detectEngine.paths = modules.getPaths(); sdkSvc.detectEngine.paths = sdk.getPaths(); })); - await cliSvc.activate(cfg); + await cliSvc.activate(); appcd.register('/cli', cliSvc); - await moduleSvc.activate(cfg); + await moduleSvc.activate(); appcd.register([ '/module', '/modules' ], moduleSvc); - await projectSvc.activate(cfg); + await projectSvc.activate(); appcd.register('/project', projectSvc); - await sdkSvc.activate(cfg); + await sdkSvc.activate(); appcd.register('/sdk', sdkSvc); } diff --git a/src/legacy/hooks/aca.js b/src/legacy/hooks/aca.js index db79398..d4fd66e 100644 --- a/src/legacy/hooks/aca.js +++ b/src/legacy/hooks/aca.js @@ -1,11 +1,9 @@ import fs from 'fs-extra'; -import got from 'got'; import isPlatformGuid from '@titanium-sdk/node-is-platform-guid'; import path from 'path'; import stream from 'stream'; import tar from 'tar'; import tunnel from '../tunnel'; - import { isDir } from 'appcd-fs'; import { promisify } from 'util'; @@ -45,7 +43,7 @@ exports.init = (logger, config, cli) => { return; } - const { api_token, limit, url } = (await tunnel.call('/amplify/1.x/ti/aca-upload-url', { + const { api_token, limit, url } = (await tunnel.call('/amplify/2.x/ti/aca-upload-url', { data: { accountName: account.name, appGuid: tiapp.guid @@ -66,7 +64,7 @@ exports.init = (logger, config, cli) => { return; } - const { headers, statusCode } = await got(`${url}?app=${tiapp.guid}&platform=${platformName}&version=${tiapp.version}`, { + const { headers, statusCode } = await cli.got(`${url}?app=${tiapp.guid}&platform=${platformName}&version=${tiapp.version}`, { followRedirect: false, headers: { 'X-Auth-Token': api_token }, retry: 0 @@ -80,7 +78,7 @@ exports.init = (logger, config, cli) => { logger.info('Uploading debug symbols...'); await pipeline( fs.createReadStream(symbolsTarFile), - await got.stream.put(headers.location, { + await cli.got.stream.put(headers.location, { headers: { 'Content-Length': stat.size } diff --git a/src/legacy/hooks/app-preview.js b/src/legacy/hooks/app-preview.js index 77a2820..5689c65 100644 --- a/src/legacy/hooks/app-preview.js +++ b/src/legacy/hooks/app-preview.js @@ -7,7 +7,6 @@ import FormData from 'form-data'; import fs from 'fs'; -import got from 'got'; import open from 'open'; import tmp from 'tmp'; import tunnel from '../tunnel'; @@ -79,7 +78,7 @@ exports.init = async (logger, config, cli) => { const post = async (url, body) => { try { - return (await got(url, { + return (await cli.got(url, { body, headers: { Accept: 'application/json', diff --git a/src/legacy/hooks/appc.js b/src/legacy/hooks/appc.js index 0f5f35d..89b5088 100644 --- a/src/legacy/hooks/appc.js +++ b/src/legacy/hooks/appc.js @@ -89,7 +89,7 @@ exports.init = (logger, config, cli) => { cli.on('build.post.compile', { priority: 10000, async post() { - await tunnel.call('/amplify/1.x/ti/app/set', { + await tunnel.call('/amplify/2.x/ti/app/set', { data: { accountName: account.name, tiapp: await fs.readFile(path.join(projectDir, 'tiapp.xml'), 'utf-8') @@ -178,7 +178,7 @@ exports.init = (logger, config, cli) => { const signature = privateKey.sign(md.update(buildData.i, 'utf8')); const signatureBase64 = Buffer.from(forge.util.bytesToHex(signature), 'hex').toString('base64'); const signatureShaBase64 = Buffer.from(forge.util.bytesToHex(md.digest().bytes()), 'hex').toString('base64'); - const appVerifyURL = await tunnel.call('/amplify/1.x/ti/app-verify-url'); + const appVerifyURL = await tunnel.call('/amplify/2.x/ti/app-verify-url'); // step 2: encrypt the source files and write them into the await fs.mkdirs(outputDir); @@ -211,7 +211,7 @@ exports.init = (logger, config, cli) => { // step 3: notify the platform with encryption info try { - await tunnel.call('/amplify/1.x/ti/build-update', { + await tunnel.call('/amplify/2.x/ti/build-update', { data: { buildId: buildData.i, buildSHA: shaofshas, @@ -374,7 +374,7 @@ $1`); const publicKey = pki.publicKeyToPem(keys.publicKey); logger.info('Registering developer certificate'); - const certificate = await tunnel.call('/amplify/1.x/ti/enroll', { + const certificate = await tunnel.call('/amplify/2.x/ti/enroll', { data: { accountName: account.name, fingerprint: cli.fingerprint, @@ -434,7 +434,7 @@ $1`); const verify = async data => { try { - return await tunnel.call('/amplify/1.x/ti/build-verify', { data }); + return await tunnel.call('/amplify/2.x/ti/build-verify', { data }); } catch (err) { if (err.code === 'com.appcelerator.platform.app.notregistered') { // this is ok, just means the app isn't registered with the platform yet @@ -444,7 +444,7 @@ $1`); try { // try again - return await tunnel.call('/amplify/1.x/ti/build-verify', { data }); + return await tunnel.call('/amplify/2.x/ti/build-verify', { data }); } catch (err2) { if (err2.code !== 'com.appcelerator.platform.app.notregistered') { throw err2; diff --git a/src/legacy/index.js b/src/legacy/index.js index 6fb9569..af88d24 100644 --- a/src/legacy/index.js +++ b/src/legacy/index.js @@ -66,6 +66,7 @@ export async function exec({ argv, command, config, console, cwd, prompt }) { config: config || {}, cwd: projectDir, fingerprint: (await appcd.call('/appcd/status/system/mid')).response, + network: appcd.config.get('network'), promptingEnabled: !!prompt, sdkPath: sdkInfo.path, type: 'exec' diff --git a/src/legacy/ti/cli.js b/src/legacy/ti/cli.js index cdb3868..385f2c2 100644 --- a/src/legacy/ti/cli.js +++ b/src/legacy/ti/cli.js @@ -7,7 +7,7 @@ import Module from 'module'; import path from 'path'; import tunnel from '../tunnel'; import * as version from '../../lib/version'; - +import * as request from '@axway/amplify-request'; import { CLI_VERSION } from './version'; import { expandPath } from 'appcd-path'; import { format } from 'util'; @@ -122,6 +122,8 @@ export default class CLI { * @param {String} opts.command - The name of the command to execute. * @param {Object} [opts.config] - User-defined Titanium CLI config settings from appcd's user * config. + * @param {Object} [opts.network] - Network configuration settings including `caFile`, + * `certFile`, `keyFile`, `proxy`, and `strictSSL`. * @param {Boolean} [opts.promptingEnabled] - When `true`, invalid and missing values will be * prompted for. * @param {String} opts.sdkPath - The path to the Titanium SDK. @@ -137,6 +139,8 @@ export default class CLI { this.promptingEnabled = !!opts.promptingEnabled; this.sdk = new sdk.TitaniumSDK(opts.sdkPath); + this.got = request.init({ defaults: opts.network }); + // initialize the CLI argument values this.argv = { $command: opts.command, diff --git a/src/legacy/tunnel.js b/src/legacy/tunnel.js index b59d320..9462ac9 100644 --- a/src/legacy/tunnel.js +++ b/src/legacy/tunnel.js @@ -62,11 +62,11 @@ class Tunnel extends EventEmitter { */ async getAccount() { if (!this._account) { - const { response: accounts } = await this.call('/amplify/1.x/auth'); + const { response: accounts } = await this.call('/amplify/2.x/auth'); this._account = accounts.find(a => a.active) || accounts[0]; } if (!this._account) { - this._account = (await this.call('/amplify/1.x/auth/login')).response; + this._account = (await this.call('/amplify/2.x/auth/login')).response; } return this._account; } diff --git a/src/module/module-list-service.js b/src/module/module-list-service.js index 29552f8..a92ebdd 100644 --- a/src/module/module-list-service.js +++ b/src/module/module-list-service.js @@ -4,7 +4,7 @@ import sortObject from 'sort-object-keys'; import { compare } from '../lib/version'; import { DataServiceDispatcher } from 'appcd-dispatcher'; -import { debounce, get } from 'appcd-util'; +import { debounce } from 'appcd-util'; import { modules, options } from 'titaniumlib'; /** @@ -14,11 +14,10 @@ export default class ModuleListService extends DataServiceDispatcher { /** * Starts detecting Titanium SDKs and modules. * - * @param {Object} cfg - An Appc Daemon config object. * @returns {Promise} * @access public */ - async activate(cfg) { + async activate() { this.data = gawk({ android: {}, commonjs: {}, @@ -26,7 +25,7 @@ export default class ModuleListService extends DataServiceDispatcher { windows: {} }); - options.module.searchPaths = get(cfg, 'titanium.module.searchPaths'); + options.module.searchPaths = appcd.config.get('titanium.module.searchPaths'); this.detectEngine = new DetectEngine({ checkDir(dir) { @@ -77,7 +76,7 @@ export default class ModuleListService extends DataServiceDispatcher { gawk.set(this.data, modules); }); - gawk.watch(cfg, [ 'titanium', 'module', 'searchPaths' ], debounce(value => { + appcd.config.watch('titanium.module.searchPaths', debounce(value => { options.module.searchPaths = value; this.detectEngine.paths = modules.getPaths(); })); diff --git a/src/module/module-service.js b/src/module/module-service.js index 79e7e7e..e3d1899 100644 --- a/src/module/module-service.js +++ b/src/module/module-service.js @@ -3,8 +3,8 @@ import ModuleListService from './module-list-service'; import { AppcdError } from 'appcd-response'; import { expandPath } from 'appcd-path'; -import { get, unique } from 'appcd-util'; import { modules } from 'titaniumlib'; +import { unique } from 'appcd-util'; const { log } = appcd.logger('module-service'); const { highlight } = appcd.logger.styles; @@ -16,15 +16,12 @@ export default class ModuleService extends Dispatcher { /** * Registers all of the endpoints and initializes the installed modules detect engine. * - * @param {Object} cfg - The Appc Daemon config object. * @returns {Promise} * @access public */ - async activate(cfg) { - this.config = cfg; - + async activate() { this.installed = new ModuleListService(); - await this.installed.activate(cfg); + await this.installed.activate(); this.register('/', (ctx, next) => { ctx.path = '/list'; @@ -37,7 +34,7 @@ export default class ModuleService extends Dispatcher { const check = async () => { try { - const { response: accounts } = await appcd.call('/amplify/1.x/auth'); + const { response: accounts } = await appcd.call('/amplify/2.x/auth'); const account = accounts.find(a => a.active) || accounts[0]; await this.checkDownloads(account?.name); log('Successfully checked downloads, checking again in 1 hour'); @@ -69,7 +66,7 @@ export default class ModuleService extends Dispatcher { throw err; } - const { response: downloads } = await appcd.call('/amplify/1.x/ti/downloads', { + const { response: downloads } = await appcd.call('/amplify/2.x/ti/downloads', { data: { accountName } @@ -90,8 +87,9 @@ export default class ModuleService extends Dispatcher { log(`${highlight(`${id}@${version}`)} (${platform}) already installed`); } else { log(`Installing ${highlight(`${id}@${version}`)} (${platform})...`); + const tiHome = appcd.config.get('titanium.home'); result.push.apply(result, await modules.install({ - downloadDir: this.config.titanium.home && expandPath(this.config.titanium.home, 'downloads'), + downloadDir: tiHome && expandPath(tiHome, 'downloads'), uri: url })); break; @@ -126,7 +124,7 @@ export default class ModuleService extends Dispatcher { */ getInstallPaths() { const paths = modules.locations[process.platform].map(p => expandPath(p)); - const defaultPath = get(this.config, 'titanium.modules.defaultInstallLocation'); + const defaultPath = appcd.config.get('titanium.modules.defaultInstallLocation'); if (defaultPath) { paths.unshift(expandPath(defaultPath)); } @@ -145,9 +143,10 @@ export default class ModuleService extends Dispatcher { */ install({ request, response }) { const { data } = request; + const tiHome = appcd.config.get('titanium.home'); modules.install({ - downloadDir: this.config.titanium.home && expandPath(this.config.titanium.home, 'downloads'), + downloadDir: tiHome && expandPath(tiHome, 'downloads'), keep: data.keep, onProgress(evt) { if (data.progress) { diff --git a/src/project/project-service.js b/src/project/project-service.js index 86002cf..728e8df 100644 --- a/src/project/project-service.js +++ b/src/project/project-service.js @@ -15,13 +15,10 @@ export default class ProjectService extends Dispatcher { /** * Registers all of the endpoints. * - * @param {Object} cfg - The Appc Daemon config object. * @returns {Promise} * @access public */ - async activate(cfg) { - this.config = cfg; - + async activate() { const runLegacyCLI = async (command, ctx) => { const { cwd } = ctx.request.data; const argv = { ...ctx.request.data }; @@ -29,7 +26,7 @@ export default class ProjectService extends Dispatcher { await exec({ argv, command, - config: cfg.titanium, + config: appcd.config.get('titanium'), console: new Console(ctx.response, ctx.response), cwd }); @@ -60,7 +57,7 @@ export default class ProjectService extends Dispatcher { // TODO: in the future, run will call project.build and we'll "run" it ourselves this.register('/run', ctx => runLegacyCLI('run', ctx)); - await this.templateSvc.activate(cfg); + await this.templateSvc.activate(); this.register('/templates', this.templateSvc); } diff --git a/src/project/templates-service.js b/src/project/templates-service.js index 2e31305..920d357 100644 --- a/src/project/templates-service.js +++ b/src/project/templates-service.js @@ -3,7 +3,6 @@ import fs from 'fs-extra'; import gawk from 'gawk'; import globalModules from 'global-modules'; import path from 'path'; - import { DataServiceDispatcher } from 'appcd-dispatcher'; import { mergeDeep } from 'appcd-util'; import { templates } from 'titaniumlib'; diff --git a/src/sdk/sdk-list-service.js b/src/sdk/sdk-list-service.js index 1ccfc0d..824731a 100644 --- a/src/sdk/sdk-list-service.js +++ b/src/sdk/sdk-list-service.js @@ -4,7 +4,7 @@ import semver from 'semver'; import { compare } from '../lib/version'; import { DataServiceDispatcher } from 'appcd-dispatcher'; -import { debounce, get } from 'appcd-util'; +import { debounce } from 'appcd-util'; import { options, sdk } from 'titaniumlib'; /** @@ -14,14 +14,13 @@ export default class SDKListService extends DataServiceDispatcher { /** * Starts detecting Titanium SDKs. * - * @param {Object} cfg - The Appc Daemon config object. * @returns {Promise} * @access public */ - async activate(cfg) { + async activate() { this.data = gawk([]); - options.sdk.searchPaths = get(cfg, 'titanium.sdk.searchPaths'); + options.sdk.searchPaths = appcd.config.get('titanium.sdk.searchPaths'); this.detectEngine = new DetectEngine({ checkDir(dir) { @@ -50,7 +49,7 @@ export default class SDKListService extends DataServiceDispatcher { this.detectEngine.on('results', results => gawk.set(this.data, results)); - gawk.watch(cfg, [ 'titanium', 'sdk', 'searchPaths' ], debounce(value => { + appcd.config.watch('titanium.sdk.searchPaths', debounce(value => { options.sdk.searchPaths = value; this.detectEngine.paths = sdk.getPaths(); })); diff --git a/src/sdk/sdk-service.js b/src/sdk/sdk-service.js index 3e874dc..e146773 100644 --- a/src/sdk/sdk-service.js +++ b/src/sdk/sdk-service.js @@ -1,6 +1,5 @@ import Dispatcher, { DispatcherError } from 'appcd-dispatcher'; import SDKListService from './sdk-list-service'; - import { AppcdError, codes } from 'appcd-response'; import { expandPath } from 'appcd-path'; import { sdk } from 'titaniumlib'; @@ -12,15 +11,12 @@ export default class SDKService extends Dispatcher { /** * Registers all of the endpoints and initializes the installed SDKs detect engine. * - * @param {Object} cfg - The Appc Daemon config object. * @returns {Promise} * @access public */ - async activate(cfg) { - this.config = cfg; - + async activate() { this.installed = new SDKListService(); - await this.installed.activate(cfg); + await this.installed.activate(); this.register('/', (ctx, next) => { ctx.path = '/list'; @@ -75,9 +71,10 @@ export default class SDKService extends Dispatcher { */ install({ request, response }) { const { data, params } = request; + const tiHome = appcd.config.get('titanium.home'); sdk.install({ - downloadDir: this.config.titanium.home && expandPath(this.config.titanium.home, 'downloads'), + downloadDir: tiHome && expandPath(tiHome, 'downloads'), keep: data.keep, onProgress(evt) { if (data.progress) { From 750e54bb8aad1752e106dd0c650622352e57cd9c Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Thu, 22 Oct 2020 10:37:15 -0500 Subject: [PATCH 34/39] chore: Updated deps. --- README.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 49ae728..f1f08dc 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Titanium SDK services for the Appc Daemon. > This plugin requires appcd plugin API version 2.x which was introduced in appcd@4.0.0 and the > AMPLIFY appcd plugin v2.x. -## Services +## Service Endpoints * [SDKs](#SDKs) - [`/sdk/list/installed`](#sdklistinstalled) diff --git a/package.json b/package.json index 6eb0436..976a4ed 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "test": "gulp test" }, "dependencies": { - "@axway/amplify-request": "^2.0.0", + "@axway/amplify-request": "^2.0.1", "@titanium-sdk/node-is-platform-guid": "^1.0.2", "appc-security": "^0.1.0", "appcd-fs": "^2.0.0", From ba420a28faf33f4fbf4133f37a52c102b71fc822 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Thu, 22 Oct 2020 17:58:03 -0500 Subject: [PATCH 35/39] feat: Added register command and service endpoint. --- CHANGELOG.md | 2 + src/cli/commands/register.js | 19 +++++++++ src/project/project-service.js | 71 +++++++++++++++++++++++++++++++++- 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 src/cli/commands/register.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 153b77b..94ba950 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,8 @@ * feat: Support for Titanium-specific telemetry. * feat: Added HTTP proxy support. * feat: Adopted appcd 4.x new `appcd.config.*`. + * feat(cli): Added `register` command to replace old --import flag. + * feat(project): Added `/project/register` endpoint. * feat(amplify): Upgraded from AMPLIFY appcd plugin v1.x to v2.x. * refactor: Updated to latest cli-kit with support for the new client/server architecture. * refactor: Updated `config` command actions to be subcommands with improved help output. diff --git a/src/cli/commands/register.js b/src/cli/commands/register.js new file mode 100644 index 0000000..d88c05f --- /dev/null +++ b/src/cli/commands/register.js @@ -0,0 +1,19 @@ +import { promptLoop } from '../../lib/prompt'; + +export default { + desc: 'Registers an existing app with the Axway platform', + options: { + '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory' + }, + async action(ctx) { + await promptLoop({ + ctx, + data: { + cwd: ctx.data.cwd, + projectDir: ctx.argv.projectDir + }, + path: '/project/register', + ns: 'cli:register' + }); + } +}; diff --git a/src/project/project-service.js b/src/project/project-service.js index 728e8df..3fe4c63 100644 --- a/src/project/project-service.js +++ b/src/project/project-service.js @@ -1,10 +1,17 @@ /* eslint-disable node/prefer-global/console */ import Dispatcher from 'appcd-dispatcher'; +import path from 'path'; import TemplateService from './templates-service'; +import { AppcdError, codes } from 'appcd-response'; import { Console } from 'console'; import { exec } from '../legacy'; -import { Project } from 'titaniumlib'; +import { isFile } from 'appcd-fs'; +import { Project /* , Tiapp */ } from 'titaniumlib'; +import { PromptError } from '../lib/prompt'; + +const { log } = appcd.logger('project'); +const { highlight } = appcd.logger.styles; /** * Service for creating and building Titanium applications. @@ -54,6 +61,68 @@ export default class ProjectService extends Dispatcher { } }); + this.register('/register', async ctx => { + // step 1: make sure you're logged in + log('!!!!!!!!!!! GETTING ACCOUNT'); + const { response: accounts } = await appcd.call('/amplify/2.x/auth'); + log(accounts); + const account = accounts.find(a => a.active) || accounts[0]; + log(account); + if (!account) { + throw new AppcdError(codes.FORBIDDEN, 'You must be authenticated to perform production builds'); + } + + // step 2: determine the project directory + let { cwd, projectDir } = ctx.request.data; + + if (projectDir !== undefined && typeof projectDir !== 'string') { + throw new PromptError('Invalid project directory', { + message: 'Where is the project located?', + name: 'projectDir', + type: 'text' + }); + } + + if (projectDir === undefined || !path.isAbsolute(projectDir)) { + if (!cwd || typeof cwd !== 'string') { + throw new AppcdError(codes.BAD_REQUEST, 'Current working directory required when project directory is relative'); + } + projectDir = path.resolve(cwd, projectDir || '.'); + } + + // step 3: load the tiapp and get the guid + const tiappFile = path.resolve(projectDir, 'tiapp.xml'); + if (!isFile(tiappFile)) { + throw new AppcdError(codes.BAD_REQUEST, 'Invalid project directory'); + } + + log(`Loading: ${highlight(tiappFile)}`); + // FIX ME! + // const tiapp = new Tiapp({ file: tiappFile }); + // const { guid } = tiapp.get('guid'); + const guid = '28463e4d-0c2a-4eaf-9999-fdb4468c8778'; + + // step 4: verify that the app is registered + try { + await appcd.call('/amplify/2.x/ti/app', { + data: { + accountName: account.name, + appGuid: guid + } + }); + + ctx.response = 'Application already registered\n'; + return; + } catch (err) { + if (err.code !== 404) { + throw new AppcdError(codes.SERVER_ERROR, `Failed to verify app: ${err.message}`); + } + } + + // step 5: register the app + ctx.response = 'registering! :)\n'; + }); + // TODO: in the future, run will call project.build and we'll "run" it ourselves this.register('/run', ctx => runLegacyCLI('run', ctx)); From 5c32ffb33a57c166edddab85e72521fe219fcea8 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Mon, 26 Oct 2020 12:19:05 -0500 Subject: [PATCH 36/39] feat(register): Finished the register command and endpoint. feat(add): Added add component/service endpoint. chore: Removed unused prompts package. fix(prompt): Added error handling for PromptErrors. --- package.json | 1 - src/cli/commands/add.js | 16 +++- src/cli/commands/register.js | 6 +- src/cli/commands/switch.js | 2 +- src/lib/prompt.js | 4 +- src/project/project-service.js | 162 ++++++++++++++++++++++++++------- 6 files changed, 149 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 976a4ed..a789867 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "open": "^7.3.0", "pluralize": "^8.0.0", "progress": "^2.0.3", - "prompts": "^2.3.2", "semver": "^7.3.2", "simple-plist": "^1.1.1", "sort-object-keys": "^1.1.3", diff --git a/src/cli/commands/add.js b/src/cli/commands/add.js index 08b5a24..fe4378a 100644 --- a/src/cli/commands/add.js +++ b/src/cli/commands/add.js @@ -1,9 +1,19 @@ +import { promptLoop } from '../../lib/prompt'; + export default { - desc: 'Add a component to a project', + desc: 'Add a component or service to a project', options: { '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory' }, - action({ console }) { - console.log('Add!'); + async action(ctx) { + await promptLoop({ + ctx, + data: { + cwd: ctx.data.cwd, + ...ctx.argv + }, + path: '/project/add', + ns: 'cli:add' + }); } }; diff --git a/src/cli/commands/register.js b/src/cli/commands/register.js index d88c05f..6ef0aae 100644 --- a/src/cli/commands/register.js +++ b/src/cli/commands/register.js @@ -3,13 +3,17 @@ import { promptLoop } from '../../lib/prompt'; export default { desc: 'Registers an existing app with the Axway platform', options: { - '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory' + '-d, --project-dir [path]': 'The directory containing the project; defaults to the current directory', + '--force': 'Forces an app to be re-registered', + '--org [guid|id|name]': 'The organization to register the app with' }, async action(ctx) { await promptLoop({ ctx, data: { cwd: ctx.data.cwd, + force: ctx.argv.force, + org: ctx.argv.org, projectDir: ctx.argv.projectDir }, path: '/project/register', diff --git a/src/cli/commands/switch.js b/src/cli/commands/switch.js index ff767d4..a446b54 100644 --- a/src/cli/commands/switch.js +++ b/src/cli/commands/switch.js @@ -55,7 +55,7 @@ export default { } return { name: org.name, - message: `${org.name} (${org.guid} : ${org.id})`, + message: org.name, value: org.guid }; }) diff --git a/src/lib/prompt.js b/src/lib/prompt.js index 7ca7c22..2bec63d 100644 --- a/src/lib/prompt.js +++ b/src/lib/prompt.js @@ -98,7 +98,7 @@ export async function promptLoop({ ctx, data, footer, header, ns, path, print }) } return; } catch (err) { - if (!err.prompt || !argv.prompt) { + if ((!(err instanceof PromptError) && !err.prompt) || !argv.prompt) { throw err; } @@ -108,7 +108,7 @@ export async function promptLoop({ ctx, data, footer, header, ns, path, print }) } // prompt and try again - let ask = err.prompt; + let ask = err instanceof PromptError ? err.ask : err.prompt; let { name } = ask; let result; logger.warn(`${err.toString()}, prompting for ${highlight(`"${name}"`)}`); diff --git a/src/project/project-service.js b/src/project/project-service.js index 3fe4c63..ddd8992 100644 --- a/src/project/project-service.js +++ b/src/project/project-service.js @@ -1,6 +1,7 @@ /* eslint-disable node/prefer-global/console */ import Dispatcher from 'appcd-dispatcher'; +import fs from 'fs-extra'; import path from 'path'; import TemplateService from './templates-service'; import { AppcdError, codes } from 'appcd-response'; @@ -44,6 +45,34 @@ export default class ProjectService extends Dispatcher { return 'tiapp coming soon!'; }); + this.register('/add', ctx => { + /* + this endpoint is for adding a component to an existing app such as: + * ACA: + - Ensure logged in + - Entitlement check + - Download/install acs module + - Add per platform + * Alloy + * Apple Watch app: + - Prompt for name + - Install from template into project dir + * Hyperloop: + - Add per platform + * MBS: + - Create ACS apps + - Add acs-* properties to tiapp.xml + - Prompt if acs keys exist + - Set appc-org-id and appc-creator-user-id properites + - Add ti.cloud commonjs module + - Add ti.cloud bootstrap to app.js + */ + + const projectDir = assertProjectDir(ctx.request.data); + + ctx.response = 'Not implemented yet'; + }); + this.register('/build', ctx => runLegacyCLI('build', ctx)); this.register('/clean', ctx => runLegacyCLI('clean', ctx)); @@ -63,33 +92,48 @@ export default class ProjectService extends Dispatcher { this.register('/register', async ctx => { // step 1: make sure you're logged in - log('!!!!!!!!!!! GETTING ACCOUNT'); const { response: accounts } = await appcd.call('/amplify/2.x/auth'); - log(accounts); const account = accounts.find(a => a.active) || accounts[0]; - log(account); if (!account) { - throw new AppcdError(codes.FORBIDDEN, 'You must be authenticated to perform production builds'); + throw new AppcdError(codes.FORBIDDEN, 'You must be authenticated to register an app'); } - // step 2: determine the project directory - let { cwd, projectDir } = ctx.request.data; - - if (projectDir !== undefined && typeof projectDir !== 'string') { - throw new PromptError('Invalid project directory', { - message: 'Where is the project located?', - name: 'projectDir', - type: 'text' - }); + if (!account.orgs.length) { + throw new AppcdError(codes.SERVER_ERROR, `Your account "${account.name}" has no organizations, please logout and login again`); } - if (projectDir === undefined || !path.isAbsolute(projectDir)) { - if (!cwd || typeof cwd !== 'string') { - throw new AppcdError(codes.BAD_REQUEST, 'Current working directory required when project directory is relative'); + let { force, org } = ctx.request.data; + let org_id = null; + + // step 1: check the org + if (org) { + org = String(org).toLowerCase(); + org_id = account.orgs.find(({ id, guid, name }) => String(id) === org || guid === org || name.toLowerCase() === org)?.id; + } + + if (!org_id) { + // no `org` or `org` not found + if (account.orgs.length > 1) { + throw new PromptError('Organization required to register app', { + choices: account.orgs + .map(org => ({ + name: org.name, + message: org.name, + value: org.id + })) + .sort((a, b) => a.message.localeCompare(b.message)), + message: 'Which organization should the app be registered with', + name: 'org', + type: 'select' + }); + } else { + org_id = accounts.orgs[0].id; } - projectDir = path.resolve(cwd, projectDir || '.'); } + // step 2: determine the project directory + const projectDir = assertProjectDir(ctx.request.data); + // step 3: load the tiapp and get the guid const tiappFile = path.resolve(projectDir, 'tiapp.xml'); if (!isFile(tiappFile)) { @@ -97,30 +141,53 @@ export default class ProjectService extends Dispatcher { } log(`Loading: ${highlight(tiappFile)}`); - // FIX ME! - // const tiapp = new Tiapp({ file: tiappFile }); - // const { guid } = tiapp.get('guid'); - const guid = '28463e4d-0c2a-4eaf-9999-fdb4468c8778'; + /* FIX ME! + const tiapp = new Tiapp({ file: tiappFile }); + const { guid } = tiapp.get('guid'); + */ + const guid = '28463e4d-0c2a-4eaf-9999-fdb4468c8778'; // already registered + // const guid = '28463e4d-0c2a-4eaf-9999-fdb4468c8779'; // step 4: verify that the app is registered - try { - await appcd.call('/amplify/2.x/ti/app', { - data: { - accountName: account.name, - appGuid: guid + if (!force) { + try { + await appcd.call('/amplify/2.x/ti/app', { + data: { + accountName: account.name, + appGuid: guid + } + }); + + ctx.response = 'Application already registered\n'; + return; + } catch (err) { + if (err.code !== 404) { + throw new AppcdError(codes.SERVER_ERROR, `Failed to verify app: ${err.message}`); } - }); - - ctx.response = 'Application already registered\n'; - return; - } catch (err) { - if (err.code !== 404) { - throw new AppcdError(codes.SERVER_ERROR, `Failed to verify app: ${err.message}`); } } // step 5: register the app - ctx.response = 'registering! :)\n'; + log(`Registering app with org ${highlight(org_id)}`); + const { response } = await appcd.call('/amplify/2.x/ti/app/set', { + data: { + accountName: account.name, + tiapp: fs.readFileSync(tiappFile, 'utf8'), + params: { + import: true, + org_id + } + } + }); + + // step 6: update the tiapp.xml + /* FIX ME! + tiapp.set('guid', response.app_guid); + tiapp.set([ 'property', 'appc-app-id' ], { type: 'string', value: response._id }); + tiapp.save(); + */ + + ctx.response = 'Registration completed successfully!\n'; }); // TODO: in the future, run will call project.build and we'll "run" it ourselves @@ -140,3 +207,30 @@ export default class ProjectService extends Dispatcher { await this.templateSvc.deactivate(); } } + +/** + * Checks that the project diretory is valid and resolves the absolute path. + * + * @param {Object} opts - Various options. + * @param {String} opts.cwd - The current working directory. + * @param {String} opts.projectDir - The path to the project. + * @returns {String} + */ +function assertProjectDir({ cwd, projectDir }) { + if (projectDir !== undefined && typeof projectDir !== 'string') { + throw new PromptError('Invalid project directory', { + message: 'Where is the project located?', + name: 'projectDir', + type: 'text' + }); + } + + if (projectDir === undefined || !path.isAbsolute(projectDir)) { + if (!cwd || typeof cwd !== 'string') { + throw new AppcdError(codes.BAD_REQUEST, 'Current working directory required when project directory is relative'); + } + projectDir = path.resolve(cwd, projectDir || '.'); + } + + return projectDir; +} From becaa29dc727839ec37fe63790c29b56ad74f06b Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Fri, 13 Nov 2020 17:15:01 -0600 Subject: [PATCH 37/39] chore: Updated deps. fix: Hid config and logout aliases. --- package.json | 5 ++--- src/cli/commands/config.js | 4 ++-- src/cli/commands/logout.js | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index a789867..82cf779 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "appcd-path": "^2.0.1", "appcd-util": "^3.0.0", "chalk": "^4.1.0", - "cli-kit": "^1.8.2", + "cli-kit": "^1.8.6", "colors": "^1.4.0", "dateformat": "^3.0.3", "enquirer": "^2.3.6", @@ -35,7 +35,6 @@ "get-port": "^5.1.1", "global-modules": "^2.0.0", "node-forge": "^0.10.0", - "node-pty-prebuilt-multiarch": "^0.9.0", "open": "^7.3.0", "pluralize": "^8.0.0", "progress": "^2.0.3", @@ -45,7 +44,7 @@ "tar": "^6.0.5", "titaniumlib": "^3.0.1", "tmp": "^0.2.1", - "v8-compile-cache": "^2.1.1", + "v8-compile-cache": "^2.2.0", "yauzl": "^2.10.0" }, "devDependencies": { diff --git a/src/cli/commands/config.js b/src/cli/commands/config.js index 5a58885..7c62164 100644 --- a/src/cli/commands/config.js +++ b/src/cli/commands/config.js @@ -1,5 +1,6 @@ export default { - aliases: 'conf', + aliases: '!conf', + banner: false, commands: { '@ls, list': { desc: 'Display all config settings', @@ -45,7 +46,6 @@ async function runConfig(action, { argv, cmd, console, setExitCode }) { const print = ({ code = 0, key = null, value }) => { setExitCode(code); - cmd.banner = false; if (json) { console.log(JSON.stringify(value, null, 2)); diff --git a/src/cli/commands/logout.js b/src/cli/commands/logout.js index 5e46a1d..08c93cb 100644 --- a/src/cli/commands/logout.js +++ b/src/cli/commands/logout.js @@ -1,5 +1,5 @@ export default { - aliases: [ 'revoke' ], + aliases: '!revoke', args: [ { name: 'account', From 872b965af0b491efedabf9300db6ec99655fc58b Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Sat, 14 Nov 2020 21:50:29 -0600 Subject: [PATCH 38/39] chore: Updated deps. --- package.json | 2 +- src/cli/commands/config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 82cf779..50c50b3 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "appcd-path": "^2.0.1", "appcd-util": "^3.0.0", "chalk": "^4.1.0", - "cli-kit": "^1.8.6", + "cli-kit": "^1.8.8", "colors": "^1.4.0", "dateformat": "^3.0.3", "enquirer": "^2.3.6", diff --git a/src/cli/commands/config.js b/src/cli/commands/config.js index 7c62164..a4e3a03 100644 --- a/src/cli/commands/config.js +++ b/src/cli/commands/config.js @@ -41,7 +41,7 @@ export default { } }; -async function runConfig(action, { argv, cmd, console, setExitCode }) { +async function runConfig(action, { argv, console, setExitCode }) { let { json, key, value } = argv; const print = ({ code = 0, key = null, value }) => { From d01cac8e895681bd11df8147fa5f7193dd2f7543 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Tue, 26 Jan 2021 13:54:50 -0600 Subject: [PATCH 39/39] fix(cli): Explicitly enabled server mode. chore: Updated dependencies. --- .npmignore | 1 + package.json | 24 +- src/cli/cli-service.js | 1 + yarn.lock | 7647 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 7661 insertions(+), 12 deletions(-) create mode 100644 yarn.lock diff --git a/.npmignore b/.npmignore index a3dc90c..61b9dcc 100644 --- a/.npmignore +++ b/.npmignore @@ -12,5 +12,6 @@ junit.xml node_modules npm-debug.log yarn-error.log +yarn.lock /src /test diff --git a/package.json b/package.json index 50c50b3..dce177b 100644 --- a/package.json +++ b/package.json @@ -16,39 +16,39 @@ "test": "gulp test" }, "dependencies": { - "@axway/amplify-request": "^2.0.1", + "@axway/amplify-request": "^2.1.1", "@titanium-sdk/node-is-platform-guid": "^1.0.2", "appc-security": "^0.1.0", - "appcd-fs": "^2.0.0", - "appcd-path": "^2.0.1", - "appcd-util": "^3.0.0", + "appcd-fs": "^2.0.4", + "appcd-path": "^2.0.5", + "appcd-util": "^3.1.3", "chalk": "^4.1.0", - "cli-kit": "^1.8.8", + "cli-kit": "^1.9.3", "colors": "^1.4.0", - "dateformat": "^3.0.3", + "dateformat": "^4.5.0", "enquirer": "^2.3.6", "figures": "^3.2.0", "filesize": "^6.1.0", "form-data": "^3.0.0", - "fs-extra": "^9.0.1", - "gawk": "^4.7.1", + "fs-extra": "^9.1.0", + "gawk": "^5.0.0", "get-port": "^5.1.1", "global-modules": "^2.0.0", "node-forge": "^0.10.0", - "open": "^7.3.0", + "open": "^7.3.1", "pluralize": "^8.0.0", "progress": "^2.0.3", - "semver": "^7.3.2", + "semver": "^7.3.4", "simple-plist": "^1.1.1", "sort-object-keys": "^1.1.3", - "tar": "^6.0.5", + "tar": "^6.1.0", "titaniumlib": "^3.0.1", "tmp": "^0.2.1", "v8-compile-cache": "^2.2.0", "yauzl": "^2.10.0" }, "devDependencies": { - "appcd-gulp": "^3.0.1" + "appcd-gulp": "^3.1.3" }, "homepage": "https://github.com/appcelerator/appc-daemon", "bugs": "https://github.com/appcelerator/appcd-plugin-titanium/issues", diff --git a/src/cli/cli-service.js b/src/cli/cli-service.js index 76602c6..72fd891 100644 --- a/src/cli/cli-service.js +++ b/src/cli/cli-service.js @@ -36,6 +36,7 @@ export default class CLIService extends Dispatcher { options: { '--no-prompt': 'Disable interactive prompting' }, + serverMode: true, styles: { subheading(s) { return `\n${String(s).toUpperCase()}`; diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..b067f54 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,7647 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@axway/amplify-request@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@axway/amplify-request/-/amplify-request-2.1.1.tgz#96f9bd275509337d4cc22c7838097571ae9e8b74" + integrity sha512-9T4MJnCn67clBvRIZEvu764tpedzSaLUOifHcbC3yRVMYnzjhsJXESgevy9OaJ2CAapUiwglvZoj9otXj6X8WA== + dependencies: + appcd-util "^3.1.1" + got "^11.8.1" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + pretty-bytes "^5.5.0" + snooplogg "^3.0.2" + source-map-support "^0.5.19" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/core@^7.12.10", "@babel/core@^7.7.5": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.10.tgz#b79a2e1b9f70ed3d84bbfb6d8c4ef825f606bccd" + integrity sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.10" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.5" + "@babel/parser" "^7.12.10" + "@babel/template" "^7.12.7" + "@babel/traverse" "^7.12.10" + "@babel/types" "^7.12.10" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.12.10", "@babel/generator@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.11.tgz#98a7df7b8c358c9a37ab07a24056853016aba3af" + integrity sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA== + dependencies: + "@babel/types" "^7.12.11" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.10.4": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.10.tgz#54ab9b000e60a93644ce17b3f37d313aaf1d115d" + integrity sha512-XplmVbC1n+KY6jL8/fgLVXXUauDIB+lD5+GsQEh6F6GBF1dq1qy4DP4yXWzDKcoqXB3X58t61e85Fitoww4JVQ== + dependencies: + "@babel/types" "^7.12.10" + +"@babel/helper-create-class-features-plugin@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz#3c45998f431edd4a9214c5f1d3ad1448a6137f6e" + integrity sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.12.1" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.10.4" + +"@babel/helper-function-name@^7.10.4", "@babel/helper-function-name@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz#1fd7738aee5dcf53c3ecff24f1da9c511ec47b42" + integrity sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA== + dependencies: + "@babel/helper-get-function-arity" "^7.12.10" + "@babel/template" "^7.12.7" + "@babel/types" "^7.12.11" + +"@babel/helper-get-function-arity@^7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz#b158817a3165b5faa2047825dfa61970ddcc16cf" + integrity sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag== + dependencies: + "@babel/types" "^7.12.10" + +"@babel/helper-member-expression-to-functions@^7.12.1", "@babel/helper-member-expression-to-functions@^7.12.7": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz#aa77bd0396ec8114e5e30787efa78599d874a855" + integrity sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw== + dependencies: + "@babel/types" "^7.12.7" + +"@babel/helper-module-imports@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz#1bfc0229f794988f76ed0a4d4e90860850b54dfb" + integrity sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA== + dependencies: + "@babel/types" "^7.12.5" + +"@babel/helper-module-transforms@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz#7954fec71f5b32c48e4b303b437c34453fd7247c" + integrity sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w== + dependencies: + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-simple-access" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/helper-validator-identifier" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.1" + "@babel/types" "^7.12.1" + lodash "^4.17.19" + +"@babel/helper-optimise-call-expression@^7.10.4", "@babel/helper-optimise-call-expression@^7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz#94ca4e306ee11a7dd6e9f42823e2ac6b49881e2d" + integrity sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ== + dependencies: + "@babel/types" "^7.12.10" + +"@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.8.0": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@babel/helper-remap-async-to-generator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz#8c4dbbf916314f6047dc05e6a2217074238347fd" + integrity sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-wrap-function" "^7.10.4" + "@babel/types" "^7.12.1" + +"@babel/helper-replace-supers@^7.12.1": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz#ea511658fc66c7908f923106dd88e08d1997d60d" + integrity sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.12.7" + "@babel/helper-optimise-call-expression" "^7.12.10" + "@babel/traverse" "^7.12.10" + "@babel/types" "^7.12.11" + +"@babel/helper-simple-access@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz#32427e5aa61547d38eb1e6eaf5fd1426fdad9136" + integrity sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA== + dependencies: + "@babel/types" "^7.12.1" + +"@babel/helper-skip-transparent-expression-wrappers@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" + integrity sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA== + dependencies: + "@babel/types" "^7.12.1" + +"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0", "@babel/helper-split-export-declaration@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz#1b4cc424458643c47d37022223da33d76ea4603a" + integrity sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g== + dependencies: + "@babel/types" "^7.12.11" + +"@babel/helper-validator-identifier@^7.10.4", "@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== + +"@babel/helper-wrap-function@^7.10.4": + version "7.12.3" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz#3332339fc4d1fbbf1c27d7958c27d34708e990d9" + integrity sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helpers@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.12.5.tgz#1a1ba4a768d9b58310eda516c449913fe647116e" + integrity sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.5" + "@babel/types" "^7.12.5" + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.12.10", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.7.0": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.11.tgz#9ce3595bcd74bc5c466905e86c535b8b25011e79" + integrity sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg== + +"@babel/plugin-proposal-class-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz#a082ff541f2a29a4821065b8add9346c0c16e5de" + integrity sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" + integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" + +"@babel/plugin-proposal-optional-chaining@^7.12.7": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.7.tgz#e02f0ea1b5dc59d401ec16fb824679f683d3303c" + integrity sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + +"@babel/plugin-syntax-object-rest-spread@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-transform-async-to-generator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz#3849a49cc2a22e9743cbd6b52926d30337229af1" + integrity sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A== + dependencies: + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.12.1" + +"@babel/plugin-transform-destructuring@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz#b9a570fe0d0a8d460116413cb4f97e8e08b2f847" + integrity sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-modules-commonjs@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz#fa403124542636c786cf9b460a0ffbb48a86e648" + integrity sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag== + dependencies: + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-simple-access" "^7.12.1" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-parameters@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz#d2e963b038771650c922eff593799c96d853255d" + integrity sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/register@^7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.12.10.tgz#19b87143f17128af4dbe7af54c735663b3999f60" + integrity sha512-EvX/BvMMJRAA3jZgILWgbsrHwBQvllC5T8B29McyME8DvkdOxk4ujESfrMvME8IHSDvWXrmMXxPvA/lx2gqPLQ== + dependencies: + find-cache-dir "^2.0.0" + lodash "^4.17.19" + make-dir "^2.1.0" + pirates "^4.0.0" + source-map-support "^0.5.16" + +"@babel/template@^7.10.4", "@babel/template@^7.12.7": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.7.tgz#c817233696018e39fbb6c491d2fb684e05ed43bc" + integrity sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.12.7" + "@babel/types" "^7.12.7" + +"@babel/traverse@^7.10.4", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.10", "@babel/traverse@^7.12.5", "@babel/traverse@^7.7.0": + version "7.12.12" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.12.tgz#d0cd87892704edd8da002d674bc811ce64743376" + integrity sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w== + dependencies: + "@babel/code-frame" "^7.12.11" + "@babel/generator" "^7.12.11" + "@babel/helper-function-name" "^7.12.11" + "@babel/helper-split-export-declaration" "^7.12.11" + "@babel/parser" "^7.12.11" + "@babel/types" "^7.12.12" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + +"@babel/types@^7.10.4", "@babel/types@^7.12.1", "@babel/types@^7.12.10", "@babel/types@^7.12.11", "@babel/types@^7.12.12", "@babel/types@^7.12.5", "@babel/types@^7.12.7", "@babel/types@^7.7.0": + version "7.12.12" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.12.tgz#4608a6ec313abbd87afa55004d373ad04a96c299" + integrity sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + +"@eslint/eslintrc@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.3.0.tgz#d736d6963d7003b6514e6324bec9c602ac340318" + integrity sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + lodash "^4.17.20" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + +"@gulp-sourcemaps/identity-map@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz#a6e8b1abec8f790ec6be2b8c500e6e68037c0019" + integrity sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q== + dependencies: + acorn "^6.4.1" + normalize-path "^3.0.0" + postcss "^7.0.16" + source-map "^0.6.0" + through2 "^3.0.1" + +"@gulp-sourcemaps/map-sources@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz#890ae7c5d8c877f6d384860215ace9d7ec945bda" + integrity sha1-iQrnxdjId/bThIYCFazp1+yUW9o= + dependencies: + normalize-path "^2.0.1" + through2 "^2.0.3" + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" + integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== + +"@sindresorhus/is@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4" + integrity sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ== + +"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.2.tgz#858f5c4b48d80778fde4b9d541f27edc0d56488b" + integrity sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/samsam@^5.3.1": + version "5.3.1" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.1.tgz#375a45fe6ed4e92fca2fb920e007c48232a6507f" + integrity sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg== + dependencies: + "@sinonjs/commons" "^1.6.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + +"@sinonjs/text-encoding@^0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" + integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== + +"@szmarczak/http-timer@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" + integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== + dependencies: + defer-to-connect "^2.0.0" + +"@titanium-sdk/node-is-platform-guid@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@titanium-sdk/node-is-platform-guid/-/node-is-platform-guid-1.0.2.tgz#dd393ec1460525b55c22bb3498912deb28ddbcb3" + integrity sha512-K0fvIe6FY6NUxFcPeZIARQjSqTNRge2p5avkuOX8h9B1GO3mbU1cpNnQhwqZcQXOi/xBxyABzqk32dQ/t7XLMQ== + dependencies: + napi-macros "^2.0.0" + node-gyp-build "^4.2.3" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@types/cacheable-request@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" + integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" + "@types/node" "*" + "@types/responselike" "*" + +"@types/eslint-scope@^3.7.0": + version "3.7.0" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86" + integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "7.2.6" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.6.tgz#5e9aff555a975596c03a98b59ecd103decc70c3c" + integrity sha512-I+1sYH+NPQ3/tVqCeUSBwTE/0heyvtXqpIopUUArlBm0Kpocb8FbMa3AZ/ASKIFpN3rnEx932TTXDbt9OXsNDw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^0.0.46": + version "0.0.46" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe" + integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg== + +"@types/http-cache-semantics@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" + integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== + +"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": + version "7.0.7" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" + integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + +"@types/keyv@*": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" + integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "14.14.22" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.22.tgz#0d29f382472c4ccf3bd96ff0ce47daf5b7b84b18" + integrity sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw== + +"@types/responselike@*", "@types/responselike@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + +"@ungap/promise-all-settled@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" + integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== + +"@webassemblyjs/ast@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.0.tgz#a5aa679efdc9e51707a4207139da57920555961f" + integrity sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + +"@webassemblyjs/floating-point-hex-parser@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz#34d62052f453cd43101d72eab4966a022587947c" + integrity sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA== + +"@webassemblyjs/helper-api-error@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz#aaea8fb3b923f4aaa9b512ff541b013ffb68d2d4" + integrity sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w== + +"@webassemblyjs/helper-buffer@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz#d026c25d175e388a7dbda9694e91e743cbe9b642" + integrity sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA== + +"@webassemblyjs/helper-numbers@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz#7ab04172d54e312cc6ea4286d7d9fa27c88cd4f9" + integrity sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.0" + "@webassemblyjs/helper-api-error" "1.11.0" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz#85fdcda4129902fe86f81abf7e7236953ec5a4e1" + integrity sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA== + +"@webassemblyjs/helper-wasm-section@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz#9ce2cc89300262509c801b4af113d1ca25c1a75b" + integrity sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-buffer" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/wasm-gen" "1.11.0" + +"@webassemblyjs/ieee754@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz#46975d583f9828f5d094ac210e219441c4e6f5cf" + integrity sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.0.tgz#f7353de1df38aa201cba9fb88b43f41f75ff403b" + integrity sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.0.tgz#86e48f959cf49e0e5091f069a709b862f5a2cadf" + integrity sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw== + +"@webassemblyjs/wasm-edit@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz#ee4a5c9f677046a210542ae63897094c2027cb78" + integrity sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-buffer" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/helper-wasm-section" "1.11.0" + "@webassemblyjs/wasm-gen" "1.11.0" + "@webassemblyjs/wasm-opt" "1.11.0" + "@webassemblyjs/wasm-parser" "1.11.0" + "@webassemblyjs/wast-printer" "1.11.0" + +"@webassemblyjs/wasm-gen@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz#3cdb35e70082d42a35166988dda64f24ceb97abe" + integrity sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/ieee754" "1.11.0" + "@webassemblyjs/leb128" "1.11.0" + "@webassemblyjs/utf8" "1.11.0" + +"@webassemblyjs/wasm-opt@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz#1638ae188137f4bb031f568a413cd24d32f92978" + integrity sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-buffer" "1.11.0" + "@webassemblyjs/wasm-gen" "1.11.0" + "@webassemblyjs/wasm-parser" "1.11.0" + +"@webassemblyjs/wasm-parser@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz#3e680b8830d5b13d1ec86cc42f38f3d4a7700754" + integrity sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/helper-api-error" "1.11.0" + "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@webassemblyjs/ieee754" "1.11.0" + "@webassemblyjs/leb128" "1.11.0" + "@webassemblyjs/utf8" "1.11.0" + +"@webassemblyjs/wast-printer@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz#680d1f6a5365d6d401974a8e949e05474e1fab7e" + integrity sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ== + dependencies: + "@webassemblyjs/ast" "1.11.0" + "@xtuc/long" "4.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +abab@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" + integrity sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4= + +acorn-globals@^1.0.4: + version "1.0.9" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-1.0.9.tgz#55bb5e98691507b74579d0513413217c380c54cf" + integrity sha1-VbtemGkVB7dFedBRNBMhfDgMVM8= + dependencies: + acorn "^2.1.0" + +acorn-jsx@^5.2.0, acorn-jsx@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + +acorn@^2.1.0, acorn@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-2.7.0.tgz#ab6e7d9d886aaca8b085bc3312b79a198433f0e7" + integrity sha1-q259nYhqrKiwhbwzEreaGYQz8Oc= + +acorn@^6.4.1: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + +acorn@^7.1.1, acorn@^7.4.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.0.4: + version "8.0.5" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.0.5.tgz#a3bfb872a74a6a7f661bc81b9849d9cac12601b7" + integrity sha512-v+DieK/HJkJOpFBETDJioequtc3PfxsWMaxIdIwujtF7FEV/MAyDQLlm6/zPvr7Mix07mLh6ccVwIsloceodlg== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.0.3.tgz#13ae747eff125cafb230ac504b2406cf371eece2" + integrity sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ansi-colors@4.1.1, ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-colors@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" + integrity sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA== + dependencies: + ansi-wrap "^0.1.0" + +ansi-cyan@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873" + integrity sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM= + dependencies: + ansi-wrap "0.1.0" + +ansi-escapes@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + +ansi-gray@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" + integrity sha1-KWLPVOyXksSFEKPetSRDaGHvclE= + dependencies: + ansi-wrap "0.1.0" + +ansi-red@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ansi-red/-/ansi-red-0.1.1.tgz#8c638f9d1080800a353c9c28c8a81ca4705d946c" + integrity sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw= + dependencies: + ansi-wrap "0.1.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-wrap@0.1.0, ansi-wrap@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" + integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +appc-security@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/appc-security/-/appc-security-0.1.0.tgz#519b08cb32fa6aec62c606dc5aedf11f1cbf9b26" + integrity sha512-UT6UzAZtfZKvbeUMgLVjA3q6ayfYpFbFDxbzZOv6ScZma4IXoTUn/glaJJyfAGyPEaBIgZbu2BHiAmriv8bhPw== + dependencies: + jsonwebtoken "^8.3.0" + +appcd-fs@^1.1.10: + version "1.1.10" + resolved "https://registry.yarnpkg.com/appcd-fs/-/appcd-fs-1.1.10.tgz#feb17938e2e3d8a09b565a75611203e000aae861" + integrity sha512-hLtsgLpR5T5HacraXPCjtNP8yDc9sdGx6cLie0PoutwvhqVJcpkD1LXRLRweKBM/O/Nwwyb8Ry3Um/HxCtMN9g== + dependencies: + source-map-support "^0.5.16" + +appcd-fs@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/appcd-fs/-/appcd-fs-2.0.4.tgz#7388af54839f4ab92fd9707c7ef57d962b5a024b" + integrity sha512-ulJwq2DB2pT4B4tNStB319n9eJzj9lPSrYD5ptF8283AYZUTo9uiL46OgLQSIwxD9wFfZTay+NfpHwNfqWXlkg== + dependencies: + source-map-support "^0.5.19" + +appcd-gulp@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/appcd-gulp/-/appcd-gulp-3.1.3.tgz#5819f5f513d31be10c0649cffb432698785cb37c" + integrity sha512-Z/1J3mkZUHHlQ+HDzLhhiHvo59zd5gamZPgIjHErzIqndRZnulVcG50gSYFdWCDJuKaE++eQK1j6RcZQ0V5y3w== + dependencies: + "@babel/core" "^7.12.10" + "@babel/plugin-proposal-class-properties" "^7.12.1" + "@babel/plugin-proposal-object-rest-spread" "^7.12.1" + "@babel/plugin-proposal-optional-chaining" "^7.12.7" + "@babel/plugin-transform-async-to-generator" "^7.12.1" + "@babel/plugin-transform-destructuring" "^7.12.1" + "@babel/plugin-transform-modules-commonjs" "^7.12.1" + "@babel/plugin-transform-parameters" "^7.12.1" + "@babel/register" "^7.12.10" + ansi-colors "^4.1.1" + babel-eslint "^10.1.0" + babel-loader "^8.2.2" + babel-plugin-dynamic-import-node "^2.3.3" + chai "^4.2.0" + chai-as-promised "^7.1.1" + core-js "^3.8.3" + esdoc "^1.1.0" + esdoc-ecmascript-proposal-plugin "^1.0.0" + esdoc-standard-plugin "^1.0.0" + eslint "^7.18.0" + eslint-config-axway "^5.0.0" + eslint-plugin-chai-friendly "^0.6.0" + eslint-plugin-mocha "^8.0.0" + eslint-plugin-node "^11.1.0" + fancy-log "^1.3.3" + fs-extra "^9.1.0" + gulp "^4.0.2" + gulp-babel "^8.0.0" + gulp-chug "^0.5.1" + gulp-debug "^4.0.0" + gulp-eslint "^6.0.0" + gulp-load-plugins "^2.0.6" + gulp-plumber "^1.2.1" + gulp-sourcemaps "^3.0.0" + mocha "^8.2.1" + mocha-jenkins-reporter "^0.4.5" + nyc "^15.1.0" + sinon "^9.2.4" + sinon-chai "^3.5.0" + webpack "^5.17.0" + +appcd-path@^1.1.10: + version "1.1.10" + resolved "https://registry.yarnpkg.com/appcd-path/-/appcd-path-1.1.10.tgz#62d8b61a1e0394d613958488e59acff410cede2a" + integrity sha512-SHVMoAjhwJIYkqrP3P8JdKGNOOfK9TTsGnXHJ8/FprmvWEvv7p7NbUCPX6GcTahF46QrMIoAqEUeKzO744bd9Q== + dependencies: + source-map-support "^0.5.16" + +appcd-path@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/appcd-path/-/appcd-path-2.0.5.tgz#48591e4bba0d16889f411f858d6b88c6cead8177" + integrity sha512-vr43I+tMjjmWWDqCIabrnoMaBGD1kKBnoLJiE7452uOKBTGt94JJ4nxpHwHaNXTvkTdXwGhdDnLUX9MXSZdi/Q== + dependencies: + source-map-support "^0.5.19" + +appcd-util@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/appcd-util/-/appcd-util-2.0.2.tgz#8c524fe78ba6748f6bcf56147be21265caee8ca1" + integrity sha512-57fIlDxdrENiist+uBv/1GsyQxC2vJhldxmXk5O+ue4WMtD8BdLDTmy4zX3aXtiFDGF4bTc4Hw1gEHauyWqHSA== + dependencies: + appcd-fs "^1.1.10" + lodash.get "^4.4.2" + semver "^7.1.1" + source-map-support "^0.5.16" + +appcd-util@^3.1.1, appcd-util@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/appcd-util/-/appcd-util-3.1.3.tgz#6080c5274487d9d8ed07e16940b119167d4ed2f3" + integrity sha512-76Go21u4TOOG9PjPTW7grnNUtRw8drWN0YZFC7mJrAUhRpamSc1AZhz8w9yhCVioUb8xvOQxPlm000AhbyMMIQ== + dependencies: + appcd-fs "^2.0.4" + lodash.get "^4.4.2" + lodash.set "^4.3.2" + semver "^7.3.4" + source-map-support "^0.5.19" + +append-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1" + integrity sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE= + dependencies: + buffer-equal "^1.0.0" + +append-transform@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-2.0.0.tgz#99d9d29c7b38391e6f428d28ce136551f0b77e12" + integrity sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg== + dependencies: + default-require-extensions "^3.0.0" + +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argv-split@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argv-split/-/argv-split-2.0.1.tgz#be264117790dbd5ccd63ec3f449a1804814ac4c5" + integrity sha1-viZBF3kNvVzNY+w/RJoYBIFKxMU= + +arr-diff@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-1.1.0.tgz#687c32758163588fef7de7b36fabe495eb1a399a" + integrity sha1-aHwydYFjWI/vfeezb6vklesaOZo= + dependencies: + arr-flatten "^1.0.1" + array-slice "^0.2.3" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-filter@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/arr-filter/-/arr-filter-1.1.2.tgz#43fdddd091e8ef11aa4c45d9cdc18e2dff1711ee" + integrity sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4= + dependencies: + make-iterator "^1.0.0" + +arr-flatten@^1.0.1, arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-map@^2.0.0, arr-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/arr-map/-/arr-map-2.0.2.tgz#3a77345ffc1cf35e2a91825601f9e58f2e24cac4" + integrity sha1-Onc0X/wc814qkYJWAfnljy4kysQ= + dependencies: + make-iterator "^1.0.0" + +arr-union@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-2.1.0.tgz#20f9eab5ec70f5c7d215b1077b1c39161d292c7d" + integrity sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0= + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-differ@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" + integrity sha1-7/UuN1gknTO+QCuLuOVkuytdQDE= + +array-each@^1.0.0, array-each@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" + integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= + +array-includes@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.2.tgz#a8db03e0b88c8c6aeddc49cb132f9bcab4ebf9c8" + integrity sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + get-intrinsic "^1.0.1" + is-string "^1.0.5" + +array-initial@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/array-initial/-/array-initial-1.1.0.tgz#2fa74b26739371c3947bd7a7adc73be334b3d795" + integrity sha1-L6dLJnOTccOUe9enrcc74zSz15U= + dependencies: + array-slice "^1.0.0" + is-number "^4.0.0" + +array-last@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/array-last/-/array-last-1.3.0.tgz#7aa77073fec565ddab2493f5f88185f404a9d336" + integrity sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg== + dependencies: + is-number "^4.0.0" + +array-slice@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5" + integrity sha1-3Tz7gO15c6dRF82sabC5nshhhvU= + +array-slice@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" + integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== + +array-sort@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" + integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== + dependencies: + default-compare "^1.0.0" + get-value "^2.0.6" + kind-of "^5.0.2" + +array-uniq@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +array.prototype.flat@^1.2.3: + version "1.2.4" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz#6ef638b43312bd401b4c6199fdec7e2dc9e9a123" + integrity sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +async-done@^1.2.0, async-done@^1.2.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/async-done/-/async-done-1.3.2.tgz#5e15aa729962a4b07414f528a88cdf18e0b290a2" + integrity sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.2" + process-nextick-args "^2.0.0" + stream-exhaust "^1.0.1" + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async-settle@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-settle/-/async-settle-1.0.0.tgz#1d0a914bb02575bec8a8f3a74e5080f72b2c0c6b" + integrity sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs= + dependencies: + async-done "^1.2.2" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-eslint@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" + integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.7.0" + "@babel/traverse" "^7.7.0" + "@babel/types" "^7.7.0" + eslint-visitor-keys "^1.0.0" + resolve "^1.12.0" + +babel-generator@6.11.4: + version "6.11.4" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.11.4.tgz#14f6933abb20c62666d27e3b7b9f5b9dc0712a9a" + integrity sha1-FPaTOrsgxiZm0n47e59bncBxKpo= + dependencies: + babel-messages "^6.8.0" + babel-runtime "^6.9.0" + babel-types "^6.10.2" + detect-indent "^3.0.1" + lodash "^4.2.0" + source-map "^0.5.0" + +babel-generator@6.26.1: + version "6.26.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" + integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.7" + trim-right "^1.0.1" + +babel-loader@^8.2.2: + version "8.2.2" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81" + integrity sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g== + dependencies: + find-cache-dir "^3.3.1" + loader-utils "^1.4.0" + make-dir "^3.1.0" + schema-utils "^2.6.5" + +babel-messages@^6.23.0, babel-messages@^6.8.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + +babel-runtime@^6.22.0, babel-runtime@^6.26.0, babel-runtime@^6.9.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-traverse@6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.10.2, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@6.18.0, babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + +bach@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" + integrity sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA= + dependencies: + arr-filter "^1.1.1" + arr-flatten "^1.0.1" + arr-map "^2.0.0" + array-each "^1.0.0" + array-initial "^1.0.0" + array-last "^1.1.1" + async-done "^1.2.2" + async-settle "^1.0.0" + now-and-later "^2.0.0" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-js@^1.1.2, base64-js@^1.2.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + 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" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +beeper@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" + integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak= + +big-integer@^1.6.44: + version "1.6.48" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e" + integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +bplist-creator@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.0.8.tgz#56b2a6e79e9aec3fc33bf831d09347d73794e79c" + integrity sha512-Za9JKzD6fjLC16oX2wsXfc+qBEhJBJB1YPInoAQpMLhDuj5aVOv1baGeIQSq1Fr3OCqzvsoQcSBSwGId/Ja2PA== + dependencies: + stream-buffers "~2.2.0" + +bplist-parser@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" + integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw== + dependencies: + big-integer "^1.6.44" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + 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" + +braces@^3.0.1, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brotli@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/brotli/-/brotli-1.3.2.tgz#525a9cad4fcba96475d7d388f6aecb13eed52f46" + integrity sha1-UlqcrU/LqWR119OI9q7LE+7VL0Y= + dependencies: + base64-js "^1.1.2" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserslist@^4.14.5: + version "4.16.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.1.tgz#bf757a2da376b3447b800a16f0f1c96358138766" + integrity sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA== + dependencies: + caniuse-lite "^1.0.30001173" + colorette "^1.2.1" + electron-to-chromium "^1.3.634" + escalade "^3.1.1" + node-releases "^1.1.69" + +bryt@^1.0.1, bryt@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bryt/-/bryt-1.0.2.tgz#041b58d0038aabb7517920e4d7ccad27f172cdd3" + integrity sha512-K/qt3xOAOU71q5Y2yJKOqleBwfeJ3J5tOn1pds9b/iQvQ/cq984oSzuv6iOXRxoTdZH3WGOazX1HXu0+sLU7VA== + dependencies: + brotli "^1.3.2" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + +buffer-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" + integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74= + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + 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" + +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-request@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58" + integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^2.0.0" + +caching-transform@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" + integrity sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA== + dependencies: + hasha "^5.0.0" + make-dir "^3.0.0" + package-hash "^4.0.0" + write-file-atomic "^3.0.0" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + +caniuse-lite@^1.0.30001173: + version "1.0.30001179" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001179.tgz#b0803883b4471a6c62066fb1752756f8afc699c8" + integrity sha512-blMmO0QQujuUWZKyVrD1msR4WNDAqb/UPO1Sw2WWsQ7deoM5bJiicKnWJ1Y0NS/aGINSnKPIWBMw5luX+NDUCA== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chai-as-promised@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" + integrity sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA== + dependencies: + check-error "^1.0.2" + +chai@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" + integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.0" + type-detect "^4.0.5" + +chalk@^1.0.0, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +cheerio@0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.20.0.tgz#5c710f2bab95653272842ba01c6ea61b3545ec35" + integrity sha1-XHEPK6uVZTJyhCugHG6mGzVF7DU= + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "~3.8.1" + lodash "^4.1.0" + optionalDependencies: + jsdom "^7.0.2" + +cheerio@0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" + integrity sha1-qbqoYKP5tZWmuBsahocxIe06Jp4= + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash.assignin "^4.0.9" + lodash.bind "^4.1.4" + lodash.defaults "^4.0.1" + lodash.filter "^4.4.0" + lodash.flatten "^4.2.0" + lodash.foreach "^4.3.0" + lodash.map "^4.4.0" + lodash.merge "^4.4.0" + lodash.pick "^4.2.1" + lodash.reduce "^4.4.0" + lodash.reject "^4.4.0" + lodash.some "^4.4.0" + +cheerio@1.0.0-rc.2: + version "1.0.0-rc.2" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db" + integrity sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs= + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash "^4.15.0" + parse5 "^3.0.1" + +chokidar@3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" + integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.1.2" + +chokidar@^2.0.0: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +chrome-trace-event@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" + integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== + dependencies: + tslib "^1.9.0" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-kit@^1.9.3: + version "1.9.3" + resolved "https://registry.yarnpkg.com/cli-kit/-/cli-kit-1.9.3.tgz#4ae6cfbf767ad8ca9e5773aafa605e58817f3642" + integrity sha512-mgdyf2fEv8+7rZlpIiVo4MkPoEZk2AiwKeX9w1V9U21RG3BDLdyxdOlr+7+JmN49L+WT5j5C/JubRoAigEdwVw== + dependencies: + argv-split "^2.0.1" + fastest-levenshtein "^1.0.12" + fs-extra "^9.0.1" + hook-emitter "^4.1.1" + lodash.camelcase "^4.3.0" + pkg-dir "^5.0.0" + pluralize "^8.0.0" + semver "^7.3.4" + snooplogg "^3.0.2" + source-map-support "^0.5.19" + which "^2.0.2" + ws "^7.4.2" + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +clone-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" + integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clone-stats@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" + integrity sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE= + +clone-stats@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" + integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= + +clone@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + +clone@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +cloneable-readable@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec" + integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== + dependencies: + inherits "^2.0.1" + process-nextick-args "^2.0.0" + readable-stream "^2.3.5" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collection-map@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-map/-/collection-map-1.0.0.tgz#aea0f06f8d26c780c2b75494385544b2255af18c" + integrity sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw= + dependencies: + arr-map "^2.0.2" + for-own "^1.0.0" + make-iterator "^1.0.0" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-logger@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/color-logger/-/color-logger-0.0.3.tgz#d9b22dd1d973e166b18bf313f9f481bba4df2018" + integrity sha1-2bIt0dlz4Waxi/MT+fSBu6TfIBg= + +color-logger@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/color-logger/-/color-logger-0.0.6.tgz#e56245ef29822657110c7cb75a9cd786cb69ed1b" + integrity sha1-5WJF7ymCJlcRDHy3WpzXhstp7Rs= + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-support@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +colorette@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" + integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== + +colors@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.6.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= + +convert-source-map@^1.0.0, convert-source-map@^1.5.0, convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +copy-props@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-2.0.4.tgz#93bb1cadfafd31da5bb8a9d4b41f471ec3a72dfe" + integrity sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A== + dependencies: + each-props "^1.3.0" + is-plain-object "^2.0.1" + +core-js@^2.4.0: + version "2.6.12" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" + integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== + +core-js@^3.8.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.8.3.tgz#c21906e1f14f3689f93abcc6e26883550dd92dd0" + integrity sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0, cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-what@2.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== + +css@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d" + integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ== + dependencies: + inherits "^2.0.4" + source-map "^0.6.1" + source-map-resolve "^0.6.0" + +cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0": + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +"cssstyle@>= 0.2.29 < 0.3.0": + version "0.2.37" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" + integrity sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ= + dependencies: + cssom "0.3.x" + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +dateformat@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" + integrity sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI= + +dateformat@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.5.0.tgz#2b5fbae50d9f30dd0c82bf85344878cbc41b08cc" + integrity sha512-YkS64mgmj9At0Ua9p3qyqoHqjqkOTV/FGCluRIcrFNbhFmltQGl2x/DFBgpbRRPTNPoyxfzjqgNaGYjMbfnsdg== + +debug-fabulous@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-1.1.0.tgz#af8a08632465224ef4174a9f06308c3c2a1ebc8e" + integrity sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg== + dependencies: + debug "3.X" + memoizee "0.4.X" + object-assign "4.X" + +debug@3.X: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +debug@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + dependencies: + ms "2.1.2" + +debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +decamelize@^1.1.1, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + +deep-is@^0.1.3, deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +default-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" + integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== + dependencies: + kind-of "^5.0.2" + +default-require-extensions@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-3.0.0.tgz#e03f93aac9b2b6443fc52e5e4a37b3ad9ad8df96" + integrity sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg== + dependencies: + strip-bom "^4.0.0" + +default-resolution@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/default-resolution/-/default-resolution-2.0.0.tgz#bcb82baa72ad79b426a76732f1a81ad6df26d684" + integrity sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ= + +defer-to-connect@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.0.tgz#83d6b199db041593ac84d781b5222308ccf4c2c1" + integrity sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg== + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +detect-file@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" + integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= + +detect-indent@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-3.0.1.tgz#9dc5e5ddbceef8325764b9451b02bc6d54084f75" + integrity sha1-ncXl3bzu+DJXZLlFGwK8bVQIT3U= + dependencies: + get-stdin "^4.0.1" + minimist "^1.1.0" + repeating "^1.1.0" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= + dependencies: + repeating "^2.0.0" + +detect-newline@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" + integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= + +diff@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.1.tgz#0c667cb467ebbb5cea7f14f135cc2dba7780a8ff" + integrity sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q== + +diff@4.0.2, diff@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-serializer@0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +dom-serializer@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" + integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== + dependencies: + domelementtype "^1.3.0" + entities "^1.1.1" + +domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + +domelementtype@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" + integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== + +domhandler@2.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" + integrity sha1-LeWaCCLVAn+r/28DLCsloqir5zg= + dependencies: + domelementtype "1" + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domutils@1.5, domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + +duplexer2@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" + integrity sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds= + dependencies: + readable-stream "~1.1.9" + +duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +each-props@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/each-props/-/each-props-1.3.2.tgz#ea45a414d16dd5cfa419b1a81720d5ca06892333" + integrity sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA== + dependencies: + is-plain-object "^2.0.1" + object.defaults "^1.1.0" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +electron-to-chromium@^1.3.634: + version "1.3.645" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.645.tgz#c0b269ae2ecece5aedc02dd4586397d8096affb1" + integrity sha512-T7mYop3aDpRHIQaUYcmzmh6j9MAe560n6ukqjJMbVC6bVTau7dSpvB18bcsBPPtOSe10cKxhJFtlbEzLa0LL1g== + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^5.7.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz#525c5d856680fbd5052de453ac83e32049958b5c" + integrity sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +enquirer@^2.3.5, enquirer@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + +entities@1.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" + integrity sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY= + +entities@^1.1.1, entities@~1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +error-ex@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.18.0-next.1: + version "1.18.0-next.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.2.tgz#088101a55f0541f595e7e057199e27ddc8f3a5c2" + integrity sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.2" + is-negative-zero "^2.0.1" + is-regex "^1.1.1" + object-inspect "^1.9.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.3" + string.prototype.trimstart "^1.0.3" + +es-module-lexer@^0.3.26: + version "0.3.26" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.3.26.tgz#7b507044e97d5b03b01d4392c74ffeb9c177a83b" + integrity sha512-Va0Q/xqtrss45hWzP8CZJwzGSZJjDM5/MJRE3IXXnUCcVLElR9BRaE9F62BopysASyc4nM3uwhSW7FFB9nlWAA== + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-error@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + +es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +es6-weak-map@^2.0.1, es6-weak-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escodegen@^1.6.1: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +esdoc-accessor-plugin@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esdoc-accessor-plugin/-/esdoc-accessor-plugin-1.0.0.tgz#791ba4872e6c403515ce749b1348d6f0293ad9eb" + integrity sha1-eRukhy5sQDUVznSbE0jW8Ck62es= + +esdoc-brand-plugin@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esdoc-brand-plugin/-/esdoc-brand-plugin-1.0.1.tgz#7c0e1ae90e84c30b2d3369d3a6449f9dc9f8d511" + integrity sha512-Yv9j3M7qk5PSLmSeD6MbPsfIsEf8K43EdH8qZpE/GZwnJCRVmDPrZJ1cLDj/fPu6P35YqgcEaJK4E2NL/CKA7g== + dependencies: + cheerio "0.22.0" + +esdoc-coverage-plugin@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/esdoc-coverage-plugin/-/esdoc-coverage-plugin-1.1.0.tgz#3869869cd7f87891f972625787695a299aece45c" + integrity sha1-OGmGnNf4eJH5cmJXh2laKZrs5Fw= + +esdoc-ecmascript-proposal-plugin@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esdoc-ecmascript-proposal-plugin/-/esdoc-ecmascript-proposal-plugin-1.0.0.tgz#390dc5656ba8a2830e39dba3570d79138df2ffd9" + integrity sha1-OQ3FZWuoooMOOdujVw15E43y/9k= + +esdoc-external-ecmascript-plugin@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esdoc-external-ecmascript-plugin/-/esdoc-external-ecmascript-plugin-1.0.0.tgz#78f565d4a0c5185ac63152614dce1fe1a86688db" + integrity sha1-ePVl1KDFGFrGMVJhTc4f4ahmiNs= + dependencies: + fs-extra "1.0.0" + +esdoc-integrate-manual-plugin@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esdoc-integrate-manual-plugin/-/esdoc-integrate-manual-plugin-1.0.0.tgz#1854a6aa1c081035d7c8c51e3bdd4fb65aa4711c" + integrity sha1-GFSmqhwIEDXXyMUeO91PtlqkcRw= + +esdoc-integrate-test-plugin@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esdoc-integrate-test-plugin/-/esdoc-integrate-test-plugin-1.0.0.tgz#e2d0d00090f7f0c35e5d2f2c033327a79e53e409" + integrity sha1-4tDQAJD38MNeXS8sAzMnp55T5Ak= + +esdoc-lint-plugin@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/esdoc-lint-plugin/-/esdoc-lint-plugin-1.0.2.tgz#4962930c6dc5b25d80cf6eff1b0f3c24609077f7" + integrity sha512-24AYqD2WbZI9We02I7/6dzAa7yUliRTFUaJCZAcYJMQicJT5gUrNFVaI8XmWEN/mhF3szIn1uZBNWeLul4CmNw== + +esdoc-publish-html-plugin@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/esdoc-publish-html-plugin/-/esdoc-publish-html-plugin-1.1.2.tgz#bdece7bc7a0a3e419933503252db7a6772879dab" + integrity sha512-hG1fZmTcEp3P/Hv/qKiMdG1qSp8MjnVZMMkxL5P5ry7I2sX0HQ4P9lt2lms+90Lt0r340HHhSuVx107UL7dphg== + dependencies: + babel-generator "6.11.4" + cheerio "0.22.0" + escape-html "1.0.3" + fs-extra "1.0.0" + ice-cap "0.0.4" + marked "0.3.19" + taffydb "2.7.2" + +esdoc-standard-plugin@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esdoc-standard-plugin/-/esdoc-standard-plugin-1.0.0.tgz#661201cac7ef868924902446fdac1527253c5d4d" + integrity sha1-ZhIBysfvhokkkCRG/awVJyU8XU0= + dependencies: + esdoc-accessor-plugin "^1.0.0" + esdoc-brand-plugin "^1.0.0" + esdoc-coverage-plugin "^1.0.0" + esdoc-external-ecmascript-plugin "^1.0.0" + esdoc-integrate-manual-plugin "^1.0.0" + esdoc-integrate-test-plugin "^1.0.0" + esdoc-lint-plugin "^1.0.0" + esdoc-publish-html-plugin "^1.0.0" + esdoc-type-inference-plugin "^1.0.0" + esdoc-undocumented-identifier-plugin "^1.0.0" + esdoc-unexported-identifier-plugin "^1.0.0" + +esdoc-type-inference-plugin@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/esdoc-type-inference-plugin/-/esdoc-type-inference-plugin-1.0.2.tgz#916e3f756de1d81d9c0dbe1c008e8dafd322cfaf" + integrity sha512-tMIcEHNe1uhUGA7lT1UTWc9hs2dzthnTgmqXpmeUhurk7fL2tinvoH+IVvG/sLROzwOGZQS9zW/F9KWnpMzLIQ== + +esdoc-undocumented-identifier-plugin@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esdoc-undocumented-identifier-plugin/-/esdoc-undocumented-identifier-plugin-1.0.0.tgz#82e05d371c32d12871140f1d5c81ec99fd9cc2c8" + integrity sha1-guBdNxwy0ShxFA8dXIHsmf2cwsg= + +esdoc-unexported-identifier-plugin@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esdoc-unexported-identifier-plugin/-/esdoc-unexported-identifier-plugin-1.0.0.tgz#1f9874c6a7c2bebf9ad397c3ceb75c9c69dabab1" + integrity sha1-H5h0xqfCvr+a05fDzrdcnGnaurE= + +esdoc@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/esdoc/-/esdoc-1.1.0.tgz#07d40ebf791764cd537929c29111e20a857624f3" + integrity sha512-vsUcp52XJkOWg9m1vDYplGZN2iDzvmjDL5M/Mp8qkoDG3p2s0yIQCIjKR5wfPBaM3eV14a6zhQNYiNTCVzPnxA== + dependencies: + babel-generator "6.26.1" + babel-traverse "6.26.0" + babylon "6.18.0" + cheerio "1.0.0-rc.2" + color-logger "0.0.6" + escape-html "1.0.3" + fs-extra "5.0.0" + ice-cap "0.0.4" + marked "0.3.19" + minimist "1.2.0" + taffydb "2.7.3" + +eslint-config-axway@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-axway/-/eslint-config-axway-5.0.0.tgz#beea66ebd46a94d294045557bc387d64b6861c52" + integrity sha512-r7GqGGJI68vD8foYLE6qCXNVTn44LGWjJkwNg4nQHvjWT5MDamd4PofXBtPTfFOtp4rY38bDH+S5pvwef71ovA== + dependencies: + eslint-plugin-chai-expect "^2.1.0" + eslint-plugin-import "^2.20.2" + eslint-plugin-node "^11.1.0" + eslint-plugin-promise "^4.2.1" + eslint-plugin-security "^1.4.0" + find-root "^1.1.0" + semver "^7.3.2" + +eslint-import-resolver-node@^0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" + integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== + dependencies: + debug "^2.6.9" + resolve "^1.13.1" + +eslint-module-utils@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" + integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== + dependencies: + debug "^2.6.9" + pkg-dir "^2.0.0" + +eslint-plugin-chai-expect@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-chai-expect/-/eslint-plugin-chai-expect-2.2.0.tgz#ff41fb00067fe6ff26e72b04ab1011ecaf3cb249" + integrity sha512-ExTJKhgeYMfY8wDj3UiZmgpMKJOUHGNHmWMlxT49JUDB1vTnw0sSNfXJSxnX+LcebyBD/gudXzjzD136WqPJrQ== + +eslint-plugin-chai-friendly@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.6.0.tgz#54052fab79302ed0cea76ab997351ea4809bfb77" + integrity sha512-Uvvv1gkbRGp/qfN15B0kQyQWg+oFA8buDSqrwmW3egNSk/FpqH2MjQqKOuKwmEL6w4QIQrIjDp+gg6kGGmD3oQ== + +eslint-plugin-es@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" + integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== + dependencies: + eslint-utils "^2.0.0" + regexpp "^3.0.0" + +eslint-plugin-import@^2.20.2: + version "2.22.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702" + integrity sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw== + dependencies: + array-includes "^3.1.1" + array.prototype.flat "^1.2.3" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.4" + eslint-module-utils "^2.6.0" + has "^1.0.3" + minimatch "^3.0.4" + object.values "^1.1.1" + read-pkg-up "^2.0.0" + resolve "^1.17.0" + tsconfig-paths "^3.9.0" + +eslint-plugin-mocha@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-8.0.0.tgz#7ec5d228bcb3735301701dfbc3376320a1ca3791" + integrity sha512-n67etbWDz6NQM+HnTwZHyBwz/bLlYPOxUbw7bPuCyFujv7ZpaT/Vn6KTAbT02gf7nRljtYIjWcTxK/n8a57rQQ== + dependencies: + eslint-utils "^2.1.0" + ramda "^0.27.1" + +eslint-plugin-node@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" + integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== + dependencies: + eslint-plugin-es "^3.0.0" + eslint-utils "^2.0.0" + ignore "^5.1.1" + minimatch "^3.0.4" + resolve "^1.10.1" + semver "^6.1.0" + +eslint-plugin-promise@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" + integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== + +eslint-plugin-security@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-security/-/eslint-plugin-security-1.4.0.tgz#d4f314484a80b1b613b8c8886e84f52efe1526c2" + integrity sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA== + dependencies: + safe-regex "^1.1.0" + +eslint-scope@^5.0.0, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-utils@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-utils@^2.0.0, eslint-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint-visitor-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + +eslint@^6.0.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^1.4.3" + eslint-visitor-keys "^1.1.0" + espree "^6.1.2" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^7.0.0" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.14" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.3" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +eslint@^7.18.0: + version "7.18.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.18.0.tgz#7fdcd2f3715a41fe6295a16234bd69aed2c75e67" + integrity sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ== + dependencies: + "@babel/code-frame" "^7.0.0" + "@eslint/eslintrc" "^0.3.0" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + enquirer "^2.3.5" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.1" + esquery "^1.2.0" + esutils "^2.0.2" + file-entry-cache "^6.0.0" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash "^4.17.20" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^6.0.4" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== + dependencies: + acorn "^7.1.1" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.1.0" + +espree@^7.3.0, espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== + dependencies: + acorn "^7.4.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^1.3.0" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1, esquery@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1, estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= + dependencies: + d "1" + es5-ext "~0.10.14" + +events@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" + integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + 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" + +expand-tilde@^2.0.0, expand-tilde@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" + integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= + dependencies: + homedir-polyfill "^1.0.1" + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +extend-shallow@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-1.1.4.tgz#19d6bf94dfc09d76ba711f39b872d21ff4dd9071" + integrity sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE= + dependencies: + kind-of "^1.1.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@^3.0.0, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + 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" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fancy-log@^1.1.0, fancy-log@^1.2.0, fancy-log@^1.3.2, fancy-log@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" + integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== + dependencies: + ansi-gray "^0.1.1" + color-support "^1.1.3" + parse-node-version "^1.0.0" + time-stamp "^1.0.0" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz#e6a754cc8f15e58987aa9cbd27af66fd6f4e5af9" + integrity sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk= + +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fastest-levenshtein@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" + integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + +figures@^3.0.0, figures@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +file-entry-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.0.tgz#7921a89c391c6d93efec2169ac6bf300c527ea0a" + integrity sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA== + dependencies: + flat-cache "^3.0.4" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +filesize@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00" + integrity sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-cache-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-cache-dir@^3.2.0, find-cache-dir@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" + integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-root@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + +find-up@5.0.0, find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +findup-sync@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" + integrity sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw= + dependencies: + detect-file "^1.0.0" + is-glob "^3.1.0" + micromatch "^3.0.4" + resolve-dir "^1.0.1" + +findup-sync@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" + integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== + dependencies: + detect-file "^1.0.0" + is-glob "^4.0.0" + micromatch "^3.0.4" + resolve-dir "^1.0.1" + +findup-sync@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-4.0.0.tgz#956c9cdde804052b881b428512905c4a5f2cdef0" + integrity sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ== + dependencies: + detect-file "^1.0.0" + is-glob "^4.0.0" + micromatch "^4.0.2" + resolve-dir "^1.0.1" + +fined@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fined/-/fined-1.2.0.tgz#d00beccf1aa2b475d16d423b0238b713a2c4a37b" + integrity sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng== + dependencies: + expand-tilde "^2.0.2" + is-plain-object "^2.0.3" + object.defaults "^1.1.0" + object.pick "^1.2.0" + parse-filepath "^1.0.1" + +flagged-respawn@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41" + integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +flatted@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" + integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== + +flush-write-stream@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + dependencies: + inherits "^2.0.3" + readable-stream "^2.3.6" + +for-in@^1.0.1, for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +for-own@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" + integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs= + dependencies: + for-in "^1.0.1" + +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^3.0.2" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" + integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fromentries@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" + integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg== + +fs-extra@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" + integrity sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + +fs-extra@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" + integrity sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^9.0.1, fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs-mkdirp-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb" + integrity sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes= + dependencies: + graceful-fs "^4.1.11" + through2 "^2.0.3" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.2.7: + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +gawk@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/gawk/-/gawk-5.0.0.tgz#440f6eeeab5f894994399f311c82ffd4581ebc99" + integrity sha512-2AtcevNNnQwG/NU86sdpB4ACH7yftPnIs4/AiwkmIWb8CIqh0Dp3c8nt98446vwmmBiEC4oazhGXtSZZR5CizQ== + dependencies: + fast-deep-equal "^3.1.3" + source-map-support "^0.5.19" + +gensync@^1.0.0-beta.1: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + +get-intrinsic@^1.0.1, get-intrinsic@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.0.tgz#892e62931e6938c8a23ea5aaebcfb67bd97da97e" + integrity sha512-M11rgtQp5GZMZzDL7jLTNxbDfurpzuau5uqRWDPvlHjfvg3TdScAZo96GLvhMjImrmR8uAt0FS2RLoMrfWGKlg== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-port@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@^5.0.0, glob-parent@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + +glob-stream@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4" + integrity sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ= + dependencies: + extend "^3.0.0" + glob "^7.1.1" + glob-parent "^3.1.0" + is-negated-glob "^1.0.0" + ordered-read-streams "^1.0.0" + pumpify "^1.3.5" + readable-stream "^2.1.5" + remove-trailing-separator "^1.0.1" + to-absolute-glob "^2.0.0" + unique-stream "^2.0.2" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob-watcher@^5.0.3: + version "5.0.5" + resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-5.0.5.tgz#aa6bce648332924d9a8489be41e3e5c52d4186dc" + integrity sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw== + dependencies: + anymatch "^2.0.0" + async-done "^1.2.0" + chokidar "^2.0.0" + is-negated-glob "^1.0.0" + just-debounce "^1.0.0" + normalize-path "^3.0.0" + object.defaults "^1.1.0" + +glob@7.1.6, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + 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" + +global-modules@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" + integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== + dependencies: + global-prefix "^1.0.1" + is-windows "^1.0.1" + resolve-dir "^1.0.0" + +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" + integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= + dependencies: + expand-tilde "^2.0.2" + homedir-polyfill "^1.0.1" + ini "^1.3.4" + is-windows "^1.0.1" + which "^1.2.14" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== + +glogg@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.2.tgz#2d7dd702beda22eb3bffadf880696da6d846313f" + integrity sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA== + dependencies: + sparkles "^1.0.0" + +got@^11.8.1: + version "11.8.1" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.1.tgz#df04adfaf2e782babb3daabc79139feec2f7e85d" + integrity sha512-9aYdZL+6nHmvJwHALLwKSUZ0hMwGaJGYv3hoPLPgnT8BoBXm1SjnZeky+91tfwJaDzun2s4RsBRy48IEYv2q2Q== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.1" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + +graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +gulp-babel@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/gulp-babel/-/gulp-babel-8.0.0.tgz#e0da96f4f2ec4a88dd3a3030f476e38ab2126d87" + integrity sha512-oomaIqDXxFkg7lbpBou/gnUkX51/Y/M2ZfSjL2hdqXTAlSWZcgZtd2o0cOH0r/eE8LWD0+Q/PsLsr2DKOoqToQ== + dependencies: + plugin-error "^1.0.1" + replace-ext "^1.0.0" + through2 "^2.0.0" + vinyl-sourcemaps-apply "^0.2.0" + +gulp-chug@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/gulp-chug/-/gulp-chug-0.5.1.tgz#b1918881b2bb52fd47e3cb2371587fca4c45e5c6" + integrity sha1-sZGIgbK7Uv1H48sjcVh/ykxF5cY= + dependencies: + gulp-util "^3.0.7" + lodash "^4.0.0" + resolve "^1.1.6" + through2 "^2.0.0" + +gulp-cli@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-2.3.0.tgz#ec0d380e29e52aa45e47977f0d32e18fd161122f" + integrity sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A== + dependencies: + ansi-colors "^1.0.1" + archy "^1.0.0" + array-sort "^1.0.0" + color-support "^1.1.3" + concat-stream "^1.6.0" + copy-props "^2.0.1" + fancy-log "^1.3.2" + gulplog "^1.0.0" + interpret "^1.4.0" + isobject "^3.0.1" + liftoff "^3.1.0" + matchdep "^2.0.0" + mute-stdout "^1.0.0" + pretty-hrtime "^1.0.0" + replace-homedir "^1.0.0" + semver-greatest-satisfied-range "^1.1.0" + v8flags "^3.2.0" + yargs "^7.1.0" + +gulp-debug@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/gulp-debug/-/gulp-debug-4.0.0.tgz#036f9539c3fb6af720e01a9ea5c195fc73f29d5b" + integrity sha512-cn/GhMD2nVZCVxAl5vWao4/dcoZ8wUJ8w3oqTvQaGDmC1vT7swNOEbhQTWJp+/otKePT64aENcqAQXDcdj5H1g== + dependencies: + chalk "^2.3.0" + fancy-log "^1.3.2" + plur "^3.0.0" + stringify-object "^3.0.0" + through2 "^2.0.0" + tildify "^1.1.2" + +gulp-eslint@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gulp-eslint/-/gulp-eslint-6.0.0.tgz#7d402bb45f8a67652b868277011812057370a832" + integrity sha512-dCVPSh1sA+UVhn7JSQt7KEb4An2sQNbOdB3PA8UCfxsoPlAKjJHxYHGXdXC7eb+V1FAnilSFFqslPrq037l1ig== + dependencies: + eslint "^6.0.0" + fancy-log "^1.3.2" + plugin-error "^1.0.1" + +gulp-load-plugins@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/gulp-load-plugins/-/gulp-load-plugins-2.0.6.tgz#e4d34b614de93775ad2ed80fd145dcf1eaba0482" + integrity sha512-HP5jUhPzvib37kWYLFvhDQJpBar2pXG7diFOFI4/PgCrQWobV5/MfnU0dMx0d5NfyJGcRrpUI1E0MROlLvNO4A== + dependencies: + array-unique "^0.3.2" + fancy-log "^1.2.0" + findup-sync "^4.0.0" + gulplog "^1.0.0" + has-gulplog "^0.1.0" + micromatch "^4.0.2" + resolve "^1.17.0" + +gulp-plumber@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/gulp-plumber/-/gulp-plumber-1.2.1.tgz#d38700755a300b9d372318e4ffb5ff7ced0b2c84" + integrity sha512-mctAi9msEAG7XzW5ytDVZ9PxWMzzi1pS2rBH7lA095DhMa6KEXjm+St0GOCc567pJKJ/oCvosVAZEpAey0q2eQ== + dependencies: + chalk "^1.1.3" + fancy-log "^1.3.2" + plugin-error "^0.1.2" + through2 "^2.0.3" + +gulp-sourcemaps@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz#2e154e1a2efed033c0e48013969e6f30337b2743" + integrity sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ== + dependencies: + "@gulp-sourcemaps/identity-map" "^2.0.1" + "@gulp-sourcemaps/map-sources" "^1.0.0" + acorn "^6.4.1" + convert-source-map "^1.0.0" + css "^3.0.0" + debug-fabulous "^1.0.0" + detect-newline "^2.0.0" + graceful-fs "^4.0.0" + source-map "^0.6.0" + strip-bom-string "^1.0.0" + through2 "^2.0.0" + +gulp-util@^3.0.7: + version "3.0.8" + resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" + integrity sha1-AFTh50RQLifATBh8PsxQXdVLu08= + dependencies: + array-differ "^1.0.0" + array-uniq "^1.0.2" + beeper "^1.0.0" + chalk "^1.0.0" + dateformat "^2.0.0" + fancy-log "^1.1.0" + gulplog "^1.0.0" + has-gulplog "^0.1.0" + lodash._reescape "^3.0.0" + lodash._reevaluate "^3.0.0" + lodash._reinterpolate "^3.0.0" + lodash.template "^3.0.0" + minimist "^1.1.0" + multipipe "^0.1.2" + object-assign "^3.0.0" + replace-ext "0.0.1" + through2 "^2.0.0" + vinyl "^0.5.0" + +gulp@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/gulp/-/gulp-4.0.2.tgz#543651070fd0f6ab0a0650c6a3e6ff5a7cb09caa" + integrity sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA== + dependencies: + glob-watcher "^5.0.3" + gulp-cli "^2.2.0" + undertaker "^1.2.1" + vinyl-fs "^3.0.0" + +gulplog@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" + integrity sha1-4oxNRdBey77YGDY86PnFkmIp/+U= + dependencies: + glogg "^1.0.0" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-gulplog@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" + integrity sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4= + dependencies: + sparkles "^1.0.0" + +has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hasha@^5.0.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1" + integrity sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ== + dependencies: + is-stream "^2.0.0" + type-fest "^0.8.0" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +homedir-polyfill@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" + integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== + dependencies: + parse-passwd "^1.0.0" + +hook-emitter@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/hook-emitter/-/hook-emitter-4.1.1.tgz#b0c5a14d99382d64cad7f3ddb48a3a85e746793a" + integrity sha512-atobP7v7JN3Q4exr9L88YyEWjtFD9Ua31tlUckjBtn5/vHcYlXrEupZmF38SSAZpQlyC/tY5MR0Aq1xrEcKVMg== + dependencies: + source-map-support "^0.5.19" + +hosted-git-info@^2.1.4: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +htmlparser2@^3.9.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + +htmlparser2@~3.8.1: + version "3.8.3" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068" + integrity sha1-mWwosZFRaovoZQGn15dX5ccMEGg= + dependencies: + domelementtype "1" + domhandler "2.3" + domutils "1.5" + entities "1.0" + readable-stream "1.1" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.0-beta.5.2" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz#8b923deb90144aea65cf834b016a340fc98556f3" + integrity sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +ice-cap@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/ice-cap/-/ice-cap-0.0.4.tgz#8a6d31ab4cac8d4b56de4fa946df3352561b6e18" + integrity sha1-im0xq0ysjUtW3k+pRt8zUlYbbhg= + dependencies: + cheerio "0.20.0" + color-logger "0.0.3" + +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.1.1: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@^1.3.4, ini@^1.3.5: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +inquirer@^7.0.0: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +interpret@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +invariant@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= + +irregular-plurals@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-2.0.0.tgz#39d40f05b00f656d0b7fa471230dd3b714af2872" + integrity sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw== + +is-absolute@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" + integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== + dependencies: + is-relative "^1.0.0" + is-windows "^1.0.1" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.4, is-callable@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" + integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== + +is-core-module@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" + integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-docker@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156" + integrity sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw== + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-negated-glob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" + integrity sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI= + +is-negative-zero@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-promise@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + +is-regex@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= + +is-relative@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" + integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== + dependencies: + is-unc-path "^1.0.0" + +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-unc-path@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" + integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== + dependencies: + unc-path-regex "^0.1.2" + +is-utf8@^0.2.0, is-utf8@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +is-valid-glob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa" + integrity sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao= + +is-windows@^1.0.1, is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.0.0-alpha.1: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" + integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== + +istanbul-lib-hook@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz#8f84c9434888cc6b1d0a9d7092a76d239ebf0cc6" + integrity sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ== + dependencies: + append-transform "^2.0.0" + +istanbul-lib-instrument@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-processinfo@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz#e1426514662244b2f25df728e8fd1ba35fe53b9c" + integrity sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw== + dependencies: + archy "^1.0.0" + cross-spawn "^7.0.0" + istanbul-lib-coverage "^3.0.0-alpha.1" + make-dir "^3.0.0" + p-map "^3.0.0" + rimraf "^3.0.0" + uuid "^3.3.3" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + +js-yaml@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsdom@^7.0.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-7.2.2.tgz#40b402770c2bda23469096bee91ab675e3b1fc6e" + integrity sha1-QLQCdwwr2iNGkJa+6Rq2deOx/G4= + dependencies: + abab "^1.0.0" + acorn "^2.4.0" + acorn-globals "^1.0.4" + cssom ">= 0.3.0 < 0.4.0" + cssstyle ">= 0.2.29 < 0.3.0" + escodegen "^1.6.1" + nwmatcher ">= 1.3.7 < 2.0.0" + parse5 "^1.5.1" + request "^2.55.0" + sax "^1.1.4" + symbol-tree ">= 3.1.0 < 4.0.0" + tough-cookie "^2.2.0" + webidl-conversions "^2.0.0" + whatwg-url-compat "~0.6.5" + xml-name-validator ">= 2.0.1 < 3.0.0" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonwebtoken@^8.3.0: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +just-debounce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.0.0.tgz#87fccfaeffc0b68cd19d55f6722943f929ea35ea" + integrity sha1-h/zPrv/AtozRnVX2cilD+SnqNeo= + +just-extend@^4.0.2: + version "4.1.1" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.1.tgz#158f1fdb01f128c411dc8b286a7b4837b3545282" + integrity sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA== + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +keyv@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254" + integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA== + dependencies: + json-buffer "3.0.1" + +kind-of@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44" + integrity sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ= + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0, kind-of@^5.0.2: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= + optionalDependencies: + graceful-fs "^4.1.9" + +last-run@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/last-run/-/last-run-1.1.1.tgz#45b96942c17b1c79c772198259ba943bebf8ca5b" + integrity sha1-RblpQsF7HHnHchmCWbqUO+v4yls= + dependencies: + default-resolution "^2.0.0" + es6-weak-map "^2.0.1" + +lazystream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" + integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= + dependencies: + readable-stream "^2.0.5" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= + dependencies: + invert-kv "^1.0.0" + +lead@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42" + integrity sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI= + dependencies: + flush-write-stream "^1.0.2" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +liftoff@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3" + integrity sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog== + dependencies: + extend "^3.0.0" + findup-sync "^3.0.0" + fined "^1.0.1" + flagged-respawn "^1.0.0" + is-plain-object "^2.0.4" + object.map "^1.0.0" + rechoir "^0.6.2" + resolve "^1.1.7" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +loader-runner@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" + integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== + +loader-utils@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash._basecopy@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" + integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= + +lodash._basetostring@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" + integrity sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U= + +lodash._basevalues@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" + integrity sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc= + +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= + +lodash._isiterateecall@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" + integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw= + +lodash._reescape@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" + integrity sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo= + +lodash._reevaluate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" + integrity sha1-WLx0xAZklTrgsSTYBpltrKQx4u0= + +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + +lodash._root@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" + integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI= + +lodash.assignin@^4.0.9: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" + integrity sha1-uo31+4QesKPoBEIysOJjqNxqKKI= + +lodash.bind@^4.1.4: + version "4.2.1" + resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" + integrity sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU= + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + +lodash.defaults@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= + +lodash.escape@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" + integrity sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg= + dependencies: + lodash._root "^3.0.0" + +lodash.filter@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" + integrity sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4= + +lodash.flatten@^4.2.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= + +lodash.flattendeep@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" + integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= + +lodash.foreach@^4.3.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" + integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM= + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + +lodash.keys@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash.map@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" + integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM= + +lodash.merge@^4.4.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + +lodash.pick@^4.2.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= + +lodash.reduce@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" + integrity sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs= + +lodash.reject@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" + integrity sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU= + +lodash.restparam@^3.0.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" + integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= + +lodash.set@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" + integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= + +lodash.some@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" + integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0= + +lodash.template@^3.0.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" + integrity sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8= + dependencies: + lodash._basecopy "^3.0.0" + lodash._basetostring "^3.0.0" + lodash._basevalues "^3.0.0" + lodash._isiterateecall "^3.0.0" + lodash._reinterpolate "^3.0.0" + lodash.escape "^3.0.0" + lodash.keys "^3.0.0" + lodash.restparam "^3.0.0" + lodash.templatesettings "^3.0.0" + +lodash.templatesettings@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" + integrity sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU= + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.escape "^3.0.0" + +lodash@^4.0.0, lodash@^4.1.0, lodash@^4.15.0, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.0: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +log-symbols@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== + dependencies: + chalk "^4.0.0" + +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lru-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= + dependencies: + es5-ext "~0.10.2" + +make-dir@^2.0.0, make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-iterator@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" + integrity sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw== + dependencies: + kind-of "^6.0.2" + +map-cache@^0.2.0, map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +marked@0.3.19: + version "0.3.19" + resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.19.tgz#5d47f709c4c9fc3c216b6d46127280f40b39d790" + integrity sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg== + +matchdep@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e" + integrity sha1-xvNINKDY28OzfCfui7yyfHd1WC4= + dependencies: + findup-sync "^2.0.0" + micromatch "^3.0.4" + resolve "^1.4.0" + stack-trace "0.0.10" + +memoizee@0.4.X: + version "0.4.15" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" + integrity sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ== + dependencies: + d "^1.0.1" + es5-ext "^0.10.53" + es6-weak-map "^2.0.3" + event-emitter "^0.3.5" + is-promise "^2.2.2" + lru-queue "^0.1.0" + next-tick "^1.1.0" + timers-ext "^0.1.7" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + 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" + +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + +mime-db@1.45.0: + version "1.45.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" + integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== + +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19: + version "2.1.28" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" + integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== + dependencies: + mime-db "1.45.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +minimist@^1.1.0, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@^0.5.1, mkdirp@^0.5.4: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mocha-jenkins-reporter@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/mocha-jenkins-reporter/-/mocha-jenkins-reporter-0.4.5.tgz#d12865e0d991afbaa1d011b966195ebb8936850f" + integrity sha512-QoKXaxWz3gpzCBgfaqu2OZKVyibAwRTD/BF7ApMfNgafzzch9s8hMNVPTxRom9smmUAfaDfzARWKvrQMK7XACA== + dependencies: + diff "4.0.1" + mkdirp "^0.5.4" + xml "^1.0.1" + +mocha@^8.2.1: + version "8.2.1" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.2.1.tgz#f2fa68817ed0e53343d989df65ccd358bc3a4b39" + integrity sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w== + dependencies: + "@ungap/promise-all-settled" "1.1.2" + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.4.3" + debug "4.2.0" + diff "4.0.2" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.1.6" + growl "1.10.5" + he "1.2.0" + js-yaml "3.14.0" + log-symbols "4.0.0" + minimatch "3.0.4" + ms "2.1.2" + nanoid "3.1.12" + serialize-javascript "5.0.1" + strip-json-comments "3.1.1" + supports-color "7.2.0" + which "2.0.2" + wide-align "1.1.3" + workerpool "6.0.2" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "2.0.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multipipe@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" + integrity sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s= + dependencies: + duplexer2 "0.0.2" + +mute-stdout@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331" + integrity sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +nan@^2.12.1: + version "2.14.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" + integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + +nanobuffer@^1.1.6, nanobuffer@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/nanobuffer/-/nanobuffer-1.1.7.tgz#d81b71411c1dc47af58c4c5f864cd2b0eb0bf404" + integrity sha512-LP1JYWQh4qFmpLaauE3mWuOjGJSxts+wnYeTlSb4zurdn8xIhJ906oyb7M7Ih6EiImaUskFdHfYx9gyijf1FLA== + dependencies: + source-map-support "^0.5.19" + +nanoid@3.1.12: + version "3.1.12" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.12.tgz#6f7736c62e8d39421601e4a0c77623a97ea69654" + integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + 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" + +napi-macros@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" + integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +next-tick@1, next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +nise@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/nise/-/nise-4.0.4.tgz#d73dea3e5731e6561992b8f570be9e363c4512dd" + integrity sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/fake-timers" "^6.0.0" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + path-to-regexp "^1.7.0" + +node-forge@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" + integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== + +node-gyp-build@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" + integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== + +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + +node-preload@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/node-preload/-/node-preload-0.2.1.tgz#c03043bb327f417a18fee7ab7ee57b408a144301" + integrity sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ== + dependencies: + process-on-spawn "^1.0.0" + +node-releases@^1.1.69: + version "1.1.70" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.70.tgz#66e0ed0273aa65666d7fe78febe7634875426a08" + integrity sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw== + +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.1, normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + +now-and-later@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.1.tgz#8e579c8685764a7cc02cb680380e94f43ccb1f7c" + integrity sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ== + dependencies: + once "^1.3.2" + +nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +"nwmatcher@>= 1.3.7 < 2.0.0": + version "1.4.4" + resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.4.tgz#2285631f34a95f0d0395cd900c96ed39b58f346e" + integrity sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ== + +nyc@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.1.0.tgz#1335dae12ddc87b6e249d5a1994ca4bdaea75f02" + integrity sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A== + dependencies: + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + caching-transform "^4.0.0" + convert-source-map "^1.7.0" + decamelize "^1.2.0" + find-cache-dir "^3.2.0" + find-up "^4.1.0" + foreground-child "^2.0.0" + get-package-type "^0.1.0" + glob "^7.1.6" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-hook "^3.0.0" + istanbul-lib-instrument "^4.0.0" + istanbul-lib-processinfo "^2.0.2" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + make-dir "^3.0.0" + node-preload "^0.2.1" + p-map "^3.0.0" + process-on-spawn "^1.0.0" + resolve-from "^5.0.0" + rimraf "^3.0.0" + signal-exit "^3.0.2" + spawn-wrap "^2.0.0" + test-exclude "^6.0.0" + yargs "^15.0.2" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@4.X: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-assign@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" + integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" + integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.0.4, object.assign@^4.1.0, object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.defaults@^1.0.0, object.defaults@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" + integrity sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8= + dependencies: + array-each "^1.0.1" + array-slice "^1.0.0" + for-own "^1.0.0" + isobject "^3.0.0" + +object.map@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" + integrity sha1-z4Plncj8wK1fQlDh94s7gb2AHTc= + dependencies: + for-own "^1.0.0" + make-iterator "^1.0.0" + +object.pick@^1.2.0, object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +object.reduce@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.reduce/-/object.reduce-1.0.1.tgz#6fe348f2ac7fa0f95ca621226599096825bb03ad" + integrity sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60= + dependencies: + for-own "^1.0.0" + make-iterator "^1.0.0" + +object.values@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.2.tgz#7a2015e06fcb0f546bd652486ce8583a4731c731" + integrity sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + has "^1.0.3" + +once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +open@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/open/-/open-7.3.1.tgz#111119cb919ca1acd988f49685c4fdd0f4755356" + integrity sha512-f2wt9DCBKKjlFbjzGb8MOAW8LH8F0mrs1zc7KTjAJ9PZNQbfenzWbNP1VZJvw6ICMG9r14Ah6yfwPn7T7i646A== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + +optionator@^0.8.1, optionator@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + +ordered-read-streams@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" + integrity sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4= + dependencies: + readable-stream "^2.0.1" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= + dependencies: + lcid "^1.0.0" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-cancelable@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" + integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2, p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" + integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== + dependencies: + aggregate-error "^3.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-hash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506" + integrity sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ== + dependencies: + graceful-fs "^4.1.15" + hasha "^5.0.0" + lodash.flattendeep "^4.4.0" + release-zalgo "^1.0.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-filepath@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" + integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= + dependencies: + is-absolute "^1.0.0" + map-cache "^0.2.0" + path-root "^0.1.1" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-node-version@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= + +parse5@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" + integrity sha1-m387DeMr543CQBsXVzzK8Pb1nZQ= + +parse5@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" + integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== + dependencies: + "@types/node" "*" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-root-regex@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" + integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= + +path-root@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" + integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= + dependencies: + path-root-regex "^0.1.0" + +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + +pathval@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pirates@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-dir@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" + integrity sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA== + dependencies: + find-up "^5.0.0" + +plist@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.1.tgz#a9b931d17c304e8912ef0ba3bdd6182baf2e1f8c" + integrity sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ== + dependencies: + base64-js "^1.2.3" + xmlbuilder "^9.0.7" + xmldom "0.1.x" + +plugin-error@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-0.1.2.tgz#3b9bb3335ccf00f425e07437e19276967da47ace" + integrity sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4= + dependencies: + ansi-cyan "^0.1.1" + ansi-red "^0.1.1" + arr-diff "^1.0.1" + arr-union "^2.0.1" + extend-shallow "^1.1.2" + +plugin-error@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" + integrity sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA== + dependencies: + ansi-colors "^1.0.1" + arr-diff "^4.0.0" + arr-union "^3.1.0" + extend-shallow "^3.0.2" + +plur@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/plur/-/plur-3.1.1.tgz#60267967866a8d811504fe58f2faaba237546a5b" + integrity sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w== + dependencies: + irregular-plurals "^2.0.0" + +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +postcss@^7.0.16: + version "7.0.35" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24" + integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +pretty-bytes@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.5.0.tgz#0cecda50a74a941589498011cf23275aa82b339e" + integrity sha512-p+T744ZyjjiaFlMUZZv6YPC5JrkNj8maRmPaQCWFJFplUAzpIUTRaTcS+7wmZtUoFXHtESJb23ISliaWyz3SHA== + +pretty-hrtime@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" + integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= + +process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process-on-spawn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-on-spawn/-/process-on-spawn-1.0.0.tgz#95b05a23073d30a17acfdc92a440efd2baefdc93" + integrity sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg== + dependencies: + fromentries "^1.2.0" + +progress@^2.0.0, progress@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.5: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +ramda@^0.27.1: + version "0.27.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9" + integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +readable-stream@1.1: + version "1.1.13" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e" + integrity sha1-9u73ZPUUyJ4rniMUanW6EGdW0j4= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +"readable-stream@2 || 3", readable-stream@^3.1.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= + dependencies: + resolve "^1.1.6" + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +regexpp@^3.0.0, regexpp@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== + +release-zalgo@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" + integrity sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA= + dependencies: + es6-error "^4.0.1" + +remove-bom-buffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53" + integrity sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ== + dependencies: + is-buffer "^1.1.5" + is-utf8 "^0.2.1" + +remove-bom-stream@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz#05f1a593f16e42e1fb90ebf59de8e569525f9523" + integrity sha1-BfGlk/FuQuH7kOv1nejlaVJflSM= + dependencies: + remove-bom-buffer "^3.0.0" + safe-buffer "^5.1.0" + through2 "^2.0.3" + +remove-trailing-separator@^1.0.1, remove-trailing-separator@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +repeating@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-1.1.3.tgz#3d4114218877537494f97f77f9785fab810fa4ac" + integrity sha1-PUEUIYh3U3SU+X93+Xhfq4EPpKw= + dependencies: + is-finite "^1.0.0" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +replace-ext@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" + integrity sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ= + +replace-ext@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" + integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== + +replace-homedir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/replace-homedir/-/replace-homedir-1.0.0.tgz#e87f6d513b928dde808260c12be7fec6ff6e798c" + integrity sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw= + dependencies: + homedir-polyfill "^1.0.1" + is-absolute "^1.0.0" + remove-trailing-separator "^1.1.0" + +request@^2.55.0, request@^2.88.2: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + 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.3" + 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.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-alpn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.0.0.tgz#745ad60b3d6aff4b4a48e01b8c0bdc70959e0e8c" + integrity sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA== + +resolve-dir@^1.0.0, resolve-dir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" + integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= + dependencies: + expand-tilde "^2.0.0" + global-modules "^1.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-options@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131" + integrity sha1-MrueOcBtZzONyTeMDW1gdFZq0TE= + dependencies: + value-or-function "^3.0.0" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.4.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + +responselike@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" + integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== + dependencies: + lowercase-keys "^2.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +rxjs@^6.6.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.1.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +schema-utils@^2.6.5: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" + +schema-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" + integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== + dependencies: + "@types/json-schema" "^7.0.6" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +semver-greatest-satisfied-range@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz#13e8c2658ab9691cb0cd71093240280d36f77a5b" + integrity sha1-E+jCZYq5aRywzXEJMkAoDTb3els= + dependencies: + sver-compat "^1.5.0" + +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" + +serialize-javascript@5.0.1, serialize-javascript@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + dependencies: + randombytes "^2.1.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +simple-plist@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.1.1.tgz#54367ca28bc5996a982c325c1c4a4c1a05f4047c" + integrity sha512-pKMCVKvZbZTsqYR6RKgLfBHkh2cV89GXcA/0CVPje3sOiNOnXA8+rp/ciAMZ7JRaUdLzlEM6JFfUn+fS6Nt3hg== + dependencies: + bplist-creator "0.0.8" + bplist-parser "0.2.0" + plist "^3.0.1" + +sinon-chai@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-3.5.0.tgz#c9a78304b0e15befe57ef68e8a85a00553f5c60e" + integrity sha512-IifbusYiQBpUxxFJkR3wTU68xzBN0+bxCScEaKMjBvAQERg6FnTTc1F17rseLb1tjmkJ23730AXpFI0c47FgAg== + +sinon@^9.2.4: + version "9.2.4" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.4.tgz#e55af4d3b174a4443a8762fa8421c2976683752b" + integrity sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg== + dependencies: + "@sinonjs/commons" "^1.8.1" + "@sinonjs/fake-timers" "^6.0.1" + "@sinonjs/samsam" "^5.3.1" + diff "^4.0.2" + nise "^4.0.4" + supports-color "^7.1.0" + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + 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" + +snooplogg@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/snooplogg/-/snooplogg-2.3.3.tgz#5ff87d24e6dd4d59bb45f9c97372b45a64eaeb9d" + integrity sha512-ibqjTxEjn4axSxl+bX17h71ioK/MN8xHdaLFNAkyqHvywOI5xqT7blyUm6ALPDYzfVNIe6Itr+61odaVSEU20A== + dependencies: + bryt "^1.0.1" + chalk "^3.0.0" + nanobuffer "^1.1.6" + source-map-support "^0.5.16" + supports-color "^7.1.0" + +snooplogg@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/snooplogg/-/snooplogg-3.0.2.tgz#86f38bd06254c90c91e3df565face959e16b976e" + integrity sha512-WIeAbTRd64HEtkdoqwK8i9A+anSp1FZXai31fxBzcW8ESvg9wsBuD8+XPVNpX4i6ASUqH73NvXOUVeXzEb0Bcg== + dependencies: + bryt "^1.0.2" + chalk "^4.1.0" + nanobuffer "^1.1.7" + source-map-support "^0.5.19" + supports-color "^8.1.0" + +sort-object-keys@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45" + integrity sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg== + +source-list-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-resolve@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" + integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + +source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@~0.5.19: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.6, source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@~0.7.2: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + +sparkles@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" + integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== + +spawn-wrap@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-2.0.0.tgz#103685b8b8f9b79771318827aa78650a610d457e" + integrity sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg== + dependencies: + foreground-child "^2.0.0" + is-windows "^1.0.2" + make-dir "^3.0.0" + rimraf "^3.0.0" + signal-exit "^3.0.2" + which "^2.0.1" + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.7" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" + integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + 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-trace@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +stream-buffers@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" + integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ= + +stream-exhaust@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stream-exhaust/-/stream-exhaust-1.0.2.tgz#acdac8da59ef2bc1e17a2c0ccf6c320d120e555d" + integrity sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw== + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimend@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b" + integrity sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa" + integrity sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +stringify-object@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-bom-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" + integrity sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI= + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-json-comments@3.1.1, strip-json-comments@^3.0.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@7.2.0, supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +supports-color@^8.1.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +sver-compat@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8" + integrity sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg= + dependencies: + es6-iterator "^2.0.1" + es6-symbol "^3.1.1" + +"symbol-tree@>= 3.1.0 < 4.0.0": + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +table@^6.0.4: + version "6.0.7" + resolved "https://registry.yarnpkg.com/table/-/table-6.0.7.tgz#e45897ffbcc1bcf9e8a87bf420f2c9e5a7a52a34" + integrity sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g== + dependencies: + ajv "^7.0.2" + lodash "^4.17.20" + slice-ansi "^4.0.0" + string-width "^4.2.0" + +taffydb@2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.7.2.tgz#7bf8106a5c1a48251b3e3bc0a0e1732489fd0dc8" + integrity sha1-e/gQalwaSCUbPjvAoOFzJIn9Dcg= + +taffydb@2.7.3: + version "2.7.3" + resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.7.3.tgz#2ad37169629498fca5bc84243096d3cde0ec3a34" + integrity sha1-KtNxaWKUmPylvIQkMJbTzeDsOjQ= + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" + integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== + +tar@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" + integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +terser-webpack-plugin@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz#7effadee06f7ecfa093dbbd3e9ab23f5f3ed8673" + integrity sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q== + dependencies: + jest-worker "^26.6.2" + p-limit "^3.1.0" + schema-utils "^3.0.0" + serialize-javascript "^5.0.1" + source-map "^0.6.1" + terser "^5.5.1" + +terser@^5.5.1: + version "5.5.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.1.tgz#540caa25139d6f496fdea056e414284886fb2289" + integrity sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ== + dependencies: + commander "^2.20.0" + source-map "~0.7.2" + source-map-support "~0.5.19" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through2-filter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254" + integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA== + dependencies: + through2 "~2.0.0" + xtend "~4.0.0" + +through2@^2.0.0, through2@^2.0.3, through2@~2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through2@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" + integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ== + dependencies: + inherits "^2.0.4" + readable-stream "2 || 3" + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +tildify@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tildify/-/tildify-1.2.0.tgz#dcec03f55dca9b7aa3e5b04f21817eb56e63588a" + integrity sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo= + dependencies: + os-homedir "^1.0.0" + +time-stamp@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" + integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= + +timers-ext@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" + integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== + dependencies: + es5-ext "~0.10.46" + next-tick "1" + +titaniumlib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/titaniumlib/-/titaniumlib-3.0.1.tgz#959ae37dd70ed2aac8a26f9bebf7ba0836b8f8d7" + integrity sha512-uEwyPWIREFprITSKHfuwInZTUxky170YtKS4tCo1kBwkuFqutee/nbebbktT1/b+C1UpnaeDHqLatdXXOmxLoA== + dependencies: + appcd-fs "^1.1.10" + appcd-path "^1.1.10" + appcd-util "^2.0.2" + fs-extra "^8.1.0" + pluralize "^8.0.0" + request "^2.88.2" + semver "^7.1.3" + snooplogg "^2.3.3" + source-map-support "^0.5.16" + tmp "^0.1.0" + yauzl "^2.10.0" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmp@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" + integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== + dependencies: + rimraf "^2.6.3" + +tmp@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + +to-absolute-glob@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b" + integrity sha1-GGX0PZ50sIItufFFt4z/fQ98hJs= + dependencies: + is-absolute "^1.0.0" + is-negated-glob "^1.0.0" + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +to-through@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-through/-/to-through-2.0.0.tgz#fc92adaba072647bc0b67d6b03664aa195093af6" + integrity sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY= + dependencies: + through2 "^2.0.3" + +tough-cookie@^2.2.0, tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tr46@~0.0.1: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= + +tsconfig-paths@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" + integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.0" + strip-bom "^3.0.0" + +tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-fest@^0.8.0, type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.1.0.tgz#9bdc22c648cf8cf86dd23d32336a41cfb6475e3f" + integrity sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +unc-path-regex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= + +undertaker-registry@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50" + integrity sha1-XkvaMI5KiirlhPm5pDWaSZglzFA= + +undertaker@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/undertaker/-/undertaker-1.3.0.tgz#363a6e541f27954d5791d6fa3c1d321666f86d18" + integrity sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg== + dependencies: + arr-flatten "^1.0.1" + arr-map "^2.0.0" + bach "^1.0.0" + collection-map "^1.0.0" + es6-weak-map "^2.0.1" + fast-levenshtein "^1.0.0" + last-run "^1.1.0" + object.defaults "^1.0.0" + object.reduce "^1.0.0" + undertaker-registry "^1.0.0" + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +unique-stream@^2.0.2: + version "2.3.1" + resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac" + integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A== + dependencies: + json-stable-stringify-without-jsonify "^1.0.1" + through2-filter "^3.0.0" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +uuid@^3.3.2, uuid@^3.3.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +v8-compile-cache@^2.0.3, v8-compile-cache@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" + integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== + +v8flags@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656" + integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== + dependencies: + homedir-polyfill "^1.0.1" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +value-or-function@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" + integrity sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM= + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vinyl-fs@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" + integrity sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng== + dependencies: + fs-mkdirp-stream "^1.0.0" + glob-stream "^6.1.0" + graceful-fs "^4.0.0" + is-valid-glob "^1.0.0" + lazystream "^1.0.0" + lead "^1.0.0" + object.assign "^4.0.4" + pumpify "^1.3.5" + readable-stream "^2.3.3" + remove-bom-buffer "^3.0.0" + remove-bom-stream "^1.2.0" + resolve-options "^1.1.0" + through2 "^2.0.0" + to-through "^2.0.0" + value-or-function "^3.0.0" + vinyl "^2.0.0" + vinyl-sourcemap "^1.1.0" + +vinyl-sourcemap@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16" + integrity sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY= + dependencies: + append-buffer "^1.0.2" + convert-source-map "^1.5.0" + graceful-fs "^4.1.6" + normalize-path "^2.1.1" + now-and-later "^2.0.0" + remove-bom-buffer "^3.0.0" + vinyl "^2.0.0" + +vinyl-sourcemaps-apply@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" + integrity sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU= + dependencies: + source-map "^0.5.1" + +vinyl@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" + integrity sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4= + dependencies: + clone "^1.0.0" + clone-stats "^0.0.1" + replace-ext "0.0.1" + +vinyl@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974" + integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== + dependencies: + clone "^2.1.1" + clone-buffer "^1.0.0" + clone-stats "^1.0.0" + cloneable-readable "^1.0.0" + remove-trailing-separator "^1.0.1" + replace-ext "^1.0.0" + +watchpack@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.0.tgz#e63194736bf3aa22026f7b191cd57907b0f9f696" + integrity sha512-UjgD1mqjkG99+3lgG36at4wPnUXNvis2v1utwTgQ43C22c4LD71LsYMExdWXh4HZ+RmW+B0t1Vrg2GpXAkTOQw== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +webidl-conversions@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-2.0.1.tgz#3bf8258f7d318c7443c36f2e169402a1a6703506" + integrity sha1-O/glj30xjHRDw28uFpQCoaZwNQY= + +webpack-sources@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac" + integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w== + dependencies: + source-list-map "^2.0.1" + source-map "^0.6.1" + +webpack@^5.17.0: + version "5.17.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.17.0.tgz#e92aebad45be25f86f788dc72fc11daacdcfd55d" + integrity sha512-R+IdNEaYcYaACpXZOt7reyc8txBK7J06lOPkX1SbgmeoAnUbyBZivJIksrDBnmMA3wlTWvPcX7DubxELyPB8rA== + dependencies: + "@types/eslint-scope" "^3.7.0" + "@types/estree" "^0.0.46" + "@webassemblyjs/ast" "1.11.0" + "@webassemblyjs/wasm-edit" "1.11.0" + "@webassemblyjs/wasm-parser" "1.11.0" + acorn "^8.0.4" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.7.0" + es-module-lexer "^0.3.26" + eslint-scope "^5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.4" + json-parse-better-errors "^1.0.2" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + pkg-dir "^5.0.0" + schema-utils "^3.0.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.1" + watchpack "^2.0.0" + webpack-sources "^2.1.1" + +whatwg-url-compat@~0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/whatwg-url-compat/-/whatwg-url-compat-0.6.5.tgz#00898111af689bb097541cd5a45ca6c8798445bf" + integrity sha1-AImBEa9om7CXVBzVpFymyHmERb8= + dependencies: + tr46 "~0.0.1" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@2.0.2, which@^2.0.1, which@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +which@^1.2.14, which@^1.2.9, which@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +word-wrap@^1.2.3, word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +workerpool@6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.2.tgz#e241b43d8d033f1beb52c7851069456039d1d438" + integrity sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q== + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.2.tgz#782100048e54eb36fe9843363ab1c68672b261dd" + integrity sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA== + +"xml-name-validator@>= 2.0.1 < 3.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" + integrity sha1-TYuPHszTQZqjYgYb7O9RXh5VljU= + +xml@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" + integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= + +xmlbuilder@^9.0.7: + version "9.0.7" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" + integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= + +xmldom@0.1.x: + version "0.1.31" + resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" + integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== + +xtend@~4.0.0, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^3.2.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696" + integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== + +y18n@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" + integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@13.1.2, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@5.0.0-security.0: + version "5.0.0-security.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz#4ff7271d25f90ac15643b86076a2ab499ec9ee24" + integrity sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ== + dependencies: + camelcase "^3.0.0" + object.assign "^4.1.0" + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + +yargs@^15.0.2: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + +yargs@^7.1.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.1.tgz#67f0ef52e228d4ee0d6311acede8850f53464df6" + integrity sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g== + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "5.0.0-security.0" + +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/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