diff --git a/.gitignore b/.gitignore index 8d1232d3..7f169bde 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules dist .reify-cache yarn-error.log +.rpt2_cache diff --git a/.hound.yml b/.hound.yml deleted file mode 100644 index 8530e422..00000000 --- a/.hound.yml +++ /dev/null @@ -1,6 +0,0 @@ -jshint: - enabled: false - -eslint: - enabled: true - config_file: .eslintrc diff --git a/.travis.yml b/.travis.yml index 7129761e..f722b6ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,3 @@ language: node_js script: - - npm run lint - npm run test diff --git a/README.md b/README.md index c40cbb81..7702df95 100644 --- a/README.md +++ b/README.md @@ -2,24 +2,16 @@ This is a websocket client written in JavaScript that allows retrieving authentication tokens and communicate with the Home Assistant websocket API. It can be used to integrate Home Assistant into your apps. It has 0 dependencies. -```javascript -import { createConnection, subscribeEntities } from 'home-assistant-js-websocket'; +## Trying it out -function stateChanged(event) { - console.log('state changed', event); -} +We've included an [example client](https://github.com/home-assistant/home-assistant-js-websocket/blob/master/example.html) based on this lib so that it's easy to try it out: -createConnection('ws://localhost:8123/api/websocket').then( - (conn) => { - console.log('Connection established!'); - subscribeEntities(conn, entities => console.log('New entities!', entities)); - }, - err => console.error('Connection failed with code', err) -) +```bash +yarn build +npx http-server -o +# A browser will open, navigate to example.html ``` -[Try it on JSFiddle.](https://jsfiddle.net/balloob/9w3oyswa/) - ## Usage ### Initializing connection @@ -32,8 +24,8 @@ import { getAuth, createConnection, subscribeEntities, - ERR_HASS_HOST_REQUIRED, -} from 'home-assistant-js-websocket'; + ERR_HASS_HOST_REQUIRED +} from "home-assistant-js-websocket"; async function connect() { let auth; @@ -42,7 +34,9 @@ async function connect() { } catch (err) { if (err === ERR_HASS_HOST_REQUIRED) { const hassUrl = prompt( - "What host to connect to?", "http://localhost:8123"); + "What host to connect to?", + "http://localhost:8123" + ); auth = await getAuth({ hassUrl }); } else { alert(`Unknown error: ${err}`); @@ -51,36 +45,40 @@ async function connect() { } const connection = await createConnection(auth); subscribeEntities(connection, ent => console.log(ent)); -}; +} connect(); ``` -Connections to the websocket API are initiated by calling `createConnection(url[, options])`. `createConnection` will return a promise that will resolve to either a `Connection` object or rejects with an error code. +Connections to the websocket API are initiated by calling `createConnection(auth[, options])`. This method will return a promise that will resolve to either a `Connection` object or rejects with error codes `ERR_INVALID_AUTH` or `ERR_CANNOT_CONNECT`. #### Available options Currently the following options are available: -| Option | Description | -| ------ | ----------- | -| setupRetry | Number of times to retry initial connection when it fails. -1 means infinite. +| Option | Description | +| ------------ | -------------------------------------------------------------------------------------------------------------------- | +| setupRetry | Number of times to retry initial connection when it fails. Set to -1 for infinite retries. Default is 0 (no retries) | +| createSocket | Override the createSocket method with your own. `(auth, options) => Promise` | #### Possible error codes Currently the following error codes can be expected: -| Error | Description | -| ----- | ----------- | -| ERR_CANNOT_CONNECT | If the client was unable to connect to the websocket API. -| ERR_INVALID_AUTH | If the supplied authentication was invalid. -| ERR_CONNECTION_LOST | Raised if connection closed while waiting for a message to be returned. -| ERR_HASS_HOST_REQUIRED | If the authentication requires a host to be defined. +| Error | Description | +| ---------------------- | ----------------------------------------------------------------------- | +| ERR_CANNOT_CONNECT | If the client was unable to connect to the websocket API. | +| ERR_INVALID_AUTH | If the supplied authentication was invalid. | +| ERR_CONNECTION_LOST | Raised if connection closed while waiting for a message to be returned. | +| ERR_HASS_HOST_REQUIRED | If the authentication requires a host to be defined. | You can import them into your code as follows: ```javascript -import { ERR_CANNOT_CONNECT, ERR_INVALID_AUTH } from 'home-assistant-js-websocket'; +import { + ERR_CANNOT_CONNECT, + ERR_INVALID_AUTH +} from "home-assistant-js-websocket"; ``` #### Automatic reconnecting @@ -89,21 +87,21 @@ The connection object will automatically try to reconnect to the server when the The `Connection` object implements three events related to the reconnecting logic. -| Event | Data | Description | -| ----- | ---- | ----------- | -| ready | - | Fired when authentication is successful and the connection is ready to take commands. -| disconnected | - | Fired when the connection is lost. -| reconnect-error | Error code | Fired when we encounter a fatal error when trying to reconnect. Currently limited to `ERR_INVALID_AUTH`. +| Event | Data | Description | +| --------------- | ---------- | -------------------------------------------------------------------------------------------------------- | +| ready | - | Fired when authentication is successful and the connection is ready to take commands. | +| disconnected | - | Fired when the connection is lost. | +| reconnect-error | Error code | Fired when we encounter a fatal error when trying to reconnect. Currently limited to `ERR_INVALID_AUTH`. | You can attach and remove listeners as follows: ```javascript function eventHandler(connection, data) { - console.log('Connection has been established again'); + console.log("Connection has been established again"); } -conn.addEventListener('ready', eventHandler); -conn.removeEventListener('ready', eventHandler); +conn.addEventListener("ready", eventHandler); +conn.removeEventListener("ready", eventHandler); ``` ### Entities @@ -113,12 +111,11 @@ You can subscribe to the entities of Home Assistant. Your callback will be calle The function `subscribeEntities` will return an unsubscribe function. ```javascript -import { subscribeEntities } from 'home-assistant-js-websocket'; +import { subscribeEntities } from "home-assistant-js-websocket"; // conn is the connection from earlier. -subscribeEntities( - conn, entities => console.log('New entities!', entities)); +subscribeEntities(conn, entities => console.log("New entities!", entities)); ``` ### Config @@ -128,12 +125,11 @@ You can subscribe to the config of Home Assistant. Config can change when either The function `subscribeConfig` will return an unsubscribe function. ```javascript -import { subscribeConfig } from 'home-assistant-js-websocket'; +import { subscribeConfig } from "home-assistant-js-websocket"; // conn is the connection from earlier. -subscribeConfig( - conn, config => console.log('New config!', config)); +subscribeConfig(conn, config => console.log("New config!", config)); ``` ### Services @@ -143,12 +139,11 @@ You can subscribe to the available services of Home Assistant. Services can chan The function `subscribeServices` will return an unsubscribe function. ```javascript -import { subscribeServices } from 'home-assistant-js-websocket'; +import { subscribeServices } from "home-assistant-js-websocket"; // conn is the connection from earlier. -subscribeServices( - conn, services => console.log('New services!', services)); +subscribeServices(conn, services => console.log("New services!", services)); ``` ## Connection API Reference @@ -184,18 +179,20 @@ Listen for events on the connection. [See docs.](#automatic-reconnecting) To use this package in NodeJS, install the [ws package](https://www.npmjs.com/package/ws) and make it available as `WebSocket` on the `global` object before importing this package. ```js -const WebSocket = require('ws'); +const WebSocket = require("ws"); global.WebSocket = WebSocket; const HAWS = require("home-assistant-js-websocket"); const getWsUrl = haUrl => `ws://${haUrl}/api/websocket`; -HAWS.createConnection(getWsUrl('localhost:8123')).then(conn => { +HAWS.createConnection(getWsUrl("localhost:8123")).then(conn => { HAWS.subscribeEntities(conn, logEntities); }); function logEntities(entities) { - Object.keys(entities).forEach(key => console.log(`${key}: ${entities[key].state}`)); - console.log('') + Object.keys(entities).forEach(key => + console.log(`${key}: ${entities[key].state}`) + ); + console.log(""); } ``` diff --git a/example.html b/example.html index 2b6d5d0d..17aed6a6 100644 --- a/example.html +++ b/example.html @@ -1,62 +1,64 @@ - - - - - - -
- - + const connection = await createConnection(auth); + subscribeEntities(connection, entities => renderEntities(connection, entities)); + })(); + + function renderEntities(connection, entities) { + const root = document.querySelector('tbody'); + while (root.lastChild) root.removeChild(root.lastChild); + + Object.keys(entities).sort().forEach((entId) => { + const tr = document.createElement('tr'); + + const tdName = document.createElement('td'); + tdName.innerHTML = entId; + tr.appendChild(tdName); + + const tdState = document.createElement('td'); + const text = document.createTextNode(entities[entId].state); + tdState.appendChild(text); + + if (['switch', 'light', 'input_boolean'].includes(entId.split('.', 1)[0])) { + const button = document.createElement('button'); + button.innerHTML = 'toggle'; + button.onclick = () => connection.callService('homeassistant', 'toggle', { entity_id: entId }); + tdState.appendChild(button); + } + tr.appendChild(tdState); + + root.appendChild(tr); + }); + } + + + diff --git a/lib/auth.js b/lib/auth.js deleted file mode 100644 index 473cefda..00000000 --- a/lib/auth.js +++ /dev/null @@ -1,125 +0,0 @@ -import { parseQuery } from './util.js'; -import { ERR_HASS_HOST_REQUIRED } from './const.js'; - -const CALLBACK_KEY = 'auth_callback'; - -function genClientId() { - return `${location.protocol}//${location.host}/`; -} - -function genAuthorizeUrl(hassUrl, clientId, redirectUri, state) { - // eslint-disable-next-line - let authorizeUrl = `${hassUrl}/frontend_es5/authorize.html?response_type=code&client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}`; - - // During development, replace frontend_es5 with frontend_latest. - - if (state) { - authorizeUrl += `&state=${encodeURIComponent(state)}`; - } - return authorizeUrl; -} - -function redirectAuthorize(hassUrl, state) { - let redirectUri = location.toString(); - // Add either ?auth_callback=1 or &auth_callback=1 - redirectUri += redirectUri.includes('?') ? '&' : '?'; - redirectUri += `${CALLBACK_KEY}=1`; - - document.location = genAuthorizeUrl(hassUrl, genClientId(), redirectUri, state); -} - -async function tokenRequest(hassUrl, clientId, data) { - const formData = new FormData(); - formData.append('client_id', clientId); - Object.keys(data).forEach((key) => { formData.append(key, data[key]); }); - - const resp = await fetch(`${hassUrl}/auth/token`, { - method: 'POST', - body: formData, - }); - - if (!resp.ok) throw new Error('Unable to fetch tokens'); - - const tokens = await resp.json(); - tokens.hassUrl = hassUrl; - tokens.expires = (tokens.expires_in * 1000) + Date.now(); - return tokens; -} - -function fetchToken(hassUrl, clientId, code) { - return tokenRequest(hassUrl, clientId, { - code, - grant_type: 'authorization_code', - }); -} - -function refreshAccessToken(hassUrl, clientId, refreshToken) { - return tokenRequest(hassUrl, clientId, { - grant_type: 'refresh_token', - refresh_token: refreshToken, - }); -} - -function encodeOauthState(state) { - return btoa(JSON.stringify(state)); -} - -function decodeOauthState(encoded) { - return JSON.parse(atob(encoded)); -} - -class Auth { - constructor(data, saveCache) { - Object.assign(this, data); - this._saveCache = saveCache; - } - - get expired() { - // Token needs to be at least 10 seconds valid - return Date.now() - 10000 < this.expires; - } - - async refreshAccessToken() { - const data = await refreshAccessToken(this.hassUrl, genClientId(), this.refresh_token); - Object.assign(this, data); - if (this._saveCache) this._saveCache(data); - } -} - -export default async function getAuth({ hassUrl, loadCache, saveCache } = {}) { - // Check if we came back from an authorize redirect - const query = parseQuery(location.search.substr(1)); - - let data; - - // Check if we got redirected here from authorize page - if (query[CALLBACK_KEY]) { - // Restore state - const state = decodeOauthState(query.state); - try { - data = await fetchToken(state.hassUrl, genClientId(), query.code); - if (saveCache) saveCache(data); - } catch (err) { - // Do we want to tell user we were unable to fetch tokens? - // For now we don't do anything, having rest of code pick it up. - } - } - - // Check for cached tokens - if (!data && loadCache) { - data = await loadCache(); - } - - // If no tokens found but a hassUrl was passed in, let's go get some tokens! - if (!data && hassUrl) { - redirectAuthorize(hassUrl, encodeOauthState({ - hassUrl, - })); - // Just don't resolve while we navigate to next page - return new Promise(resolve => setTimeout(resolve, 60000)); - } else if (!data) { - throw ERR_HASS_HOST_REQUIRED; - } else { - return new Auth(data, saveCache); - } -} diff --git a/lib/auth.ts b/lib/auth.ts new file mode 100644 index 00000000..a765a37e --- /dev/null +++ b/lib/auth.ts @@ -0,0 +1,182 @@ +import { parseQuery } from "./util"; +import { ERR_HASS_HOST_REQUIRED } from "./const"; + +type AuthData = { + hassUrl: string; + expires: number; + refresh_token: string; + access_token: string; + expires_in: number; +}; + +type SaveCacheFunc = (data: AuthData) => void; +type LoadCacheFunc = () => Promise; + +type getAuthOptions = { + hassUrl?: string; + saveCache?: SaveCacheFunc; + loadCache?: LoadCacheFunc; +}; + +type queryData = { + state?: string; + code?: string; +}; + +const CALLBACK_KEY = "auth_callback"; + +function genClientId() { + return `${location.protocol}//${location.host}/`; +} + +function genAuthorizeUrl( + hassUrl: string, + clientId: string, + redirectUri: string, + state: string +) { + let authorizeUrl = `${hassUrl}/auth/authorize?response_type=code&client_id=${encodeURIComponent( + clientId + )}&redirect_uri=${encodeURIComponent(redirectUri)}`; + + if (state) { + authorizeUrl += `&state=${encodeURIComponent(state)}`; + } + return authorizeUrl; +} + +function redirectAuthorize(hassUrl: string, state: string) { + // Get current url but without # part. + const { protocol, host, pathname, search } = location; + let redirectUri = `${protocol}//${host}${pathname}${search}`; + + // Add either ?auth_callback=1 or &auth_callback=1 + redirectUri += redirectUri.includes("?") ? "&" : "?"; + redirectUri += `${CALLBACK_KEY}=1`; + + document.location.href = genAuthorizeUrl( + hassUrl, + genClientId(), + redirectUri, + state + ); +} + +async function tokenRequest(hassUrl: string, clientId: string, data: object) { + const formData = new FormData(); + formData.append("client_id", clientId); + Object.keys(data).forEach(key => { + formData.append(key, data[key]); + }); + + const resp = await fetch(`${hassUrl}/auth/token`, { + method: "POST", + body: formData + }); + + if (!resp.ok) throw new Error("Unable to fetch tokens"); + + const tokens: AuthData = await resp.json(); + tokens.hassUrl = hassUrl; + tokens.expires = tokens.expires_in * 1000 + Date.now(); + return tokens; +} + +function fetchToken(hassUrl: string, clientId: string, code: string) { + return tokenRequest(hassUrl, clientId, { + code, + grant_type: "authorization_code" + }); +} + +function refreshAccessToken( + hassUrl: string, + clientId: string, + refreshToken: string +) { + return tokenRequest(hassUrl, clientId, { + grant_type: "refresh_token", + refresh_token: refreshToken + }); +} + +function encodeOauthState(state: object) { + return btoa(JSON.stringify(state)); +} + +function decodeOauthState(encoded: string) { + return JSON.parse(atob(encoded)); +} + +export class Auth { + _saveCache?: SaveCacheFunc; + expires: number; + hassUrl: string; + refresh_token: string; + access_token: string; + + constructor(data: AuthData, saveCache: SaveCacheFunc) { + Object.assign(this, data); + this._saveCache = saveCache; + } + + get expired() { + // Token needs to be at least 10 seconds valid + return Date.now() - 10000 < this.expires; + } + + async refreshAccessToken() { + const data = await refreshAccessToken( + this.hassUrl, + genClientId(), + this.refresh_token + ); + Object.assign(this, data); + if (this._saveCache) this._saveCache(data); + } +} + +export default async function getAuth({ + hassUrl, + loadCache, + saveCache +}: getAuthOptions = {}): Promise { + // Check if we came back from an authorize redirect + const query: queryData = parseQuery(location.search.substr(1)); + + let data: AuthData; + + // Check if we got redirected here from authorize page + if (query[CALLBACK_KEY]) { + // Restore state + const state = decodeOauthState(query.state); + try { + data = await fetchToken(state.hassUrl, genClientId(), query.code); + if (saveCache) saveCache(data); + } catch (err) { + // Do we want to tell user we were unable to fetch tokens? + // For now we don't do anything, having rest of code pick it up. + } + } + + // Check for cached tokens + if (!data && loadCache) { + data = await loadCache(); + } + + // If no tokens found but a hassUrl was passed in, let's go get some tokens! + if (!data && hassUrl) { + redirectAuthorize( + hassUrl, + encodeOauthState({ + hassUrl + }) + ); + // Just don't resolve while we navigate to next page + return new Promise(() => {}); + } else if (!data) { + throw ERR_HASS_HOST_REQUIRED; + } else { + return new Auth(data, saveCache); + } +} diff --git a/lib/collection.js b/lib/collection.js deleted file mode 100644 index f50757ae..00000000 --- a/lib/collection.js +++ /dev/null @@ -1,42 +0,0 @@ -import { createStore } from './store'; - -let collectionID = 0; - -// fetchCollection returns promise that resolves to current value of collection. -// subscribeUpdates(connection, store) returns promise that resolves -// to an unsubscription function. - -export default function createCollection(fetchCollection, subscribeUpdates) { - const key = `_collection_${collectionID++}`; - - return function (conn, onChange) { - if (key in conn) { - return conn[key](onChange); - } - - let unsubProm; - - const store = createStore(() => { - unsubProm.then(unsub => unsub()); - // eslint-disable-next-line - conn.removeEventListener('ready', refresh); - delete conn[key]; - }); - - conn[key] = store.subscribe; - - // Subscribe to changes - unsubProm = subscribeUpdates(conn, store); - - async function refresh() { - store.setState(await fetchCollection(conn), true); - } - - // Fetch when connection re-established. - conn.addEventListener('ready', refresh); - - refresh(); - - return store.subscribe(onChange); - }; -} diff --git a/lib/collection.ts b/lib/collection.ts new file mode 100644 index 00000000..6d91a57a --- /dev/null +++ b/lib/collection.ts @@ -0,0 +1,46 @@ +import Store from "./store"; +import { Connection } from "./connection"; +import { UnsubscribeFunc } from "./types"; + +// fetchCollection returns promise that resolves to current value of collection. +// subscribeUpdates(connection, store) returns promise that resolves +// to an unsubscription function. + +export default function createCollection( + key: string, + fetchCollection: (conn: Connection) => Promise, + subscribeUpdates: ( + conn: Connection, + store: Store + ) => Promise, + conn: Connection, + onChange: (state: State) => void +): UnsubscribeFunc { + if (key in conn) { + return conn[key](onChange); + } + + let unsubProm: Promise; + + const store = new Store(() => { + unsubProm.then(unsub => unsub()); + conn.removeEventListener("ready", refresh); + delete conn[key]; + }); + + conn[key] = store.subscribe; + + // Subscribe to changes + unsubProm = subscribeUpdates(conn, store); + + async function refresh() { + store.setState(await fetchCollection(conn), true); + } + + // Fetch when connection re-established. + conn.addEventListener("ready", refresh); + + refresh(); + + return store.subscribe(onChange); +} diff --git a/lib/config.js b/lib/config.js deleted file mode 100644 index 9c8acf8d..00000000 --- a/lib/config.js +++ /dev/null @@ -1,15 +0,0 @@ -import createCollection from './collection.js'; - -function processComponentLoaded(state, event) { - if (state === undefined) return null; - - return { - components: state.components.concat(event.data.component) - }; -} - -const fetchConfig = conn => conn.getConfig(); -const subscribeUpdates = (conn, store) => - conn.subscribeEvents(store.action(processComponentLoaded), 'component_loaded'); - -export default createCollection(fetchConfig, subscribeUpdates); diff --git a/lib/config.ts b/lib/config.ts new file mode 100644 index 00000000..7ac74648 --- /dev/null +++ b/lib/config.ts @@ -0,0 +1,36 @@ +import createCollection from "./collection"; +import { HassConfig } from "./types"; +import { Connection } from "./connection"; + +type ComponentLoadedEvent = { + data: { + component: string; + }; +}; + +function processComponentLoaded( + state: HassConfig, + event: ComponentLoadedEvent +): Partial { + if (state === undefined) return null; + + return { + components: state.components.concat(event.data.component) + }; +} + +const fetchConfig = conn => conn.getConfig(); +const subscribeUpdates = (conn, store) => + conn.subscribeEvents( + store.action(processComponentLoaded), + "component_loaded" + ); + +export default (conn: Connection, onChange: (state: HassConfig) => void) => + createCollection( + "_cnf", + fetchConfig, + subscribeUpdates, + conn, + onChange + ); diff --git a/lib/connection.js b/lib/connection.js deleted file mode 100644 index 4953d0fa..00000000 --- a/lib/connection.js +++ /dev/null @@ -1,324 +0,0 @@ -import * as messages from './messages.js'; -import { - ERR_CANNOT_CONNECT, - ERR_INVALID_AUTH, - ERR_CONNECTION_LOST, - - MSG_TYPE_AUTH_REQUIRED, - MSG_TYPE_AUTH_INVALID, - MSG_TYPE_AUTH_OK, - - MSG_TYPE_EVENT, - MSG_TYPE_RESULT, - MSG_TYPE_PONG, -} from './const.js'; - -const DEBUG = true; - -function getSocket(auth, options) { - // Convert from http:// -> ws://, https:// -> wss:// - const url = `ws${auth.hassUrl.substr(4)}/api/websocket`; - - if (DEBUG) { - // eslint-disable-next-line - console.log('[Auth phase] Initializing', url); - } - - function connect(triesLeft, promResolve, promReject) { - if (DEBUG) { - // eslint-disable-next-line - console.log('[Auth Phase] New connection', url); - } - - const socket = new WebSocket(url); - - // If invalid auth, we will not try to reconnect. - let invalidAuth = false; - - const closeMessage = () => { - // If we are in error handler make sure close handler doesn't also fire. - socket.removeEventListener('close', closeMessage); - - if (invalidAuth) { - promReject(ERR_INVALID_AUTH); - return; - } - - // Reject if we no longer have to retry - if (triesLeft === 0) { - // We never were connected and will not retry - promReject(ERR_CANNOT_CONNECT); - return; - } - - const newTries = triesLeft === -1 ? -1 : triesLeft - 1; - // Try again in a second - setTimeout(() => connect(newTries, promResolve, promReject), 1000); - }; - - const handleMessage = async (event) => { - const message = JSON.parse(event.data); - - if (DEBUG) { - // eslint-disable-next-line - console.log('[Auth phase] Received', message); - } - switch (message.type) { - case MSG_TYPE_AUTH_REQUIRED: - try { - if (auth.expired) await auth.refreshAccessToken(); - socket.send(JSON.stringify(messages.authAccessToken(auth.access_token))); - } catch (err) { - // Refresh token failed - invalidAuth = true; - socket.close(); - } - break; - - case MSG_TYPE_AUTH_INVALID: - invalidAuth = true; - socket.close(); - break; - - case MSG_TYPE_AUTH_OK: - socket.removeEventListener('message', handleMessage); - socket.removeEventListener('close', closeMessage); - socket.removeEventListener('error', closeMessage); - promResolve(socket); - break; - - default: - if (DEBUG) { - // eslint-disable-next-line - console.warn('[Auth phase] Unhandled message', message); - } - } - }; - - socket.addEventListener('message', handleMessage); - socket.addEventListener('close', closeMessage); - socket.addEventListener('error', closeMessage); - } - - return new Promise((resolve, reject) => connect(options.setupRetry || 0, resolve, reject)); -} - -function extractResult(message) { - return message.result; -} - -class Connection { - constructor(auth, options) { - this.auth = auth; - // connection options - // - setupRetry: amount of ms to retry when unable to connect on initial setup - this.options = options || {}; - // id if next command to send - this.commandId = 1; - // info about active subscriptions and commands in flight - this.commands = {}; - // map of event listeners - this.eventListeners = {}; - // true if a close is requested by the user - this.closeRequested = false; - - this._handleMessage = this._handleMessage.bind(this); - this._handleClose = this._handleClose.bind(this); - } - - setSocket(socket) { - const oldSocket = this.socket; - this.socket = socket; - socket.addEventListener('message', this._handleMessage); - socket.addEventListener('close', this._handleClose); - - if (oldSocket) { - const oldCommands = this.commands; - - // reset to original state - this.commandId = 1; - this.commands = {}; - - Object.keys(oldCommands).forEach((id) => { - const info = oldCommands[id]; - - if (info.eventType) { - this.subscribeEvents(info.eventCallback, info.eventType) - .then((unsub) => { info.unsubscribe = unsub; }); - } - }); - - this.fireEvent('ready'); - } - } - - addEventListener(eventType, callback) { - let listeners = this.eventListeners[eventType]; - - if (!listeners) { - listeners = this.eventListeners[eventType] = []; - } - - listeners.push(callback); - } - - removeEventListener(eventType, callback) { - const listeners = this.eventListeners[eventType]; - - if (!listeners) { - return; - } - - const index = listeners.indexOf(callback); - - if (index !== -1) { - listeners.splice(index, 1); - } - } - - fireEvent(eventType, eventData) { - (this.eventListeners[eventType] || []).forEach(callback => callback(this, eventData)); - } - - close() { - this.closeRequested = true; - this.socket.close(); - } - - getStates() { - return this.sendMessagePromise(messages.states()).then(extractResult); - } - - getServices() { - return this.sendMessagePromise(messages.services()).then(extractResult); - } - - getConfig() { - return this.sendMessagePromise(messages.config()).then(extractResult); - } - - callService(domain, service, serviceData) { - return this.sendMessagePromise(messages.callService(domain, service, serviceData)); - } - - // eventCallback will be called when a new event fires - // Returned promise resolves to an unsubscribe function. - async subscribeEvents(eventCallback, eventType) { - const resultMessage = await this.sendMessagePromise(messages.subscribeEvents(eventType)); - - // We store unsubscribe on info object. That way we can overwrite it in case - // we get disconnected and we have to subscribe again. - const info = { - eventCallback, - eventType, - unsubscribe: async () => { - await this.sendMessagePromise(messages.unsubscribeEvents(resultMessage.id)); - delete this.commands[resultMessage.id]; - }, - }; - - this.commands[resultMessage.id] = info; - - return () => info.unsubscribe(); - } - - ping() { - return this.sendMessagePromise(messages.ping()); - } - - sendMessage(message) { - if (DEBUG) { - // eslint-disable-next-line - console.log('Sending', message); - } - - this.socket.send(JSON.stringify(message)); - } - - sendMessagePromise(message) { - return new Promise((resolve, reject) => { - this.commandId += 1; - const commandId = this.commandId; - message.id = commandId; - this.commands[commandId] = { resolve, reject }; - this.sendMessage(message); - }); - } - - _handleMessage(event) { - const message = JSON.parse(event.data); - - if (DEBUG) { - // eslint-disable-next-line - console.log('Received', message); - } - - switch (message.type) { - case MSG_TYPE_EVENT: - this.commands[message.id].eventCallback(message.event); - break; - - case MSG_TYPE_RESULT: - if (message.success) { - this.commands[message.id].resolve(message); - } else { - this.commands[message.id].reject(message.error); - } - delete this.commands[message.id]; - break; - - case MSG_TYPE_PONG: - break; - - default: - if (DEBUG) { - // eslint-disable-next-line - console.warn('Unhandled message', message); - } - } - } - - _handleClose() { - // Reject in-flight requests - Object.keys(this.commands).forEach((id) => { - const { reject } = this.commands[id]; - if (reject) { - reject(messages.error(ERR_CONNECTION_LOST, 'Connection lost')); - } - }); - - if (this.closeRequested) { - return; - } - - this.fireEvent('disconnected'); - - // Disable setupRetry, we control it here with auto-backoff - const options = Object.assign({}, this.options, { setupRetry: 0 }); - - const reconnect = (tries) => { - setTimeout(() => { - if (DEBUG) { - // eslint-disable-next-line - console.log('Trying to reconnect'); - } - getSocket(this.auth, options).then( - socket => this.setSocket(socket), - err => (err === ERR_INVALID_AUTH ? - this.fireEvent('reconnect-error', err) : reconnect(tries + 1)) - ); - }, Math.min(tries, 5) * 1000); - }; - - reconnect(0); - } -} - -export default function createConnection(auth, options = {}) { - return getSocket(auth, options) - .then((socket) => { - const conn = new Connection(auth, options); - conn.setSocket(socket); - return conn; - }); -} diff --git a/lib/connection.ts b/lib/connection.ts new file mode 100644 index 00000000..a59ef4cf --- /dev/null +++ b/lib/connection.ts @@ -0,0 +1,321 @@ +/** + * Connection that wraps a socket and provides an interface to interact with + * the Home Assistant websocket API. + */ +import * as messages from "./messages"; +import { ERR_INVALID_AUTH, ERR_CONNECTION_LOST } from "./const"; +import { + ConnectionOptions, + HassEvent, + HassEntities, + HassServices, + HassConfig +} from "./types"; +import { Auth } from "./auth"; +import createSocket from "./socket"; + +const DEBUG = false; + +type EventListener = (conn: Connection, eventData?: any) => void; + +type WebSocketPongResponse = { + id: number; + type: "pong"; +}; + +type WebSocketEventResponse = { + id: number; + type: "event"; + event: HassEvent; +}; + +type WebSocketResultResponse = { + id: number; + type: "result"; + success: true; + result: any; +}; + +type WebSocketResultErrorResponse = { + id: number; + type: "result"; + success: false; + error: { + code: string; + message: string; + }; +}; + +type WebSocketResponse = + | WebSocketPongResponse + | WebSocketEventResponse + | WebSocketResultResponse + | WebSocketResultErrorResponse; + +export class Connection { + auth: Auth; + options: ConnectionOptions; + commandId: number; + commands: { + [commandId: number]: any; + }; + eventListeners: { + [eventType: string]: EventListener[]; + }; + closeRequested: boolean; + socket: WebSocket; + + constructor(auth: Auth, options: ConnectionOptions) { + this.auth = auth; + // connection options + // - setupRetry: amount of ms to retry when unable to connect on initial setup + this.options = options; + // id if next command to send + this.commandId = 1; + // info about active subscriptions and commands in flight + this.commands = {}; + // map of event listeners + this.eventListeners = {}; + // true if a close is requested by the user + this.closeRequested = false; + + this._handleClose = this._handleClose.bind(this); + } + + setSocket(socket: WebSocket) { + const oldSocket = this.socket; + this.socket = socket; + socket.addEventListener("message", ev => this._handleMessage(ev)); + socket.addEventListener("close", this._handleClose); + + if (oldSocket) { + const oldCommands = this.commands; + + // reset to original state + this.commandId = 1; + this.commands = {}; + + Object.keys(oldCommands).forEach(id => { + const info = oldCommands[id]; + + if (info.eventType) { + this.subscribeEvents(info.eventCallback, info.eventType).then( + unsub => { + info.unsubscribe = unsub; + } + ); + } + }); + + this.fireEvent("ready"); + } + } + + addEventListener(eventType, callback) { + let listeners = this.eventListeners[eventType]; + + if (!listeners) { + listeners = this.eventListeners[eventType] = []; + } + + listeners.push(callback); + } + + removeEventListener(eventType, callback) { + const listeners = this.eventListeners[eventType]; + + if (!listeners) { + return; + } + + const index = listeners.indexOf(callback); + + if (index !== -1) { + listeners.splice(index, 1); + } + } + + fireEvent(eventType, eventData?) { + (this.eventListeners[eventType] || []).forEach(callback => + callback(this, eventData) + ); + } + + close() { + this.closeRequested = true; + this.socket.close(); + } + + getStates() { + return this.sendMessagePromise(messages.states()); + } + + getServices() { + return this.sendMessagePromise(messages.services()); + } + + getConfig() { + return this.sendMessagePromise(messages.config()); + } + + callService(domain, service, serviceData) { + return this.sendMessagePromise( + messages.callService(domain, service, serviceData) + ); + } + + // eventCallback will be called when a new event fires + // Returned promise resolves to an unsubscribe function. + async subscribeEvents( + eventCallback: (ev: EventType) => void, + eventType: string + ) { + // Command ID that will be used + const commandId = this._genCmdId(); + + await this.sendMessagePromise( + messages.subscribeEvents(eventType), + commandId + ); + + // We store unsubscribe on info object. That way we can overwrite it in case + // we get disconnected and we have to subscribe again. + const info = { + eventCallback, + eventType, + unsubscribe: async () => { + await this.sendMessagePromise(messages.unsubscribeEvents(commandId)); + delete this.commands[commandId]; + } + }; + + this.commands[commandId] = info; + + return () => info.unsubscribe(); + } + + ping() { + return this.sendMessagePromise(messages.ping()); + } + + sendMessage(message, commandId?: number): void { + if (DEBUG) { + console.log("Sending", message); + } + + if (!commandId) { + commandId = this._genCmdId(); + } + message.id = commandId; + + this.socket.send(JSON.stringify(message)); + } + + sendMessagePromise(message, commandId?: number): Promise { + return new Promise((resolve, reject) => { + if (!commandId) { + commandId = this._genCmdId(); + } + this.commands[commandId] = { resolve, reject }; + this.sendMessage(message, commandId); + }); + } + + private _handleMessage(event: MessageEvent) { + const message: WebSocketResponse = JSON.parse(event.data); + + if (DEBUG) { + console.log("Received", message); + } + + switch (message.type) { + case "event": + this.commands[message.id].eventCallback(message.event); + break; + + case "result": + // If just sendMessage is used, we will not store promise for result + if (message.id in this.commands) { + if (message.success == true) { + this.commands[message.id].resolve(message.result); + } else { + this.commands[message.id].reject(message.error); + } + delete this.commands[message.id]; + } + break; + + case "pong": + break; + + default: + if (DEBUG) { + console.warn("Unhandled message", message); + } + } + } + + private _handleClose() { + // Reject in-flight requests + Object.keys(this.commands).forEach(id => { + const { reject } = this.commands[id]; + if (reject) { + reject(messages.error(ERR_CONNECTION_LOST, "Connection lost")); + } + }); + + if (this.closeRequested) { + return; + } + + this.fireEvent("disconnected"); + + // Disable setupRetry, we control it here with auto-backoff + const options = Object.assign({}, this.options, { setupRetry: 0 }); + + const reconnect = (tries: number) => { + setTimeout(async () => { + if (DEBUG) { + console.log("Trying to reconnect"); + } + try { + const socket = await options.createSocket(this.auth, options); + this.setSocket(socket); + } catch (err) { + if (err === ERR_INVALID_AUTH) { + this.fireEvent("reconnect-error", err); + } else { + reconnect(tries + 1); + } + } + }, Math.min(tries, 5) * 1000); + }; + + reconnect(0); + } + + private _genCmdId() { + this.commandId += 1; + return this.commandId; + } +} + +const defaultConnectionOptions: ConnectionOptions = { + setupRetry: 0, + createSocket +}; + +export default async function createConnection( + auth: Auth, + options?: Partial +) { + const connOptions: ConnectionOptions = Object.assign( + {}, + defaultConnectionOptions, + options + ); + const socket = await options.createSocket(auth, connOptions); + const conn = new Connection(auth, connOptions); + conn.setSocket(socket); + return conn; +} diff --git a/lib/const.js b/lib/const.js deleted file mode 100644 index 19b88460..00000000 --- a/lib/const.js +++ /dev/null @@ -1,12 +0,0 @@ -export const ERR_CANNOT_CONNECT = 1; -export const ERR_INVALID_AUTH = 2; -export const ERR_CONNECTION_LOST = 3; -export const ERR_HASS_HOST_REQUIRED = 4; - -export const MSG_TYPE_AUTH_REQUIRED = 'auth_required'; -export const MSG_TYPE_AUTH_INVALID = 'auth_invalid'; -export const MSG_TYPE_AUTH_OK = 'auth_ok'; - -export const MSG_TYPE_EVENT = 'event'; -export const MSG_TYPE_RESULT = 'result'; -export const MSG_TYPE_PONG = 'pong'; diff --git a/lib/const.ts b/lib/const.ts new file mode 100644 index 00000000..1b7e4394 --- /dev/null +++ b/lib/const.ts @@ -0,0 +1,8 @@ +export const ERR_CANNOT_CONNECT = 1; +export const ERR_INVALID_AUTH = 2; +export const ERR_CONNECTION_LOST = 3; +export const ERR_HASS_HOST_REQUIRED = 4; + +export const MSG_TYPE_AUTH_REQUIRED = "auth_required"; +export const MSG_TYPE_AUTH_INVALID = "auth_invalid"; +export const MSG_TYPE_AUTH_OK = "auth_ok"; diff --git a/lib/entities.js b/lib/entities.js deleted file mode 100644 index bab4f87e..00000000 --- a/lib/entities.js +++ /dev/null @@ -1,31 +0,0 @@ -import createCollection from './collection.js'; - -function processEvent(store, event) { - const state = store.getState(); - if (state === undefined) return; - - /* eslint-disable camelcase */ - const { entity_id, new_state } = event.data; - if (new_state) { - store.setState({ [new_state.entity_id]: new_state }); - } else { - const newEntities = Object.assign({}, state); - delete newEntities[entity_id]; - store.setState(newEntities, true); - } -} - -async function fetchEntities(conn) { - const states = await conn.getStates(); - const entities = {}; - for (let i = 0; i < states.length; i++) { - const state = states[i]; - entities[state.entity_id] = state; - } - return entities; -} - -const subscribeUpdates = (conn, store) => - conn.subscribeEvents(ev => processEvent(store, ev), 'state_changed'); - -export default createCollection(fetchEntities, subscribeUpdates); diff --git a/lib/entities.ts b/lib/entities.ts new file mode 100644 index 00000000..f748ec48 --- /dev/null +++ b/lib/entities.ts @@ -0,0 +1,52 @@ +import createCollection from "./collection"; +import { HassEntities, HassEntity } from "./types"; +import { Connection } from "./connection"; +import Store from "./store"; + +type StateChangedEvent = { + type: "state_changed"; + data: { + entity_id: string; + new_state: HassEntity | null; + old_state: HassEntity | null; + }; +}; + +function processEvent(store: Store, event: StateChangedEvent) { + const state = store.state; + if (state === undefined) return; + + const { entity_id, new_state } = event.data; + if (new_state) { + store.setState({ [new_state.entity_id]: new_state }); + } else { + const newEntities = Object.assign({}, state); + delete newEntities[entity_id]; + store.setState(newEntities, true); + } +} + +async function fetchEntities(conn) { + const states = await conn.getStates(); + const entities = {}; + for (let i = 0; i < states.length; i++) { + const state = states[i]; + entities[state.entity_id] = state; + } + return entities; +} + +const subscribeUpdates = (conn: Connection, store: Store) => + conn.subscribeEvents( + ev => processEvent(store, ev as StateChangedEvent), + "state_changed" + ); + +export default (conn: Connection, onChange: (state: HassEntities) => void) => + createCollection( + "_ent", + fetchEntities, + subscribeUpdates, + conn, + onChange + ); diff --git a/lib/index.js b/lib/index.js deleted file mode 100644 index 88fd9a91..00000000 --- a/lib/index.js +++ /dev/null @@ -1,24 +0,0 @@ -import getAuth from './auth.js'; -import createConnection from './connection.js'; -import subscribeConfig from './config.js'; -import subscribeServices from './services.js'; -import subscribeEntities from './entities.js'; -import { - ERR_CANNOT_CONNECT, - ERR_INVALID_AUTH, - ERR_CONNECTION_LOST, - ERR_HASS_HOST_REQUIRED, -} from './const.js'; - -export { - ERR_CANNOT_CONNECT, - ERR_INVALID_AUTH, - ERR_CONNECTION_LOST, - ERR_HASS_HOST_REQUIRED, - - getAuth, - createConnection, - subscribeConfig, - subscribeServices, - subscribeEntities, -}; diff --git a/lib/index.ts b/lib/index.ts new file mode 100644 index 00000000..05d9394d --- /dev/null +++ b/lib/index.ts @@ -0,0 +1,23 @@ +import getAuth from "./auth"; +import createConnection from "./connection"; +import subscribeConfig from "./config"; +import subscribeServices from "./services"; +import subscribeEntities from "./entities"; +import { + ERR_CANNOT_CONNECT, + ERR_INVALID_AUTH, + ERR_CONNECTION_LOST, + ERR_HASS_HOST_REQUIRED +} from "./const"; + +export { + ERR_CANNOT_CONNECT, + ERR_INVALID_AUTH, + ERR_CONNECTION_LOST, + ERR_HASS_HOST_REQUIRED, + getAuth, + createConnection, + subscribeConfig, + subscribeServices, + subscribeEntities +}; diff --git a/lib/messages.js b/lib/messages.js deleted file mode 100644 index 6c91abc7..00000000 --- a/lib/messages.js +++ /dev/null @@ -1,74 +0,0 @@ -export function authAccessToken(accessToken) { - return { - type: 'auth', - access_token: accessToken, - }; -} - -export function states() { - return { - type: 'get_states', - }; -} - -export function config() { - return { - type: 'get_config', - }; -} - -export function services() { - return { - type: 'get_services', - }; -} - -export function callService(domain, service, serviceData) { - const message = { - type: 'call_service', - domain, - service, - }; - - if (serviceData) { - message.service_data = serviceData; - } - - return message; -} - -export function subscribeEvents(eventType) { - const message = { - type: 'subscribe_events', - }; - - if (eventType) { - message.event_type = eventType; - } - - return message; -} - -export function unsubscribeEvents(subscription) { - return { - type: 'unsubscribe_events', - subscription, - }; -} - -export function ping() { - return { - type: 'ping', - }; -} - -export function error(code, message) { - return { - type: 'result', - success: false, - error: { - code, - message, - }, - }; -} diff --git a/lib/messages.ts b/lib/messages.ts new file mode 100644 index 00000000..7a059810 --- /dev/null +++ b/lib/messages.ts @@ -0,0 +1,92 @@ +import { Error } from "./types"; + +export function authAccessToken(accessToken: string) { + return { + type: "auth", + access_token: accessToken + }; +} + +export function states() { + return { + type: "get_states" + }; +} + +export function config() { + return { + type: "get_config" + }; +} + +export function services() { + return { + type: "get_services" + }; +} + +type ServiceCallMessage = { + type: "call_service"; + domain: string; + service: string; + service_data?: object; +}; + +export function callService( + domain: string, + service: string, + serviceData?: object +) { + const message: ServiceCallMessage = { + type: "call_service", + domain, + service + }; + + if (serviceData) { + message.service_data = serviceData; + } + + return message; +} + +type SubscribeEventMessage = { + type: "subscribe_events"; + event_type?: string; +}; + +export function subscribeEvents(eventType: string) { + const message: SubscribeEventMessage = { + type: "subscribe_events" + }; + + if (eventType) { + message.event_type = eventType; + } + + return message; +} + +export function unsubscribeEvents(subscription: number) { + return { + type: "unsubscribe_events", + subscription + }; +} + +export function ping() { + return { + type: "ping" + }; +} + +export function error(code: Error, message: string) { + return { + type: "result", + success: false, + error: { + code, + message + } + }; +} diff --git a/lib/services.js b/lib/services.js deleted file mode 100644 index 084d4db2..00000000 --- a/lib/services.js +++ /dev/null @@ -1,39 +0,0 @@ -import createCollection from './collection.js'; - -function processServiceRegistered(state, event) { - if (state === undefined) return null; - - const { domain, service } = event.data; - - const domainInfo = Object.assign( - {}, state[domain], - { [service]: { description: '', fields: {} } } - ); - - return { [domain]: domainInfo }; -} - -function processServiceRemoved(state, event) { - if (state === undefined) return null; - - const { domain, service } = event.data; - const curDomainInfo = state[domain]; - - if (!curDomainInfo || !(service in curDomainInfo)) return null; - - const domainInfo = {}; - Object.keys(curDomainInfo).forEach((sKey) => { - if (sKey !== service) domainInfo[sKey] = curDomainInfo[sKey]; - }); - - return { [domain]: domainInfo }; -} - - -const fetchServices = conn => conn.getServices(); -const subscribeUpdates = (conn, store) => Promise.all([ - conn.subscribeEvents(store.action(processServiceRegistered), 'service_registered'), - conn.subscribeEvents(store.action(processServiceRemoved), 'service_removed'), -]).then(unsubs => () => unsubs.forEach(fn => fn())); - -export default createCollection(fetchServices, subscribeUpdates); diff --git a/lib/services.ts b/lib/services.ts new file mode 100644 index 00000000..7b267203 --- /dev/null +++ b/lib/services.ts @@ -0,0 +1,74 @@ +import createCollection from "./collection"; +import { HassServices } from "./types"; +import { Connection } from "./connection"; +import Store from "./store"; + +type ServiceRegisteredEvent = { + data: { + domain: string; + service: string; + }; +}; + +type ServiceRemovedEvent = { + data: { + domain: string; + service: string; + }; +}; + +function processServiceRegistered( + state: HassServices, + event: ServiceRegisteredEvent +) { + if (state === undefined) return null; + + const { domain, service } = event.data; + + const domainInfo = Object.assign({}, state[domain], { + [service]: { description: "", fields: {} } + }); + + return { [domain]: domainInfo }; +} + +function processServiceRemoved( + state: HassServices, + event: ServiceRemovedEvent +) { + if (state === undefined) return null; + + const { domain, service } = event.data; + const curDomainInfo = state[domain]; + + if (!curDomainInfo || !(service in curDomainInfo)) return null; + + const domainInfo = {}; + Object.keys(curDomainInfo).forEach(sKey => { + if (sKey !== service) domainInfo[sKey] = curDomainInfo[sKey]; + }); + + return { [domain]: domainInfo }; +} + +const fetchServices = (conn: Connection) => conn.getServices(); +const subscribeUpdates = (conn: Connection, store: Store) => + Promise.all([ + conn.subscribeEvents( + store.action(processServiceRegistered), + "service_registered" + ), + conn.subscribeEvents( + store.action(processServiceRemoved), + "service_removed" + ) + ]).then(unsubs => () => unsubs.forEach(fn => fn())); + +export default (conn: Connection, onChange: (state: HassServices) => void) => + createCollection( + "_srv", + fetchServices, + subscribeUpdates, + conn, + onChange + ); diff --git a/lib/socket.ts b/lib/socket.ts new file mode 100644 index 00000000..37a0472f --- /dev/null +++ b/lib/socket.ts @@ -0,0 +1,116 @@ +/** + * Create a web socket connection with a Home Assistant instance. + */ +import { + MSG_TYPE_AUTH_OK, + MSG_TYPE_AUTH_REQUIRED, + ERR_INVALID_AUTH, + ERR_CANNOT_CONNECT +} from "./const"; +import { MSG_TYPE_AUTH_INVALID } from "./const"; +import { ConnectionOptions } from "./types"; + +const DEBUG = false; + +export default function createSocket( + auth, + options: ConnectionOptions +): Promise { + // Convert from http:// -> ws://, https:// -> wss:// + const url = `ws${auth.hassUrl.substr(4)}/api/websocket`; + + if (DEBUG) { + console.log("[Auth phase] Initializing", url); + } + + function connect(triesLeft, promResolve, promReject) { + if (DEBUG) { + console.log("[Auth Phase] New connection", url); + } + + const socket = new WebSocket(url); + + // If invalid auth, we will not try to reconnect. + let invalidAuth = false; + + const closeMessage = () => { + // If we are in error handler make sure close handler doesn't also fire. + socket.removeEventListener("close", closeMessage); + + if (invalidAuth) { + promReject(ERR_INVALID_AUTH); + return; + } + + // Reject if we no longer have to retry + if (triesLeft === 0) { + // We never were connected and will not retry + promReject(ERR_CANNOT_CONNECT); + return; + } + + const newTries = triesLeft === -1 ? -1 : triesLeft - 1; + // Try again in a second + setTimeout( + () => + connect( + newTries, + promResolve, + promReject + ), + 1000 + ); + }; + + const handleMessage = async event => { + const message = JSON.parse(event.data); + + if (DEBUG) { + console.log("[Auth phase] Received", message); + } + switch (message.type) { + case MSG_TYPE_AUTH_REQUIRED: + try { + if (auth.expired) await auth.refreshAccessToken(); + socket.send( + JSON.stringify(message.authAccessToken(auth.access_token)) + ); + } catch (err) { + // Refresh token failed + invalidAuth = true; + socket.close(); + } + break; + + case MSG_TYPE_AUTH_INVALID: + invalidAuth = true; + socket.close(); + break; + + case MSG_TYPE_AUTH_OK: + socket.removeEventListener("message", handleMessage); + socket.removeEventListener("close", closeMessage); + socket.removeEventListener("error", closeMessage); + promResolve(socket); + break; + + default: + if (DEBUG) { + console.warn("[Auth phase] Unhandled message", message); + } + } + }; + + socket.addEventListener("message", handleMessage); + socket.addEventListener("close", closeMessage); + socket.addEventListener("error", closeMessage); + } + + return new Promise((resolve, reject) => + connect( + options.setupRetry, + resolve, + reject + ) + ); +} diff --git a/lib/store.js b/lib/store.js deleted file mode 100644 index 8d05a29a..00000000 --- a/lib/store.js +++ /dev/null @@ -1,108 +0,0 @@ -// (c) Jason Miller -// Unistore - MIT license -// And then slightly adopted - -/* eslint-disable prefer-rest-params, consistent-return, max-len */ - -/** - * Creates a new store, which is a tiny evented state container. - * @name createStore - * @param {Object} initial state - * @returns {store} - * @example - * let store = createStore(); - * store.subscribe( state => console.log(state) ); - * store.setState({ a: 'b' }); // logs { a: 'b' } - * store.setState({ c: 'd' }); // logs { a: 'b', c: 'd' } - */ -export function createStore(noSubscribers) { - let listeners = []; - let state; - - function unsubscribe(listener) { - const out = []; - for (let i = 0; i < listeners.length; i++) { - if (listeners[i] === listener) { - listener = null; - } else { - out.push(listeners[i]); - } - } - listeners = out; - if (out.length === 0) { - noSubscribers(); - } - } - - function setState(update, overwrite) { - state = overwrite ? update : Object.assign({}, state, update); - const currentListeners = listeners; - for (let i = 0; i < currentListeners.length; i++) { currentListeners[i](state); } - } - - /** - * An observable state container, returned from {@link createStore} - * @name store - */ - - return /** @lends store */ { - /** - * Create a bound copy of the given action function. - * The bound returned function invokes action() and persists the result back to the store. - * If the return value of `action` is a Promise, the resolved value will be used as state. - * @param {Function} action An action of the form `action(state, ...args) -> stateUpdate` - * @returns {Function} boundAction() - */ - action(action) { - function apply(result) { - setState(result, false, action); - } - - // Note: perf tests verifying this implementation: https://esbench.com/bench/5a295e6299634800a0349500 - return function () { - const args = [state]; - for (let i = 0; i < arguments.length; i++) args.push(arguments[i]); - const ret = action.apply(this, args); - if (ret != null) { - if (ret.then) return ret.then(apply); - return apply(ret); - } - }; - }, - - /** - * Apply a partial state object to the current state, invoking registered listeners. - * @param {Object} update An object with properties to be merged into state - * @param {Boolean} [overwrite=false] If `true`, update will replace state instead of being merged into it - */ - setState, - - /** - * Register a listener function to be called whenever state is changed. Returns an `unsubscribe()` function. - * @param {Function} listener A function to call when state changes. Gets passed the new state. - * @returns {Function} unsubscribe() - */ - subscribe(listener) { - listeners.push(listener); - if (state !== undefined) listener(state); - return () => { - unsubscribe(listener); - }; - }, - - /** - * Remove a previously-registered listener function. - * @param {Function} listener The callback previously passed to `subscribe()` that should be removed. - * @function - */ - unsubscribe, - - /** - * Retrieve the current state object. - * @returns {Object} state - */ - getState() { - return state; - } - }; -} diff --git a/lib/store.ts b/lib/store.ts new file mode 100644 index 00000000..d1ac73bb --- /dev/null +++ b/lib/store.ts @@ -0,0 +1,87 @@ +import { UnsubscribeFunc } from "./types"; + +// (c) Jason Miller +// Unistore - MIT license +// And then adopted to our needs + typescript + +type Listener = (state: State) => void; +type NoSubscribersCallback = () => void; + +export default class Store { + private _noSub: NoSubscribersCallback; + listeners: Listener[]; + state: State | undefined; + + constructor(noSubscriptions: NoSubscribersCallback) { + this._noSub = noSubscriptions; + this.listeners = []; + } + + /** + * Create a bound copy of the given action function. + * The bound returned function invokes action() and persists the result back to the store. + * If the return value of `action` is a Promise, the resolved value will be used as state. + * @param {Function} action An action of the form `action(state, ...args) -> stateUpdate` + * @returns {Function} boundAction() + */ + action( + action: ( + state: State, + ...args + ) => Partial | Promise> | null + ) { + const apply = (result: Partial) => this.setState(result, false); + + // Note: perf tests verifying this implementation: https://esbench.com/bench/5a295e6299634800a0349500 + return (...args) => { + const ret = action(this.state, ...args); + if (ret != null) { + return "then" in ret ? ret.then(apply) : apply(ret); + } + }; + } + + setState(update: Partial, overwrite?: boolean) { + this.state = overwrite + ? (update as State) + : Object.assign({}, this.state, update); + const currentListeners = this.listeners; + for (let i = 0; i < currentListeners.length; i++) { + currentListeners[i](this.state); + } + } + + /** + * Register a listener function to be called whenever state is changed. Returns an `unsubscribe()` function. + * @param {Function} listener A function to call when state changes. Gets passed the new state. + * @returns {Function} unsubscribe() + */ + subscribe(listener: Listener): UnsubscribeFunc { + this.listeners.push(listener); + if (this.state !== undefined) listener(this.state); + return () => { + this.unsubscribe(listener); + }; + } + + /** + * Remove a previously-registered listener function. + * @param {Function} listener The callback previously passed to `subscribe()` that should be removed. + * @function + */ + unsubscribe(listener: Listener) { + const out = []; + const listeners = this.listeners; + for (let i = 0; i < listeners.length; i++) { + if (listeners[i] === listener) { + listener = null; + } else { + out.push(listeners[i]); + } + } + this.listeners = out; + if (out.length === 0) { + this._noSub(); + } + } +} diff --git a/lib/types.ts b/lib/types.ts new file mode 100644 index 00000000..78cc7853 --- /dev/null +++ b/lib/types.ts @@ -0,0 +1,65 @@ +import { Auth } from "./auth"; + +export type Error = 1 | 2 | 3 | 4; + +export type UnsubscribeFunc = () => void; + +export type ConnectionOptions = { + setupRetry: number; + createSocket: (auth: Auth, options: ConnectionOptions) => Promise; +}; + +export type HassEvent = { + event_type: string; + data: object; + origin: string; + time_fired: string; + context: { + id: string; + user_id: string; + }; +}; + +export type HassConfig = { + latitude: number; + longitude: number; + elevation: number; + unit_system: { + length: string; + mass: string; + volume: string; + temperature: string; + }; + location_name: string; + time_zone: string; + components: string[]; + config_dir: string; + whitelist_external_dirs: string[]; + version: string; +}; + +export type HassEntity = { + entity_id: string; + state: string; + last_changed: string; + last_updated: string; + attributes: { [s: string]: any }; +}; + +export type HassEntities = { [entity_id: string]: HassEntity }; + +export type HassService = { + description: string; + fields: { + [field_name: string]: { + description: string; + example: string; + }; + }; +}; + +export type HassServices = { + [domain: string]: { + [service_name: string]: HassService; + }; +}; diff --git a/lib/util.js b/lib/util.ts similarity index 64% rename from lib/util.js rename to lib/util.ts index 8f884fee..7d1030bd 100644 --- a/lib/util.js +++ b/lib/util.ts @@ -1,8 +1,8 @@ -export function parseQuery(queryString) { +export function parseQuery(queryString: string) { const query = {}; - const items = queryString.split('&'); + const items = queryString.split("&"); for (let i = 0; i < items.length; i++) { - const item = items[i].split('='); + const item = items[i].split("="); const key = decodeURIComponent(item[0]); const value = item.length > 1 ? decodeURIComponent(item[1]) : undefined; query[key] = value; diff --git a/package.json b/package.json index 596fb48f..9785fd04 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "sideEffects": false, "version": "2.0.1", "description": "Home Assistant websocket client", - "source": "lib/index.js", + "source": "lib/index.ts", "main": "dist/haws.umd.js", "module": "dist/haws.es.js", "repository": { @@ -13,22 +13,32 @@ "scripts": { "watch": "microbundle watch", "build": "microbundle", - "lint": "eslint lib test", - "test": "mocha", - "test-watch": "mocha --watch" + "test": "microbundle && mocha" }, "author": "Paulus Schoutsen ", "license": "Apache-2.0", "devDependencies": { - "eslint": "^4.19.1", - "eslint-config-airbnb-base": "^12.1.0", - "eslint-plugin-import": "^2.2.0", + "husky": "^1.0.0-rc.13", + "lint-staged": "^7.2.0", "microbundle": "^0.5.1", "mocha": "^5.1.1", - "reify": "^0.15.1" + "prettier": "1.14.0", + "reify": "^0.15.1", + "ts-node": "^7.0.0" }, "files": [ "dist" ], - "dependencies": {} + "dependencies": {}, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "*.{ts,js,json,css,md}": [ + "prettier --write", + "git add" + ] + } } diff --git a/test/config.spec.js b/test/config.spec.js index a4828ab9..35eda847 100644 --- a/test/config.spec.js +++ b/test/config.spec.js @@ -1,14 +1,14 @@ -import assert from 'assert'; +import assert from "assert"; -import subscribeConfig from '../lib/config'; -import { mockConnection, createAwaitableEvent } from './util'; +import { subscribeConfig } from "../dist/haws.es"; +import { mockConnection, createAwaitableEvent } from "./util"; const MOCK_CONFIG = { - hello: 'bla', - components: ['frontend'] + hello: "bla", + components: ["frontend"] }; -describe('subscribeConfig', () => { +describe("subscribeConfig", () => { let conn; let awaitableEvent; @@ -18,7 +18,7 @@ describe('subscribeConfig', () => { awaitableEvent = createAwaitableEvent(); }); - it('should load initial config', async () => { + it("should load initial config", async () => { awaitableEvent.prime(); subscribeConfig(conn, awaitableEvent.set); @@ -27,7 +27,7 @@ describe('subscribeConfig', () => { assert.deepStrictEqual(config, MOCK_CONFIG); }); - it('should handle component loaded events', async () => { + it("should handle component loaded events", async () => { subscribeConfig(conn, awaitableEvent.set); // We need to sleep to have it process the first full load @@ -35,17 +35,17 @@ describe('subscribeConfig', () => { awaitableEvent.prime(); - conn.mockEvent('component_loaded', { + conn.mockEvent("component_loaded", { data: { - component: 'api' + component: "api" } }); const config = await awaitableEvent.wait(); assert.deepEqual(config, { - hello: 'bla', - components: ['frontend', 'api'], + hello: "bla", + components: ["frontend", "api"] }); }); }); diff --git a/test/entities.spec.js b/test/entities.spec.js index ff8bcbe3..97b919e5 100644 --- a/test/entities.spec.js +++ b/test/entities.spec.js @@ -1,21 +1,21 @@ -import assert from 'assert'; +import assert from "assert"; -import subscribeEntities from '../lib/entities'; -import { mockConnection, createAwaitableEvent } from './util'; +import { subscribeEntities } from "../dist/haws.es"; +import { mockConnection, createAwaitableEvent } from "./util"; const MOCK_LIGHT = { - entity_id: 'light.kitchen', - state: 'on' + entity_id: "light.kitchen", + state: "on" }; const MOCK_SWITCH = { - entity_id: 'switch.ac', - state: 'off' + entity_id: "switch.ac", + state: "off" }; const MOCK_ENTITIES = [MOCK_LIGHT, MOCK_SWITCH]; -describe('subscribeEntities', () => { +describe("subscribeEntities", () => { let conn; let awaitableEvent; @@ -25,18 +25,18 @@ describe('subscribeEntities', () => { awaitableEvent = createAwaitableEvent(); }); - it('should load initial entities', async () => { + it("should load initial entities", async () => { awaitableEvent.prime(); subscribeEntities(conn, awaitableEvent.set); const entities = await awaitableEvent.wait(); assert.deepStrictEqual(entities, { [MOCK_LIGHT.entity_id]: MOCK_LIGHT, - [MOCK_SWITCH.entity_id]: MOCK_SWITCH, + [MOCK_SWITCH.entity_id]: MOCK_SWITCH }); }); - it('should handle state changed with updated state', async () => { + it("should handle state changed with updated state", async () => { subscribeEntities(conn, awaitableEvent.set); await 0; @@ -45,28 +45,28 @@ describe('subscribeEntities', () => { awaitableEvent.prime(); - conn.mockEvent('state_changed', { + conn.mockEvent("state_changed", { data: { - entity_id: 'light.kitchen', + entity_id: "light.kitchen", new_state: { - entity_id: 'light.kitchen', - state: 'off', - }, - }, + entity_id: "light.kitchen", + state: "off" + } + } }); const entities = await awaitableEvent.wait(); assert.deepEqual(entities, { [MOCK_SWITCH.entity_id]: MOCK_SWITCH, - 'light.kitchen': { - entity_id: 'light.kitchen', - state: 'off', - }, + "light.kitchen": { + entity_id: "light.kitchen", + state: "off" + } }); }); - it('should handle state changed with new state', async () => { + it("should handle state changed with new state", async () => { subscribeEntities(conn, awaitableEvent.set); await 0; @@ -75,14 +75,14 @@ describe('subscribeEntities', () => { awaitableEvent.prime(); - conn.mockEvent('state_changed', { + conn.mockEvent("state_changed", { data: { - entity_id: 'light.living_room', + entity_id: "light.living_room", new_state: { - entity_id: 'light.living_room', - state: 'off', - }, - }, + entity_id: "light.living_room", + state: "off" + } + } }); const entities = await awaitableEvent.wait(); @@ -90,14 +90,14 @@ describe('subscribeEntities', () => { assert.deepEqual(entities, { [MOCK_SWITCH.entity_id]: MOCK_SWITCH, [MOCK_LIGHT.entity_id]: MOCK_LIGHT, - 'light.living_room': { - entity_id: 'light.living_room', - state: 'off', - }, + "light.living_room": { + entity_id: "light.living_room", + state: "off" + } }); }); - it('should handle state changed with removed state', async () => { + it("should handle state changed with removed state", async () => { subscribeEntities(conn, awaitableEvent.set); await 0; @@ -106,17 +106,17 @@ describe('subscribeEntities', () => { awaitableEvent.prime(); - conn.mockEvent('state_changed', { + conn.mockEvent("state_changed", { data: { - entity_id: 'light.kitchen', - new_state: null, - }, + entity_id: "light.kitchen", + new_state: null + } }); const entities = await awaitableEvent.wait(); assert.deepEqual(entities, { - [MOCK_SWITCH.entity_id]: MOCK_SWITCH, + [MOCK_SWITCH.entity_id]: MOCK_SWITCH }); }); }); diff --git a/test/messages.spec.js b/test/messages.spec.js deleted file mode 100644 index 8dcbc241..00000000 --- a/test/messages.spec.js +++ /dev/null @@ -1,12 +0,0 @@ -import assert from 'assert'; - -import * as messages from '../lib/messages'; - -describe('auth access token message', () => { - it('should contain access token', () => { - assert.deepEqual(messages.authAccessToken('hello'), { - type: 'auth', - access_token: 'hello', - }); - }); -}); diff --git a/test/mocha.opts b/test/mocha.opts index b9a19833..9549c3e3 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,3 +1,4 @@ -r reify +-r ts-node/register --recursive --timeout 10000 diff --git a/test/services.spec.js b/test/services.spec.js index deb4cd55..6857beda 100644 --- a/test/services.spec.js +++ b/test/services.spec.js @@ -1,23 +1,23 @@ -import assert from 'assert'; +import assert from "assert"; -import subscribeServices from '../lib/services'; -import { mockConnection, createAwaitableEvent } from './util'; +import { subscribeServices } from "../dist/haws.es"; +import { mockConnection, createAwaitableEvent } from "./util"; const MOCK_SERVICES = { light: { turn_on: { - description: 'Turn a light on', + description: "Turn a light on", fields: { entity_id: { - description: 'Entity ID to turn on', - example: 'light.kitchen', + description: "Entity ID to turn on", + example: "light.kitchen" } } } } }; -describe('subscribeServices', () => { +describe("subscribeServices", () => { let conn; let awaitableEvent; @@ -27,7 +27,7 @@ describe('subscribeServices', () => { awaitableEvent = createAwaitableEvent(); }); - it('should load initial services', async () => { + it("should load initial services", async () => { awaitableEvent.prime(); subscribeServices(conn, awaitableEvent.set); @@ -35,17 +35,17 @@ describe('subscribeServices', () => { assert.deepStrictEqual(services, MOCK_SERVICES); }); - it('should handle service registered events for existing domains', async () => { + it("should handle service registered events for existing domains", async () => { subscribeServices(conn, awaitableEvent.set); await 0; awaitableEvent.prime(); - conn.mockEvent('service_registered', { + conn.mockEvent("service_registered", { data: { - domain: 'light', - service: 'toggle', + domain: "light", + service: "toggle" } }); @@ -54,23 +54,23 @@ describe('subscribeServices', () => { assert.deepEqual(services, { light: { turn_on: { - description: 'Turn a light on', + description: "Turn a light on", fields: { entity_id: { - description: 'Entity ID to turn on', - example: 'light.kitchen', + description: "Entity ID to turn on", + example: "light.kitchen" } } }, toggle: { - description: '', - fields: {}, + description: "", + fields: {} } } }); }); - it('should handle service registered events for new domains', async () => { + it("should handle service registered events for new domains", async () => { subscribeServices(conn, awaitableEvent.set); // We need to sleep to have it process the first full load @@ -78,10 +78,10 @@ describe('subscribeServices', () => { awaitableEvent.prime(); - conn.mockEvent('service_registered', { + conn.mockEvent("service_registered", { data: { - domain: 'switch', - service: 'turn_on', + domain: "switch", + service: "turn_on" } }); @@ -90,25 +90,25 @@ describe('subscribeServices', () => { assert.deepEqual(services, { light: { turn_on: { - description: 'Turn a light on', + description: "Turn a light on", fields: { entity_id: { - description: 'Entity ID to turn on', - example: 'light.kitchen', + description: "Entity ID to turn on", + example: "light.kitchen" } } } }, switch: { turn_on: { - description: '', - fields: {}, + description: "", + fields: {} } } }); }); - it('should handle service removed events for existing services', async () => { + it("should handle service removed events for existing services", async () => { subscribeServices(conn, awaitableEvent.set); // We need to sleep to have it process the first full load @@ -116,10 +116,10 @@ describe('subscribeServices', () => { awaitableEvent.prime(); - conn.mockEvent('service_removed', { + conn.mockEvent("service_removed", { data: { - domain: 'light', - service: 'turn_on', + domain: "light", + service: "turn_on" } }); diff --git a/test/util.js b/test/util.js index 75f56615..69f8cb08 100644 --- a/test/util.js +++ b/test/util.js @@ -2,13 +2,10 @@ export function mockConnection() { const listeners = {}; return { // connection events - // eslint-disable-next-line - addEventListener(event, cb) { - - }, + addEventListener(event, cb) {}, // hass events - subscribeEvents(cb, event = '*') { + subscribeEvents(cb, event = "*") { if (!(event in listeners)) { listeners[event] = []; } @@ -17,7 +14,7 @@ export function mockConnection() { mockEvent(event, data) { listeners[event].forEach(cb => cb(data)); - }, + } }; } @@ -31,11 +28,13 @@ export function createAwaitableEvent() { }, prime() { - curPromise = new Promise((resolve) => { curResolve = resolve; }); + curPromise = new Promise(resolve => { + curResolve = resolve; + }); }, wait() { return curPromise; - }, + } }; } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..92b3dd6f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "lib": ["es2015", "dom"], + "target": "es2018", + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noUnusedLocals": true + } +} diff --git a/yarn.lock b/yarn.lock index 4d1fecb7..38e54fc6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,12 @@ # yarn lockfile v1 +"@samverschueren/stream-to-observable@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" + dependencies: + any-observable "^0.3.0" + "@types/estree@0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" @@ -34,46 +40,23 @@ acorn-es7-plugin@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/acorn-es7-plugin/-/acorn-es7-plugin-1.1.7.tgz#f2ee1f3228a90eead1245f9ab1922eb2e71d336b" -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" - dependencies: - acorn "^3.0.4" - acorn-jsx@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-4.1.1.tgz#e8e41e48ea2fe0c896740610ab6a4ffd8add225e" dependencies: acorn "^5.0.3" -acorn@>=2.5.2, acorn@^5.0.0, acorn@^5.0.3, acorn@^5.2.1, acorn@^5.4.1, acorn@^5.5.0: +acorn@>=2.5.2, acorn@^5.0.0, acorn@^5.0.3, acorn@^5.2.1, acorn@^5.4.1: version "5.7.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" -acorn@^3.0.4: - version "3.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" - -ajv-keywords@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" - -ajv@^5.2.3, ajv@^5.3.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" - dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" - alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" -ansi-escapes@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" +ansi-escapes@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" ansi-regex@^2.0.0: version "2.1.1" @@ -87,12 +70,20 @@ ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" dependencies: color-convert "^1.9.0" +any-observable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" + +app-root-path@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.1.0.tgz#98bf6599327ecea199309866e8140368fd2e646a" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -105,32 +96,42 @@ 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" -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - dependencies: - array-uniq "^1.0.1" - -array-uniq@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" 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" + arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + asyncro@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/asyncro/-/asyncro-3.0.0.tgz#3c7a732e263bc4a42499042f48d7d858e9c0134e" +atob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a" + autoprefixer@^6.3.1: version "6.7.7" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" @@ -153,14 +154,6 @@ autoprefixer@^8.3.0: postcss "^6.0.23" postcss-value-parser "^3.2.3" -babel-code-frame@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - babel-polyfill@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" @@ -188,6 +181,18 @@ balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" +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" + big.js@^3.1.3: version "3.2.0" resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" @@ -211,6 +216,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" + browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -246,6 +266,10 @@ buffer-from@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04" +buffer-from@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -254,15 +278,19 @@ builtin-modules@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-2.0.0.tgz#60b7ef5ae6546bd7deefa74b08b62a43a232648e" -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" - dependencies: - callsites "^0.2.0" - -callsites@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" +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" camelcase@^5.0.0: version "5.0.0" @@ -285,7 +313,7 @@ caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30000864: version "1.0.30000865" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000865.tgz#70026616e8afe6e1442f8bb4e1092987d81a2f25" -chalk@^1.0.0, chalk@^1.1.3: +chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: @@ -295,7 +323,7 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.0, chalk@^2.4.1: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.1, chalk@^2.4.0, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: @@ -303,13 +331,9 @@ chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.0, chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chardet@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" - -circular-json@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" +ci-info@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.3.tgz#710193264bb05c77b8c90d02f5aaf22216a667b2" clap@^1.0.9: version "1.2.3" @@ -317,30 +341,53 @@ clap@^1.0.9: dependencies: chalk "^1.1.3" -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" dependencies: - restore-cursor "^2.0.0" + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" -cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" +cli-cursor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + dependencies: + restore-cursor "^1.0.1" + +cli-spinners@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" + +cli-truncate@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" + dependencies: + slice-ansi "0.0.4" + string-width "^1.0.1" clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - coa@~1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd" dependencies: q "^1.1.2" +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +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.3.0, color-convert@^1.9.0: version "1.9.2" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" @@ -385,23 +432,22 @@ commander@2.15.1: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" +commander@^2.14.1, commander@^2.9.0: + version "2.17.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" +component-emitter@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - concat-with-sourcemaps@^1.0.5: version "1.1.0" resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" @@ -414,18 +460,14 @@ consolidate@^0.15.1: dependencies: bluebird "^3.1.1" -contains-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" +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@^2.4.0, core-js@^2.5.0: version "2.5.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - cosmiconfig@^2.1.0, cosmiconfig@^2.1.1: version "2.2.2" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-2.2.2.tgz#6173cebd56fac042c1f4390edf7af6c07c7cb892" @@ -438,7 +480,15 @@ cosmiconfig@^2.1.0, cosmiconfig@^2.1.1: parse-json "^2.2.0" require-from-string "^1.1.0" -cross-spawn@^5.1.0: +cosmiconfig@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.5.tgz#a809e3c2306891ce17ab70359dc8bdf661fe2cd0" + dependencies: + is-directory "^0.3.1" + js-yaml "^3.9.0" + parse-json "^4.0.0" + +cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" dependencies: @@ -517,13 +567,17 @@ csso@~2.3.1: clap "^1.0.9" source-map "^0.5.3" +date-fns@^1.27.2: + version "1.29.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6" + debug@3.1.0, debug@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: ms "2.0.0" -debug@^2.6.8, debug@^2.6.9: +debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -533,42 +587,40 @@ decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" -deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" +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" -defined@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" -del@^2.0.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" dependencies: - globby "^5.0.0" - is-path-cwd "^1.0.0" - is-path-in-cwd "^1.0.0" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - rimraf "^2.2.8" - -diff@3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + is-descriptor "^0.1.0" -doctrine@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" dependencies: - esutils "^2.0.2" - isarray "^1.0.0" + is-descriptor "^1.0.0" -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" dependencies: - esutils "^2.0.2" + 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" + +diff@3.5.0, diff@^3.1.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" dot-prop@^4.1.1: version "4.2.0" @@ -584,11 +636,15 @@ electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.47: version "1.3.52" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.52.tgz#d2d9f1270ba4a3b967b831c40ef71fb4d9ab5ce0" +elegant-spinner@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" + emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" -error-ex@^1.2.0: +error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" dependencies: @@ -602,106 +658,6 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" -eslint-config-airbnb-base@^12.1.0: - version "12.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz#386441e54a12ccd957b0a92564a4bafebd747944" - dependencies: - eslint-restricted-globals "^0.1.1" - -eslint-import-resolver-node@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" - dependencies: - debug "^2.6.9" - resolve "^1.5.0" - -eslint-module-utils@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz#b270362cd88b1a48ad308976ce7fa54e98411746" - dependencies: - debug "^2.6.8" - pkg-dir "^1.0.0" - -eslint-plugin-import@^2.2.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.13.0.tgz#df24f241175e312d91662dc91ca84064caec14ed" - dependencies: - contains-path "^0.1.0" - debug "^2.6.8" - doctrine "1.5.0" - eslint-import-resolver-node "^0.3.1" - eslint-module-utils "^2.2.0" - has "^1.0.1" - lodash "^4.17.4" - minimatch "^3.0.3" - read-pkg-up "^2.0.0" - resolve "^1.6.0" - -eslint-restricted-globals@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7" - -eslint-scope@^3.7.1: - version "3.7.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-visitor-keys@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" - -eslint@^4.19.1: - version "4.19.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" - dependencies: - ajv "^5.3.0" - babel-code-frame "^6.22.0" - chalk "^2.1.0" - concat-stream "^1.6.0" - cross-spawn "^5.1.0" - debug "^3.1.0" - doctrine "^2.1.0" - eslint-scope "^3.7.1" - eslint-visitor-keys "^1.0.0" - espree "^3.5.4" - esquery "^1.0.0" - esutils "^2.0.2" - file-entry-cache "^2.0.0" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^11.0.1" - ignore "^3.3.3" - imurmurhash "^0.1.4" - inquirer "^3.0.6" - is-resolvable "^1.0.0" - js-yaml "^3.9.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.4" - minimatch "^3.0.2" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - pluralize "^7.0.0" - progress "^2.0.0" - regexpp "^1.0.1" - require-uncached "^1.0.3" - semver "^5.3.0" - strip-ansi "^4.0.0" - strip-json-comments "~2.0.1" - table "4.0.2" - text-table "~0.2.0" - -espree@^3.5.4: - version "3.5.4" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" - dependencies: - acorn "^5.5.0" - acorn-jsx "^3.0.0" - esprima@^2.6.0: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -710,22 +666,6 @@ esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" -esquery@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" - dependencies: - estraverse "^4.0.0" - -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" - dependencies: - estraverse "^4.1.0" - -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - estree-walker@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.2.1.tgz#bdafe8095383d8414d5dc2ecf4c9173b6db9412e" @@ -734,9 +674,21 @@ 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.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" +execa@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.9.0.tgz#adb7ce62cf985071f60580deb4a88b9e34712d01" + 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" + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" expand-brackets@^0.1.4: version "0.1.5" @@ -744,19 +696,36 @@ 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" dependencies: fill-range "^2.1.0" -external-editor@^2.0.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" +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: - chardet "^0.4.0" - iconv-lite "^0.4.17" - tmp "^0.0.33" + assign-symbols "^1.0.0" + is-extendable "^1.0.1" extglob@^0.3.1: version "0.3.2" @@ -764,42 +733,30 @@ extglob@^0.3.1: dependencies: is-extglob "^1.0.0" -fast-deep-equal@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" - -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - -fast-levenshtein@~2.0.4: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" +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" fastparse@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" -figures@^1.0.1: +figures@^1.0.1, figures@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" dependencies: escape-string-regexp "^1.0.5" object-assign "^4.1.0" -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" - dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" - filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" @@ -818,27 +775,24 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" -find-up@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - dependencies: - locate-path "^2.0.0" +find-parent-dir@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54" -flat-cache@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" dependencies: - circular-json "^0.3.1" - del "^2.0.2" - graceful-fs "^4.1.2" - write "^0.2.1" + locate-path "^3.0.0" flatten@^1.0.2: version "1.0.2" @@ -851,7 +805,7 @@ flow-remove-types@^1.1.0: babylon "^6.15.0" vlq "^0.2.1" -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" @@ -861,6 +815,12 @@ for-own@^0.1.4: dependencies: for-in "^1.0.1" +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" + fs-extra@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" @@ -877,16 +837,28 @@ function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - generic-names@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/generic-names/-/generic-names-1.0.3.tgz#2d786a121aee508876796939e8e3bff836c20917" dependencies: loader-utils "^0.2.16" +get-own-enumerable-property-symbols@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-2.0.1.tgz#5c4ad87f2834c4b9b4e84549dc1e0650fb38c24b" + +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + +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" + glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" @@ -900,7 +872,7 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob@7.1.2, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2: +glob@7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -911,25 +883,10 @@ glob@7.1.2, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^11.0.1: - version "11.7.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673" - globalyzer@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" -globby@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" - dependencies: - array-union "^1.0.1" - arrify "^1.0.0" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - globrex@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.1.tgz#cfe565cfa910707d0ef98eb0b9d78c3c055ca2ef" @@ -969,6 +926,33 @@ has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" +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" + has@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -991,20 +975,25 @@ html-comment-regex@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" -iconv-lite@^0.4.17: - version "0.4.23" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" - dependencies: - safer-buffer ">= 2.1.2 < 3" +husky@^1.0.0-rc.13: + version "1.0.0-rc.13" + resolved "https://registry.yarnpkg.com/husky/-/husky-1.0.0-rc.13.tgz#49c3cc210bfeac24d4ad272f770b7505c9091828" + dependencies: + cosmiconfig "^5.0.2" + execa "^0.9.0" + find-up "^3.0.0" + get-stdin "^6.0.0" + is-ci "^1.1.0" + pkg-dir "^3.0.0" + please-upgrade-node "^3.1.1" + read-pkg "^4.0.1" + run-node "^1.0.0" + slash "^2.0.0" icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" -ignore@^3.3.3: - version "3.3.10" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" - import-cwd@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" @@ -1017,9 +1006,15 @@ import-from@^2.1.0: dependencies: resolve-from "^3.0.0" -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + dependencies: + repeating "^2.0.0" + +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" indexes-of@^1.0.1: version "1.0.1" @@ -1032,33 +1027,26 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@~2.0.3: +inherits@2: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" -inquirer@^3.0.6: - version "3.3.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" - dependencies: - ansi-escapes "^3.0.0" - chalk "^2.0.0" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^2.0.4" - figures "^2.0.0" - lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rx-lite "^4.0.8" - rx-lite-aggregates "^4.0.8" - string-width "^2.1.0" - strip-ansi "^4.0.0" - through "^2.3.6" - is-absolute-url@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" +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" @@ -1073,6 +1061,40 @@ is-builtin-module@^1.0.0: dependencies: builtin-modules "^1.0.0" +is-ci@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.1.0.tgz#247e4162e7860cebbdaf30b774d6b0ac7dcfe7a5" + dependencies: + ci-info "^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-directory@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" @@ -1087,17 +1109,35 @@ 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" -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" is-glob@^2.0.0, is-glob@^2.0.1: version "2.0.1" @@ -1105,6 +1145,12 @@ is-glob@^2.0.0, is-glob@^2.0.1: dependencies: is-extglob "^1.0.0" +is-glob@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" + dependencies: + is-extglob "^2.1.1" + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -1115,34 +1161,36 @@ is-number@^2.1.0: dependencies: kind-of "^3.0.2" +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + 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-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" - -is-path-in-cwd@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" - dependencies: - is-path-inside "^1.0.0" - -is-path-inside@^1.0.0: +is-obj@^1.0.0, is-obj@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + +is-observable@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e" dependencies: - path-is-inside "^1.0.1" + symbol-observable "^1.1.0" is-plain-obj@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" +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" @@ -1155,9 +1203,13 @@ is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" -is-resolvable@^1.0.0: +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + +is-stream@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" is-svg@^2.0.0: version "2.1.0" @@ -1165,7 +1217,11 @@ is-svg@^2.0.0: dependencies: html-comment-regex "^1.1.0" -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + +isarray@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -1179,15 +1235,28 @@ 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" + +jest-get-type@^22.1.0: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" + +jest-validate@^23.0.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.4.0.tgz#d96eede01ef03ac909c009e9c8e455197d48c201" + dependencies: + chalk "^2.0.1" + jest-get-type "^22.1.0" + leven "^2.1.0" + pretty-format "^23.2.0" + js-base64@^2.1.9: version "2.4.6" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.6.tgz#1d49f618bef43630cd191f4e122447acfdb947d8" -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - -js-yaml@^3.4.3, js-yaml@^3.9.1: +js-yaml@^3.4.3, js-yaml@^3.9.0: version "3.12.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" dependencies: @@ -1205,13 +1274,9 @@ jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" -json-schema-traverse@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" json5@^0.5.0: version "0.5.1" @@ -1223,31 +1288,104 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" -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: is-buffer "^1.1.5" -kind-of@^6.0.0: +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + 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" -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" +leven@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" + +lint-staged@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-7.2.0.tgz#bdf4bb7f2f37fe689acfaec9999db288a5b26888" dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" + app-root-path "^2.0.1" + chalk "^2.3.1" + commander "^2.14.1" + cosmiconfig "^5.0.2" + debug "^3.1.0" + dedent "^0.7.0" + execa "^0.9.0" + find-parent-dir "^0.3.0" + is-glob "^4.0.0" + is-windows "^1.0.2" + jest-validate "^23.0.0" + listr "^0.14.1" + lodash "^4.17.5" + log-symbols "^2.2.0" + micromatch "^3.1.8" + npm-which "^3.0.1" + p-map "^1.1.1" + path-is-inside "^1.0.2" + pify "^3.0.0" + please-upgrade-node "^3.0.2" + staged-git-files "1.1.1" + string-argv "^0.0.2" + stringify-object "^3.2.2" -load-json-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" +listr-silent-renderer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" + +listr-update-renderer@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz#344d980da2ca2e8b145ba305908f32ae3f4cc8a7" dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - strip-bom "^3.0.0" + chalk "^1.1.3" + cli-truncate "^0.2.1" + elegant-spinner "^1.0.1" + figures "^1.7.0" + indent-string "^3.0.0" + log-symbols "^1.0.2" + log-update "^1.0.2" + strip-ansi "^3.0.1" + +listr-verbose-renderer@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#8206f4cf6d52ddc5827e5fd14989e0e965933a35" + dependencies: + chalk "^1.1.3" + cli-cursor "^1.0.2" + date-fns "^1.27.2" + figures "^1.7.0" + +listr@^0.14.1: + version "0.14.1" + resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.1.tgz#8a7afa4a7135cee4c921d128e0b7dfc6e522d43d" + dependencies: + "@samverschueren/stream-to-observable" "^0.3.0" + cli-truncate "^0.2.1" + figures "^1.7.0" + indent-string "^2.1.0" + is-observable "^1.1.0" + is-promise "^2.1.0" + is-stream "^1.1.0" + listr-silent-renderer "^1.1.1" + listr-update-renderer "^0.4.0" + listr-verbose-renderer "^0.4.0" + log-symbols "^1.0.2" + log-update "^1.0.2" + ora "^0.2.3" + p-map "^1.1.1" + rxjs "^6.1.0" + strip-ansi "^3.0.1" loader-utils@^0.2.16: version "0.2.17" @@ -1258,11 +1396,11 @@ loader-utils@^0.2.16: json5 "^0.5.0" object-assign "^4.0.1" -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" dependencies: - p-locate "^2.0.0" + p-locate "^3.0.0" path-exists "^3.0.0" lodash.foreach@^4.5.0: @@ -1281,10 +1419,29 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@^4.17.4, lodash@^4.3.0: +lodash@^4.17.5: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" +log-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + dependencies: + chalk "^1.0.0" + +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" + +log-update@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1" + dependencies: + ansi-escapes "^1.0.0" + cli-cursor "^1.0.2" + lru-cache@^4.0.1, lru-cache@^4.1.2: version "4.1.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" @@ -1298,6 +1455,20 @@ magic-string@^0.22.4: dependencies: vlq "^0.2.2" +make-error@^1.1.1: + version "1.3.4" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.4.tgz#19978ed575f9e9545d2ff8c13e33b5d18a67d535" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + +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" + math-expression-evaluator@^1.2.14: version "1.2.17" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" @@ -1373,11 +1544,25 @@ micromatch@^2.3.11: parse-glob "^3.0.4" regex-cache "^0.4.2" -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - -minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: +micromatch@^3.1.8: + 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" + +minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -1391,6 +1576,13 @@ 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.5.1, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -1425,13 +1617,21 @@ ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" nodent-compiler@^3.1.6: version "3.2.6" @@ -1474,6 +1674,26 @@ normalize-url@^1.4.0: query-string "^4.1.0" sort-keys "^1.0.0" +npm-path@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.4.tgz#c641347a5ff9d6a09e4d9bce5580c4f505278e64" + dependencies: + which "^1.2.10" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + dependencies: + path-key "^2.0.0" + +npm-which@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa" + dependencies: + commander "^2.9.0" + npm-path "^2.0.2" + which "^1.2.10" + num2fraction@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" @@ -1486,6 +1706,20 @@ object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" +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" @@ -1493,56 +1727,62 @@ 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" + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: wrappy "1" -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - dependencies: - mimic-fn "^1.0.0" +onetime@^1.0.0: + version "1.1.0" + resolved "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" -optionator@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" +ora@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.4" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - wordwrap "~1.0.0" + chalk "^1.1.1" + cli-cursor "^1.0.2" + cli-spinners "^0.1.2" + object-assign "^4.0.1" os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" +p-limit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec" dependencies: - p-try "^1.0.0" + p-try "^2.0.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" dependencies: - p-limit "^1.1.0" + p-limit "^2.0.0" + +p-map@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" p-queue@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-2.4.2.tgz#03609826682b743be9a22dba25051bd46724fc34" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" +p-try@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" pad-right@^0.2.2: version "0.2.2" @@ -1565,11 +1805,16 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" dependencies: - pinkie-promise "^2.0.0" + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" path-exists@^3.0.0: version "3.0.0" @@ -1579,47 +1824,37 @@ path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@^1.0.1, path-is-inside@^1.0.2: +path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" +path-key@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + path-parse@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" -path-type@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" - dependencies: - pify "^2.0.0" - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + find-up "^3.0.0" -pkg-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" +please-upgrade-node@^3.0.2, please-upgrade-node@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz#ed320051dfcc5024fae696712c8288993595e8ac" dependencies: - find-up "^1.0.0" + semver-compare "^1.0.0" -pluralize@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" +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-calc@^5.2.0: version "5.3.1" @@ -1914,10 +2149,6 @@ postcss@^6.0.1, postcss@^6.0.20, postcss@^6.0.21, postcss@^6.0.23: source-map "^0.6.1" supports-color "^5.4.0" -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - prepend-http@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" @@ -1926,6 +2157,10 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" +prettier@1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.0.tgz#847c235522035fd988100f1f43cf20a7d24f9372" + prettier@^1.13.0: version "1.13.7" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.7.tgz#850f3b8af784a49a6ea2d2eaa7ed1428a34b7281" @@ -1940,13 +2175,12 @@ pretty-bytes@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.1.0.tgz#6237ecfbdc6525beaef4de722cc60a58ae0e6c6d" -process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - -progress@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" +pretty-format@^23.2.0: + version "23.2.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.2.0.tgz#3b0aaa63c018a53583373c1cb3a5d96cc5e83017" + dependencies: + ansi-regex "^3.0.0" + ansi-styles "^3.2.0" promise.series@^0.2.0: version "0.2.0" @@ -1975,32 +2209,13 @@ randomatic@^3.0.0: kind-of "^6.0.0" math-random "^1.0.1" -read-pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" - dependencies: - find-up "^2.0.0" - read-pkg "^2.0.0" - -read-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" +read-pkg@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237" dependencies: - load-json-file "^2.0.0" normalize-package-data "^2.3.2" - path-type "^2.0.0" - -readable-stream@^2.2.2: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" + parse-json "^4.0.0" + pify "^3.0.0" reduce-css-calc@^1.2.6: version "1.3.0" @@ -2034,9 +2249,12 @@ regex-cache@^0.4.2: dependencies: is-equal-shallow "^0.1.3" -regexpp@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" +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@^1.0.0: version "1.0.0" @@ -2071,51 +2289,48 @@ 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" +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + require-from-string@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" -require-uncached@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" - dependencies: - caller-path "^0.1.0" - resolve-from "^1.0.0" - reserved-words@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/reserved-words/-/reserved-words-0.1.2.tgz#00a0940f98cd501aeaaac316411d9adc52b31ab1" -resolve-from@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" - resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" -resolve@^1.1.6, resolve@^1.5.0, resolve@^1.6.0, resolve@^1.7.1: +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.1.6, resolve@^1.5.0, resolve@^1.7.1: version "1.8.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" dependencies: path-parse "^1.0.5" -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" + exit-hook "^1.0.0" + onetime "^1.0.0" -rimraf@^2.2.8: - version "2.6.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" - dependencies: - glob "^7.0.5" +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" rollup-plugin-buble@^0.19.2: version "0.19.2" @@ -2245,21 +2460,15 @@ rollup@^0.62.0: "@types/estree" "0.0.39" "@types/node" "*" -run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" - dependencies: - is-promise "^2.1.0" +run-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/run-node/-/run-node-1.0.0.tgz#46b50b946a2aa2d4947ae1d886e9856fd9cabe5e" -rx-lite-aggregates@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" +rxjs@^6.1.0: + version "6.2.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.2.2.tgz#eb75fa3c186ff5289907d06483a77884586e1cf9" dependencies: - rx-lite "*" - -rx-lite@*, rx-lite@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" + tslib "^1.9.0" sade@^1.4.0: version "1.4.1" @@ -2268,22 +2477,42 @@ sade@^1.4.0: mri "^1.1.0" pad-right "^0.2.2" -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" +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" sax@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1: +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + +"semver@2 || 3 || 4 || 5", semver@^5.4.1: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" +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" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -2294,15 +2523,44 @@ shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" -signal-exit@^3.0.2: +signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" -slice-ansi@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + +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: - is-fullwidth-code-point "^2.0.0" + 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" sort-keys@^1.0.0: version "1.1.2" @@ -2310,11 +2568,32 @@ sort-keys@^1.0.0: dependencies: is-plain-obj "^1.0.0" +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.6.tgz#4435cee46b1aab62b8e8610ce60f788091c51c13" + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" -source-map@^0.6.1, source-map@~0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -2340,50 +2619,64 @@ spdx-license-ids@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" +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.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" +staged-git-files@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-1.1.1.tgz#37c2218ef0d6d26178b1310719309a16a59f8f7b" + +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" + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" +string-argv@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.0.2.tgz#dac30408690c21f3c3630a3ff3a05877bdcbd736" + string-hash@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" -string-width@^2.1.0, string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" +stringify-object@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.2.2.tgz#9853052e5a88fb605a44cd27445aa257ad7ffbcd" dependencies: - safe-buffer "~5.1.0" + get-own-enumerable-property-symbols "^2.0.1" + is-obj "^1.0.1" + is-regexp "^1.0.0" -strip-ansi@^3.0.0: +strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" dependencies: ansi-regex "^2.0.0" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - dependencies: - ansi-regex "^3.0.0" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" style-inject@^0.3.0: version "0.3.0" @@ -2417,24 +2710,9 @@ svgo@^0.7.0: sax "~1.2.1" whet.extend "~0.9.9" -table@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" - dependencies: - ajv "^5.2.3" - ajv-keywords "^2.1.0" - chalk "^2.1.0" - lodash "^4.17.4" - slice-ansi "1.0.0" - string-width "^2.1.1" - -text-table@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - -through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" +symbol-observable@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" tiny-glob@^0.2.0: version "0.2.2" @@ -2443,25 +2721,44 @@ tiny-glob@^0.2.0: globalyzer "^0.1.0" globrex "^0.1.1" -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" +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: - os-tmpdir "~1.0.2" + kind-of "^3.0.2" -tslib@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" +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" -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" +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: - prelude-ls "~1.1.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +ts-node@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.0.tgz#a94a13c75e5e1aa6b82814b84c68deb339ba7bff" + dependencies: + arrify "^1.0.0" + buffer-from "^1.1.0" + diff "^3.1.0" + make-error "^1.1.1" + minimist "^1.2.0" + mkdirp "^0.5.1" + source-map-support "^0.5.6" + yn "^2.0.0" -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" +tslib@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" typescript@^2.8.3: version "2.9.2" @@ -2474,6 +2771,15 @@ uglify-es@^3.3.7: commander "~2.13.0" source-map "~0.6.1" +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" + uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" @@ -2486,9 +2792,20 @@ universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" +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" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" validate-npm-package-license@^3.0.1: version "3.0.3" @@ -2517,26 +2834,20 @@ whet.extend@~0.9.9: version "0.9.9" resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" -which@^1.2.9: +which@^1.2.10, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" dependencies: isexe "^2.0.0" -wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" - dependencies: - mkdirp "^0.5.1" - yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + +yn@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a"