diff --git a/ui/app/adapters/console.js b/ui/app/adapters/console.js new file mode 100644 index 0000000000..473f2ce424 --- /dev/null +++ b/ui/app/adapters/console.js @@ -0,0 +1,8 @@ +import ApplicationAdapter from './application'; + +export default ApplicationAdapter.extend({ + namespace: 'v1', + pathForType(modelName) { + return modelName; + }, +}); diff --git a/ui/app/components/console/command-input.js b/ui/app/components/console/command-input.js new file mode 100644 index 0000000000..5ee6eeffe1 --- /dev/null +++ b/ui/app/components/console/command-input.js @@ -0,0 +1,36 @@ +import Ember from 'ember'; +import keys from 'vault/lib/keycodes'; + +export default Ember.Component.extend({ + 'data-test-component': 'console/command-input', + classNames: 'console-ui-input', + onExecuteCommand() {}, + onFullscreen() {}, + onValueUpdate() {}, + onShiftCommand() {}, + value: null, + isFullscreen: null, + + didRender() { + this.element.scrollIntoView(); + }, + actions: { + handleKeyUp(event) { + const keyCode = event.keyCode; + switch (keyCode) { + case keys.ENTER: + this.get('onExecuteCommand')(event.target.value); + break; + case keys.UP: + case keys.DOWN: + this.get('onShiftCommand')(keyCode); + break; + default: + this.get('onValueUpdate')(event.target.value); + } + }, + fullscreen() { + this.get('onFullscreen')(); + } + }, +}); diff --git a/ui/app/components/console/log-command.js b/ui/app/components/console/log-command.js new file mode 100644 index 0000000000..6e705e6764 --- /dev/null +++ b/ui/app/components/console/log-command.js @@ -0,0 +1,3 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({}); diff --git a/ui/app/components/console/log-error.js b/ui/app/components/console/log-error.js new file mode 100644 index 0000000000..6e705e6764 --- /dev/null +++ b/ui/app/components/console/log-error.js @@ -0,0 +1,3 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({}); diff --git a/ui/app/components/console/log-help.js b/ui/app/components/console/log-help.js new file mode 100644 index 0000000000..6e705e6764 --- /dev/null +++ b/ui/app/components/console/log-help.js @@ -0,0 +1,3 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({}); diff --git a/ui/app/components/console/log-json.js b/ui/app/components/console/log-json.js new file mode 100644 index 0000000000..6e705e6764 --- /dev/null +++ b/ui/app/components/console/log-json.js @@ -0,0 +1,3 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({}); diff --git a/ui/app/components/console/log-list.js b/ui/app/components/console/log-list.js new file mode 100644 index 0000000000..fcca15f272 --- /dev/null +++ b/ui/app/components/console/log-list.js @@ -0,0 +1,9 @@ +import Ember from 'ember'; +const { computed } = Ember; + +export default Ember.Component.extend({ + content: null, + list: computed('content', function() { + return this.get('content').keys; + }), +}); diff --git a/ui/app/components/console/log-object.js b/ui/app/components/console/log-object.js new file mode 100644 index 0000000000..275f1edb72 --- /dev/null +++ b/ui/app/components/console/log-object.js @@ -0,0 +1,28 @@ +import Ember from 'ember'; +import columnify from 'columnify'; +const { computed } = Ember; + +export function stringifyObjectValues(data) { + Object.keys(data).forEach(item => { + let val = data[item]; + if (typeof val !== 'string') { + val = JSON.stringify(val); + } + data[item] = val; + }); +} + +export default Ember.Component.extend({ + content: null, + columns: computed('content', function() { + let data = this.get('content'); + stringifyObjectValues(data); + + return columnify(data, { + preserveNewLines: true, + headingTransform: function(heading) { + return Ember.String.capitalize(heading); + }, + }); + }), +}); diff --git a/ui/app/components/console/log-success.js b/ui/app/components/console/log-success.js new file mode 100644 index 0000000000..6e705e6764 --- /dev/null +++ b/ui/app/components/console/log-success.js @@ -0,0 +1,3 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({}); diff --git a/ui/app/components/console/log-text.js b/ui/app/components/console/log-text.js new file mode 100644 index 0000000000..6e705e6764 --- /dev/null +++ b/ui/app/components/console/log-text.js @@ -0,0 +1,3 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({}); diff --git a/ui/app/components/console/output-log.js b/ui/app/components/console/output-log.js new file mode 100644 index 0000000000..a4c209e243 --- /dev/null +++ b/ui/app/components/console/output-log.js @@ -0,0 +1,6 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({ + 'data-test-component': 'console/output-log', + log: null, +}); diff --git a/ui/app/components/console/ui-panel.js b/ui/app/components/console/ui-panel.js new file mode 100644 index 0000000000..fe35294d3b --- /dev/null +++ b/ui/app/components/console/ui-panel.js @@ -0,0 +1,84 @@ +import Ember from 'ember'; +import { + parseCommand, + extractDataAndFlags, + logFromResponse, + logFromError, + logErrorFromInput, + executeUICommand, +} from 'vault/lib/console-helpers'; + +const { inject, computed } = Ember; + +export default Ember.Component.extend({ + classNames: 'console-ui-panel-scroller', + classNameBindings: ['isFullscreen:fullscreen'], + isFullscreen: false, + console: inject.service(), + inputValue: null, + log: computed.alias('console.log'), + + logAndOutput(command, logContent) { + this.set('inputValue', ''); + this.get('console').logAndOutput(command, logContent); + }, + + executeCommand(command, shouldThrow = false) { + let service = this.get('console'); + let serviceArgs; + + if(executeUICommand(command, (args) => this.logAndOutput(args), (args) => service.clearLog(args), () => this.toggleProperty('isFullscreen'))){ + return; + } + + // parse to verify it's valid + try { + serviceArgs = parseCommand(command, shouldThrow); + } catch (e) { + this.logAndOutput(command, { type: 'help' }); + return; + } + // we have a invalid command but don't want to throw + if (serviceArgs === false) { + return; + } + + let [method, flagArray, path, dataArray] = serviceArgs; + + if (dataArray || flagArray) { + var { data, flags } = extractDataAndFlags(dataArray, flagArray); + } + + let inputError = logErrorFromInput(path, method, flags, dataArray); + if (inputError) { + this.logAndOutput(command, inputError); + return; + } + let serviceFn = service[method]; + serviceFn.call(service, path, data, flags.wrapTTL) + .then(resp => { + this.logAndOutput(command, logFromResponse(resp, path, method, flags)); + }) + .catch(error => { + this.logAndOutput(command, logFromError(error, path, method)); + }); + }, + + shiftCommandIndex(keyCode) { + this.get('console').shiftCommandIndex(keyCode, (val) => { + this.set('inputValue', val); + }); + }, + + actions: { + toggleFullscreen() { + this.toggleProperty('isFullscreen'); + }, + executeCommand(val) { + this.executeCommand(val, true); + }, + shiftCommandIndex(direction) { + this.shiftCommandIndex(direction); + }, + }, +}); diff --git a/ui/app/components/json-editor.js b/ui/app/components/json-editor.js index 7b1a1c6692..8377e75e31 100644 --- a/ui/app/components/json-editor.js +++ b/ui/app/components/json-editor.js @@ -18,6 +18,10 @@ export default IvyCodemirrorComponent.extend({ 'data-test-component': 'json-editor', updateCodeMirrorOptions() { const options = assign({}, JSON_EDITOR_DEFAULTS, this.get('options')); + if (options.autoHeight) { + options.viewportMargin = Infinity; + delete options.autoHeight; + } if (options) { Object.keys(options).forEach(function(option) { diff --git a/ui/app/controllers/application.js b/ui/app/controllers/application.js index 5f6f6c9a9e..353649234f 100644 --- a/ui/app/controllers/application.js +++ b/ui/app/controllers/application.js @@ -1,18 +1,21 @@ import Ember from 'ember'; import config from '../config/environment'; +const { computed, inject } = Ember; export default Ember.Controller.extend({ env: config.environment, - auth: Ember.inject.service(), - vaultVersion: Ember.inject.service('version'), - activeCluster: Ember.computed('auth.activeCluster', function() { + auth: inject.service(), + vaultVersion: inject.service('version'), + console: inject.service(), + consoleOpen: computed.alias('console.isOpen'), + activeCluster: computed('auth.activeCluster', function() { return this.store.peekRecord('cluster', this.get('auth.activeCluster')); }), - activeClusterName: Ember.computed('auth.activeCluster', function() { + activeClusterName: computed('auth.activeCluster', function() { const activeCluster = this.store.peekRecord('cluster', this.get('auth.activeCluster')); return activeCluster ? activeCluster.get('name') : null; }), - showNav: Ember.computed( + showNav: computed( 'activeClusterName', 'auth.currentToken', 'activeCluster.dr.isSecondary', @@ -30,4 +33,9 @@ export default Ember.Controller.extend({ } } ), + actions: { + toggleConsole() { + this.toggleProperty('consoleOpen'); + }, + }, }); diff --git a/ui/app/helpers/multi-line-join.js b/ui/app/helpers/multi-line-join.js new file mode 100644 index 0000000000..cd22380f11 --- /dev/null +++ b/ui/app/helpers/multi-line-join.js @@ -0,0 +1,7 @@ +import Ember from 'ember'; + +export function multiLineJoin([arr]) { + return arr.join('\n'); +} + +export default Ember.Helper.helper(multiLineJoin); diff --git a/ui/app/lib/console-helpers.js b/ui/app/lib/console-helpers.js new file mode 100644 index 0000000000..0d8b12128d --- /dev/null +++ b/ui/app/lib/console-helpers.js @@ -0,0 +1,183 @@ +import keys from 'vault/lib/keycodes'; +import argTokenizer from 'yargs-parser-tokenizer'; + +const supportedCommands = ['read', 'write', 'list', 'delete']; +const uiCommands = ['clearall', 'clear', 'fullscreen']; + +export function extractDataAndFlags(data, flags) { + return data.concat(flags).reduce((accumulator, val) => { + // will be "key=value" or "-flag=value" or "foo=bar=baz" + // split on the first = + let [item, value] = val.split(/=(.+)/); + if (item.startsWith('-')) { + let flagName = item.replace(/^-/, ''); + if (flagName === 'wrap-ttl') { + flagName = 'wrapTTL'; + } + accumulator.flags[flagName] = value || true; + return accumulator; + } + // if it exists in data already, then we have multiple + // foo=bar in the list and need to make it an array + if (accumulator.data[item]) { + accumulator.data[item] = [].concat(accumulator.data[item], value); + return accumulator; + } + accumulator.data[item] = value; + + return accumulator; + }, { data: {}, flags: {} }); +} + +export function executeUICommand(command, logAndOutput, clearLog, toggleFullscreen){ + const isUICommand = uiCommands.includes(command); + if(isUICommand){ + logAndOutput(command); + } + switch(command){ + case 'clearall': + clearLog(true); + break; + case 'clear': + clearLog(); + break; + case 'fullscreen': + toggleFullscreen(); + break; + } + + return isUICommand; +} + +export function parseCommand(command, shouldThrow) { + let args = argTokenizer(command); + if (args[0] === 'vault') { + args.shift(); + } + + let [method, ...rest] = args; + let path; + let flags = []; + let data = []; + + rest.forEach(arg => { + if (arg.startsWith('-')) { + flags.push(arg); + } else { + if (path) { + data.push(arg); + } else { + path = arg; + } + } + }); + + if (!supportedCommands.includes(method)) { + if (shouldThrow) { + throw new Error('invalid command'); + } + return false; + } + return [method, flags, path, data]; +} + +export function logFromResponse(response, path, method, flags) { + if (!response) { + let message = + method === 'write' + ? `Success! Data written to: ${path}` + : `Success! Data deleted (if it existed) at: ${path}`; + + return { type: 'success', content: message }; + } + let { format, field } = flags; + let secret = response.auth || response.data || response.wrap_info; + + if (field) { + let fieldValue = secret[field]; + let response; + if (fieldValue) { + if (format && format === 'json') { + return { type: 'json', content: fieldValue }; + } + switch (typeof fieldValue) { + case 'string': + response = { type: 'text', content: fieldValue }; + break; + default: + response = { type: 'object', content: fieldValue }; + break; + } + } else { + response = { type: 'error', content: `Field "${field}" not present in secret` }; + } + return response; + } + + if (format && format === 'json') { + // just print whole response + return { type: 'json', content: response }; + } + + if (method === 'list') { + return { type: 'list', content: secret }; + } + + return { type: 'object', content: secret }; +} + +export function logFromError(error, vaultPath, method) { + let content; + let { httpStatus, path } = error; + let verbClause = { + read: 'reading from', + write: 'writing to', + list: 'listing', + delete: 'deleting at', + }[method]; + + content = `Error ${verbClause}: ${vaultPath}.\nURL: ${path}\nCode: ${httpStatus}`; + + if (typeof error.errors[0] === 'string') { + content = `${content}\nErrors:\n ${error.errors.join('\n ')}`; + } + + return { type: 'error', content }; +} + +export function shiftCommandIndex(keyCode, history, index) { + let newInputValue; + let commandHistoryLength = history.length; + + if (!commandHistoryLength) { return []; } + + if (keyCode === keys.UP) { + index -= 1; + if (index < 0) { + index = commandHistoryLength - 1; + } + } else { + index += 1; + if (index === commandHistoryLength) { + newInputValue = ''; + } + if (index > commandHistoryLength) { + index -= 1; + } + } + + if (newInputValue !== '') { + newInputValue = history.objectAt(index).content; + } + + return [index, newInputValue]; +} + +export function logErrorFromInput(path, method, flags, dataArray) { + if (path === undefined) { + return { type: 'error', content: 'A path is required to make a request.' }; + } + if (method === 'write' && !flags.force && dataArray.length === 0) { + return { type: 'error', content: 'Must supply data or use -force' }; + } +} diff --git a/ui/app/routes/vault/cluster/logout.js b/ui/app/routes/vault/cluster/logout.js index 0ddc7c110f..824d931164 100644 --- a/ui/app/routes/vault/cluster/logout.js +++ b/ui/app/routes/vault/cluster/logout.js @@ -1,14 +1,18 @@ import Ember from 'ember'; import ModelBoundaryRoute from 'vault/mixins/model-boundary-route'; +const { inject } = Ember; export default Ember.Route.extend(ModelBoundaryRoute, { - auth: Ember.inject.service(), - flashMessages: Ember.inject.service(), + auth: inject.service(), + flashMessages: inject.service(), + console: inject.service(), modelTypes: ['secret', 'secret-engine'], beforeModel() { this.get('auth').deleteCurrentToken(); + this.get('console').set('isOpen', false); + this.get('console').clearLog(true); this.clearModelCache(); this.replaceWith('vault.cluster'); this.get('flashMessages').clearMessages(); diff --git a/ui/app/routes/vault/cluster/secrets/backend/list.js b/ui/app/routes/vault/cluster/secrets/backend/list.js index dfbde5b705..1731c1525a 100644 --- a/ui/app/routes/vault/cluster/secrets/backend/list.js +++ b/ui/app/routes/vault/cluster/secrets/backend/list.js @@ -49,25 +49,25 @@ export default Ember.Route.extend({ return Ember.RSVP.hash({ secret, secrets: this.store - .lazyPaginatedQuery(this.getModelType(backend, params.tab), { - id: secret, - backend, - responsePath: 'data.keys', - page: params.page, - pageFilter: params.pageFilter, - size: 100, - }) - .then(model => { - this.set('has404', false); - return model; - }) - .catch(err => { - if (backendModel && err.httpStatus === 404 && secret === '') { - return []; - } else { - throw err; - } - }) + .lazyPaginatedQuery(this.getModelType(backend, params.tab), { + id: secret, + backend, + responsePath: 'data.keys', + page: params.page, + pageFilter: params.pageFilter, + size: 100, + }) + .then(model => { + this.set('has404', false); + return model; + }) + .catch(err => { + if (backendModel && err.httpStatus === 404 && secret === '') { + return []; + } else { + throw err; + } + }), }); }, diff --git a/ui/app/services/console.js b/ui/app/services/console.js new file mode 100644 index 0000000000..e35555f3bf --- /dev/null +++ b/ui/app/services/console.js @@ -0,0 +1,105 @@ +// Low level service that allows users to input paths to make requests to vault +// this service provides the UI synecdote to the cli commands read, write, delete, and list +import Ember from 'ember'; +import { + shiftCommandIndex, +} from 'vault/lib/console-helpers'; + +const { Service, getOwner, computed } = Ember; + +export function sanitizePath(path) { + //remove whitespace + remove trailing and leading slashes + return path.trim().replace(/^\/+|\/+$/g, ''); +} +export function ensureTrailingSlash(path) { + return path.replace(/(\w+[^/]$)/g, '$1/'); +} + +const VERBS = { + read: 'GET', + list: 'GET', + write: 'POST', + delete: 'DELETE', +}; + +export default Service.extend({ + isOpen: false, + + adapter() { + return getOwner(this).lookup('adapter:console'); + }, + commandHistory: computed('log.[]', function() { + return this.get('log').filterBy('type', 'command'); + }), + log: computed(function() { + return []; + }), + commandIndex: null, + + shiftCommandIndex(keyCode, setCommandFn = () => {}) { + let [newIndex, newCommand] = shiftCommandIndex( + keyCode, + this.get('commandHistory'), + this.get('commandIndex') + ); + if (newCommand !== undefined && newIndex !== undefined) { + this.set('commandIndex', newIndex); + setCommandFn(newCommand); + } + }, + + clearLog(clearAll=false) { + let log = this.get('log'); + let history; + if (!clearAll) { + history = this.get('commandHistory').slice(); + history.setEach('hidden', true); + } + log.clear(); + if (history) { + log.addObjects(history); + } + }, + + logAndOutput(command, logContent) { + let log = this.get('log'); + log.pushObject({ type: 'command', content: command }); + this.set('commandIndex', null); + if (logContent) { + log.pushObject(logContent); + } + }, + + ajax(operation, path, options = {}) { + let verb = VERBS[operation]; + let adapter = this.adapter(); + let url = adapter.buildURL(path); + let { data, wrapTTL } = options; + return adapter.ajax(url, verb, { + data, + wrapTTL, + }); + }, + + read(path, data, wrapTTL) { + return this.ajax('read', sanitizePath(path), { wrapTTL }); + }, + + write(path, data, wrapTTL) { + return this.ajax('write', sanitizePath(path), { data, wrapTTL }); + }, + + delete(path) { + return this.ajax('delete', sanitizePath(path)); + }, + + list(path, data, wrapTTL) { + let listPath = ensureTrailingSlash(sanitizePath(path)); + return this.ajax('list', listPath, { + data: { + list: true, + }, + wrapTTL, + }); + }, +}); diff --git a/ui/app/styles/components/codemirror.scss b/ui/app/styles/components/codemirror.scss index 37f773a040..68cb9971c6 100644 --- a/ui/app/styles/components/codemirror.scss +++ b/ui/app/styles/components/codemirror.scss @@ -171,3 +171,7 @@ $gutter-grey: #2a2f36; } } } + +.cm-s-auto-height.CodeMirror { + height: auto; +} diff --git a/ui/app/styles/components/console-ui-panel.scss b/ui/app/styles/components/console-ui-panel.scss new file mode 100644 index 0000000000..9649bb8bb1 --- /dev/null +++ b/ui/app/styles/components/console-ui-panel.scss @@ -0,0 +1,149 @@ +.console-ui-panel-scroller { + background: linear-gradient(to right, #191A1C, #1B212D); + height: 0; + left: 0; + min-height: 400px; + overflow: auto; + position: fixed; + right: 0; + transform: translate3d(0, -400px, 0); + transition: min-height $speed ease-out, transform $speed ease-in; + will-change: transform, min-height; + z-index: 199; +} + +.console-ui-panel { + display: flex; + flex-direction: column; + justify-content: flex-end; + padding: $size-8 $size-8 $size-4; + min-height: 100%; + color: $white; + font-size: $body-size; + font-weight: $font-weight-semibold; + transition: justify-content $speed ease-in; + + + pre, p { + background: none; + color: inherit; + font-size: $body-size; + + &:not(.console-ui-command):not(.CodeMirror-line) { + padding-left: $console-spacing; + } + } + + .cm-s-hashi.CodeMirror { + background-color: rgba($black, 0.5) !important; + font-weight: $font-weight-normal; + margin-left: $console-spacing; + padding: $size-8 $size-4; + } + + .button, + { + background: transparent; + border: none; + color: $grey-dark; + min-width: 0; + padding: 0 $size-8; + + &.active, + &:hover { + background: $blue; + color: $white; + } + } +} + +.console-ui-input { + align-items: center; + display: flex; + + + input { + background-color: rgba($black, 0.5); + border: 0; + caret-color: $white; + color: $white; + flex: 1; + font-family: $family-monospace; + font-size: $body-size; + font-weight: $font-weight-bold; + margin-left: -$size-10; + outline: none; + padding: $size-10; + transition: background-color $speed; + } +} + +.console-ui-command { + line-height: 2; +} + +.console-ui-output { + transition: background-color $speed; + padding-right: $size-2; + position: relative; + + .console-ui-output-actions { + opacity: 0; + position: absolute; + right: 0; + top: 0; + transition: opacity $speed; + will-change: opacity; + } + + &:hover { + background: rgba($black, 0.25); + + .console-ui-output-actions { + opacity: 1; + } + } +} + +.console-ui-alert { + margin-left: calc(#{$console-spacing} - 0.33rem); + position: relative; + + .icon { + position: absolute; + left: 0; + top: 0; + } +} + +.panel-open .console-ui-panel-scroller { + transform: translate3d(0, 0, 0); +} +.panel-open .console-ui-panel-scroller.fullscreen { + bottom: 0; + top: 0; + min-height: 100%; +} + +.panel-open { + .navbar, .navbar-sections{ + transition: transform $speed ease-in; + } +} + +.panel-open.panel-fullscreen { + .navbar, .navbar-sections{ + transform: translate3d(0, -100px, 0); + } +} + +.page-container > header { + background: linear-gradient(to right, #191A1C, #1B212D); +} + +header .navbar, +header .navbar-sections { + z-index: 200; + transform: translate3d(0, 0, 0); + will-change: transform; +} diff --git a/ui/app/styles/components/env-banner.scss b/ui/app/styles/components/env-banner.scss new file mode 100644 index 0000000000..d2851dcf69 --- /dev/null +++ b/ui/app/styles/components/env-banner.scss @@ -0,0 +1,10 @@ +.env-banner { + &, + &:not(:last-child):not(:last-child) { + margin: 0; + } + + .level-item { + padding: $size-10 $size-8; + } +} diff --git a/ui/app/styles/components/status-menu.scss b/ui/app/styles/components/status-menu.scss index 1c6937dae0..1fe965b998 100644 --- a/ui/app/styles/components/status-menu.scss +++ b/ui/app/styles/components/status-menu.scss @@ -59,7 +59,7 @@ .is-status-chevron { line-height: 0; - padding: 0.25em 0 0.25em 0.25em; + padding: 0.3em 0 0 $size-11; } .status-menu-user-trigger { diff --git a/ui/app/styles/components/tool-tip.scss b/ui/app/styles/components/tool-tip.scss index a971e6e9b2..5d8c02b3cf 100644 --- a/ui/app/styles/components/tool-tip.scss +++ b/ui/app/styles/components/tool-tip.scss @@ -5,7 +5,7 @@ .box { position: relative; color: $white; - width: 200px; + max-width: 200px; background: $grey; padding: 0.5rem; line-height: 1.4; @@ -28,6 +28,16 @@ .ember-basic-dropdown-content--left.tool-tip { margin: 8px 0 0 -11px; } + +.ember-basic-dropdown-content--below.ember-basic-dropdown-content--right.tool-tip { + @include css-top-arrow(8px, $grey, 1px, $grey-dark, calc(100% - 20px)); +} +.ember-basic-dropdown-content--above.ember-basic-dropdown-content--right.tool-tip { + @include css-bottom-arrow(8px, $grey, 1px, $grey-dark, calc(100% - 20px)); +} +.ember-basic-dropdown-content--above.tool-tip { + margin-top: -2px; +} .tool-tip-trigger { border: none; border-radius: 20px; diff --git a/ui/app/styles/components/upgrade-overlay.scss b/ui/app/styles/components/upgrade-overlay.scss index 5ec71408eb..4ad81dc4ca 100644 --- a/ui/app/styles/components/upgrade-overlay.scss +++ b/ui/app/styles/components/upgrade-overlay.scss @@ -10,7 +10,8 @@ } .modal-background { - background-image: url("/ui/vault-hex.svg"), linear-gradient(90deg, #191A1C, #1B212D); + background-image: url("/ui/vault-hex.svg"), + linear-gradient(90deg, #191a1c, #1b212d); opacity: 0.97; } diff --git a/ui/app/styles/components/vault-loading.scss b/ui/app/styles/components/vault-loading.scss index 1634dfa7ee..610375007d 100644 --- a/ui/app/styles/components/vault-loading.scss +++ b/ui/app/styles/components/vault-loading.scss @@ -1,52 +1,52 @@ - @keyframes vault-loading-animation { - 0%, - 70%, - 100% { - transform: scale3D(1, 1, 1); - } - - 35% { - transform: scale3D(0, 0, 1); - } +@keyframes vault-loading-animation { + 0%, + 70%, + 100% { + transform: scale3D(1, 1, 1); } - #vault-loading { - polygon { - animation: vault-loading-animation 1.3s infinite ease-in-out; - transform-origin: 50% 50%; - fill: #DCE2E9; - } - - .vault-loading-order-1 { - animation-delay: .1s; - } - - .vault-loading-order-2 { - animation-delay: .2s; - } - - .vault-loading-order-3 { - animation-delay: .3s; - } - - .vault-loading-order-4 { - animation-delay: .4s; - } + 35% { + transform: scale3D(0, 0, 1); + } +} + +#vault-loading { + polygon { + animation: vault-loading-animation 1.3s infinite ease-in-out; + transform-origin: 50% 50%; + fill: #dce2e9; } - #vault-loading-animated { - @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { - // For IE11 - display: none; - } + .vault-loading-order-1 { + animation-delay: .1s; } - #vault-loading-static { + .vault-loading-order-2 { + animation-delay: .2s; + } + + .vault-loading-order-3 { + animation-delay: .3s; + } + + .vault-loading-order-4 { + animation-delay: .4s; + } +} + +#vault-loading-animated { + @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + // For IE11 display: none; - font-size: 9px; + } +} - @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { - // For IE11 - display: block; - } - } \ No newline at end of file +#vault-loading-static { + display: none; + font-size: 9px; + + @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + // For IE11 + display: block; + } +} diff --git a/ui/app/styles/core.scss b/ui/app/styles/core.scss index 3c8acc6437..bad506107d 100644 --- a/ui/app/styles/core.scss +++ b/ui/app/styles/core.scss @@ -46,6 +46,8 @@ @import "./components/box-label"; @import "./components/codemirror"; @import "./components/confirm"; +@import "./components/console-ui-panel"; +@import "./components/env-banner"; @import "./components/form-section"; @import "./components/global-flash"; @import "./components/init-illustration"; diff --git a/ui/app/styles/core/buttons.scss b/ui/app/styles/core/buttons.scss index 5efa15a61e..9472a28733 100644 --- a/ui/app/styles/core/buttons.scss +++ b/ui/app/styles/core/buttons.scss @@ -12,7 +12,8 @@ $button-box-shadow-standard: 0 3px 1px 0 rgba($black, 0.12); min-width: 6rem; padding: $size-10 $size-8; text-decoration: none; - transition: background-color $speed, border-color $speed, box-shadow $speed, color $speed; + transition: background-color $speed, border-color $speed, box-shadow $speed, + color $speed; vertical-align: middle; &.is-icon { diff --git a/ui/app/styles/core/generic.scss b/ui/app/styles/core/generic.scss index 358b6dd323..664c2a56b9 100644 --- a/ui/app/styles/core/generic.scss +++ b/ui/app/styles/core/generic.scss @@ -33,13 +33,16 @@ input::-webkit-inner-spin-button { .link { background: transparent; border: 0; - color: $blue; - cursor: pointer; - display: inline; - font: inherit; - line-height: normal; - margin: 0; - padding: 0; - text-decoration: underline; - -moz-user-select: text; + color: $blue; + cursor: pointer; + display: inline; + font: inherit; + line-height: normal; + margin: 0; + padding: 0; + text-decoration: underline; + -webkit-user-select: text; /* Chrome all / Safari all */ + -moz-user-select: text; /* Firefox all */ + -ms-user-select: text; /* IE 10+ */ + user-select: text; } diff --git a/ui/app/styles/utils/_bulma_variables.scss b/ui/app/styles/utils/_bulma_variables.scss index 7d68128498..6a97abeda0 100644 --- a/ui/app/styles/utils/_bulma_variables.scss +++ b/ui/app/styles/utils/_bulma_variables.scss @@ -37,7 +37,9 @@ $border: $grey-light; $hr-margin: 1rem 0; //typography -$family-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; +$family-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; $family-primary: $family-sans; $body-size: 14px; $size-3: (24/14) + 0rem; @@ -46,6 +48,7 @@ $size-8: (12/14) + 0rem; $size-9: 0.75rem; $size-10: 0.5rem; $size-11: 0.25rem; +$console-spacing: 1.5rem; $size-small: $size-8; $font-weight-normal: 400; $font-weight-semibold: 600; diff --git a/ui/app/styles/utils/animations.scss b/ui/app/styles/utils/animations.scss index 8c7c3b2d7b..0020823b45 100644 --- a/ui/app/styles/utils/animations.scss +++ b/ui/app/styles/utils/animations.scss @@ -14,7 +14,7 @@ } @include keyframes(drop-fade-below) { - 0% { + 0% { opacity: 0; transform: translateY(-1rem); } @@ -25,7 +25,7 @@ } @include keyframes(drop-fade-above) { - 0% { + 0% { opacity: 0; transform: translateY(1rem); } diff --git a/ui/app/styles/utils/mixins.scss b/ui/app/styles/utils/mixins.scss index 97f3cc0126..9832a8e86c 100644 --- a/ui/app/styles/utils/mixins.scss +++ b/ui/app/styles/utils/mixins.scss @@ -1,11 +1,15 @@ -@mixin css-top-arrow($size, $color, $border-width, $border-color, $left: 50%, $left-offset: 0px) { +@mixin css-arrow($vertical-direction, $size, $color, $border-width, $border-color, $left: 50%, $left-offset: 0px) { & { border: 1px solid $border-color; } &:after, &:before { - bottom: 100%; + @if ($vertical-direction == 'top') { + bottom: 100%; + } @else { + top: 100%; + } border: solid transparent; content: " "; height: 0; @@ -28,6 +32,12 @@ left: calc(#{$left} + #{$left-offset}); margin-left: -($size + round(1.41421356 * $border-width)); } + &:before, + &:after { + @if ($vertical-direction == 'bottom') { + transform: rotate(180deg); + } + } @at-root .ember-basic-dropdown-content--left#{&} { &:after, @@ -38,6 +48,13 @@ } } +@mixin css-top-arrow($size, $color, $border-width, $border-color, $left: 50%, $left-offset: 0px) { + @include css-arrow('top', $size, $color, $border-width, $border-color, $left, $left-offset); +} +@mixin css-bottom-arrow($size, $color, $border-width, $border-color, $left: 50%, $left-offset: 0px) { + @include css-arrow('bottom', $size, $color, $border-width, $border-color, $left, $left-offset); +} + @mixin vault-block { &:not(:last-child) { margin-bottom: (5/14) + 0rem; diff --git a/ui/app/templates/application.hbs b/ui/app/templates/application.hbs index c3d67ecfe2..e4480ce1be 100644 --- a/ui/app/templates/application.hbs +++ b/ui/app/templates/application.hbs @@ -1,6 +1,6 @@
{{#if showNav}} -
+
{{/if}}
@@ -129,7 +141,7 @@
{{#if (eq env "development") }} -
+
{{i-con glyph="wand" class="type-icon"}}Local Development
diff --git a/ui/app/templates/components/console/command-input.hbs b/ui/app/templates/components/console/command-input.hbs new file mode 100644 index 0000000000..3d14968688 --- /dev/null +++ b/ui/app/templates/components/console/command-input.hbs @@ -0,0 +1,16 @@ +{{i-con glyph="chevron-right" size=12}} + +{{#tool-tip horizontalPosition="auto-right" verticalPosition=(if isFullscreen "above" "below") as |d|}} + {{#d.trigger tagName="button" type="button" class=(concat "button is-compact" (if isFullscreen " active")) click=(action "fullscreen") data-test-tool-tip-trigger=true}} + {{i-con glyph=(if isFullscreen "fullscreen-close" "fullscreen-open") aria-hidden="true" size=16}} + {{/d.trigger}} + {{#d.content class="tool-tip"}} +
+ {{#if isFullscreen}} + Minimize + {{else}} + Maximize + {{/if}} +
+ {{/d.content}} +{{/tool-tip}} diff --git a/ui/app/templates/components/console/log-command.hbs b/ui/app/templates/components/console/log-command.hbs new file mode 100644 index 0000000000..39b9e7d1f2 --- /dev/null +++ b/ui/app/templates/components/console/log-command.hbs @@ -0,0 +1 @@ +
{{i-con glyph="chevron-right" size=12}}{{content}}
diff --git a/ui/app/templates/components/console/log-error.hbs b/ui/app/templates/components/console/log-error.hbs new file mode 100644 index 0000000000..add86ec492 --- /dev/null +++ b/ui/app/templates/components/console/log-error.hbs @@ -0,0 +1,4 @@ +
+ {{i-con glyph="close-circled" aria-hidden="true" size=12}} +
{{content}}
+
diff --git a/ui/app/templates/components/console/log-help.hbs b/ui/app/templates/components/console/log-help.hbs new file mode 100644 index 0000000000..0022c1f399 --- /dev/null +++ b/ui/app/templates/components/console/log-help.hbs @@ -0,0 +1,16 @@ +
+{{i-con glyph="information-circled" aria-hidden="true" size=12}} +
Usage: vault <command> [args]
+
+Commands:
+  read        Read data and retrieves secrets
+  write       Write data, configuration, and secrets
+  delete      Delete secrets and configuration
+  list        List data or secrets
+
+Web CLI Commands:
+  fullscreen  Toggle fullscreen display
+  clear       Clear output from the log
+  clearall    Clear output and command history
+
+
diff --git a/ui/app/templates/components/console/log-json.hbs b/ui/app/templates/components/console/log-json.hbs new file mode 100644 index 0000000000..293ee6f238 --- /dev/null +++ b/ui/app/templates/components/console/log-json.hbs @@ -0,0 +1,10 @@ +{{json-editor + value=(stringify content) + options=(hash + readOnly=true + lineNumbers=false + autoHeight=true + gutters=false + theme='hashi auto-height' + ) + }} diff --git a/ui/app/templates/components/console/log-list.hbs b/ui/app/templates/components/console/log-list.hbs new file mode 100644 index 0000000000..83fc544bdc --- /dev/null +++ b/ui/app/templates/components/console/log-list.hbs @@ -0,0 +1,21 @@ +
+
Keys
+{{#each list as |item|}}
+{{item}}
+{{/each}}
+
+
+ {{#tool-tip renderInPlace=true as |d|}} + {{#d.trigger data-test-tool-tip-trigger=true}} + {{#copy-button clipboardText=(multi-line-join list) class="button is-compact"}} + {{i-con glyph="copy" aria-hidden="true" size=16}} + {{/copy-button}} + {{/d.trigger}} + {{#d.content class="tool-tip"}} +
+ Copy +
+ {{/d.content}} + {{/tool-tip}} +
+
diff --git a/ui/app/templates/components/console/log-object.hbs b/ui/app/templates/components/console/log-object.hbs new file mode 100644 index 0000000000..3c8d77cf4a --- /dev/null +++ b/ui/app/templates/components/console/log-object.hbs @@ -0,0 +1,18 @@ +
+
{{columns}}
+ +
+ {{#tool-tip renderInPlace=true as |d|}} + {{#d.trigger data-test-tool-tip-trigger=true}} + {{#copy-button clipboardText=columns class="button is-compact"}} + {{i-con glyph="copy" aria-hidden="true" size=16}} + {{/copy-button}} + {{/d.trigger}} + {{#d.content class="tool-tip"}} +
+ Copy +
+ {{/d.content}} + {{/tool-tip}} +
+
diff --git a/ui/app/templates/components/console/log-success.hbs b/ui/app/templates/components/console/log-success.hbs new file mode 100644 index 0000000000..e16ae924c5 --- /dev/null +++ b/ui/app/templates/components/console/log-success.hbs @@ -0,0 +1,4 @@ +
+ {{i-con glyph="checkmark-circled" aria-hidden="true" size=12}} +
{{content}}
+
diff --git a/ui/app/templates/components/console/log-text.hbs b/ui/app/templates/components/console/log-text.hbs new file mode 100644 index 0000000000..3da4104754 --- /dev/null +++ b/ui/app/templates/components/console/log-text.hbs @@ -0,0 +1 @@ +
{{content}}
\ No newline at end of file diff --git a/ui/app/templates/components/console/output-log.hbs b/ui/app/templates/components/console/output-log.hbs new file mode 100644 index 0000000000..e464c7fa0b --- /dev/null +++ b/ui/app/templates/components/console/output-log.hbs @@ -0,0 +1,5 @@ +{{#each log as |message|}} + {{#unless message.hidden}} + {{component (concat 'console/log-' message.type) content=message.content}} + {{/unless}} +{{/each}} diff --git a/ui/app/templates/components/console/ui-panel.hbs b/ui/app/templates/components/console/ui-panel.hbs new file mode 100644 index 0000000000..f374e20d29 --- /dev/null +++ b/ui/app/templates/components/console/ui-panel.hbs @@ -0,0 +1,16 @@ +
+
+

+ The Vault Browser CLI provides an easy way to execute the most common CLI commands, such as write, read, delete, and list. +

+
+ {{console/output-log log=log}} + {{console/command-input + isFullscreen=isFullscreen + value=inputValue + onValueUpdate=(action (mut inputValue)) + onFullscreen=(action 'toggleFullscreen') + onExecuteCommand=(action 'executeCommand') + onShiftCommand=(action 'shiftCommandIndex') + }} +
diff --git a/ui/app/templates/svg/icons/console-active.hbs b/ui/app/templates/svg/icons/console-active.hbs new file mode 100644 index 0000000000..aa155909cd --- /dev/null +++ b/ui/app/templates/svg/icons/console-active.hbs @@ -0,0 +1,21 @@ + + + + + + diff --git a/ui/app/templates/svg/icons/console.hbs b/ui/app/templates/svg/icons/console.hbs new file mode 100644 index 0000000000..f201234cc2 --- /dev/null +++ b/ui/app/templates/svg/icons/console.hbs @@ -0,0 +1,17 @@ + + + + + diff --git a/ui/app/templates/svg/icons/copy.hbs b/ui/app/templates/svg/icons/copy.hbs new file mode 100644 index 0000000000..5a1bfb1b1e --- /dev/null +++ b/ui/app/templates/svg/icons/copy.hbs @@ -0,0 +1 @@ + diff --git a/ui/app/templates/svg/icons/fullscreen-close.hbs b/ui/app/templates/svg/icons/fullscreen-close.hbs new file mode 100644 index 0000000000..43f026bb9b --- /dev/null +++ b/ui/app/templates/svg/icons/fullscreen-close.hbs @@ -0,0 +1 @@ + diff --git a/ui/app/templates/svg/icons/fullscreen-open.hbs b/ui/app/templates/svg/icons/fullscreen-open.hbs new file mode 100644 index 0000000000..3702768ce7 --- /dev/null +++ b/ui/app/templates/svg/icons/fullscreen-open.hbs @@ -0,0 +1 @@ + diff --git a/ui/ember-cli-build.js b/ui/ember-cli-build.js index f758b6d63b..74461efe3c 100644 --- a/ui/ember-cli-build.js +++ b/ui/ember-cli-build.js @@ -55,6 +55,18 @@ module.exports = function(defaults) { app.import('node_modules/text-encoder-lite/index.js'); app.import('node_modules/Duration.js/duration.js'); + app.import('node_modules/columnify/columnify.js', { + using: [ + { transformation: 'cjs', as: 'columnify' } + ] + }); + + app.import('node_modules/yargs-parser/lib/tokenize-arg-string.js', { + using: [ + { transformation: 'cjs', as: 'yargs-parser-tokenizer' } + ] + }); + // Use `app.import` to add additional libraries to the generated // output files. // diff --git a/ui/package.json b/ui/package.json index 89bd31042b..9263e7039b 100644 --- a/ui/package.json +++ b/ui/package.json @@ -14,7 +14,7 @@ "start2": "ember server --proxy=http://localhost:8202 --port=4202", "test": "node scripts/start-vault.js & ember test", "test-oss": "yarn run test -f='!enterprise'", - "fmt-js": "prettier-eslint --single-quote --trailing-comma es5 --print-width=110 --write {app,tests,config,lib,mirage}/**/*.js", + "fmt-js": "prettier-eslint --single-quote --no-use-tabs --trailing-comma es5 --print-width=110 --write '{app,tests,config,lib,mirage}/**/*.js'", "fmt-styles": "prettier --write app/styles/**/*.*", "fmt": "yarn run fmt-js && yarn run fmt-styles", "precommit": "lint-staged" @@ -33,19 +33,24 @@ } }, "devDependencies": { + "Duration.js": "icholy/Duration.js#golang_compatible", + "autosize": "3.0.17", "babel-plugin-transform-object-rest-spread": "^6.23.0", + "base64-js": "1.2.1", "broccoli-asset-rev": "^2.4.5", "broccoli-sri-hash": "meirish/broccoli-sri-hash#rooturl", "bulma": "^0.5.2", "bulma-switch": "^0.0.1", + "codemirror": "5.15.2", "cool-checkboxes-for-bulma.io": "^1.1.0", "ember-ajax": "^3.0.0", "ember-api-actions": "^0.1.8", "ember-basic-dropdown": "^0.33.5", "ember-basic-dropdown-hover": "^0.2.0", - "ember-cli": "~2.15.0", + "ember-cli": "~2.16.0", "ember-cli-autoprefixer": "^0.8.1", "ember-cli-babel": "^6.3.0", + "ember-cli-cjs-transform": "^1.2.0", "ember-cli-clipboard": "^0.8.0", "ember-cli-content-security-policy": "^1.0.0", "ember-cli-dependency-checker": "^1.3.0", @@ -83,18 +88,16 @@ "ember-test-selectors": "^0.3.6", "ember-truth-helpers": "1.2.0", "ivy-codemirror": "2.1.0", + "jsonlint": "1.6.0", "loader.js": "^4.2.3", "normalize.css": "4.1.1", "prettier": "^1.5.3", "prettier-eslint-cli": "^4.2.1", "qunit-dom": "^0.6.2", + "string.prototype.startswith": "mathiasbynens/String.prototype.startsWith", "text-encoder-lite": "1.0.0", - "base64-js": "1.2.1", - "autosize": "3.0.17", - "jsonlint": "1.6.0", - "codemirror": "5.15.2", - "Duration.js": "icholy/Duration.js#golang_compatible", - "string.prototype.startswith": "mathiasbynens/String.prototype.startsWith" + "columnify": "^1.5.4", + "yargs-parser": "^10.0.0" }, "engines": { "node": "^4.5 || 6.* || >= 7.*" diff --git a/ui/tests/integration/components/console/log-command-test.js b/ui/tests/integration/components/console/log-command-test.js new file mode 100644 index 0000000000..72a9514ac7 --- /dev/null +++ b/ui/tests/integration/components/console/log-command-test.js @@ -0,0 +1,15 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('console/log-command', 'Integration | Component | console/log command', { + integration: true, +}); + +test('it renders', function(assert) { + const commandText = 'list this/path'; + this.set('content', commandText); + + this.render(hbs`{{console/log-command content=content}}`); + + assert.dom('pre').includesText(commandText); +}); diff --git a/ui/tests/integration/components/console/log-error-test.js b/ui/tests/integration/components/console/log-error-test.js new file mode 100644 index 0000000000..f32c2dad9e --- /dev/null +++ b/ui/tests/integration/components/console/log-error-test.js @@ -0,0 +1,13 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('console/log-error', 'Integration | Component | console/log error', { + integration: true, +}); + +test('it renders', function(assert) { + const errorText = 'Error deleting at: sys/foo.\nURL: v1/sys/foo\nCode: 404'; + this.set('content', errorText); + this.render(hbs`{{console/log-error content=content}}`); + assert.dom('pre').includesText(errorText); +}); diff --git a/ui/tests/integration/components/console/log-json-test.js b/ui/tests/integration/components/console/log-json-test.js new file mode 100644 index 0000000000..353835df01 --- /dev/null +++ b/ui/tests/integration/components/console/log-json-test.js @@ -0,0 +1,24 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('console/log-json', 'Integration | Component | console/log json', { + integration: true, + + beforeEach() { + this.inject.service('code-mirror', { as: 'codeMirror' }); + }, +}); + +test('it renders', function(assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.on('myAction', function(val) { ... }); + const objectContent = { one: 'two', three: 'four', seven: { five: 'six' }, eight: [5, 6] }; + const expectedText = JSON.stringify(objectContent, null, 2); + + this.set('content', objectContent); + + this.render(hbs`{{console/log-json content=content}}`); + const instance = this.codeMirror.instanceFor(this.$('[data-test-component=json-editor]').attr('id')); + + assert.equal(instance.getValue(), expectedText); +}); diff --git a/ui/tests/integration/components/console/log-list-test.js b/ui/tests/integration/components/console/log-list-test.js new file mode 100644 index 0000000000..625787b54b --- /dev/null +++ b/ui/tests/integration/components/console/log-list-test.js @@ -0,0 +1,19 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('console/log-list', 'Integration | Component | console/log list', { + integration: true, +}); + +test('it renders', function(assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.on('myAction', function(val) { ... }); + const listContent = { keys: ['one', 'two'] }; + const expectedText = 'Keys\none\ntwo'; + + this.set('content', listContent); + + this.render(hbs`{{console/log-list content=content}}`); + + assert.dom('pre').includesText(`${expectedText}`); +}); diff --git a/ui/tests/integration/components/console/log-object-test.js b/ui/tests/integration/components/console/log-object-test.js new file mode 100644 index 0000000000..1b53470046 --- /dev/null +++ b/ui/tests/integration/components/console/log-object-test.js @@ -0,0 +1,27 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; +import columnify from 'columnify'; +import { capitalize } from 'vault/helpers/capitalize'; +import { stringifyObjectValues } from 'vault/components/console/log-object'; + +moduleForComponent('console/log-object', 'Integration | Component | console/log object', { + integration: true, +}); + +test('it renders', function(assert) { + const objectContent = { one: 'two', three: 'four', seven: { five: 'six' }, eight: [5, 6] }; + const data = { one: 'two', three: 'four', seven: { five: 'six' }, eight: [5, 6] }; + stringifyObjectValues(data); + const expectedText = columnify(data, { + preserveNewLines: true, + headingTransform: function(heading) { + return capitalize([heading]); + }, + }); + + this.set('content', objectContent); + + this.render(hbs`{{console/log-object content=content}}`); + + assert.dom('pre').includesText(`${expectedText}`); +}); diff --git a/ui/tests/integration/components/console/log-text-test.js b/ui/tests/integration/components/console/log-text-test.js new file mode 100644 index 0000000000..357c99cc89 --- /dev/null +++ b/ui/tests/integration/components/console/log-text-test.js @@ -0,0 +1,17 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('console/log-text', 'Integration | Component | console/log text', { + integration: true, +}); + +test('it renders', function(assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.on('myAction', function(val) { ... }); + const text = 'Success! You did a thing!'; + this.set('content', text); + + this.render(hbs`{{console/log-text content=content}}`); + + assert.dom('pre').includesText(text); +}); diff --git a/ui/tests/integration/components/console/ui-panel-test.js b/ui/tests/integration/components/console/ui-panel-test.js new file mode 100644 index 0000000000..57d6b8d384 --- /dev/null +++ b/ui/tests/integration/components/console/ui-panel-test.js @@ -0,0 +1,118 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import { create } from 'ember-cli-page-object'; +import wait from 'ember-test-helpers/wait'; +import uiPanel from 'vault/tests/pages/components/console/ui-panel'; +import hbs from 'htmlbars-inline-precompile'; + +const component = create(uiPanel); + +moduleForComponent('console/ui-panel', 'Integration | Component | console/ui panel', { + integration: true, + + beforeEach() { + component.setContext(this); + }, + + afterEach() { + component.removeContext(); + }, +}); + +test('it renders', function(assert) { + this.render(hbs`{{console/ui-panel}}`); + assert.ok(component.hasInput); +}); + +test('it clears console input on enter', function(assert) { + this.render(hbs`{{console/ui-panel}}`); + component.consoleInput('list this/thing/here').enter(); + return wait().then(() => { + assert.equal(component.consoleInputValue, '', 'empties input field on enter'); + }); +}); + +test('it clears the log when using clear command', function(assert) { + this.render(hbs`{{console/ui-panel}}`); + component.consoleInput('list this/thing/here').enter(); + component.consoleInput('list this/other/thing').enter(); + component.consoleInput('read another/thing').enter(); + wait().then(() => { + assert.notEqual(component.logOutput, '', 'there is output in the log'); + component.consoleInput('clear').enter(); + }); + + wait().then(() => component.up()); + return wait().then(() => { + assert.equal(component.logOutput, '', 'clears the output log'); + assert.equal( + component.consoleInputValue, + 'clear', + 'populates console input with previous command on up after enter' + ); + }); +}); + +test('it adds command to history on enter', function(assert) { + this.render(hbs`{{console/ui-panel}}`); + component.consoleInput('list this/thing/here').enter(); + wait().then(() => component.up()); + wait().then(() => { + assert.equal( + component.consoleInputValue, + 'list this/thing/here', + 'populates console input with previous command on up after enter' + ); + }); + wait().then(() => component.down()); + return wait().then(() => { + assert.equal(component.consoleInputValue, '', 'populates console input with next command on down'); + }); +}); + +test('it cycles through history with more than one command', function(assert) { + this.render(hbs`{{console/ui-panel}}`); + component.consoleInput('list this/thing/here').enter(); + wait().then(() => component.consoleInput('read that/thing/there').enter()); + wait().then(() => component.consoleInput('qwerty').enter()); + + wait().then(() => component.up()); + wait().then(() => { + assert.equal( + component.consoleInputValue, + 'qwerty', + 'populates console input with previous command on up after enter' + ); + }); + wait().then(() => component.up()); + wait().then(() => { + assert.equal( + component.consoleInputValue, + 'read that/thing/there', + 'populates console input with previous command on up' + ); + }); + wait().then(() => component.up()); + wait().then(() => { + assert.equal( + component.consoleInputValue, + 'list this/thing/here', + 'populates console input with previous command on up' + ); + }); + wait().then(() => component.up()); + wait().then(() => { + assert.equal( + component.consoleInputValue, + 'qwerty', + 'populates console input with initial command if cycled through all previous commands' + ); + }); + wait().then(() => component.down()); + return wait().then(() => { + assert.equal( + component.consoleInputValue, + '', + 'clears console input if down pressed after history is on most recent command' + ); + }); +}); diff --git a/ui/tests/pages/components/console/ui-panel.js b/ui/tests/pages/components/console/ui-panel.js new file mode 100644 index 0000000000..b17858a0aa --- /dev/null +++ b/ui/tests/pages/components/console/ui-panel.js @@ -0,0 +1,18 @@ +import { text, triggerable, fillable, value, isPresent } from 'ember-cli-page-object'; +import keys from 'vault/lib/keycodes'; + +export default { + consoleInput: fillable('[data-test-component="console/command-input"] input'), + consoleInputValue: value('[data-test-component="console/command-input"] input'), + logOutput: text('[data-test-component="console/output-log"]'), + up: triggerable('keyup', '[data-test-component="console/command-input"] input', { + eventProperties: { keyCode: keys.UP }, + }), + down: triggerable('keyup', '[data-test-component="console/command-input"] input', { + eventProperties: { keyCode: keys.DOWN }, + }), + enter: triggerable('keyup', '[data-test-component="console/command-input"] input', { + eventProperties: { keyCode: keys.ENTER }, + }), + hasInput: isPresent('[data-test-component="console/command-input"] input'), +}; diff --git a/ui/tests/unit/adapters/console-test.js b/ui/tests/unit/adapters/console-test.js new file mode 100644 index 0000000000..6d6fd2da92 --- /dev/null +++ b/ui/tests/unit/adapters/console-test.js @@ -0,0 +1,13 @@ +import { moduleFor, test } from 'ember-qunit'; + +moduleFor('adapter:console', 'Unit | Adapter | console', { + needs: ['service:auth', 'service:flash-messages', 'service:version'], +}); + +test('it builds the correct URL', function(assert) { + let adapter = this.subject(); + let sysPath = 'sys/health'; + let awsPath = 'aws/roles/my-other-role'; + assert.equal(adapter.buildURL(sysPath), '/v1/sys/health'); + assert.equal(adapter.buildURL(awsPath), '/v1/aws/roles/my-other-role'); +}); diff --git a/ui/tests/unit/lib/console-helpers-test.js b/ui/tests/unit/lib/console-helpers-test.js new file mode 100644 index 0000000000..ea63e1e410 --- /dev/null +++ b/ui/tests/unit/lib/console-helpers-test.js @@ -0,0 +1,328 @@ +import { module, test } from 'qunit'; +import { + parseCommand, + extractDataAndFlags, + logFromResponse, + logFromError, + logErrorFromInput, +} from 'vault/lib/console-helpers'; + +module('lib/console-helpers', 'Unit | Lib | console helpers'); + +const testCommands = [ + { + name: 'write with data', + command: `vault write aws/config/root \ + access_key=AKIAJWVN5Z4FOFT7NLNA \ + secret_key=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 \ + region=us-east-1`, + expected: [ + 'write', + [], + 'aws/config/root', + [ + 'access_key=AKIAJWVN5Z4FOFT7NLNA', + 'secret_key=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', + 'region=us-east-1', + ], + ], + }, + { + name: 'read with field', + command: `vault read -field=access_key aws/creds/my-role`, + expected: ['read', ['-field=access_key'], 'aws/creds/my-role', []], + }, +]; + +testCommands.forEach(function(testCase) { + test(`#parseCommand: ${testCase.name}`, function(assert) { + let result = parseCommand(testCase.command); + assert.deepEqual(result, testCase.expected); + }); +}); + +test('#parseCommand: invalid commands', function(assert) { + let command = 'vault kv get foo'; + let result = parseCommand(command); + assert.equal(result, false, 'parseCommand returns false by default'); + + assert.throws( + () => { + parseCommand(command, true); + }, + /invalid command/, + 'throws on invalid command when `shouldThrow` is true' + ); +}); + +const testExtractCases = [ + { + name: 'data fields', + input: [ + [ + 'access_key=AKIAJWVN5Z4FOFT7NLNA', + 'secret_key=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', + 'region=us-east-1', + ], + [], + ], + expected: { + data: { + access_key: 'AKIAJWVN5Z4FOFT7NLNA', + secret_key: 'R4nm063hgMVo4BTT5xOs5nHLeLXA6lar7ZJ3Nt0i', + region: 'us-east-1', + }, + flags: {}, + }, + }, + { + name: 'repeated data and a flag', + input: [['allowed_domains=example.com', 'allowed_domains=foo.example.com'], ['-wrap-ttl=2h']], + expected: { + data: { + allowed_domains: ['example.com', 'foo.example.com'], + }, + flags: { + wrapTTL: '2h', + }, + }, + }, + { + name: 'data with more than one equals sign', + input: [['foo=bar=baz', 'foo=baz=bop', 'some=value=val'], []], + expected: { + data: { + foo: ['bar=baz', 'baz=bop'], + some: 'value=val', + }, + flags: {}, + }, + }, +]; + +testExtractCases.forEach(function(testCase) { + test(`#extractDataAndFlags: ${testCase.name}`, function(assert) { + let { data, flags } = extractDataAndFlags(...testCase.input); + assert.deepEqual(data, testCase.expected.data, 'has expected data'); + assert.deepEqual(flags, testCase.expected.flags, 'has expected flags'); + }); +}); + +let testResponseCases = [ + { + name: 'write response, no content', + args: [null, 'foo/bar', 'write', {}], + expectedData: { + type: 'success', + content: 'Success! Data written to: foo/bar', + }, + }, + { + name: 'delete response, no content', + args: [null, 'foo/bar', 'delete', {}], + expectedData: { + type: 'success', + content: 'Success! Data deleted (if it existed) at: foo/bar', + }, + }, + { + name: 'write, with content', + args: [{ data: { one: 'two' } }, 'foo/bar', 'write', {}], + expectedData: { + type: 'object', + content: { one: 'two' }, + }, + }, + { + name: 'with wrap-ttl flag', + args: [{ wrap_info: { one: 'two' } }, 'foo/bar', 'read', { wrapTTL: '1h' }], + expectedData: { + type: 'object', + content: { one: 'two' }, + }, + }, + { + name: 'with -format=json flag and wrap-ttl flag', + args: [{ foo: 'bar', wrap_info: { one: 'two' } }, 'foo/bar', 'read', { format: 'json', wrapTTL: '1h' }], + expectedData: { + type: 'json', + content: { foo: 'bar', wrap_info: { one: 'two' } }, + }, + }, + { + name: 'with -format=json and -field flags', + args: [{ foo: 'bar', data: { one: 'two' } }, 'foo/bar', 'read', { format: 'json', field: 'one' }], + expectedData: { + type: 'json', + content: 'two', + }, + }, + { + name: 'with -format=json and -field, and -wrap-ttl flags', + args: [ + { foo: 'bar', wrap_info: { one: 'two' } }, + 'foo/bar', + 'read', + { format: 'json', wrapTTL: '1h', field: 'one' }, + ], + expectedData: { + type: 'json', + content: 'two', + }, + }, + { + name: 'with string field flag and wrap-ttl flag', + args: [{ foo: 'bar', wrap_info: { one: 'two' } }, 'foo/bar', 'read', { field: 'one', wrapTTL: '1h' }], + expectedData: { + type: 'text', + content: 'two', + }, + }, + { + name: 'with object field flag and wrap-ttl flag', + args: [ + { foo: 'bar', wrap_info: { one: { two: 'three' } } }, + 'foo/bar', + 'read', + { field: 'one', wrapTTL: '1h' }, + ], + expectedData: { + type: 'object', + content: { two: 'three' }, + }, + }, + { + name: 'with response data and string field flag', + args: [{ foo: 'bar', data: { one: 'two' } }, 'foo/bar', 'read', { field: 'one', wrapTTL: '1h' }], + expectedData: { + type: 'text', + content: 'two', + }, + }, + { + name: 'with response data and object field flag ', + args: [ + { foo: 'bar', data: { one: { two: 'three' } } }, + 'foo/bar', + 'read', + { field: 'one', wrapTTL: '1h' }, + ], + expectedData: { + type: 'object', + content: { two: 'three' }, + }, + }, + { + name: 'response with data', + args: [{ foo: 'bar', data: { one: 'two' } }, 'foo/bar', 'read', {}], + expectedData: { + type: 'object', + content: { one: 'two' }, + }, + }, + { + name: 'with response data, field flag, and field missing', + args: [{ foo: 'bar', data: { one: 'two' } }, 'foo/bar', 'read', { field: 'foo' }], + expectedData: { + type: 'error', + content: 'Field "foo" not present in secret', + }, + }, + { + name: 'with response data and auth block', + args: [{ data: { one: 'two' }, auth: { three: 'four' } }, 'auth/token/create', 'write', {}], + expectedData: { + type: 'object', + content: { three: 'four' }, + }, + }, + { + name: 'with -field and -format with an object field', + args: [{ data: { one: { three: 'two' } } }, 'sys/mounts', 'read', { field: 'one', format: 'json' }], + expectedData: { + type: 'json', + content: { three: 'two' }, + }, + }, + { + name: 'with -field and -format with a string field', + args: [{ data: { one: 'two' } }, 'sys/mounts', 'read', { field: 'one', format: 'json' }], + expectedData: { + type: 'json', + content: 'two', + }, + }, +]; + +testResponseCases.forEach(function(testCase) { + test(`#logFromResponse: ${testCase.name}`, function(assert) { + let data = logFromResponse(...testCase.args); + assert.deepEqual(data, testCase.expectedData); + }); +}); + +let testErrorCases = [ + { + name: 'AdapterError write', + args: [{ httpStatus: 404, path: 'v1/sys/foo', errors: [{}] }, 'sys/foo', 'write'], + expectedContent: 'Error writing to: sys/foo.\nURL: v1/sys/foo\nCode: 404', + }, + { + name: 'AdapterError read', + args: [{ httpStatus: 404, path: 'v1/sys/foo', errors: [{}] }, 'sys/foo', 'read'], + expectedContent: 'Error reading from: sys/foo.\nURL: v1/sys/foo\nCode: 404', + }, + { + name: 'AdapterError list', + args: [{ httpStatus: 404, path: 'v1/sys/foo', errors: [{}] }, 'sys/foo', 'list'], + expectedContent: 'Error listing: sys/foo.\nURL: v1/sys/foo\nCode: 404', + }, + { + name: 'AdapterError delete', + args: [{ httpStatus: 404, path: 'v1/sys/foo', errors: [{}] }, 'sys/foo', 'delete'], + expectedContent: 'Error deleting at: sys/foo.\nURL: v1/sys/foo\nCode: 404', + }, + { + name: 'VaultError single error', + args: [{ httpStatus: 404, path: 'v1/sys/foo', errors: ['no client token'] }, 'sys/foo', 'delete'], + expectedContent: 'Error deleting at: sys/foo.\nURL: v1/sys/foo\nCode: 404\nErrors:\n no client token', + }, + { + name: 'VaultErrors multiple errors', + args: [ + { httpStatus: 404, path: 'v1/sys/foo', errors: ['no client token', 'this is an error'] }, + 'sys/foo', + 'delete', + ], + expectedContent: + 'Error deleting at: sys/foo.\nURL: v1/sys/foo\nCode: 404\nErrors:\n no client token\n this is an error', + }, +]; + +testErrorCases.forEach(function(testCase) { + test(`#logFromError: ${testCase.name}`, function(assert) { + let data = logFromError(...testCase.args); + assert.deepEqual(data, { type: 'error', content: testCase.expectedContent }, 'returns the expected data'); + }); +}); + +const testCommandCases = [ + { + name: 'errors when command does not include a path', + args: [], + expectedContent: 'A path is required to make a request.', + }, + { + name: 'errors when write command does not include data and does not have force tag', + args: ['foo/bar', 'write', {}, []], + expectedContent: 'Must supply data or use -force', + }, +]; + +testCommandCases.forEach(function(testCase) { + test(`#logErrorFromInput: ${testCase.name}`, function(assert) { + let data = logErrorFromInput(...testCase.args); + + assert.deepEqual(data, { type: 'error', content: testCase.expectedContent }, 'returns the pcorrect data'); + }); +}); diff --git a/ui/tests/unit/services/console-test.js b/ui/tests/unit/services/console-test.js new file mode 100644 index 0000000000..0f4adca4df --- /dev/null +++ b/ui/tests/unit/services/console-test.js @@ -0,0 +1,94 @@ +import { moduleFor, test } from 'ember-qunit'; +import { sanitizePath, ensureTrailingSlash } from 'vault/services/console'; +import sinon from 'sinon'; + +moduleFor('service:console', 'Unit | Service | console', { + needs: ['service:auth'], + beforeEach() {}, + afterEach() {}, +}); + +test('#sanitizePath', function(assert) { + assert.equal(sanitizePath(' /foo/bar/baz/ '), 'foo/bar/baz', 'removes spaces and slashs on either side'); + assert.equal(sanitizePath('//foo/bar/baz/'), 'foo/bar/baz', 'removes more than one slash'); +}); + +test('#ensureTrailingSlash', function(assert) { + assert.equal(ensureTrailingSlash('foo/bar'), 'foo/bar/', 'adds trailing slash'); + assert.equal(ensureTrailingSlash('baz/'), 'baz/', 'keeps trailing slash if there is one'); +}); + +let testCases = [ + { + method: 'read', + args: ['/sys/health', {}], + expectedURL: 'sys/health', + expectedVerb: 'GET', + expectedOptions: { data: undefined, wrapTTL: undefined }, + }, + + { + method: 'read', + args: ['/secrets/foo/bar', {}, '30m'], + expectedURL: 'secrets/foo/bar', + expectedVerb: 'GET', + expectedOptions: { data: undefined, wrapTTL: '30m' }, + }, + + { + method: 'write', + args: ['aws/roles/my-other-role', { arn: 'arn=arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess' }], + expectedURL: 'aws/roles/my-other-role', + expectedVerb: 'POST', + expectedOptions: { + data: { arn: 'arn=arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess' }, + wrapTTL: undefined, + }, + }, + + { + method: 'list', + args: ['secret/mounts', {}], + expectedURL: 'secret/mounts/', + expectedVerb: 'GET', + expectedOptions: { data: { list: true }, wrapTTL: undefined }, + }, + + { + method: 'list', + args: ['secret/mounts', {}, '1h'], + expectedURL: 'secret/mounts/', + expectedVerb: 'GET', + expectedOptions: { data: { list: true }, wrapTTL: '1h' }, + }, + + { + method: 'delete', + args: ['secret/secrets/kv'], + expectedURL: 'secret/secrets/kv', + expectedVerb: 'DELETE', + expectedOptions: { data: undefined, wrapTTL: undefined }, + }, +]; + +test('it reads, writes, lists, deletes', function(assert) { + let ajax = sinon.stub(); + let uiConsole = this.subject({ + adapter() { + return { + buildURL(url) { + return url; + }, + ajax, + }; + }, + }); + + testCases.forEach(testCase => { + uiConsole[testCase.method](...testCase.args); + let [url, verb, options] = ajax.lastCall.args; + assert.equal(url, testCase.expectedURL, `${testCase.method}: uses trimmed passed url`); + assert.equal(verb, testCase.expectedVerb, `${testCase.method}: uses the correct verb`); + assert.deepEqual(options, testCase.expectedOptions, `${testCase.method}: uses the correct options`); + }); +}); diff --git a/ui/yarn.lock b/ui/yarn.lock index 18f4f4fa8c..5fa12cc113 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -82,6 +82,14 @@ dependencies: "@glimmer/util" "^0.22.3" +"@types/estree@0.0.38": + version "0.0.38" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.38.tgz#c1be40aa933723c608820a99a373a16d215a1ca2" + +"@types/node@*": + version "10.0.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.0.4.tgz#130598ee56e9a1210a53f557d64935571f05390d" + Duration.js@icholy/Duration.js#golang_compatible: version "2.0.0" resolved "https://codeload.github.com/icholy/Duration.js/tar.gz/cb1c58efc2772ef0f261da9e2535890734a86417" @@ -178,6 +186,12 @@ amd-name-resolver@0.0.7: dependencies: ensure-posix-path "^1.0.1" +amd-name-resolver@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/amd-name-resolver/-/amd-name-resolver-1.0.0.tgz#0e593b28d6fa3326ab1798107edaea961046e8d8" + dependencies: + ensure-posix-path "^1.0.1" + amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" @@ -274,10 +288,18 @@ arr-diff@^2.0.0: dependencies: arr-flatten "^1.0.1" -arr-flatten@^1.0.1: +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + +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" +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + array-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" @@ -314,6 +336,10 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + arraybuffer.slice@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca" @@ -338,6 +364,10 @@ assertion-error@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.0.tgz#c7f85438fdd466bc7ca16ab90c81513797a5d23b" +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + ast-traverse@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ast-traverse/-/ast-traverse-0.1.1.tgz#69cf2b8386f19dcda1bb1e05d68fe359d8897de6" @@ -398,6 +428,10 @@ asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" +atob@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a" + autoprefixer@^7.0.0: version "7.2.6" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-7.2.6.tgz#256672f86f7c735da849c4f07d008abb056067dc" @@ -1091,6 +1125,18 @@ base64id@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/base64id/-/base64id-0.1.0.tgz#02ce0fdeee0cef4f40080e1e73e834f0b1bfce3f" +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + 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" + basic-auth@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-1.1.0.tgz#45221ee429f7ee1e5035be3f51533f1cdfd29884" @@ -1216,6 +1262,21 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + 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" + breakable@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/breakable/-/breakable-1.0.0.tgz#784a797915a38ead27bad456b5572cb4bbaa78c1" @@ -1381,6 +1442,17 @@ broccoli-debug@^0.6.1, broccoli-debug@^0.6.2: symlink-or-copy "^1.1.8" tree-sync "^1.2.2" +broccoli-debug@^0.6.3, broccoli-debug@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/broccoli-debug/-/broccoli-debug-0.6.4.tgz#986eb3d2005e00e3bb91f9d0a10ab137210cd150" + dependencies: + broccoli-plugin "^1.2.1" + fs-tree-diff "^0.5.2" + heimdalljs "^0.2.1" + heimdalljs-logger "^0.1.7" + symlink-or-copy "^1.1.8" + tree-sync "^1.2.2" + broccoli-favicon@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/broccoli-favicon/-/broccoli-favicon-1.0.0.tgz#c770a5aa16032fbaf1b5c9c033f71b9cc5a5cb51" @@ -1735,6 +1807,10 @@ builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" +builtin-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-2.0.0.tgz#60b7ef5ae6546bd7deefa74b08b62a43a232648e" + builtins@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" @@ -1759,6 +1835,20 @@ bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + 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" + calculate-cache-key-for-tree@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/calculate-cache-key-for-tree/-/calculate-cache-key-for-tree-1.1.0.tgz#0c3e42c9c134f3c9de5358c0f16793627ea976d6" @@ -1890,6 +1980,14 @@ chalk@^2.0.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" +chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" @@ -1941,6 +2039,15 @@ circular-json@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + clean-base-url@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clean-base-url/-/clean-base-url-1.0.0.tgz#c901cf0a20b972435b0eccd52d056824a4351b7b" @@ -1960,7 +2067,7 @@ clean-css@^3.4.5: commander "2.8.x" source-map "0.4.x" -cli-cursor@^1.0.1, cli-cursor@^1.0.2: +cli-cursor@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" dependencies: @@ -1972,9 +2079,9 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" -cli-spinners@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" +cli-spinners@^1.1.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" cli-table2@^0.2.0: version "0.2.0" @@ -2051,6 +2158,13 @@ coffee-script@^1.10.0: version "1.12.7" resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.12.7.tgz#c05dae0cb79591d05b3070a8433a98c9a89ccc53" +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + color-convert@^1.0.0: version "1.9.0" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" @@ -2075,6 +2189,13 @@ colors@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" +columnify@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" + dependencies: + strip-ansi "^3.0.0" + wcwidth "^1.0.0" + combined-stream@^1.0.5, combined-stream@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" @@ -2125,7 +2246,7 @@ component-emitter@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.1.2.tgz#296594f2753daa63996d2af08d15a95116c9aec3" -component-emitter@1.2.1: +component-emitter@1.2.1, component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" @@ -2186,14 +2307,16 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" -console-ui@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/console-ui/-/console-ui-1.0.3.tgz#31c524461b63422769f9e89c173495d91393721c" +console-ui@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/console-ui/-/console-ui-2.2.2.tgz#b294a2934de869dd06789ab4be69555411edef29" dependencies: - chalk "^1.1.3" - inquirer "^1.2.3" - ora "^0.2.0" + chalk "^2.1.0" + inquirer "^2" + json-stable-stringify "^1.0.1" + ora "^2.0.0" through "^2.3.8" + user-info "^1.0.0" consolidate@^0.14.0: version "0.14.5" @@ -2237,6 +2360,10 @@ copy-dereference@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/copy-dereference/-/copy-dereference-1.0.0.tgz#6b131865420fd81b413ba994b44d3655311152b6" +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" @@ -2371,7 +2498,7 @@ debug@2.6.8, debug@^2.1.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.4. dependencies: ms "2.0.0" -debug@2.6.9: +debug@2.6.9, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -2381,18 +2508,43 @@ decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" +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" + deep-extend@~0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" -deep-freeze@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84" - deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + dependencies: + clone "^1.0.2" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + 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" + 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" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" @@ -2679,6 +2831,21 @@ ember-cli-broccoli-sane-watcher@^2.0.4: rsvp "^3.0.18" sane "^1.1.1" +ember-cli-cjs-transform@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ember-cli-cjs-transform/-/ember-cli-cjs-transform-1.2.0.tgz#34a0d2667673caec0248f500a954f45668027e8b" + dependencies: + broccoli-debug "^0.6.4" + broccoli-plugin "^1.3.0" + ember-cli-babel "^6.6.0" + fs-extra "^5.0.0" + hash-for-dep "^1.2.3" + pkg-dir "^2.0.0" + rollup "^0.58.1" + rollup-plugin-commonjs "^9.1.0" + rollup-plugin-node-resolve "^3.3.0" + username "^3.0.0" + ember-cli-clipboard@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/ember-cli-clipboard/-/ember-cli-clipboard-0.8.0.tgz#c2e91290b2746c1a4903097f5d7a55406de539b1" @@ -2805,10 +2972,14 @@ ember-cli-legacy-blueprints@^0.1.2: rsvp "^3.0.17" silent-error "^1.0.0" -ember-cli-lodash-subset@^1.0.11, ember-cli-lodash-subset@^1.0.7: +ember-cli-lodash-subset@^1.0.7: version "1.0.12" resolved "https://registry.yarnpkg.com/ember-cli-lodash-subset/-/ember-cli-lodash-subset-1.0.12.tgz#af2e77eba5dcb0d77f3308d3a6fd7d3450f6e537" +ember-cli-lodash-subset@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ember-cli-lodash-subset/-/ember-cli-lodash-subset-2.0.1.tgz#20cb68a790fe0fde2488ddfd8efbb7df6fe766f2" + ember-cli-mirage@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/ember-cli-mirage/-/ember-cli-mirage-0.4.1.tgz#bfdfe61e5e74dc3881ed31f12112dae1a29f0d4c" @@ -3000,11 +3171,11 @@ ember-cli-version-checker@^2.1.0: resolve "^1.3.3" semver "^5.3.0" -ember-cli@~2.15.0: - version "2.15.1" - resolved "https://registry.yarnpkg.com/ember-cli/-/ember-cli-2.15.1.tgz#773add3cc18e5068f1c5f43a77544efa2712e47b" +ember-cli@~2.16.0: + version "2.16.2" + resolved "https://registry.yarnpkg.com/ember-cli/-/ember-cli-2.16.2.tgz#53b922073a8e6f34255a6e0dcb1794a91ba3e1b7" dependencies: - amd-name-resolver "0.0.7" + amd-name-resolver "1.0.0" babel-plugin-transform-es2015-modules-amd "^6.24.0" bower-config "^1.3.0" bower-endpoint-parser "0.2.2" @@ -3014,7 +3185,8 @@ ember-cli@~2.15.0: broccoli-concat "^3.2.2" broccoli-config-loader "^1.0.0" broccoli-config-replace "^1.1.2" - broccoli-funnel "^1.0.6" + broccoli-debug "^0.6.3" + broccoli-funnel "^2.0.0" broccoli-funnel-reducer "^1.0.0" broccoli-merge-trees "^2.0.0" broccoli-middleware "^1.0.0" @@ -3022,31 +3194,30 @@ ember-cli@~2.15.0: broccoli-stew "^1.2.0" calculate-cache-key-for-tree "^1.0.0" capture-exit "^1.1.0" - chalk "^1.1.3" + chalk "^2.0.1" clean-base-url "^1.0.0" compression "^1.4.4" configstore "^3.0.0" - console-ui "^1.0.2" + console-ui "^2.0.0" core-object "^3.1.3" dag-map "^2.0.2" - deep-freeze "^0.0.1" diff "^3.2.0" ember-cli-broccoli-sane-watcher "^2.0.4" ember-cli-is-package-missing "^1.0.0" ember-cli-legacy-blueprints "^0.1.2" - ember-cli-lodash-subset "^1.0.11" + ember-cli-lodash-subset "^2.0.1" ember-cli-normalize-entity-name "^1.0.0" ember-cli-preprocess-registry "^3.1.0" ember-cli-string-utils "^1.0.0" ember-try "^0.2.15" ensure-posix-path "^1.0.2" - execa "^0.7.0" + execa "^0.8.0" exists-sync "0.0.4" exit "^0.1.2" express "^4.12.3" filesize "^3.1.3" find-up "^2.1.0" - fs-extra "^3.0.0" + fs-extra "^4.0.0" fs-tree-diff "^0.5.2" get-caller-file "^1.0.0" git-repo-info "^1.4.1" @@ -3074,7 +3245,7 @@ ember-cli@~2.15.0: promise-map-series "^0.2.1" quick-temp "^0.1.8" resolve "^1.3.0" - rsvp "^3.3.3" + rsvp "^3.6.0" sane "^1.6.0" semver "^5.1.1" silent-error "^1.0.0" @@ -3660,6 +3831,10 @@ estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" +estree-walker@^0.5.1, estree-walker@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.2.tgz#d3850be7529c9580d815600b53126515e146dd39" + esutils@^2.0.0, esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" @@ -3683,6 +3858,14 @@ events-to-array@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/events-to-array/-/events-to-array-1.1.2.tgz#2d41f563e1fe400ed4962fe1a4d5c6a7539df7f6" +exec-file-sync@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/exec-file-sync/-/exec-file-sync-2.0.2.tgz#58d441db46e40de6d1f30de5be022785bd89e328" + dependencies: + is-obj "^1.0.0" + object-assign "^4.0.1" + spawn-sync "^1.0.11" + exec-sh@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.0.tgz#14f75de3f20d286ef933099b2ce50a90359cef10" @@ -3701,6 +3884,18 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + exif-parser@^0.1.9: version "0.1.11" resolved "https://registry.yarnpkg.com/exif-parser/-/exif-parser-0.1.11.tgz#8a97d1c9315ffd4754b6ae938ce4488d1b1a26b7" @@ -3731,6 +3926,18 @@ expand-brackets@^0.1.4: dependencies: is-posix-bracket "^0.1.0" +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + 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-range@^1.8.1: version "1.8.2" resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" @@ -3776,6 +3983,19 @@ express@^4.10.7, express@^4.12.3: utils-merge "1.0.0" vary "~1.1.1" +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + 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" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + extend@^3.0.0, extend@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" @@ -3802,6 +4022,19 @@ extglob@^0.3.1: dependencies: is-extglob "^1.0.0" +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + 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" + extract-zip@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.5.0.tgz#92ccf6d81ef70a9fa4c1747114ccef6d8688a6c4" @@ -3943,6 +4176,15 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + finalhandler@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.3.tgz#ef47e77950e999780e86022a560e3217e0d0cc89" @@ -4006,7 +4248,7 @@ for-each@^0.3.2: dependencies: is-function "~1.0.0" -for-in@^1.0.1: +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" @@ -4038,6 +4280,12 @@ forwarded@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + fresh@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.0.tgz#f474ca5e6a9246d6fd8e0953cfa9b9c805afa78e" @@ -4090,12 +4338,20 @@ fs-extra@^2.0.0: graceful-fs "^4.1.2" jsonfile "^2.1.0" -fs-extra@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291" +fs-extra@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" dependencies: graceful-fs "^4.1.2" - jsonfile "^3.0.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" universalify "^0.1.0" fs-readdir-recursive@^0.1.0: @@ -4191,6 +4447,10 @@ get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" +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" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -4429,6 +4689,33 @@ has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + 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" + 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" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + hash-for-dep@^1.0.2: version "1.1.2" resolved "https://registry.yarnpkg.com/hash-for-dep/-/hash-for-dep-1.1.2.tgz#e3347ed92960eb0bb53a2c6c2b70e36d75b7cd0c" @@ -4438,6 +4725,15 @@ hash-for-dep@^1.0.2: heimdalljs-logger "^0.1.7" resolve "^1.1.6" +hash-for-dep@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/hash-for-dep/-/hash-for-dep-1.2.3.tgz#5ec69fca32c23523972d52acb5bb65ffc3664cab" + dependencies: + broccoli-kitchen-sink-helpers "^0.3.1" + heimdalljs "^0.2.3" + heimdalljs-logger "^0.1.7" + resolve "^1.4.0" + hasha@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/hasha/-/hasha-2.2.0.tgz#78d7cbfc1e6d66303fe79837365984517b2f6ee1" @@ -4657,22 +4953,22 @@ inquirer@^0.12.0: strip-ansi "^3.0.0" through "^2.3.6" -inquirer@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.2.3.tgz#4dec6f32f37ef7bb0b2ed3f1d1a5c3f545074918" +inquirer@^2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-2.0.0.tgz#e1351687b90d150ca403ceaa3cefb1e3065bef4b" dependencies: ansi-escapes "^1.1.0" chalk "^1.0.0" cli-cursor "^1.0.1" cli-width "^2.0.0" external-editor "^1.1.0" - figures "^1.3.5" + figures "^2.0.0" lodash "^4.3.0" mute-stream "0.0.6" pinkie-promise "^2.0.0" run-async "^2.2.0" rx "^4.1.0" - string-width "^1.0.1" + string-width "^2.0.0" strip-ansi "^3.0.0" through "^2.3.6" @@ -4717,6 +5013,18 @@ ipaddr.js@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.3.0.tgz#1e03a52fdad83a8bbb2b25cbf4998b4cffcd3dec" +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" + 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" + 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" @@ -4737,6 +5045,34 @@ is-builtin-module@^1.0.0: dependencies: builtin-modules "^1.0.0" +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" + 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" + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + 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" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" @@ -4747,10 +5083,16 @@ is-equal-shallow@^0.1.3: dependencies: is-primitive "^2.0.0" -is-extendable@^0.1.1: +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" +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + is-extglob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" @@ -4795,6 +5137,10 @@ is-integer@^1.0.4: dependencies: is-finite "^1.0.0" +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4: version "2.16.0" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693" @@ -4816,10 +5162,20 @@ is-number@^3.0.0: 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" + is-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" +is-odd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24" + dependencies: + is-number "^4.0.0" + is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -4836,6 +5192,12 @@ is-path-inside@^1.0.0: dependencies: path-is-inside "^1.0.1" +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" + dependencies: + isobject "^3.0.1" + is-posix-bracket@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" @@ -4880,6 +5242,10 @@ is-windows@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -4902,6 +5268,10 @@ isobject@^2.0.0: 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" + isstream@0.1.x, isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -5049,9 +5419,9 @@ jsonfile@^2.1.0: optionalDependencies: graceful-fs "^4.1.6" -jsonfile@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" optionalDependencies: graceful-fs "^4.1.6" @@ -5100,7 +5470,7 @@ kew@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b" -kind-of@^3.0.2: +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" dependencies: @@ -5112,6 +5482,14 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + klaw@^1.0.0: version "1.3.1" resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" @@ -5393,6 +5771,12 @@ lodash@~2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-2.4.2.tgz#fadd834b9683073da179b3eae6d9c0d15053f73e" +log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + dependencies: + chalk "^2.0.1" + loglevel-colored-level-prefix@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz#6a40218fdc7ae15fc76c3d0f3e676c465388603e" @@ -5440,6 +5824,12 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" +magic-string@^0.22.4: + version "0.22.5" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e" + dependencies: + vlq "^0.2.2" + make-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.0.0.tgz#97a011751e91dd87cfadef58832ebb04936de978" @@ -5458,6 +5848,10 @@ makeerror@1.0.x: dependencies: tmpl "1.0.x" +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -5466,6 +5860,12 @@ map-obj@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + markdown-it-terminal@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/markdown-it-terminal/-/markdown-it-terminal-0.1.0.tgz#545abd8dd01c3d62353bfcea71db580b51d22bd9" @@ -5510,13 +5910,19 @@ media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" +mem@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + dependencies: + mimic-fn "^1.0.0" + memory-streams@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/memory-streams/-/memory-streams-0.1.2.tgz#273ff777ab60fec599b116355255282cca2c50c2" dependencies: readable-stream "~1.0.2" -meow@^3.7.0: +meow@^3.4.0, meow@^3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" dependencies: @@ -5592,6 +5998,24 @@ micromatch@^2.1.5, micromatch@^2.3.7: parse-glob "^3.0.4" regex-cache "^0.4.2" +micromatch@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + 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" + "mime-db@>= 1.27.0 < 2": version "1.29.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.29.0.tgz#48d26d235589651704ac5916ca06001914266878" @@ -5661,6 +6085,13 @@ minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + mkdirp@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" @@ -5761,6 +6192,23 @@ nan@^2.3.0: version "2.6.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" +nanomatch@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2" + 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-odd "^2.0.0" + 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" + native-promise-only@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/native-promise-only/-/native-promise-only-0.8.1.tgz#20a318c30cb45f71fe7adfbf7b21c99c1472ef11" @@ -5977,6 +6425,20 @@ object-component@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -5984,6 +6446,12 @@ object.omit@^2.0.0: for-own "^0.1.4" is-extendable "^0.1.1" +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + dependencies: + isobject "^3.0.1" + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -6032,14 +6500,16 @@ options@>=0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" -ora@^0.2.0: - version "0.2.3" - resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" +ora@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-2.1.0.tgz#6caf2830eb924941861ec53a173799e008b51e5b" dependencies: - chalk "^1.1.1" - cli-cursor "^1.0.2" - cli-spinners "^0.1.2" - object-assign "^4.0.1" + chalk "^2.3.1" + cli-cursor "^2.1.0" + cli-spinners "^1.1.0" + log-symbols "^2.2.0" + strip-ansi "^4.0.0" + wcwidth "^1.0.1" os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" @@ -6157,6 +6627,16 @@ parseurl@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + +passwd-user@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/passwd-user/-/passwd-user-1.2.1.tgz#a01a5dc639ef007dc56364b8178569080ad3a7b8" + dependencies: + exec-file-sync "^2.0.0" + path-exists@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-1.0.0.tgz#d5a8998eb71ef37a74c34eb0d9eba6e878eea081" @@ -6251,6 +6731,12 @@ pixelmatch@^4.0.0: dependencies: pngjs "^3.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" + dependencies: + find-up "^2.1.0" + pluralize@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" @@ -6275,6 +6761,10 @@ portfinder@^1.0.7: debug "^2.2.0" mkdirp "0.5.x" +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" + postcss-value-parser@^3.2.3: version "3.3.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" @@ -6651,6 +7141,13 @@ regex-cache@^0.4.2: is-equal-shallow "^0.1.3" is-primitive "^2.0.0" +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" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + regexpu-core@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" @@ -6687,7 +7184,7 @@ repeat-element@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" -repeat-string@^1.5.2: +repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" @@ -6814,6 +7311,10 @@ resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + resolve@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.2.tgz#1f0442c9e0cbb8136e87b9305f932f46c7f28235" @@ -6826,6 +7327,12 @@ resolve@^1.1.2, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.3.0, resolve@^1.3.3: dependencies: path-parse "^1.0.5" +resolve@^1.4.0, resolve@^1.5.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3" + dependencies: + path-parse "^1.0.5" + restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" @@ -6840,6 +7347,10 @@ restore-cursor@^2.0.0: onetime "^2.0.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" + right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" @@ -6856,6 +7367,38 @@ rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" +rollup-plugin-commonjs@^9.1.0: + version "9.1.3" + resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.1.3.tgz#37bfbf341292ea14f512438a56df8f9ca3ba4d67" + dependencies: + estree-walker "^0.5.1" + magic-string "^0.22.4" + resolve "^1.5.0" + rollup-pluginutils "^2.0.1" + +rollup-plugin-node-resolve@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.3.0.tgz#c26d110a36812cbefa7ce117cadcd3439aa1c713" + dependencies: + builtin-modules "^2.0.0" + is-module "^1.0.0" + resolve "^1.1.6" + +rollup-pluginutils@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.1.0.tgz#4ee80b1bbc8eaed67aabf7f55b4fe9ccf9fd3c3a" + dependencies: + estree-walker "^0.5.2" + micromatch "^3.1.10" + tosource "^1.0.0" + +rollup@^0.58.1: + version "0.58.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.58.2.tgz#2feddea8c0c022f3e74b35c48e3c21b3433803ce" + dependencies: + "@types/estree" "0.0.38" + "@types/node" "*" + route-recognizer@^0.2.3: version "0.2.10" resolved "https://registry.yarnpkg.com/route-recognizer/-/route-recognizer-0.2.10.tgz#024b2283c2e68d13a7c7f5173a5924645e8902df" @@ -6868,6 +7411,10 @@ rsvp@^3.0.14, rsvp@^3.0.16, rsvp@^3.0.17, rsvp@^3.0.18, rsvp@^3.0.21, rsvp@^3.0. version "3.6.1" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.1.tgz#34f4a7ac2859f7bacc8f49789c5604f1e26ae702" +rsvp@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a" + rsvp@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.7.0.tgz#dc1b0b1a536f7dec9d2be45e0a12ad4197c9fd96" @@ -6924,6 +7471,12 @@ safe-json-parse@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57" +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + samsam@1.x, samsam@^1.1.3: version "1.3.0" resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50" @@ -7011,6 +7564,24 @@ set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + setprototypeof@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" @@ -7103,6 +7674,33 @@ slide@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + 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" + 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" + 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" + sntp@1.x.x: version "1.0.9" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" @@ -7163,6 +7761,16 @@ sort-package-json@^1.4.0: dependencies: sort-object-keys "^1.1.1" +source-map-resolve@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a" + dependencies: + atob "^2.0.0" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + source-map-support@^0.2.10: version "0.2.10" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.2.10.tgz#ea5a3900a1c1cb25096a0ae8cc5c2b4b10ded3dc" @@ -7179,6 +7787,10 @@ source-map-url@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.3.0.tgz#7ecaf13b57bcd09da8a40c5d269db33799d4aaf9" +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" + source-map@0.1.32: version "0.1.32" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.32.tgz#c8b6c167797ba4740a8ea33252162ff08591b266" @@ -7203,7 +7815,7 @@ spawn-args@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/spawn-args/-/spawn-args-0.2.0.tgz#fb7d0bd1d70fd4316bd9e3dec389e65f9d6361bb" -spawn-sync@^1.0.15: +spawn-sync@^1.0.11, spawn-sync@^1.0.15: version "1.0.15" resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" dependencies: @@ -7224,6 +7836,12 @@ spdx-license-ids@^1.0.2: version "1.2.2" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" +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" + dependencies: + extend-shallow "^3.0.0" + sprintf-js@^1.0.3, sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -7254,6 +7872,13 @@ stack-trace@0.0.x: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + "statuses@>= 1.3.1 < 2", statuses@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" @@ -7599,6 +8224,32 @@ to-ico@^1.1.2: parse-png "^1.0.0" resize-img "^1.1.0" +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" + 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" + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +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" + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +tosource@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tosource/-/tosource-1.0.0.tgz#42d88dd116618bcf00d6106dd5446f3427902ff1" + tough-cookie@~2.3.0: version "2.3.2" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" @@ -7724,6 +8375,15 @@ underscore@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + unique-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" @@ -7738,18 +8398,35 @@ unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + untildify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/untildify/-/untildify-2.1.0.tgz#17eb2807987f76952e9c0485fc311d06a826a2e0" dependencies: os-homedir "^1.0.0" +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + url-regex@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/url-regex/-/url-regex-3.2.0.tgz#dbad1e0c9e29e105dd0b1f09f6862f7fdb482724" dependencies: ip-regex "^1.0.1" +use@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.0.tgz#14716bf03fdfefd03040aef58d8b4b85f3a7c544" + dependencies: + kind-of "^6.0.2" + user-home@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" @@ -7760,10 +8437,31 @@ user-home@^2.0.0: dependencies: os-homedir "^1.0.0" +user-info@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/user-info/-/user-info-1.0.0.tgz#81c82b7ed63e674c2475667653413b3c76fde239" + dependencies: + os-homedir "^1.0.1" + passwd-user "^1.2.1" + username "^1.0.1" + username-sync@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/username-sync/-/username-sync-1.0.1.tgz#1cde87eefcf94b8822984d938ba2b797426dae1f" +username@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/username/-/username-1.0.1.tgz#e1f72295e3e58e06f002c6327ce06897a99cd67f" + dependencies: + meow "^3.4.0" + +username@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/username/-/username-3.0.0.tgz#b3dba982a72b4ce59d52f159fa1aeba266af5fc8" + dependencies: + execa "^0.7.0" + mem "^1.1.0" + util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -7807,6 +8505,10 @@ vinyl@^1.1.0: clone-stats "^0.0.1" replace-ext "0.0.1" +vlq@^0.2.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" + walk-sync@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-0.3.1.tgz#558a16aeac8c0db59c028b73c66f397684ece465" @@ -7842,6 +8544,12 @@ watch@~0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/watch/-/watch-0.10.0.tgz#77798b2da0f9910d595f1ace5b0c2258521f21dc" +wcwidth@^1.0.0, wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + dependencies: + defaults "^1.0.3" + websocket-driver@>=0.5.1: version "0.6.5" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" @@ -8006,6 +8714,12 @@ yam@0.0.22: fs-extra "^0.30.0" lodash.merge "^4.4.0" +yargs-parser@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.0.0.tgz#c737c93de2567657750cb1f2c00be639fd19c994" + dependencies: + camelcase "^4.1.0" + yargs-parser@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"