diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 2210825..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,31 +0,0 @@ -module.exports = { - "env": { - "browser": true, - "commonjs": true, - "es6": true - }, - "extends": "eslint:recommended", - "parserOptions": { - "ecmaVersion": 2017 - }, - "rules": { - "indent": [ - "error", - 4, - {"SwitchCase": 1} - ], - "linebreak-style": [ - "error", - "unix" - ], - "quotes": [ - "error", - "single" - ], - "semi": [ - "error", - "always" - ], - "no-console": "off" - } -}; \ No newline at end of file diff --git a/README.md b/README.md index 18dc719..29b763a 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,56 @@ -# SmartThings Javascript SDK +# SmartThings SmartApp NodeJS SDK (preview) + +

+ + + +Language grade: JavaScript +Total alerts +Known Vulnerabilities + +

[Reference Documentation](doc/index.md) -SDK that wraps the SmartThings REST API and reduces the amount of code necessary -to write a SmartApp app. Supports both web-hook and AWS Lambda implementations. -This is a pre-release version of the API and may change over -time time. +SDK that wraps the SmartThings REST API and reduces the amount of code necessary to write a SmartApp app. It supports both webhook and AWS Lambda implementations. This is a preview version of the API and will change over time time. + +## Installation + +```bash +npm i @smartthings/smartapp --save +``` + +## Importing + +`NodeJS`: -## Installation: +```javascript +const smartapp = require('@smartthings/smartapp') ``` -npm install @smartthings/smartapp --save + +Or, if you're transpiling to `ES6`/`ES2015`+: + +```javascript +import smartapp from '@smartthings/smartapp' ``` -## Key Features -* Javascript API hides details of REST calls and authentication. -* Event handler framework dispatches lifecycle evebnts to named event handlers. -* Configuration page API simplifies page definition. -* Integrated [i18n](https://www.npmjs.com/package/i18n) framework provides configuration page localization. -* [Winston](https://www.npmjs.com/package/winston) framework manges log messages. +## Highlights + +- [x] Javascript API hides details of REST calls and authentication. +- [x] Event handler framework dispatches lifecycle evebnts to named event handlers. +- [x] Configuration page API simplifies page definition. +- [x] Integrated [i18n](https://www.npmjs.com/package/i18n) framework provides configuration page localization. +- [x] [Winston](https://www.npmjs.com/package/winston) framework manges log messages. ## Example ### Run as an AWS Lambda function -Here's the equivalent of the origial SmartThings Groovy _Let There Be Light_ app that -turns on and off a light when a door opens and closes, set up to run as a Lambda. -``` -require('@smartthings/smartapp'); -app.configureI18n() +Here's the equivalent of the original SmartThings Groovy _Let There Be Light_ app that turns on and off a light when a door opens and closes, set up to run as a Lambda. + +```javascript +const smartapp = require('@smartthings/smartapp') +smartapp.configureI18n() .page('mainPage', (page) => { page.section('sensors', (section) => { section.deviceSetting('contactSensor').capabilities(['contactSensor']); @@ -37,26 +60,24 @@ app.configureI18n() }); }) .updated(() => { - app.api.devices.unsubscribeAll().then(() => { - app.api.devices.subscribe(app.config.contactSensor, 'contactSensor', 'contact', 'openCloseHandler'); + smartapp.api.devices.unsubscribeAll().then(() => { + smartapp.api.devices.subscribe(smartapp.config.contactSensor, 'contactSensor', 'contact', 'openCloseHandler'); }); }) .subscribedEventHandler('openCloseHandler', (event) => { const value = event.value === 'open' ? 'on' : 'off'; - app.api.devices.sendCommands(app.config.lights, 'switch', value); + smartapp.api.devices.sendCommands(smartapp.config.lights, 'switch', value); }); - exports.handle = (evt, context, callback) => { - app.handleLambdaCallback(evt, context, callback); + smartapp.handleLambdaCallback(evt, context, callback); }; ``` ### Localization -Configuration page strings are specified in a separate locales/en.json file, which -can be automatically created the first time you run the app. Here's a completed English localization file -for the previous example: -``` +Configuration page strings are specified in a separate `locales/en.json` file, which can be automatically created the first time you run the app. Here's a completed English localization file for the previous example: + +```json { "pages.mainPage.name": "Let There Be Light", "pages.mainPage.sections.sensors.name": "When this door or window opens or closes", @@ -71,12 +92,13 @@ for the previous example: To run the app in a webserver rather than a lambda replace the `exports.handle = ...` function with an HTTP server with the public key file specified: -``` + +```javascript const express = require('express'); const bodyParser = require('body-parser'); const server = module.exports = express(); -require('@smartthings/smartapp'); -app.publicKey(`@${process.env.HOME}/smartthings_rsa.pub`) +const smartapp = require('@smartthings/smartapp'); +smartapp.publicKey(`@${process.env.HOME}/smartthings_rsa.pub`) .configureI18n() .page('mainPage', (page) => { page.section('sensors', (section) => { @@ -87,17 +109,16 @@ app.publicKey(`@${process.env.HOME}/smartthings_rsa.pub`) }); }) .updated(() => { - app.api.devices.unsubscribeAll().then(() => { - app.api.devices.subscribe(app.config.contactSensor, 'contactSensor', 'contact', 'openCloseHandler'); + smartapp.api.devices.unsubscribeAll().then(() => { + smartapp.api.devices.subscribe(smartapp.config.contactSensor, 'contactSensor', 'contact', 'openCloseHandler'); }); }) .subscribedEventHandler('openCloseHandler', (event) => { const value = event.value === 'open' ? 'on' : 'off'; - app.api.devices.sendCommands(app.config.lights, 'switch', value); + smartapp.api.devices.sendCommands(smartapp.config.lights, 'switch', value); }); - server.use(bodyParser.json()); server.post('/', function(req, response) { - app.handleHttpCallback(req, response); + smartapp.handleHttpCallback(req, response); }); -``` \ No newline at end of file +``` \ No newline at end of file diff --git a/index.js b/index.js index 0a89ead..2df41fb 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,9 @@ -'use strict'; +'use strict' -const SmartApp = require('./lib/smart-app'); +const SmartApp = require('./lib/smart-app') module.exports = (function (options = {}) { - return new SmartApp(options); -})(); + return new SmartApp(options) +})() -module.exports.default = Object.assign({}, module.exports); \ No newline at end of file +module.exports.default = Object.assign({}, module.exports) diff --git a/lib/api.js b/lib/api.js index 4c0cb5b..4d09a7a 100644 --- a/lib/api.js +++ b/lib/api.js @@ -1,30 +1,29 @@ -'use strict'; +'use strict' -const Client = require('./platform/client'); -const Apps = require('./platform/apps'); -const DeviceProfiles = require('./platform/deviceprofiles'); -const Devices = require('./platform/devices'); -const InstalledApps = require('./platform/installedapps'); -const Locations = require('./platform/locations'); -const Modes = require('./platform/modes'); -const Scenes = require('./platform/scenes'); -const Schedules = require('./platform/schedules'); -const Subscriptions = require('./platform/subscriptions'); +const Client = require('./platform/client') +const Apps = require('./platform/apps') +const DeviceProfiles = require('./platform/deviceprofiles') +const Devices = require('./platform/devices') +const InstalledApps = require('./platform/installedapps') +const Locations = require('./platform/locations') +const Modes = require('./platform/modes') +const Scenes = require('./platform/scenes') +const Schedules = require('./platform/schedules') +const Subscriptions = require('./platform/subscriptions') module.exports = class SmartThingsApi { - - constructor(options) { - this.client = new Client(options); - this.apps = new Apps(this); - this.deviceProfiles = new DeviceProfiles(this); - this.devices = new Devices(this); - this.installedApps = new InstalledApps(this); - this.locations = new Locations(this); - this.modes = new Modes(this); - this.scenes = new Scenes(this); - this.schedules = new Schedules(this); - this.subscriptions = new Subscriptions(this); - this.installedAppId = options.installedAppId; - this.locationId = options.locationId; - } -}; \ No newline at end of file + constructor(options) { + this.client = new Client(options) + this.apps = new Apps(this) + this.deviceProfiles = new DeviceProfiles(this) + this.devices = new Devices(this) + this.installedApps = new InstalledApps(this) + this.locations = new Locations(this) + this.modes = new Modes(this) + this.scenes = new Scenes(this) + this.schedules = new Schedules(this) + this.subscriptions = new Subscriptions(this) + this.installedAppId = options.installedAppId + this.locationId = options.locationId + } +} diff --git a/lib/pages/boolean-setting.js b/lib/pages/boolean-setting.js index c95437d..91a747e 100644 --- a/lib/pages/boolean-setting.js +++ b/lib/pages/boolean-setting.js @@ -1,35 +1,37 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class BooleanSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'BOOLEAN' + } - constructor(section, id) { - super(section, id); - this._type = 'BOOLEAN'; - } - - image(value) { - this._image = value; - return this; - } - - submitOnChange(value) { - this._submitOnChange = value; - return this; - } - - toJson() { - let result = super.toJson(); - if (result.defaultValue === undefined && result.required) { - result.defaultValue = 'false'; - } - if (this._image) { - result.image = this._image; - } - if (this._submitOnChange) { - result.submitOnChange = this._submitOnChange; - } - return result; - } -}; \ No newline at end of file + image(value) { + this._image = value + return this + } + + submitOnChange(value) { + this._submitOnChange = value + return this + } + + toJson() { + const result = super.toJson() + if (result.defaultValue === undefined && result.required) { + result.defaultValue = 'false' + } + + if (this._image) { + result.image = this._image + } + + if (this._submitOnChange) { + result.submitOnChange = this._submitOnChange + } + + return result + } +} diff --git a/lib/pages/decimal-setting.js b/lib/pages/decimal-setting.js index 693d96c..cec0988 100644 --- a/lib/pages/decimal-setting.js +++ b/lib/pages/decimal-setting.js @@ -1,49 +1,52 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class DecimalSetting extends SectionSetting { - - constructor(section, id) { - super(section, id); - this._type = 'DECIMAL'; - this._description = 'Tap to set'; - } - - max(value) { - this._max = value; - return this; - } - - min(value) { - this._min = value; - return this; - } - - image(value) { - this._image = value; - return this; - } - - postMessage(value) { - this._postMessage = value; - return this; - } - - toJson() { - let result = super.toJson(); - if (this._max) { - result.max = this._max; - } - if (this._min) { - result.min = this._min; - } - if (this._image) { - result.image = this._image; - } - if (this._postMessage) { - result.postMessage = this._postMessage; - } - return result; - } -}; \ No newline at end of file + constructor(section, id) { + super(section, id) + this._type = 'DECIMAL' + this._description = 'Tap to set' + } + + max(value) { + this._max = value + return this + } + + min(value) { + this._min = value + return this + } + + image(value) { + this._image = value + return this + } + + postMessage(value) { + this._postMessage = value + return this + } + + toJson() { + const result = super.toJson() + if (this._max) { + result.max = this._max + } + + if (this._min) { + result.min = this._min + } + + if (this._image) { + result.image = this._image + } + + if (this._postMessage) { + result.postMessage = this._postMessage + } + + return result + } +} diff --git a/lib/pages/device-setting.js b/lib/pages/device-setting.js index ba5d350..7ec93b3 100644 --- a/lib/pages/device-setting.js +++ b/lib/pages/device-setting.js @@ -1,90 +1,95 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class DeviceSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'DEVICE' + this._description = 'Tap to set' + this._multiple = false + } - constructor(section, id) { - super(section, id); - this._type = 'DEVICE'; - this._description = 'Tap to set'; - this._multiple = false; - } - - multiple(value) { - this._multiple = value; - return this; - } - - closeOnSelection(value) { - this._closeOnSelection = value; - return this; - } - - submitOnChange(value) { - this._submitOnChange = value; - return this; - } - - preselect(value) { - this._preselect = value; - return this; - } - - capabilities(value) { - this._capabilities = value; - return this; - } - - capability(value) { - this._capabilities = [value]; - return this; - } - - excludeCapabilities(value) { - this._excludeCapabilities = value; - return this; - } - - // TODO -- need to be able to OR capabilities - excludeCapability(value) { - this._excludeCapabilities = [value]; - return this; - } - - permissions(value) { - this._permissions = typeof value === 'string' ? value.split('') : value; - return this; - } - - toJson() { - let result = super.toJson(); - result.multiple = this._multiple; - - if (this._closeOnSelection) { - result.closeOnSelection = this._closeOnSelection; - } - if (this._submitOnChange) { - result.submitOnChange = this._submitOnChange; - } - if (this._preselect) { - result.preselect = this._preselect; - } - if (this._capabilities) { - result.capabilities = this._capabilities; - } - if (this._excludeCapabilities) { - result.excludeCapabilities = this._excludeCapabilities; - } - if (this._permissions) { - result.permissions = this._permissions; - } - else { - result.permissions = ['r']; - } - if (this._style) { - result.style = this._style; - } - return result; - } -}; \ No newline at end of file + multiple(value) { + this._multiple = value + return this + } + + closeOnSelection(value) { + this._closeOnSelection = value + return this + } + + submitOnChange(value) { + this._submitOnChange = value + return this + } + + preselect(value) { + this._preselect = value + return this + } + + capabilities(value) { + this._capabilities = value + return this + } + + capability(value) { + this._capabilities = [value] + return this + } + + excludeCapabilities(value) { + this._excludeCapabilities = value + return this + } + + // TODO -- need to be able to OR capabilities + excludeCapability(value) { + this._excludeCapabilities = [value] + return this + } + + permissions(value) { + this._permissions = typeof value === 'string' ? value.split('') : value + return this + } + + toJson() { + const result = super.toJson() + result.multiple = this._multiple + + if (this._closeOnSelection) { + result.closeOnSelection = this._closeOnSelection + } + + if (this._submitOnChange) { + result.submitOnChange = this._submitOnChange + } + + if (this._preselect) { + result.preselect = this._preselect + } + + if (this._capabilities) { + result.capabilities = this._capabilities + } + + if (this._excludeCapabilities) { + result.excludeCapabilities = this._excludeCapabilities + } + + if (this._permissions) { + result.permissions = this._permissions + } else { + result.permissions = ['r'] + } + + if (this._style) { + result.style = this._style + } + + return result + } +} diff --git a/lib/pages/email-setting.js b/lib/pages/email-setting.js index a42bf43..f468129 100644 --- a/lib/pages/email-setting.js +++ b/lib/pages/email-setting.js @@ -1,26 +1,26 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class EmailSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'EMAIL' + this._description = 'Tap to set' + } - constructor(section, id) { - super(section, id); - this._type = 'EMAIL'; - this._description = 'Tap to set'; - } + // TODO -- Is this right? It is from Swagger + image(value) { + this._image = value + return this + } - // TODO -- Is this right? It is from Swagger - image(value) { - this._image = value; - return this; - } + toJson() { + const result = super.toJson() + if (this._image) { + result.image = this._image + } - toJson() { - let result = super.toJson(); - if (this._image) { - result.image = this._image; - } - return result; - } -}; \ No newline at end of file + return result + } +} diff --git a/lib/pages/enum-setting.js b/lib/pages/enum-setting.js index c1f424f..3885b38 100644 --- a/lib/pages/enum-setting.js +++ b/lib/pages/enum-setting.js @@ -1,87 +1,90 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class EnumSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'ENUM' + this._description = 'Tap to set' + } - constructor(section, id) { - super(section, id); - this._type = 'ENUM'; - this._description = 'Tap to set'; - } - - multiple(value) { - this._multiple = value; - return this; - } - - closeOnSelection(value) { - this._closeOnSelection = value; - return this; - } - - groupedOptions(value) { - this._groupedOptions = value; - return this; - } - - options(value) { - let values = []; - if (value instanceof Array) { - for (let it of value) { - if (it instanceof Object) { - values.push(it); - } - else { - const item = String(it); - values.push({id: item, name: item}); - } - } - } - else if (value instanceof Object) { - for (let property in value) { - if (value.hasOwnProperty(property)) { - values.push({id: property, name: value[property]}); - } - } - } - else { - throw `${typeof value} not valid for options`; - } - this._options = values; - return this; - } - - submitOnChange(value) { - this._submitOnChange = value; - return this; - } - - style(value) { - this._style = value; - return this; - } - - toJson() { - let result = super.toJson(); - if (this._multiple) { - result.multiple = this._multiple; - } - if (this._closeOnSelection) { - result.closeOnSelection = this._closeOnSelection; - } - if (this._groupedOptions) { - result.groupedOptions = this._groupedOptions; - } - if (this._options) { - result.options = this._options; - } - if (this._submitOnChange) { - result.submitOnChange = this._submitOnChange; - } - if (this._style) { - result.style = this._style; - } - return result; - } -}; \ No newline at end of file + multiple(value) { + this._multiple = value + return this + } + + closeOnSelection(value) { + this._closeOnSelection = value + return this + } + + groupedOptions(value) { + this._groupedOptions = value + return this + } + + options(value) { + const values = [] + if (Array.isArray(value)) { + for (const it of value) { + if (it instanceof Object) { + values.push(it) + } else { + const item = String(it) + values.push({id: item, name: item}) + } + } + } else if (value instanceof Object) { + for (const property in value) { + if (Object.prototype.hasOwnProperty.call(value, property)) { + values.push({id: property, name: value[property]}) + } + } + } else { + throw new TypeError(`${typeof value} not valid for options`) + } + + this._options = values + return this + } + + submitOnChange(value) { + this._submitOnChange = value + return this + } + + style(value) { + this._style = value + return this + } + + toJson() { + const result = super.toJson() + if (this._multiple) { + result.multiple = this._multiple + } + + if (this._closeOnSelection) { + result.closeOnSelection = this._closeOnSelection + } + + if (this._groupedOptions) { + result.groupedOptions = this._groupedOptions + } + + if (this._options) { + result.options = this._options + } + + if (this._submitOnChange) { + result.submitOnChange = this._submitOnChange + } + + if (this._style) { + result.style = this._style + } + + return result + } +} diff --git a/lib/pages/image-setting.js b/lib/pages/image-setting.js index 3413b7f..a648dff 100644 --- a/lib/pages/image-setting.js +++ b/lib/pages/image-setting.js @@ -1,24 +1,24 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class ImageSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'IMAGE' + } - constructor(section, id) { - super(section, id); - this._type = 'IMAGE'; - } + image(value) { + this._image = value + return this + } - image(value) { - this._image = value; - return this; - } + toJson() { + const result = super.toJson() + if (this._image) { + result.image = this._image + } - toJson() { - let result = super.toJson(); - if (this._image) { - result.image = this._image; - } - return result; - } -}; \ No newline at end of file + return result + } +} diff --git a/lib/pages/images-setting.js b/lib/pages/images-setting.js index f596f7d..d468faa 100644 --- a/lib/pages/images-setting.js +++ b/lib/pages/images-setting.js @@ -1,24 +1,24 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class ImagesSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'IMAGES' + } - constructor(section, id) { - super(section, id); - this._type = 'IMAGES'; - } + images(value) { + this._images = value + return this + } - images(value) { - this._images = value; - return this; - } + toJson() { + const result = super.toJson() + if (this._images) { + result.images = this._images + } - toJson() { - let result = super.toJson(); - if (this._images) { - result.images = this._images; - } - return result; - } -}; \ No newline at end of file + return result + } +} diff --git a/lib/pages/link-setting.js b/lib/pages/link-setting.js index 04615c1..cfc9cd8 100644 --- a/lib/pages/link-setting.js +++ b/lib/pages/link-setting.js @@ -1,41 +1,43 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class LinkSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'LINK' + } - constructor(section, id) { - super(section, id); - this._type = 'LINK'; - } - - url(value) { - this._url = value; - return this; - } - - image(value) { - this._image = value; - return this; - } - - style(value) { - this._style = value; - return this; - } - - toJson() { - let result = super.toJson(); - delete result.required; - if (this._url) { - result.url = this._url; - } - if (this._image) { - result.image = this._image; - } - if (this._style) { - result.style = this._style; - } - return result; - } -}; + url(value) { + this._url = value + return this + } + + image(value) { + this._image = value + return this + } + + style(value) { + this._style = value + return this + } + + toJson() { + const result = super.toJson() + delete result.required + if (this._url) { + result.url = this._url + } + + if (this._image) { + result.image = this._image + } + + if (this._style) { + result.style = this._style + } + + return result + } +} diff --git a/lib/pages/mode-setting.js b/lib/pages/mode-setting.js index 3838dbd..b0e42e1 100644 --- a/lib/pages/mode-setting.js +++ b/lib/pages/mode-setting.js @@ -1,49 +1,52 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class ModeSetting extends SectionSetting { - - constructor(section, id) { - super(section, id); - this._type = 'MODE'; - this._description = 'Tap to set'; - } - - multiple(value) { - this._multiple = value; - return this; - } - - closeOnSelection(value) { - this._closeOnSelection = value; - return this; - } - - submitOnChange(value) { - this._submitOnChange = value; - return this; - } - - style(value) { - this._style = value; - return this; - } - - toJson() { - let result = super.toJson(); - if (this._multiple) { - result.multiple = this._multiple; - } - if (this._closeOnSelection) { - result.closeOnSelection = this._closeOnSelection; - } - if (this._submitOnChange) { - result.submitOnChange = this._submitOnChange; - } - if (this._style) { - result.style = this._style; - } - return result; - } -}; \ No newline at end of file + constructor(section, id) { + super(section, id) + this._type = 'MODE' + this._description = 'Tap to set' + } + + multiple(value) { + this._multiple = value + return this + } + + closeOnSelection(value) { + this._closeOnSelection = value + return this + } + + submitOnChange(value) { + this._submitOnChange = value + return this + } + + style(value) { + this._style = value + return this + } + + toJson() { + const result = super.toJson() + if (this._multiple) { + result.multiple = this._multiple + } + + if (this._closeOnSelection) { + result.closeOnSelection = this._closeOnSelection + } + + if (this._submitOnChange) { + result.submitOnChange = this._submitOnChange + } + + if (this._style) { + result.style = this._style + } + + return result + } +} diff --git a/lib/pages/number-setting.js b/lib/pages/number-setting.js index 7a73f67..fdc17ec 100644 --- a/lib/pages/number-setting.js +++ b/lib/pages/number-setting.js @@ -1,65 +1,70 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class NumberSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'NUMBER' + this._description = 'Tap to set' + } - constructor(section, id) { - super(section, id); - this._type = 'NUMBER'; - this._description = 'Tap to set'; - } + max(value) { + this._max = value + return this + } - max(value) { - this._max = value; - return this; - } + min(value) { + this._min = value + return this + } - min(value) { - this._min = value; - return this; - } + step(value) { + this._step = value + return this + } - step(value) { - this._step = value; - return this; - } + style(value) { + this._style = value + return this + } - style(value) { - this._style = value; - return this; - } + image(value) { + this._image = value + return this + } - image(value) { - this._image = value; - return this; - } + postMessage(value) { + this._postMessage = value + return this + } - postMessage(value) { - this._postMessage = value; - return this; - } + toJson() { + const result = super.toJson() + if (this._max) { + result.max = this._max + } - toJson() { - let result = super.toJson(); - if (this._max) { - result.max = this._max; - } - if (this._min) { - result.min = this._min; - } - if (this._step) { - result.step = this._step; - } - if (this._style) { - result.style = this._style; - } - if (this._image) { - result.image = this._image; - } - if (this._postMessage) { - result.postMessage = this._postMessage; - } - return result; - } -}; \ No newline at end of file + if (this._min) { + result.min = this._min + } + + if (this._step) { + result.step = this._step + } + + if (this._style) { + result.style = this._style + } + + if (this._image) { + result.image = this._image + } + + if (this._postMessage) { + result.postMessage = this._postMessage + } + + return result + } +} diff --git a/lib/pages/oauth-setting.js b/lib/pages/oauth-setting.js index 1cf7527..b3fc98e 100644 --- a/lib/pages/oauth-setting.js +++ b/lib/pages/oauth-setting.js @@ -1,31 +1,31 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class OAuthSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'OAUTH' + } - constructor(section, id) { - super(section, id); - this._type = 'OAUTH'; - } + urlTemplate(value) { + this._urlTemplate = value + return this + } - urlTemplate(value) { - this._urlTemplate = value; - return this; - } + style(value) { + this._style = value + return this + } - style(value) { - this._style = value; - return this; - } + toJson() { + const result = super.toJson() + result.urlTemplate = this._urlTemplate - toJson() { - let result = super.toJson(); - result.urlTemplate = this._urlTemplate; + if (this._style) { + result.style = this._style + } - if (this._style) { - result.style = this._style; - } - return result; - } -}; + return result + } +} diff --git a/lib/pages/page-setting.js b/lib/pages/page-setting.js index 5bbdef3..6be9f02 100644 --- a/lib/pages/page-setting.js +++ b/lib/pages/page-setting.js @@ -1,42 +1,44 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class ParagraphSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'PAGE' + this._page = id + } - constructor(section, id) { - super(section, id); - this._type = 'PAGE'; - this._page = id; - } - - page(value) { - this._page = value; - return this; - } - - image(value) { - this._image = value; - return this; - } - - style(value) { - this._style = value; - return this; - } - - toJson() { - let result = super.toJson(); - delete result.required; - if (this._page) { - result.page = this._page; - } - if (this._image) { - result.image = this._image; - } - if (this._style) { - result.style = this._style; - } - return result; - } -}; \ No newline at end of file + page(value) { + this._page = value + return this + } + + image(value) { + this._image = value + return this + } + + style(value) { + this._style = value + return this + } + + toJson() { + const result = super.toJson() + delete result.required + if (this._page) { + result.page = this._page + } + + if (this._image) { + result.image = this._image + } + + if (this._style) { + result.style = this._style + } + + return result + } +} diff --git a/lib/pages/page.js b/lib/pages/page.js index f60285c..b02d586 100644 --- a/lib/pages/page.js +++ b/lib/pages/page.js @@ -1,109 +1,110 @@ -'use strict'; -const i18n = require('i18n'); +'use strict' +const i18n = require('i18n') -const Section = require('./section'); +const Section = require('./section') module.exports = class Page { - - constructor(id, language) { - this._id = id; - this._name = this.i18nKey('name'); - this._nextPageId = null; - this._previousPageId = null; - this._sections = []; - if (language) { - this.headers = {'accept-language': language}; - i18n.init(this); - } - this._settingIds = []; - this._defaultRequired = true; - } - - section(name, closure) { - let sec; - let callable = closure; - if (typeof name === 'string') { - sec = new Section(this, name); - } - else { - sec = new Section(this); - callable = name; - } - - this._sections.push(sec); - if (callable) { - callable(sec); - } - - return this; - } - - complete(value) { - this._complete = value; - return this; - } - - name(value) { - this._name = value; - return this; - } - - style(value) { - this._style = value; - return this; - } - - nextText(value) { - this._nextText = value; - return this; - } - - nextPageId(value) { - this._nextPageId = value; - return this; - } - - previousPageId(value) { - this._previousPageId = value; - return this; - } - - defaultRequired(value) { - this._defaultRequired = value; - return this; - } - - i18nKey(property) { - return `pages.${this._id}.${property}`; - } - - translate(...args) { - if (this.headers) { - return this.__(...args); - } - else { - return args[0]; - } - } - - toJson() { - let result = { - name: this.translate(this._name), - complete: this._complete === undefined ? (!this._nextPageId && !this._previousPageId) : this._complete, - pageId: this._id, - nextPageId: this._nextPageId, - previousPageId: this._previousPageId, - sections: [] - }; - if (this._style) { - result.style = this._style; - } - if (this._nextText) { - result.nextText = this._nextText; - } - for (let section of this._sections) { - result.sections.push(section.toJson()); - } - return result; - } -}; \ No newline at end of file + constructor(id, language) { + this._id = id + this._name = this.i18nKey('name') + this._nextPageId = null + this._previousPageId = null + this._sections = [] + if (language) { + this.headers = {'accept-language': language} + i18n.init(this) + } + + this._settingIds = [] + this._defaultRequired = true + } + + section(name, closure) { + let sec + let callable = closure + if (typeof name === 'string') { + sec = new Section(this, name) + } else { + sec = new Section(this) + callable = name + } + + this._sections.push(sec) + if (callable) { + callable(sec) + } + + return this + } + + complete(value) { + this._complete = value + return this + } + + name(value) { + this._name = value + return this + } + + style(value) { + this._style = value + return this + } + + nextText(value) { + this._nextText = value + return this + } + + nextPageId(value) { + this._nextPageId = value + return this + } + + previousPageId(value) { + this._previousPageId = value + return this + } + + defaultRequired(value) { + this._defaultRequired = value + return this + } + + i18nKey(property) { + return `pages.${this._id}.${property}` + } + + translate(...args) { + if (this.headers) { + return this.__(...args) + } + + return args[0] + } + + toJson() { + const result = { + name: this.translate(this._name), + complete: this._complete === undefined ? (!this._nextPageId && !this._previousPageId) : this._complete, + pageId: this._id, + nextPageId: this._nextPageId, + previousPageId: this._previousPageId, + sections: [] + } + if (this._style) { + result.style = this._style + } + + if (this._nextText) { + result.nextText = this._nextText + } + + for (const section of this._sections) { + result.sections.push(section.toJson()) + } + + return result + } +} diff --git a/lib/pages/paragraph-setting.js b/lib/pages/paragraph-setting.js index 9c458e5..5f690d7 100644 --- a/lib/pages/paragraph-setting.js +++ b/lib/pages/paragraph-setting.js @@ -1,28 +1,28 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class ParagraphSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'PARAGRAPH' + } - constructor(section, id) { - super(section, id); - this._type = 'PARAGRAPH'; - } + text(value) { + this.name(value) + } - text(value) { - this.name(value); - } + image(value) { + this._image = value + return this + } - image(value) { - this._image = value; - return this; - } + toJson() { + const result = super.toJson() + if (this._image) { + result.image = this._image + } - toJson() { - let result = super.toJson(); - if (this._image) { - result.image = this._image; - } - return result; - } -}; \ No newline at end of file + return result + } +} diff --git a/lib/pages/password-setting.js b/lib/pages/password-setting.js index 518e582..0bc67ab 100644 --- a/lib/pages/password-setting.js +++ b/lib/pages/password-setting.js @@ -1,41 +1,43 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class PasswordSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'PASSWORD' + this._description = 'Tap to set' + } - constructor(section, id) { - super(section, id); - this._type = 'PASSWORD'; - this._description = 'Tap to set'; - } - - maxLength(value) { - this._maxLength = value; - return this; - } - - minLength(value) { - this._minLength = value; - return this; - } - - image(value) { - this._image = value; - return this; - } - - toJson() { - let result = super.toJson(); - if (this._maxLength) { - result.maxLength = this._maxLength; - } - if (this._minLength) { - result.minLength = this._minLength; - } - if (this._image) { - result.image = this._image; - } - return result; - } -}; + maxLength(value) { + this._maxLength = value + return this + } + + minLength(value) { + this._minLength = value + return this + } + + image(value) { + this._image = value + return this + } + + toJson() { + const result = super.toJson() + if (this._maxLength) { + result.maxLength = this._maxLength + } + + if (this._minLength) { + result.minLength = this._minLength + } + + if (this._image) { + result.image = this._image + } + + return result + } +} diff --git a/lib/pages/phone-setting.js b/lib/pages/phone-setting.js index 58c5da8..e450d88 100644 --- a/lib/pages/phone-setting.js +++ b/lib/pages/phone-setting.js @@ -1,25 +1,25 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class PhoneSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'PHONE' + this._description = 'Tap to set' + } - constructor(section, id) { - super(section, id); - this._type = 'PHONE'; - this._description = 'Tap to set'; - } + image(value) { + this._image = value + return this + } - image(value) { - this._image = value; - return this; - } + toJson() { + const result = super.toJson() + if (this._image) { + result.image = this._image + } - toJson() { - let result = super.toJson(); - if (this._image) { - result.image = this._image; - } - return result; - } -}; + return result + } +} diff --git a/lib/pages/scene-setting.js b/lib/pages/scene-setting.js index b54263c..cb33fb0 100644 --- a/lib/pages/scene-setting.js +++ b/lib/pages/scene-setting.js @@ -1,49 +1,52 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class SceneSetting extends SectionSetting { - - constructor(section, id) { - super(section, id); - this._type = 'SCENE'; - this._description = 'Tap to set'; - } - - multiple(value) { - this._multiple = value; - return this; - } - - closeOnSelection(value) { - this._closeOnSelection = value; - return this; - } - - submitOnChange(value) { - this._submitOnChange = value; - return this; - } - - style(value) { - this._style = value; - return this; - } - - toJson() { - let result = super.toJson(); - if (this._multiple) { - result.multiple = this._multiple; - } - if (this._closeOnSelection) { - result.closeOnSelection = this._closeOnSelection; - } - if (this._submitOnChange) { - result.submitOnChange = this._submitOnChange; - } - if (this._style) { - result.style = this._style; - } - return result; - } -}; \ No newline at end of file + constructor(section, id) { + super(section, id) + this._type = 'SCENE' + this._description = 'Tap to set' + } + + multiple(value) { + this._multiple = value + return this + } + + closeOnSelection(value) { + this._closeOnSelection = value + return this + } + + submitOnChange(value) { + this._submitOnChange = value + return this + } + + style(value) { + this._style = value + return this + } + + toJson() { + const result = super.toJson() + if (this._multiple) { + result.multiple = this._multiple + } + + if (this._closeOnSelection) { + result.closeOnSelection = this._closeOnSelection + } + + if (this._submitOnChange) { + result.submitOnChange = this._submitOnChange + } + + if (this._style) { + result.style = this._style + } + + return result + } +} diff --git a/lib/pages/section-setting.js b/lib/pages/section-setting.js index 2d69c10..814afe6 100644 --- a/lib/pages/section-setting.js +++ b/lib/pages/section-setting.js @@ -1,84 +1,81 @@ -'use strict'; +'use strict' module.exports = class SectionSetting { + constructor(section, id) { + this._section = section + this._id = id + this._name = this.i18nKey('name') + this._description = '' + this._required = section._defaultRequired + if (section._page._settingIds.includes(id)) { + throw new Error(`Setting ID '${id}' has already been used.`) + } else { + section._page._settingIds.push(id) + } + } - constructor(section, id) { - this._section = section; - this._id = id; - this._name = this.i18nKey('name'); - this._description = ''; - this._required = section._defaultRequired; - if (section._page._settingIds.includes(id)) { - throw `Setting ID '${id}' has already been used.`; - } - else { - section._page._settingIds.push(id); - } - } + name(value) { + this._name = value + return this + } - name(value) { - this._name = value; - return this; - } + description(value) { + if (value === undefined || value === null) { + this._description = '' + } else { + this._description = value + } - description(value) { - if (value === undefined || value === null) { - this._description = ''; - } - else { - this._description = value; - } - return this; - } + return this + } - defaultValue(value) { - this._defaultValue = String(value); - return this; - } + defaultValue(value) { + this._defaultValue = String(value) + return this + } - required(value) { - this._required = value; - return this; - } + required(value) { + this._required = value + return this + } - disabled(value) { - this._disabled = value; - return this; - } + disabled(value) { + this._disabled = value + return this + } - i18nKey(property) { - return this._section._page.i18nKey(`settings.${this._id}.${property}`); - } + i18nKey(property) { + return this._section._page.i18nKey(`settings.${this._id}.${property}`) + } - translate(...args) { - return this._section._page.translate(...args); - } + translate(...args) { + return this._section._page.translate(...args) + } - toJson() { - let result = { - id: this._id, - name: this.translate(this._name), - required: this._required, - type: this._type, - }; + toJson() { + const result = { + id: this._id, + name: this.translate(this._name), + required: this._required, + type: this._type + } - if (!this._description) { - result.description = ''; - } - else if (this._description instanceof Function) { - result.description = this._description(); - } - else { - result.description = this.translate(this._description); - } + if (!this._description) { + result.description = '' + } else if (this._description instanceof Function) { + result.description = this._description() + } else { + result.description = this.translate(this._description) + } - if (this._defaultValue !== undefined) { - result.defaultValue = this._defaultValue; - } + if (this._defaultValue !== undefined) { + result.defaultValue = this._defaultValue + } - if (this._disabled) { - result.disabled = this._disabled; - } - return result; - } -}; \ No newline at end of file + if (this._disabled) { + result.disabled = this._disabled + } + + return result + } +} diff --git a/lib/pages/section.js b/lib/pages/section.js index 10b5778..9cbd0fa 100644 --- a/lib/pages/section.js +++ b/lib/pages/section.js @@ -1,195 +1,195 @@ -'use strict'; - -const DeviceSetting = require('./device-setting'); -const OAuthSetting = require('./oauth-setting'); -const TimeSetting = require('./time-setting'); -const TextSetting = require('./text-setting'); -const PasswordSetting = require('./password-setting'); -const PhoneSetting = require('./phone-setting'); -const EmailSetting = require('./email-setting'); -const NumberSetting = require('./number-setting'); -const DecimalSetting = require('./decimal-setting'); -const BooleanSetting = require('./boolean-setting'); -const ParagraphSetting = require('./paragraph-setting'); -const LinkSetting = require('./link-setting'); -const PageSetting = require('./page-setting'); -const ImageSetting = require('./image-setting'); -const ImagesSetting = require('./images-setting'); -const VideoSetting = require('./video-setting'); -const EnumSetting = require('./enum-setting'); -const SoundSetting = require('./sound-setting'); -const SecuritySetting = require('./security-setting'); -const ModeSetting = require('./mode-setting'); -const SceneSetting = require('./scene-setting'); +'use strict' + +const DeviceSetting = require('./device-setting') +const OAuthSetting = require('./oauth-setting') +const TimeSetting = require('./time-setting') +const TextSetting = require('./text-setting') +const PasswordSetting = require('./password-setting') +const PhoneSetting = require('./phone-setting') +const EmailSetting = require('./email-setting') +const NumberSetting = require('./number-setting') +const DecimalSetting = require('./decimal-setting') +const BooleanSetting = require('./boolean-setting') +const ParagraphSetting = require('./paragraph-setting') +const LinkSetting = require('./link-setting') +const PageSetting = require('./page-setting') +const ImageSetting = require('./image-setting') +const ImagesSetting = require('./images-setting') +const VideoSetting = require('./video-setting') +const EnumSetting = require('./enum-setting') +const SoundSetting = require('./sound-setting') +const SecuritySetting = require('./security-setting') +const ModeSetting = require('./mode-setting') +const SceneSetting = require('./scene-setting') module.exports = class Section { - - constructor(page, name) { - this._page = page; - this._name = name; - this._settings = []; - this._defaultRequired = page._defaultRequired; - } - - deviceSetting(id) { - const result = new DeviceSetting(this, id); - this._settings.push(result); - return result; - } - - oauthSetting(id) { - const result = new OAuthSetting(this, id); - this._settings.push(result); - return result; - } - - timeSetting(id) { - const result = new TimeSetting(this, id); - this._settings.push(result); - return result; - } - - textSetting(id) { - const result = new TextSetting(this, id); - this._settings.push(result); - return result; - } - - passwordSetting(id) { - const result = new PasswordSetting(this, id); - this._settings.push(result); - return result; - } - - phoneSetting(id) { - const result = new PhoneSetting(this, id); - this._settings.push(result); - return result; - } - - emailSetting(id) { - const result = new EmailSetting(this, id); - this._settings.push(result); - return result; - } - - numberSetting(id) { - const result = new NumberSetting(this, id); - this._settings.push(result); - return result; - } - - decimalSetting(id) { - const result = new DecimalSetting(this, id); - this._settings.push(result); - return result; - } - - booleanSetting(id) { - const result = new BooleanSetting(this, id); - this._settings.push(result); - return result; - } - - paragraphSetting(id) { - const result = new ParagraphSetting(this, id); - this._settings.push(result); - return result; - } - - linkSetting(id) { - const result = new LinkSetting(this, id); - this._settings.push(result); - return result; - } - - pageSetting(id) { - const result = new PageSetting(this, id); - this._settings.push(result); - return result; - } - - imageSetting(id) { - const result = new ImageSetting(this, id); - this._settings.push(result); - return result; - } - - imagesSetting(id) { - const result = new ImagesSetting(this, id); - this._settings.push(result); - return result; - } - - videoSetting(id) { - const result = new VideoSetting(this, id); - this._settings.push(result); - return result; - } - - enumSetting(id) { - const result = new EnumSetting(this, id); - this._settings.push(result); - return result; - } - - securitySetting(id) { - const result = new SecuritySetting(this, id); - this._settings.push(result); - return result; - } - - soundSetting(id) { - const result = new SoundSetting(this, id); - this._settings.push(result); - return result; - } - - modeSetting(id) { - const result = new ModeSetting(this, id); - this._settings.push(result); - return result; - } - - sceneSetting(id) { - const result = new SceneSetting(this, id); - this._settings.push(result); - return result; - } - - hideable(value) { - this._hideable = value; - return this; - } - - hidden(value) { - this._hidden = value; - return this; - } - - defaultRequired(value) { - this._defaultRequired = value; - return this; - } - - i18nKey(property, value) { - return this._page.i18nKey(`sections.${value}.${property}`) - } - - translate(...args) { - return this._page.translate(...args); - } - - toJson() { - let result = {}; - if (this._name) { - result.name = this._page.headers ? this.translate(this.i18nKey('name', this._name)) : this._name; - } - - result.settings = []; - for (let setting of this._settings) { - result.settings.push(setting.toJson()); - } - return result; - } -} \ No newline at end of file + constructor(page, name) { + this._page = page + this._name = name + this._settings = [] + this._defaultRequired = page._defaultRequired + } + + deviceSetting(id) { + const result = new DeviceSetting(this, id) + this._settings.push(result) + return result + } + + oauthSetting(id) { + const result = new OAuthSetting(this, id) + this._settings.push(result) + return result + } + + timeSetting(id) { + const result = new TimeSetting(this, id) + this._settings.push(result) + return result + } + + textSetting(id) { + const result = new TextSetting(this, id) + this._settings.push(result) + return result + } + + passwordSetting(id) { + const result = new PasswordSetting(this, id) + this._settings.push(result) + return result + } + + phoneSetting(id) { + const result = new PhoneSetting(this, id) + this._settings.push(result) + return result + } + + emailSetting(id) { + const result = new EmailSetting(this, id) + this._settings.push(result) + return result + } + + numberSetting(id) { + const result = new NumberSetting(this, id) + this._settings.push(result) + return result + } + + decimalSetting(id) { + const result = new DecimalSetting(this, id) + this._settings.push(result) + return result + } + + booleanSetting(id) { + const result = new BooleanSetting(this, id) + this._settings.push(result) + return result + } + + paragraphSetting(id) { + const result = new ParagraphSetting(this, id) + this._settings.push(result) + return result + } + + linkSetting(id) { + const result = new LinkSetting(this, id) + this._settings.push(result) + return result + } + + pageSetting(id) { + const result = new PageSetting(this, id) + this._settings.push(result) + return result + } + + imageSetting(id) { + const result = new ImageSetting(this, id) + this._settings.push(result) + return result + } + + imagesSetting(id) { + const result = new ImagesSetting(this, id) + this._settings.push(result) + return result + } + + videoSetting(id) { + const result = new VideoSetting(this, id) + this._settings.push(result) + return result + } + + enumSetting(id) { + const result = new EnumSetting(this, id) + this._settings.push(result) + return result + } + + securitySetting(id) { + const result = new SecuritySetting(this, id) + this._settings.push(result) + return result + } + + soundSetting(id) { + const result = new SoundSetting(this, id) + this._settings.push(result) + return result + } + + modeSetting(id) { + const result = new ModeSetting(this, id) + this._settings.push(result) + return result + } + + sceneSetting(id) { + const result = new SceneSetting(this, id) + this._settings.push(result) + return result + } + + hideable(value) { + this._hideable = value + return this + } + + hidden(value) { + this._hidden = value + return this + } + + defaultRequired(value) { + this._defaultRequired = value + return this + } + + i18nKey(property, value) { + return this._page.i18nKey(`sections.${value}.${property}`) + } + + translate(...args) { + return this._page.translate(...args) + } + + toJson() { + const result = {} + if (this._name) { + result.name = this._page.headers ? this.translate(this.i18nKey('name', this._name)) : this._name + } + + result.settings = [] + for (const setting of this._settings) { + result.settings.push(setting.toJson()) + } + + return result + } +} diff --git a/lib/pages/security-setting.js b/lib/pages/security-setting.js index 237ca8d..21f045b 100644 --- a/lib/pages/security-setting.js +++ b/lib/pages/security-setting.js @@ -1,57 +1,61 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class SecuritySetting extends SectionSetting { - - constructor(section, id) { - super(section, id); - this._type = 'ENUM'; - this._description = 'Tap to set'; - this._options = [ - {id:'DISARMED', name:'Disarmed'}, - {id:'ARMED_STAY', name:'Armed/Stay'}, - {id:'ARMED_AWAY', name:'Armed/Away'} - ]; - } - - multiple(value) { - this._multiple = value; - return this; - } - - closeOnSelection(value) { - this._closeOnSelection = value; - return this; - } - - submitOnChange(value) { - this._submitOnChange = value; - return this; - } - - style(value) { - this._style = value; - return this; - } - - toJson() { - let result = super.toJson(); - if (this._multiple) { - result.multiple = this._multiple; - } - if (this._closeOnSelection) { - result.closeOnSelection = this._closeOnSelection; - } - if (this._options) { - result.options = this._options; - } - if (this._submitOnChange) { - result.submitOnChange = this._submitOnChange; - } - if (this._style) { - result.style = this._style; - } - return result; - } -}; \ No newline at end of file + constructor(section, id) { + super(section, id) + this._type = 'ENUM' + this._description = 'Tap to set' + this._options = [ + {id: 'DISARMED', name: 'Disarmed'}, + {id: 'ARMED_STAY', name: 'Armed/Stay'}, + {id: 'ARMED_AWAY', name: 'Armed/Away'} + ] + } + + multiple(value) { + this._multiple = value + return this + } + + closeOnSelection(value) { + this._closeOnSelection = value + return this + } + + submitOnChange(value) { + this._submitOnChange = value + return this + } + + style(value) { + this._style = value + return this + } + + toJson() { + const result = super.toJson() + if (this._multiple) { + result.multiple = this._multiple + } + + if (this._closeOnSelection) { + result.closeOnSelection = this._closeOnSelection + } + + if (this._options) { + result.options = this._options + } + + if (this._submitOnChange) { + result.submitOnChange = this._submitOnChange + } + + if (this._style) { + result.style = this._style + } + + return result + } +} diff --git a/lib/pages/sound-setting.js b/lib/pages/sound-setting.js index 6b89e2f..9dd9bed 100644 --- a/lib/pages/sound-setting.js +++ b/lib/pages/sound-setting.js @@ -1,56 +1,60 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class SoundSetting extends SectionSetting { - - constructor(section, id) { - super(section, id); - this._type = 'SOUND'; - } - - multiple(value) { - this._multiple = value; - return this; - } - - groupedOptions(value) { - this._groupedOptions = value; - return this; - } - - options(value) { - this._options = value; - return this; - } - - submitOnChange(value) { - this._submitOnChange = value; - return this; - } - - style(value) { - this._style = value; - return this; - } - - toJson() { - let result = super.toJson(); - if (this._multiple) { - result.multiple = this._multiple; - } - if (this._groupedOptions) { - result.groupedOptions = this._groupedOptions; - } - if (this._options) { - result.options = this._options; - } - if (this._submitOnChange) { - result.submitOnChange = this._submitOnChange; - } - if (this._style) { - result.style = this._style; - } - return result; - } -}; \ No newline at end of file + constructor(section, id) { + super(section, id) + this._type = 'SOUND' + } + + multiple(value) { + this._multiple = value + return this + } + + groupedOptions(value) { + this._groupedOptions = value + return this + } + + options(value) { + this._options = value + return this + } + + submitOnChange(value) { + this._submitOnChange = value + return this + } + + style(value) { + this._style = value + return this + } + + toJson() { + const result = super.toJson() + if (this._multiple) { + result.multiple = this._multiple + } + + if (this._groupedOptions) { + result.groupedOptions = this._groupedOptions + } + + if (this._options) { + result.options = this._options + } + + if (this._submitOnChange) { + result.submitOnChange = this._submitOnChange + } + + if (this._style) { + result.style = this._style + } + + return result + } +} diff --git a/lib/pages/text-setting.js b/lib/pages/text-setting.js index f9e492a..6ce6c3f 100644 --- a/lib/pages/text-setting.js +++ b/lib/pages/text-setting.js @@ -1,49 +1,52 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class TextSetting extends SectionSetting { - - constructor(section, id) { - super(section, id); - this._type = 'TEXT'; - this._description = 'Tap to set'; - } - - maxLength(value) { - this._maxLength = value; - return this; - } - - minLength(value) { - this._minLength = value; - return this; - } - - image(value) { - this._image = value; - return this; - } - - postMessage(value) { - this._postMessage = value; - return this; - } - - toJson() { - let result = super.toJson(); - if (this._maxLength) { - result.maxLength = this._maxLength; - } - if (this._minLength) { - result.minLength = this._minLength; - } - if (this._image) { - result.image = this._image; - } - if (this._postMessage) { - result.postMessage = this._postMessage; - } - return result; - } -}; \ No newline at end of file + constructor(section, id) { + super(section, id) + this._type = 'TEXT' + this._description = 'Tap to set' + } + + maxLength(value) { + this._maxLength = value + return this + } + + minLength(value) { + this._minLength = value + return this + } + + image(value) { + this._image = value + return this + } + + postMessage(value) { + this._postMessage = value + return this + } + + toJson() { + const result = super.toJson() + if (this._maxLength) { + result.maxLength = this._maxLength + } + + if (this._minLength) { + result.minLength = this._minLength + } + + if (this._image) { + result.image = this._image + } + + if (this._postMessage) { + result.postMessage = this._postMessage + } + + return result + } +} diff --git a/lib/pages/time-setting.js b/lib/pages/time-setting.js index 1fe7a8e..1899ce8 100644 --- a/lib/pages/time-setting.js +++ b/lib/pages/time-setting.js @@ -1,24 +1,24 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class TimeSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'TIME' + } - constructor(section, id) { - super(section, id); - this._type = 'TIME'; - } + image(value) { + this._image = value + return this + } - image(value) { - this._image = value; - return this; - } + toJson() { + const result = super.toJson() + if (this._image) { + result.image = this._image + } - toJson() { - let result = super.toJson(); - if (this._image) { - result.image = this._image; - } - return result; - } -}; + return result + } +} diff --git a/lib/pages/video-setting.js b/lib/pages/video-setting.js index 64d2ef8..72c4946 100644 --- a/lib/pages/video-setting.js +++ b/lib/pages/video-setting.js @@ -1,32 +1,33 @@ -'use strict'; +'use strict' -const SectionSetting = require('./section-setting.js'); +const SectionSetting = require('./section-setting.js') module.exports = class VideoSetting extends SectionSetting { + constructor(section, id) { + super(section, id) + this._type = 'VIDEO' + } - constructor(section, id) { - super(section, id); - this._type = 'VIDEO'; - } + video(value) { + this._video = value + return this + } - video(value) { - this._video = value; - return this; - } + image(value) { + this._image = value + return this + } - image(value) { - this._image = value; - return this; - } + toJson() { + const result = super.toJson() + if (this._video) { + result.video = this._video + } - toJson() { - let result = super.toJson(); - if (this._video) { - result.video = this._video; - } - if (this._image) { - result.image = this._image; - } - return result; - } -}; + if (this._image) { + result.image = this._image + } + + return result + } +} diff --git a/lib/platform/apps.js b/lib/platform/apps.js index 29065e0..e7fe944 100644 --- a/lib/platform/apps.js +++ b/lib/platform/apps.js @@ -1,58 +1,57 @@ -'use strict'; +'use strict' -const Base = require('./base'); +const Base = require('./base') module.exports = class Apps extends Base { - - constructor(st) { - super(st); - } - - list() { - return this.st.client.request(`apps`); - } - - get(id) { - return this.st.client.request(`apps/${id}`); - } - - create(data) { - return this.st.client.request('apps', 'POST', data); - } - - update(id, data) { - return this.st.client.request(`apps/${id}`, 'PUT', data); - } - - getSettings(id) { - return this.st.client.request(`apps/${id}/settings`); - } - - updateSettings(id, data) { - return this.st.client.request(`apps/${id}/settings`, 'PUT', data); - } - - getOauth(id) { - return this.st.client.request(`apps/${id}/oauth`); - } - - updateOauth(id, data) { - return this.st.client.request(`apps/${id}/oauth`, 'PUT', data); - } - - regenerateOauth(id, data = {}) { - return this.st.client.request(`apps/${id}/oauth/generate`, 'POST', data); - } - - getTags(id) { - return this.st.client.request(`apps/${id}/tags`); - } - - updateTags(id, data) { - return this.st.client.request(`apps/${id}/tags`, 'PUT', data); - } - - addTags(id, data) { - return this.st.client.request(`apps/${id}/tags`, 'PATCH', data); - } -}; \ No newline at end of file + constructor(st) { + super(st) + } + + list() { + return this.st.client.request('apps') + } + + get(id) { + return this.st.client.request(`apps/${id}`) + } + + create(data) { + return this.st.client.request('apps', 'POST', data) + } + + update(id, data) { + return this.st.client.request(`apps/${id}`, 'PUT', data) + } + + getSettings(id) { + return this.st.client.request(`apps/${id}/settings`) + } + + updateSettings(id, data) { + return this.st.client.request(`apps/${id}/settings`, 'PUT', data) + } + + getOauth(id) { + return this.st.client.request(`apps/${id}/oauth`) + } + + updateOauth(id, data) { + return this.st.client.request(`apps/${id}/oauth`, 'PUT', data) + } + + regenerateOauth(id, data = {}) { + return this.st.client.request(`apps/${id}/oauth/generate`, 'POST', data) + } + + getTags(id) { + return this.st.client.request(`apps/${id}/tags`) + } + + updateTags(id, data) { + return this.st.client.request(`apps/${id}/tags`, 'PUT', data) + } + + addTags(id, data) { + return this.st.client.request(`apps/${id}/tags`, 'PATCH', data) + } +} diff --git a/lib/platform/base.js b/lib/platform/base.js index 4ed6a55..ead12fc 100644 --- a/lib/platform/base.js +++ b/lib/platform/base.js @@ -1,13 +1,12 @@ -'use strict'; +'use strict' module.exports = class Base { + constructor(st) { + this.st = st + } - constructor(st) { - this.st = st; - } - - installedAppId(id) { - this.st.installedAppId(id); - return this; - } -}; \ No newline at end of file + installedAppId(id) { + this.st.installedAppId(id) + return this + } +} diff --git a/lib/platform/client.js b/lib/platform/client.js index 8d63336..0ce768a 100644 --- a/lib/platform/client.js +++ b/lib/platform/client.js @@ -1,149 +1,152 @@ -'use strict'; +'use strict' -const rp = require('request-promise'); +const rp = require('request-promise') class Client { + constructor(options) { + this.authToken = options.authToken + this.refreshToken = options.refreshToken + this.clientId = options.clientId + this.clientSecret = options.clientSecret + this.apiUrl = options.apiUrl ? options.apiUrl : 'https://api.smartthings.com' + this.refreshUrl = options.refreshUrl ? options.refreshUrl : 'https://auth-global.api.smartthings.com/oauth/token' + this.contextStore = options.contextStore + this.installedAppId = options.installedAppId + this._log = options.log + this._mutex = options.apiMutex + } - constructor(options) { - this.authToken = options.authToken; - this.refreshToken = options.refreshToken; - this.clientId = options.clientId; - this.clientSecret = options.clientSecret; - this.apiUrl = options.apiUrl ? options.apiUrl : 'https://api.smartthings.com'; - this.refreshUrl = options.refreshUrl ? options.refreshUrl : 'https://auth-global.api.smartthings.com/oauth/token'; - this.contextStore = options.contextStore; - this.installedAppId = options.installedAppId; - this._log = options.log; - this._mutex = options.apiMutex; - } - - async request(path, method, data, transform, qs) { - if (this._mutex) { - return this._sequentialRequest(path, method, data, transform, qs); - } - else { - return this._request(path, method, data, transform, qs); - } - } - - async _request(path, method, data, transform, qs) { - let opts = { - url: `${this.apiUrl}/${path}`, - method: method ? method : 'GET', - json: true, - headers: { - 'Content-Type': 'application/json; charset=utf-8', - 'Authorization': 'Bearer ' + this.authToken - } - }; - if (data) { - opts.body = data; - } - if (transform) { - opts.transform = transform; - } - if (qs) { - opts.qs = qs; - } - return rp(opts).catch((error) => { - logError(this._log, error, opts); - }); - } - - async _sequentialRequest(path, method, data, transform, qs) { - const release = await this._mutex.acquire(); - - let opts = { - url: `${this.apiUrl}/${path}`, - method: method ? method : 'GET', - json: true, - headers: { - 'Content-Type': 'application/json; charset=utf-8', - 'Authorization': 'Bearer ' + this.authToken - } - }; - if (data) { - opts.body = data; - } - if (transform) { - opts.transform = transform; - } - if (qs) { - opts.qs = qs; - } - return rp(opts).catch(async (error) => { - const client = this; - if (error.statusCode === 401 && client.refreshToken && client.contextStore) { - const str = await refreshToken(client.refreshUrl, client.clientId, client.clientSecret, client.refreshToken); - const data = JSON.parse(str); - if (data.access_token) { - client.authToken = data.access_token; - client.refreshToken = data.refresh_token; - - //this._log.debug(`refresh ${JSON.stringify(data)}`); - - await client.contextStore.update(client.installedAppId, { - authToken: client.authToken, - refreshToken: client.refreshToken - }); - - release(); - - opts.headers.Authorization = 'Bearer ' + client.authToken; - - return rp(opts).catch((error) => { - logError(client._log, error, opts, 'TRY2 '); - }); - } - } - else { - logError(client._log, error, opts); - } - release(); - }).then(async (data) => { - release(); - return data; - }); - } + async request(path, method, data, transform, qs) { + if (this._mutex) { + return this._sequentialRequest(path, method, data, transform, qs) + } + + return this._request(path, method, data, transform, qs) + } + + async _request(path, method, data, transform, qs) { + const opts = { + url: `${this.apiUrl}/${path}`, + method: method ? method : 'GET', + json: true, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + Authorization: 'Bearer ' + this.authToken + } + } + if (data) { + opts.body = data + } + + if (transform) { + opts.transform = transform + } + + if (qs) { + opts.qs = qs + } + + return rp(opts).catch(error => { + logError(this._log, error, opts) + }) + } + + async _sequentialRequest(path, method, data, transform, qs) { + const release = await this._mutex.acquire() + + const opts = { + url: `${this.apiUrl}/${path}`, + method: method ? method : 'GET', + json: true, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + Authorization: 'Bearer ' + this.authToken + } + } + if (data) { + opts.body = data + } + + if (transform) { + opts.transform = transform + } + + if (qs) { + opts.qs = qs + } + + return rp(opts).catch(async error => { + const client = this + if (error.statusCode === 401 && client.refreshToken && client.contextStore) { + const str = await refreshToken(client.refreshUrl, client.clientId, client.clientSecret, client.refreshToken) + const data = JSON.parse(str) + if (data.access_token) { + client.authToken = data.access_token + client.refreshToken = data.refresh_token + + // This._log.debug(`refresh ${JSON.stringify(data)}`); + + await client.contextStore.update(client.installedAppId, { + authToken: client.authToken, + refreshToken: client.refreshToken + }) + + release() + + opts.headers.Authorization = 'Bearer ' + client.authToken + + return rp(opts).catch(error => { + logError(client._log, error, opts, 'TRY2 ') + }) + } + } else { + logError(client._log, error, opts) + } + + release() + }).then(async data => { + release() + return data + }) + } } -module.exports = function(authToken, clientId, clientSecret) { - return new Client(authToken, clientId, clientSecret); -}; +module.exports = function (authToken, clientId, clientSecret) { + return new Client(authToken, clientId, clientSecret) +} function logError(log, error, opts, prefix = '') { - try { - const msg = prefix + error.message; - const err = { - statusCode: error.statusCode, - request: opts - }; - const p0 = msg.indexOf('{'); - if (p0 > 0) { - err.message = JSON.parse(msg.slice(msg.indexOf('{'))); - } - else { - err.message = msg; - } - log.apiError(JSON.stringify(err, null, 2)); - throw error; - } - catch (e) { - log.error(e.message); - throw e; - } + try { + const msg = prefix + error.message + const err = { + statusCode: error.statusCode, + request: opts + } + const p0 = msg.indexOf('{') + if (p0 > 0) { + err.message = JSON.parse(msg.slice(msg.indexOf('{'))) + } else { + err.message = msg + } + + log.apiError(JSON.stringify(err, null, 2)) + throw error + } catch (error2) { + log.error(error2.message) + throw error2 + } } function refreshToken(url, clientId, clientSecret, refreshToken) { - //console.log(`refreshToken(${refreshToken})`); - let opts = { - url: url, - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Authorization': 'Basic ' + Buffer.from(`${clientId}:${clientSecret}`, 'ascii').toString('base64') - }, - body: `grant_type=refresh_token&client_id=${clientId}&client_secret=${clientSecret}&refresh_token=${refreshToken}` - }; - return rp(opts); -} \ No newline at end of file + // Console.log(`refreshToken(${refreshToken})`); + const opts = { + url, + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Authorization: 'Basic ' + Buffer.from(`${clientId}:${clientSecret}`, 'ascii').toString('base64') + }, + body: `grant_type=refresh_token&client_id=${clientId}&client_secret=${clientSecret}&refresh_token=${refreshToken}` + } + return rp(opts) +} diff --git a/lib/platform/deviceprofiles.js b/lib/platform/deviceprofiles.js index 589a84e..b18c88f 100644 --- a/lib/platform/deviceprofiles.js +++ b/lib/platform/deviceprofiles.js @@ -1,26 +1,25 @@ -'use strict'; +'use strict' -const Base = require('./base'); +const Base = require('./base') module.exports = class DeviceProfiles extends Base { + constructor(st) { + super(st) + } - constructor(st) { - super(st); - } + list(query = {max: 500}) { + return this.st.client.request('deviceprofiles', 'GET', null, null, query) + } - list(query = {max: 500}) { - return this.st.client.request('deviceprofiles', 'GET', null, null, query); - } + get(id) { + return this.st.client.request(`deviceprofiles/${id}`) + } - get(id) { - return this.st.client.request(`deviceprofiles/${id}`); - } + create(data) { + return this.st.client.request('deviceprofiles', 'POST', data) + } - create(data) { - return this.st.client.request('deviceprofiles', 'POST', data); - } - - update(id, data) { - return this.st.client.request(`deviceprofiles/${id}`, 'PUT', data); - } -}; \ No newline at end of file + update(id, data) { + return this.st.client.request(`deviceprofiles/${id}`, 'PUT', data) + } +} diff --git a/lib/platform/devices.js b/lib/platform/devices.js index ce20d06..4c2c330 100644 --- a/lib/platform/devices.js +++ b/lib/platform/devices.js @@ -1,133 +1,134 @@ -'use strict'; +'use strict' -const Base = require('./base'); +const Base = require('./base') module.exports = class Devices extends Base { - - constructor(st) { - super(st); - } - - create(params) { - let body = { - label: params.label, - locationId: this.st.locationId, - app: { - profileId: params.profileId, - installedAppId: this.st.installedAppId, - externalId: params.externalId ? params.externalId : 'undefined' - } - }; - return this.st.client.request('devices', 'POST', body); - } - - delete(deviceId) { - return this.st.client.request(`devices/${deviceId}`, 'DELETE'); - } - - get(deviceId) { - return this.st.client.request(`devices/${deviceId}`); - } - - update(deviceId, data) { - return this.st.client.request(`devices/${deviceId}`, 'PUT', data); - } - - sendEvents(deviceId, events) { - return this.st.client.request('devices/' + deviceId + '/events', 'POST', events); - } - - listInLocation() { - return this.st.client.request(`devices?locationId=${this.st.locationId}`); - } - - findByByCapability(capabilityId) { - return this.st.client.request(`devices?locationId=${this.st.locationId}&capability=${capabilityId}`); - } - - listAll() { - return this.st.client.request('devices'); - } - - sendCommand(item, capability, command, args) { - const deviceConfig = item.deviceConfig; - const body = { - commands: [ - { - component: deviceConfig.componentId, - capability: capability, - command: command, - arguments: args ? args : [] - } - ] - }; - return this.st.client.request(`devices/${deviceConfig.deviceId}/commands`, 'POST', body); - } - - sendCommands(items, capability, command, args) { - if (items) { - for (let it of items) { - this.sendCommand(it, capability, command, args); - } - } - } - - postCommands(deviceId, body) { - return this.st.client.request(`devices/${deviceId}/commands`, 'POST', body); - } - - getState(deviceId) { - return this.st.client.request(`devices/${deviceId}/status`); - } - - getComponentState(deviceId, componentId) { - return this.st.client.request(`devices/${deviceId}/components/${componentId}/status`); - } - - getCapabilityState(deviceId, componentId, capabilityId) { - return this.st.client.request(`devices/${deviceId}/components/${componentId}/capabilities/${capabilityId}/status`); - } - - getHealth(deviceId) { - return this.st.client.request(`devices/${deviceId}/health`).catch(() => { - return new Promise((c) => { - return c({state: 'UNKNOWN'}); - }); - }); - } - - getAttributeValue(deviceId, capability, attribute) { - return this.st.client.request(`devices/${deviceId}/components/main/capabilities/${capability}/status`, 'GET', null, (data) => { - return data[attribute].value; - }); - } - - namedColor(color, sat = 100) { - let hueColor = 0; - let saturation = sat; - switch(color) { - case 'Blue': - hueColor = 70; - break; - case 'Green': - hueColor = 39; - break; - case 'Yellow': - hueColor = 25; - break; - case 'Orange': - hueColor = 10; - break; - case 'Purple': - hueColor = 75; - break; - case 'Pink': - hueColor = 83; - break; - case 'Red': - hueColor = 100; - break; - } - return {hue: hueColor, saturation: saturation}; - } -}; + constructor(st) { + super(st) + } + + create(params) { + const body = { + label: params.label, + locationId: this.st.locationId, + app: { + profileId: params.profileId, + installedAppId: this.st.installedAppId, + externalId: params.externalId ? params.externalId : 'undefined' + } + } + return this.st.client.request('devices', 'POST', body) + } + + delete(deviceId) { + return this.st.client.request(`devices/${deviceId}`, 'DELETE') + } + + get(deviceId) { + return this.st.client.request(`devices/${deviceId}`) + } + + update(deviceId, data) { + return this.st.client.request(`devices/${deviceId}`, 'PUT', data) + } + + sendEvents(deviceId, events) { + return this.st.client.request('devices/' + deviceId + '/events', 'POST', events) + } + + listInLocation() { + return this.st.client.request(`devices?locationId=${this.st.locationId}`) + } + + findByByCapability(capabilityId) { + return this.st.client.request(`devices?locationId=${this.st.locationId}&capability=${capabilityId}`) + } + + listAll() { + return this.st.client.request('devices') + } + + sendCommand(item, capability, command, args) { + const {deviceConfig} = item + const body = { + commands: [ + { + component: deviceConfig.componentId, + capability, + command, + arguments: args ? args : [] + } + ] + } + return this.st.client.request(`devices/${deviceConfig.deviceId}/commands`, 'POST', body) + } + + sendCommands(items, capability, command, args) { + if (items) { + for (const it of items) { + this.sendCommand(it, capability, command, args) + } + } + } + + postCommands(deviceId, body) { + return this.st.client.request(`devices/${deviceId}/commands`, 'POST', body) + } + + getState(deviceId) { + return this.st.client.request(`devices/${deviceId}/status`) + } + + getComponentState(deviceId, componentId) { + return this.st.client.request(`devices/${deviceId}/components/${componentId}/status`) + } + + getCapabilityState(deviceId, componentId, capabilityId) { + return this.st.client.request(`devices/${deviceId}/components/${componentId}/capabilities/${capabilityId}/status`) + } + + getHealth(deviceId) { + return this.st.client.request(`devices/${deviceId}/health`).catch(() => { + // eslint-disable-next-line prefer-promise-reject-errors + return Promise.reject({state: 'UNKNOWN'}) + }) + } + + getAttributeValue(deviceId, capability, attribute) { + return this.st.client.request(`devices/${deviceId}/components/main/capabilities/${capability}/status`, 'GET', null, data => { + return data[attribute].value + }) + } + + namedColor(color, sat = 100) { + let hueColor = 0 + const saturation = sat + switch (color) { + case 'Blue': + hueColor = 70 + break + case 'Green': + hueColor = 39 + break + case 'Yellow': + hueColor = 25 + break + case 'Orange': + hueColor = 10 + break + case 'Purple': + hueColor = 75 + break + case 'Pink': + hueColor = 83 + break + case 'Red': + hueColor = 100 + break + default: + hueColor = 0 + } + + return {hue: hueColor, saturation} + } +} diff --git a/lib/platform/installedapps.js b/lib/platform/installedapps.js index b78b02f..fa61b5b 100644 --- a/lib/platform/installedapps.js +++ b/lib/platform/installedapps.js @@ -1,61 +1,61 @@ -'use strict'; +'use strict' -const Base = require('./base'); +const Base = require('./base') module.exports = class InstalledApps extends Base { - - constructor(st) { - super(st); - } - - list(locationId = null) { - const loc = locationId ? locationId : this.st.locationId; - return this.st.client.request('installedapps', null, {locationId: loc, max: 500}); - } - - get(id = null) { - const isa = id ? id : this.st.installedAppId; - return this.st.client.request(`installedapps/${isa}`); - } - - update(id, data) { - const isa = data ? (id ? id : this.st.installedAppId) : this.st.installedAppId; - const d = data ? data : id; - return this.st.client.request(`installedapps/${isa}`, 'PUT', d); - } - - getConfigs(id = null) { - const isa = id ? id : this.st.installedAppId; - return this.st.client.request(`installedapps/${isa}/configs`); - } - - getConfig(id = null, configId) { - const isa = configId ? (id ? id : this.st.installedAppId) : this.st.installedAppId; - const conf = configId ? configId : id; - return this.st.client.request(`installedapps/${isa}/configs/${conf}`); - } - - deleteInstalledApp(id = null) { - const isa = id ? id : this.st.installedAppId; - return this.st.client.request(`installedapps/${isa}`, 'DELETE'); - } - - listChildDevices() { - // TODO - someday this shouldn't be needed - const isa = this.st.installedSmartAppId; - return this.st.client.request(`devices?locationId=${this.st.locationId}`, (data) => { - let result = []; - if (data.items) { - for (let i = 0; i < data.items.length; i++) { - let it = data.items[i]; - if (it.app) { - if (it.app.installedAppId === isa) { - result.push(it); - } - } - } - } - return result; - }); - } -}; \ No newline at end of file + constructor(st) { + super(st) + } + + list(locationId = null) { + const loc = locationId ? locationId : this.st.locationId + return this.st.client.request('installedapps', null, {locationId: loc, max: 500}) + } + + get(id = null) { + const isa = id ? id : this.st.installedAppId + return this.st.client.request(`installedapps/${isa}`) + } + + update(id, data) { + const isa = data ? (id ? id : this.st.installedAppId) : this.st.installedAppId + const d = data ? data : id + return this.st.client.request(`installedapps/${isa}`, 'PUT', d) + } + + getConfigs(id = null) { + const isa = id ? id : this.st.installedAppId + return this.st.client.request(`installedapps/${isa}/configs`) + } + + getConfig(id = null, configId) { + const isa = configId ? (id ? id : this.st.installedAppId) : this.st.installedAppId + const conf = configId ? configId : id + return this.st.client.request(`installedapps/${isa}/configs/${conf}`) + } + + deleteInstalledApp(id = null) { + const isa = id ? id : this.st.installedAppId + return this.st.client.request(`installedapps/${isa}`, 'DELETE') + } + + listChildDevices() { + // TODO - someday this shouldn't be needed + const isa = this.st.installedSmartAppId + return this.st.client.request(`devices?locationId=${this.st.locationId}`, data => { + const result = [] + if (data.items) { + for (let i = 0; i < data.items.length; i++) { + const it = data.items[i] + if (it.app) { + if (it.app.installedAppId === isa) { + result.push(it) + } + } + } + } + + return result + }) + } +} diff --git a/lib/platform/locations.js b/lib/platform/locations.js index cf8a4c7..497cd52 100644 --- a/lib/platform/locations.js +++ b/lib/platform/locations.js @@ -1,31 +1,30 @@ -'use strict'; +'use strict' -const Base = require('./base'); +const Base = require('./base') module.exports = class Locations extends Base { - - constructor(st) { - super(st); - } - - list(query = {max: 500}) { - return this.st.client.request('locations', 'GET', null, null, query); - } - - get(id = null) { - const isa = id ? id : this.st.locationId; - return this.st.client.request(`locations/${isa}`); - } - - updateLocation(id, data) { - return this.st.client.request(`locations/${id}`, 'PUT', data); - } - - createLocation(data) { - return this.st.client.request('locations', 'POST', data); - } - - deleteLocation(id) { - return this.st.client.request(`locations/${id}`, 'DELETE'); - } -}; \ No newline at end of file + constructor(st) { + super(st) + } + + list(query = {max: 500}) { + return this.st.client.request('locations', 'GET', null, null, query) + } + + get(id = null) { + const isa = id ? id : this.st.locationId + return this.st.client.request(`locations/${isa}`) + } + + updateLocation(id, data) { + return this.st.client.request(`locations/${id}`, 'PUT', data) + } + + createLocation(data) { + return this.st.client.request('locations', 'POST', data) + } + + deleteLocation(id) { + return this.st.client.request(`locations/${id}`, 'DELETE') + } +} diff --git a/lib/platform/modes.js b/lib/platform/modes.js index 06c6626..9823914 100644 --- a/lib/platform/modes.js +++ b/lib/platform/modes.js @@ -1,22 +1,21 @@ -'use strict'; +'use strict' -const Base = require('./base'); +const Base = require('./base') module.exports = class Modes extends Base { + constructor(st) { + super(st) + } - constructor(st) { - super(st); - } + list(query = {max: 500}) { + return this.st.client.request(`locations/${this.st.locationId}/modes`, 'GET', null, null, query) + } - list(query = {max: 500}) { - return this.st.client.request(`locations/${this.st.locationId}/modes`, 'GET', null, null, query); - } + get(id) { + return this.st.client.request(`locations/${this.st.locationId}/modes/${id}`) + } - get(id) { - return this.st.client.request(`locations/${this.st.locationId}/modes/${id}`); - } - - update(id, data) { - return this.st.client.request(`locations/${this.st.locationId}/modes/${id}`, 'PUT', data); - } -}; \ No newline at end of file + update(id, data) { + return this.st.client.request(`locations/${this.st.locationId}/modes/${id}`, 'PUT', data) + } +} diff --git a/lib/platform/scenes.js b/lib/platform/scenes.js index 1525e86..3162c7e 100644 --- a/lib/platform/scenes.js +++ b/lib/platform/scenes.js @@ -1,28 +1,30 @@ -'use strict'; +'use strict' -const Base = require('./base'); +const Base = require('./base') module.exports = class Scenes extends Base { + constructor(st) { + super(st) + } - constructor(st) { - super(st); - } + list(query = {max: 500}) { + const qs = query + if (this.st.locationId) { + qs.locationid = this.st.locationId + } - list(query = {max: 500}) { - const qs = query; - if (this.st.locationId) { - qs.locationid = this.st.locationId; - } - return this.st.api.listRequest('scenes', 'GET', null, null, qs); - } + return this.st.api.listRequest('scenes', 'GET', null, null, qs) + } - getScene(id) { - return this.st.client.request('scenes', 'GET', null, ((data) => { - return data.items.find((it) => {return it.sceneId === id}) - })); - } + getScene(id) { + return this.st.client.request('scenes', 'GET', null, (data => { + return data.items.find(it => { + return it.sceneId === id + }) + })) + } - execute(id) { - return this.st.client.request(`scenes/${id}/execute`, 'POST'); - } -}; \ No newline at end of file + execute(id) { + return this.st.client.request(`scenes/${id}/execute`, 'POST') + } +} diff --git a/lib/platform/schedules.js b/lib/platform/schedules.js index fce40a5..fcff3e6 100644 --- a/lib/platform/schedules.js +++ b/lib/platform/schedules.js @@ -1,63 +1,59 @@ -'use strict'; +'use strict' -const Base = require('./base'); +const Base = require('./base') module.exports = class Schedules extends Base { - - constructor(st) { - super(st); - } - - schedule(name, cronExpression) { - let body = { - name: name, - cron: { - expression: cronExpression, - timezone: 'GMT' - } - }; - return this.st.client.request(`installedapps/${this.st.installedAppId}/schedules`, 'POST', body); - } - - // Accepts time setting, Date object, or ISO string - runDaily(name, time) { - let date; - if (Array.isArray(time) && time[0].stringConfig) { - date = new Date(time[0].stringConfig.value); - } - else if (time instanceof String) { - date = new Date(time); - } - else if (time instanceof Date) { - date = time; - } - else { - throw `Unvalid time format '${time}'`; - } - - // TODO - converting to UTC is not the right approach - const cron = `${date.getUTCMinutes()} ${date.getUTCHours()} * * ? *`; - return this.schedule(name, cron); - } - - runIn(name, delay) { - const time = Date.now() + (1000 * delay); - const body = { - name: name, - once: { - time: time, - overwrite: true - } - }; - return this.st.client.request(`installedapps/${this.st.installedAppId}/schedules`, 'POST', body); - } - - unschedule(name) { - const path = `installedapps/${this.st.installedAppId}/schedules/${name}`; - return this.st.client.request(path, 'DELETE'); - } - - unscheduleAll() { - return this.st.client.request(`installedapps/${this.st.installedAppId}/schedules`, 'DELETE'); - } -}; \ No newline at end of file + constructor(st) { + super(st) + } + + schedule(name, cronExpression) { + const body = { + name, + cron: { + expression: cronExpression, + timezone: 'GMT' + } + } + return this.st.client.request(`installedapps/${this.st.installedAppId}/schedules`, 'POST', body) + } + + // Accepts time setting, Date object, or ISO string + runDaily(name, time) { + let date + if (Array.isArray(time) && time[0].stringConfig) { + date = new Date(time[0].stringConfig.value) + } else if (time instanceof String) { + date = new Date(time) + } else if (time instanceof Date) { + date = time + } else { + throw new TypeError(`Invalid time format '${time}'`) + } + + // TODO - converting to UTC is not the right approach + const cron = `${date.getUTCMinutes()} ${date.getUTCHours()} * * ? *` + return this.schedule(name, cron) + } + + runIn(name, delay) { + const time = Date.now() + (1000 * delay) + const body = { + name, + once: { + time, + overwrite: true + } + } + return this.st.client.request(`installedapps/${this.st.installedAppId}/schedules`, 'POST', body) + } + + unschedule(name) { + const path = `installedapps/${this.st.installedAppId}/schedules/${name}` + return this.st.client.request(path, 'DELETE') + } + + unscheduleAll() { + return this.st.client.request(`installedapps/${this.st.installedAppId}/schedules`, 'DELETE') + } +} diff --git a/lib/platform/subscriptions.js b/lib/platform/subscriptions.js index 744982b..8f66945 100644 --- a/lib/platform/subscriptions.js +++ b/lib/platform/subscriptions.js @@ -1,156 +1,155 @@ -'use strict'; -const Base = require('./base'); +'use strict' +const Base = require('./base') module.exports = class Subscriptions extends Base { - - constructor(st) { - super(st); - } - - list() { - return this.st.client.listRequest(`installedapps/${this.st.installedAppId}/subscriptions`); - } - - get(name) { - return this.st.client.request(`installedapps/${this.st.installedAppId}/subscriptions/${name}`); - } - - update(name, data) { - return this.st.client.request(`installedapps/${this.st.installedAppId}/subscriptions`, 'PUT', data); - } - - unsubscribe(name) { - return this.st.client.request(`installedapps/${this.st.installedAppId}/subscriptions/${name}`, 'DELETE'); - } - - unsubscribeAll() { - return this.st.client.request(`installedapps/${this.st.installedAppId}/subscriptions/`, 'DELETE'); - } - - subscribeToDevices(devices, capability, attribute, subscriptionName, options={}) { - if (devices) { - const segs = attribute.split('.'); - const attributeName = segs[0]; - const attributeValue = segs.length > 1 ? segs[1] : '*'; - const path = `installedapps/${this.st.installedAppId}/subscriptions`; - devices.forEach((device, index) => { - let body = { - sourceType: 'DEVICE', - device: { - componentId: device.deviceConfig.componentId, - deviceId: device.deviceConfig.deviceId, - capability: capability, - attribute: attributeName, - stateChangeOnly: options.stateChangeOnly ? options.stateChangeOnly : true, - subscriptionName: `${subscriptionName}_${index}`, - value: attributeValue - } - }; - if (options.modes) { - body.capability.modes = options.modes; - } - return this.st.client.request(path, 'POST', body); - }); - } - } - - subscribeToCapability(capability, attribute, subscriptionName, options={}) { - const segs = attribute.split('.'); - const attributeName = segs[0]; - const attributeValue = segs.length > 1 ? segs[1] : '*'; - const path = `installedapps/${this.st.installedAppId}/subscriptions`; - const body = { - sourceType: 'CAPABILITY', - capability: { - locationId: this.st.locationId, - capability: capability, - attribute: attributeName, - stateChangeOnly: options.stateChangeOnly ? options.stateChangeOnly : true, - subscriptionName: subscriptionName, - value: attributeValue - } - }; - if (options.modes) { - body.capability.modes = options.modes; - } - return this.st.client.request(path, 'POST', body); - } - - // TODO there's no handler name! - subscribeToModeChange(subscriptionName) { - const path = `installedapps/${this.st.installedAppId}/subscriptions`; - const body = { - sourceType: 'MODE', - mode: { - locationId: this.st.locationId - } - }; - return this.st.client.request(path, 'POST', body); - } - - // TODO -- need this for entire location - subscribeToDeviceLifecycle(devices, subscriptionName) { - const path = `installedapps/${this.st.installedAppId}/subscriptions`; - const body = { - sourceType: 'DEVICE_LIFECYCLE', - deviceLifecycle: { - deviceIds : devices.map(it => it.deviceConfig.deviceId), - locationId: this.st.locationId, - subscriptionName: subscriptionName, - } - }; - return this.st.client.request(path, 'POST', body); - } - - // Why does this accept an array of device IDs but devices does not - subscribeToDeviceHealth(devices, subscriptionName) { - const path = `installedapps/${this.st.installedAppId}/subscriptions`; - const body = { - sourceType: 'DEVICE_HEALTH', - deviceHealth: { - deviceIds : devices.map(it => it.deviceConfig.deviceId), - locationId: this.st.locationId, - subscriptionName: subscriptionName, - } - }; - return this.st.client.request(path, 'POST', body); - } - - subscribeToSecuritySystem(subscriptionName) { - const path = `installedapps/${this.st.installedAppId}/subscriptions`; - const body = { - sourceType: 'SECURITY_ARM_STATE', - securityArmState: { - locationId: this.st.locationId, - subscriptionName: subscriptionName - } - }; - return this.st.client.request(path, 'POST', body); - } - - - subscribeToHubHealth(subscriptionName) { - const path = `installedapps/${this.st.installedAppId}/subscriptions`; - const body = { - sourceType: 'HUB_HEALTH', - hubHealth: { - locationId: this.st.locationId, - subscriptionName: subscriptionName - } - }; - return this.st.client.request(path, 'POST', body); - } - - subscribeToSceneLifecycle(subscriptionName) { - const path = `installedapps/${this.st.installedAppId}/subscriptions`; - const body = { - sourceType: 'SCENE_LIFECYCLE', - sceneLifecycle: { - locationId: this.st.locationId, - subscriptionName: subscriptionName - } - }; - return this.st.client.request(path, 'POST', body); - } - -}; \ No newline at end of file + constructor(st) { + super(st) + } + + list() { + return this.st.client.listRequest(`installedapps/${this.st.installedAppId}/subscriptions`) + } + + get(name) { + return this.st.client.request(`installedapps/${this.st.installedAppId}/subscriptions/${name}`) + } + + update(name, data) { + return this.st.client.request(`installedapps/${this.st.installedAppId}/subscriptions`, 'PUT', data) + } + + unsubscribe(name) { + return this.st.client.request(`installedapps/${this.st.installedAppId}/subscriptions/${name}`, 'DELETE') + } + + unsubscribeAll() { + return this.st.client.request(`installedapps/${this.st.installedAppId}/subscriptions/`, 'DELETE') + } + + subscribeToDevices(devices, capability, attribute, subscriptionName, options = {}) { + if (devices) { + const segs = attribute.split('.') + const attributeName = segs[0] + const attributeValue = segs.length > 1 ? segs[1] : '*' + const path = `installedapps/${this.st.installedAppId}/subscriptions` + devices.forEach((device, index) => { + const body = { + sourceType: 'DEVICE', + device: { + componentId: device.deviceConfig.componentId, + deviceId: device.deviceConfig.deviceId, + capability, + attribute: attributeName, + stateChangeOnly: options.stateChangeOnly ? options.stateChangeOnly : true, + subscriptionName: `${subscriptionName}_${index}`, + value: attributeValue + } + } + if (options.modes) { + body.capability.modes = options.modes + } + + return this.st.client.request(path, 'POST', body) + }) + } + } + + subscribeToCapability(capability, attribute, subscriptionName, options = {}) { + const segs = attribute.split('.') + const attributeName = segs[0] + const attributeValue = segs.length > 1 ? segs[1] : '*' + const path = `installedapps/${this.st.installedAppId}/subscriptions` + const body = { + sourceType: 'CAPABILITY', + capability: { + locationId: this.st.locationId, + capability, + attribute: attributeName, + stateChangeOnly: options.stateChangeOnly ? options.stateChangeOnly : true, + subscriptionName, + value: attributeValue + } + } + if (options.modes) { + body.capability.modes = options.modes + } + + return this.st.client.request(path, 'POST', body) + } + + // TODO there's no handler name! + subscribeToModeChange(_) { + const path = `installedapps/${this.st.installedAppId}/subscriptions` + const body = { + sourceType: 'MODE', + mode: { + locationId: this.st.locationId + } + } + return this.st.client.request(path, 'POST', body) + } + + // TODO -- need this for entire location + subscribeToDeviceLifecycle(devices, subscriptionName) { + const path = `installedapps/${this.st.installedAppId}/subscriptions` + const body = { + sourceType: 'DEVICE_LIFECYCLE', + deviceLifecycle: { + deviceIds: devices.map(it => it.deviceConfig.deviceId), + locationId: this.st.locationId, + subscriptionName + } + } + return this.st.client.request(path, 'POST', body) + } + + // Why does this accept an array of device IDs but devices does not + subscribeToDeviceHealth(devices, subscriptionName) { + const path = `installedapps/${this.st.installedAppId}/subscriptions` + const body = { + sourceType: 'DEVICE_HEALTH', + deviceHealth: { + deviceIds: devices.map(it => it.deviceConfig.deviceId), + locationId: this.st.locationId, + subscriptionName + } + } + return this.st.client.request(path, 'POST', body) + } + + subscribeToSecuritySystem(subscriptionName) { + const path = `installedapps/${this.st.installedAppId}/subscriptions` + const body = { + sourceType: 'SECURITY_ARM_STATE', + securityArmState: { + locationId: this.st.locationId, + subscriptionName + } + } + return this.st.client.request(path, 'POST', body) + } + + subscribeToHubHealth(subscriptionName) { + const path = `installedapps/${this.st.installedAppId}/subscriptions` + const body = { + sourceType: 'HUB_HEALTH', + hubHealth: { + locationId: this.st.locationId, + subscriptionName + } + } + return this.st.client.request(path, 'POST', body) + } + + subscribeToSceneLifecycle(subscriptionName) { + const path = `installedapps/${this.st.installedAppId}/subscriptions` + const body = { + sourceType: 'SCENE_LIFECYCLE', + sceneLifecycle: { + locationId: this.st.locationId, + subscriptionName + } + } + return this.st.client.request(path, 'POST', body) + } +} diff --git a/lib/smart-app.js b/lib/smart-app.js index 8e666cb..10eb6f7 100644 --- a/lib/smart-app.js +++ b/lib/smart-app.js @@ -1,447 +1,446 @@ -'use strict'; +'use strict' -const responders = require('./util/responders'); -const signature = require('./util/signature'); -const i18n = require('i18n'); -const fs = require('fs-extra'); -const Mutex = require('async-mutex').Mutex; +const i18n = require('i18n') +const fs = require('fs-extra') +const {Mutex} = require('async-mutex') +const signature = require('./util/signature') +const responders = require('./util/responders') -const Page = require('./pages/page'); -const EndpointContext = require('./util/endpoint-context'); -const Log = require('./util/log'); +const Page = require('./pages/page') +const EndpointContext = require('./util/endpoint-context') +const Log = require('./util/log') module.exports = class SmartApp { - - constructor(options = {}) { - this._clientId = options.clientId; - this._clientSecret = options.clientSecret; - this._id = options.appId; - this._log = new Log(options.logger, options.jsonSpace, options.enableEventLogging); - this._permissions = options.permissions ? options.permissions : []; - this._disableCustomDisplayName = options.disableCustomDisplayName === undefined ? false : options.disableCustomDisplayName; - this._disableRemoveApp = options.disableRemoveApp === undefined ? false : options.disableRemoveApp; - this._subscribedEventHandlers = {}; - this._scheduledEventHandlers = {}; - this._pages = {}; - this._installedHandler = ((ctx, evt) => { this._updatedHandler(ctx, evt.installData); }); - this._updatedHandler = (() => { }); - this._uninstalledHandler = (() => {}); - this._oauthHandler = (() => {}); - this._deviceCommandHandler = null; - this._defaultDeviceCommandHandler = ((ctx, deviceId, cmd) => { - this._log.warn(`No command handler for ${JSON.stringify(cmd)} of device ${deviceId}`); - }); - this._deviceCommands = {}; - this._executeHandler = (() => {}); - this._localizationEnabled = false; - this._apiUrl = options.apiUrl ? options.apiUrl : 'https://api.smartthings.com'; - this._refreshUrl = options.refreshUrl ? options.refreshUrl : 'https://auth-global.api.smartthings.com/oauth/token'; - - if (options.publicKey) { - signature.setPublicKey(options.publicKey); - } - - } - - //////////////////////////////// - // App Initialization Options // - //////////////////////////////// - - appId(value) { - this._id = value; - return this; - } - - apiUrl(value) { - this._apiUrl = value; - return this; - } - - refreshUrl(value) { - this._refreshUrl = value; - return this; - } - - clientId(value) { - this._clientId = value; - return this; - } - - clientSecret(value) { - this._clientSecret = value; - return this; - } - - publicKey(key) { - signature.setPublicKey(key); - return this; - } - - configureLogger(logger, jsonSpace=null, enableEvents = false) { - this._log = new Log(logger, jsonSpace, enableEvents); - } - - enableEventLogging(jsonSpace=null, enableEvents = true) { - this._log.enableEvents(jsonSpace, enableEvents); - return this; - } - - permissions(value) { - this._permissions = value; - return this; - } - - disableCustomDisplayName(value = true) { - this._disableCustomDisplayName = value; - return this; - } - - disableRemoveApp(value = true) { - this._disableRemoveApp = value; - return this; - } - - contextStore(value) { - this._contextStore = value; - return this; - } - - - ////////////////////////////// - // Configuration/Initialize // - ////////////////////////////// - - configureI18n(options = {directory: './locales'}) { - if (options.updateFiles === undefined || options.updateFiles === true) { - fs.ensureDirSync(options.directory); - } - i18n.configure(options); - this._localizationEnabled = true; - return this; - } - - firstPageId(value) { - this._firstPageId = value; - return this; - } - - - //////////////////////// - // Configuration/Page // - //////////////////////// - - page(id, callback) { - if (!this._firstPageId) { - this._firstPageId = id; - } - this._pages[id] = callback; - return this; - } - - - ///////////// - // Install // - ///////////// - - installed(callback) { - this._installedHandler = callback; - return this; - } - - - ///////////// - // Update // - ///////////// - - updated(callback) { - this._updatedHandler = callback; - return this; - } - - - /////////////// - // Uninstall // - /////////////// - - uninstalled(callback) { - this._installedHandler = callback; - return this; - } - - - ///////////// - // Events // - ///////////// - - subscribedEventHandler(name, callback) { - this._subscribedEventHandlers[name] = callback; - return this; - } - - scheduledEventHandler(name, callback) { - this._scheduledEventHandlers[name] = callback; - return this; - } - - deviceCommandHandler(callback) { - this._deviceCommandHandler = callback; - return this; - } - - defaultDeviceCommandHandler(callback) { - this._defaultDeviceCommandHandler = callback; - return this; - } - - deviceCommand(command, callback) { - this._deviceCommands[command] = callback; - return this; - } - - - //////////////// - // Utilities // - //////////////// - - translate(...args) { - if (this._localizationEnabled) { - return this.__(...args); - } - else { - return args[0]; - } - } - - handleLambdaCallback(event, context, callback) { - this._handleCallback(event, responders.lambdaResponse(callback, this._log)); - } - - handleHttpCallback(request, response) { - if (request.body && request.body.lifecycle === 'PING' || signature.signatureIsVerified(request)) { - this._handleCallback(request.body, responders.httpResponder(response, this._log)); - } else { - this._log.error('Unauthorized'); - response.status(401).send('Forbidden'); - } - } - - handleHttpCallbackUnverified(request, response) { - this._handleCallback(request.body, responders.httpResponder(response, this._log)); - } - - handleMockCallback(body) { - const responder = responders.mockResponder(this._log); - this._handleCallback(body, responder); - return responder.response; - } - - - ////////////////////////////////////////////////////////////// - // Proactive API calls (not in response to lifecycle events // - ////////////////////////////////////////////////////////////// - - withContext(installedAppId) { - const app = this; - if (this._contextStore) { - return new Promise((resolve, reject) => { - this._contextStore.get(installedAppId).then((data) => { - resolve(new EndpointContext(app, data, new Mutex())); - }).catch((err) => { - reject(err); - }); - }); - } - else { - return Promise.reject(new Error('Context not available. No context store defined')); - } - } - - - //////////////////////// - // Event Dispatching // - //////////////////////// - - _handleCallback(evt, responder) { - try { - const context = new EndpointContext(this, evt); - - switch (evt.lifecycle) { - - case 'PING': { - this._log.event(evt); - responder.respond({statusCode: 200, pingData: {challenge: evt.pingData.challenge}}); - break; - } - - case 'CONFIGURATION': { - const configurationData = evt.configurationData; - switch (configurationData.phase) { - case 'INITIALIZE': { - this._log.event(evt, configurationData.phase); - responder.respond({ - statusCode: 200, configurationData: { - initialize: { - id: this._id, - firstPageId: this._firstPageId, - permissions: this._permissions, - disableCustomDisplayName: this._disableCustomDisplayName, - disableRemoveApp: this._disableRemoveApp - } - } - }); - break; - } - - case 'PAGE': { - this._log.event(evt, configurationData.phase); - const pageId = configurationData.pageId ? configurationData.pageId : this._firstPageId; - const pageHandler = this._pages[pageId]; - if (pageHandler) { - const page = this._localizationEnabled ? new Page(pageId, context.locale) : new Page(pageId); - pageHandler(context, page, configurationData); - responder.respond({statusCode: 200, configurationData: {page: page.toJson()}}); - } - else { - throw new Error(`Page '${configurationData.pageId}' not found`); - } - break; - } - - default: - throw new Error(`Unsupported config phase: ${configurationData.phase}`); - } - break; - } - - case 'OAUTH_CALLBACK': { - this._log.event(evt); - this._oauthHandler(context, evt.oauthData); - responder.respond({statusCode: 200, oAuthCallbackData: {}}); - break; - } - - case 'INSTALL': { - this._log.event(evt); - this._installedHandler(context, evt.installData); - if (this._contextStore) { - this._contextStore.put({ - installedAppId: context.installedAppId, - locationId: context.locationId, - authToken: context.api.client.authToken, - refreshToken: context.api.client.refreshToken, - clientId: this._clientId, - clientSecret: this._clientSecret, - config: context.config - }); - } - responder.respond({statusCode: 200, installData: {}}); - break; - } - - case 'UPDATE': { - this._log.event(evt); - this._updatedHandler(context, evt.updateData); - if (this._contextStore) { - this._contextStore.put({ - installedAppId: context.installedAppId, - locationId: context.locationId, - authToken: context.api.client.authToken, - refreshToken: context.api.client.refreshToken, - clientId: this._clientId, - clientSecret: this._clientSecret, - config: context.config - }); - } - responder.respond({statusCode: 200, updateData: {}}); - break; - } - - case 'UNINSTALL': { - this._log.event(evt); - this._uninstalledHandler(context, evt.uninstallData); - if (this._contextStore) { - this._contextStore.delete(context.installedAppId); - } - responder.respond({statusCode: 200, uninstallData: {}}); - break; - } - - case 'EVENT': { - this._log.event(evt); - for (let event of evt.eventData.events) { - switch (event.eventType) { - case 'DEVICE_EVENT': { - const handlerName = event.deviceEvent.subscriptionName.split('_')[0]; - const handler = this._subscribedEventHandlers[handlerName]; - handler(context, event.deviceEvent); - break; - } - case 'TIMER_EVENT': { - const handlerName = event.timerEvent.name; - const handler = this._scheduledEventHandlers[handlerName]; - handler(context, event.timerEvent); - break; - } - case 'DEVICE_COMMANDS_EVENT': { - if (this._deviceCommandHandler) { - this._deviceCommandHandler(context, event.deviceCommandsEvent); - } - else { - const deviceCommandsEvent = event.deviceCommandsEvent; - for (let cmd of deviceCommandsEvent.commands) { - const compKey = `${cmd.componentId}/${cmd.capability}/${cmd.command}`; - const capKey = `${cmd.capability}/${cmd.command}`; - let handler = this._deviceCommands[compKey]; - if (!handler) { - handler = this._deviceCommands[capKey]; - } - if (handler) { - handler(context, deviceCommandsEvent.deviceId, cmd, deviceCommandsEvent); - } - else { - this._defaultDeviceCommandHandler(context, deviceCommandsEvent.deviceId, cmd); - } - } - } - break; - } - case 'MODE_EVENT': { - // TODO - there is no mode handler name!!! - const handlerName = 'modeChangeHandler'; - const handler = this._subscribedEventHandlers[handlerName]; - handler(context, event.modeEvent); - break; - } - case 'SECURITY_ARM_STATE_EVENT': { - // TODO - name specified but not returned!!! - //const handlerName = event.securityArmStateEvent.name; - const handlerName = 'securityArmStateHandler'; - const handler = this._subscribedEventHandlers[handlerName]; - handler(context, event.securityArmStateEvent); - break; - } - default: { - this._log.warn(`Unhandled event of type ${event.eventType}`); - } - } - } - responder.respond({statusCode: 200, eventData: {}}); - break; - } - - case 'EXECUTE': { - this._log.event(evt); - this._executeHandler(context, evt.executeData); - break; - } - - default: { - this._log.warn(`Lifecycle ${evt.lifecycle} not supported`); - } - } - } - catch (e) { - this._log.error(e.message ? e.message : e.toString()); - } - } -}; + constructor(options = {}) { + this._clientId = options.clientId + this._clientSecret = options.clientSecret + this._id = options.appId + this._log = new Log(options.logger, options.jsonSpace, options.enableEventLogging) + this._permissions = options.permissions ? options.permissions : [] + this._disableCustomDisplayName = options.disableCustomDisplayName === undefined ? false : options.disableCustomDisplayName + this._disableRemoveApp = options.disableRemoveApp === undefined ? false : options.disableRemoveApp + this._subscribedEventHandlers = {} + this._scheduledEventHandlers = {} + this._pages = {} + this._installedHandler = ((ctx, evt) => { + this._updatedHandler(ctx, evt.installData) + }) + this._updatedHandler = (() => { }) + this._uninstalledHandler = (() => {}) + this._oauthHandler = (() => {}) + this._deviceCommandHandler = null + this._defaultDeviceCommandHandler = ((ctx, deviceId, cmd) => { + this._log.warn(`No command handler for ${JSON.stringify(cmd)} of device ${deviceId}`) + }) + this._deviceCommands = {} + this._executeHandler = (() => {}) + this._localizationEnabled = false + this._apiUrl = options.apiUrl ? options.apiUrl : 'https://api.smartthings.com' + this._refreshUrl = options.refreshUrl ? options.refreshUrl : 'https://auth-global.api.smartthings.com/oauth/token' + + if (options.publicKey) { + signature.setPublicKey(options.publicKey) + } + } + + /// ///////////////////////////// + // App Initialization Options // + /// ///////////////////////////// + + appId(value) { + this._id = value + return this + } + + apiUrl(value) { + this._apiUrl = value + return this + } + + refreshUrl(value) { + this._refreshUrl = value + return this + } + + clientId(value) { + this._clientId = value + return this + } + + clientSecret(value) { + this._clientSecret = value + return this + } + + publicKey(key) { + signature.setPublicKey(key) + return this + } + + configureLogger(logger, jsonSpace = null, enableEvents = false) { + this._log = new Log(logger, jsonSpace, enableEvents) + } + + enableEventLogging(jsonSpace = null, enableEvents = true) { + this._log.enableEvents(jsonSpace, enableEvents) + return this + } + + permissions(value) { + this._permissions = value + return this + } + + disableCustomDisplayName(value = true) { + this._disableCustomDisplayName = value + return this + } + + disableRemoveApp(value = true) { + this._disableRemoveApp = value + return this + } + + contextStore(value) { + this._contextStore = value + return this + } + + /// /////////////////////////// + // Configuration/Initialize // + /// /////////////////////////// + + configureI18n(options = {directory: './locales'}) { + if (options.updateFiles === undefined || options.updateFiles === true) { + fs.ensureDirSync(options.directory) + } + + i18n.configure(options) + this._localizationEnabled = true + return this + } + + firstPageId(value) { + this._firstPageId = value + return this + } + + /// ///////////////////// + // Configuration/Page // + /// ///////////////////// + + page(id, callback) { + if (!this._firstPageId) { + this._firstPageId = id + } + + this._pages[id] = callback + return this + } + + /// ////////// + // Install // + /// ////////// + + installed(callback) { + this._installedHandler = callback + return this + } + + /// ////////// + // Update // + /// ////////// + + updated(callback) { + this._updatedHandler = callback + return this + } + + /// //////////// + // Uninstall // + /// //////////// + + uninstalled(callback) { + this._installedHandler = callback + return this + } + + /// ////////// + // Events // + /// ////////// + + subscribedEventHandler(name, callback) { + this._subscribedEventHandlers[name] = callback + return this + } + + scheduledEventHandler(name, callback) { + this._scheduledEventHandlers[name] = callback + return this + } + + deviceCommandHandler(callback) { + this._deviceCommandHandler = callback + return this + } + + defaultDeviceCommandHandler(callback) { + this._defaultDeviceCommandHandler = callback + return this + } + + deviceCommand(command, callback) { + this._deviceCommands[command] = callback + return this + } + + /// ///////////// + // Utilities // + /// ///////////// + + translate(...args) { + if (this._localizationEnabled) { + return this.__(...args) + } + + return args[0] + } + + handleLambdaCallback(event, context, callback) { + this._handleCallback(event, responders.lambdaResponse(callback, this._log)) + } + + handleHttpCallback(request, response) { + if (request.body && (request.body.lifecycle === 'PING' || signature.signatureIsVerified(request))) { + this._handleCallback(request.body, responders.httpResponder(response, this._log)) + } else { + this._log.error('Unauthorized') + response.status(401).send('Forbidden') + } + } + + handleHttpCallbackUnverified(request, response) { + this._handleCallback(request.body, responders.httpResponder(response, this._log)) + } + + handleMockCallback(body) { + const responder = responders.mockResponder(this._log) + this._handleCallback(body, responder) + return responder.response + } + + /// /////////////////////////////////////////////////////////// + // Proactive API calls (not in response to lifecycle events // + /// /////////////////////////////////////////////////////////// + + withContext(installedAppId) { + const app = this + if (this._contextStore) { + return new Promise((resolve, reject) => { + this._contextStore.get(installedAppId).then(data => { + resolve(new EndpointContext(app, data, new Mutex())) + }).catch(error => { + reject(error) + }) + }) + } + + return Promise.reject(new Error('Context not available. No context store defined')) + } + + /// ///////////////////// + // Event Dispatching // + /// ///////////////////// + + _handleCallback(evt, responder) { + try { + const context = new EndpointContext(this, evt) + + switch (evt.lifecycle) { + case 'PING': { + this._log.event(evt) + responder.respond({statusCode: 200, pingData: {challenge: evt.pingData.challenge}}) + break + } + + case 'CONFIGURATION': { + const {configurationData} = evt + switch (configurationData.phase) { + case 'INITIALIZE': { + this._log.event(evt, configurationData.phase) + responder.respond({ + statusCode: 200, configurationData: { + initialize: { + id: this._id, + firstPageId: this._firstPageId, + permissions: this._permissions, + disableCustomDisplayName: this._disableCustomDisplayName, + disableRemoveApp: this._disableRemoveApp + } + } + }) + break + } + + case 'PAGE': { + this._log.event(evt, configurationData.phase) + const pageId = configurationData.pageId ? configurationData.pageId : this._firstPageId + const pageHandler = this._pages[pageId] + if (pageHandler) { + const page = this._localizationEnabled ? new Page(pageId, context.locale) : new Page(pageId) + pageHandler(context, page, configurationData) + responder.respond({statusCode: 200, configurationData: {page: page.toJson()}}) + } else { + throw new Error(`Page '${configurationData.pageId}' not found`) + } + + break + } + + default: + throw new Error(`Unsupported config phase: ${configurationData.phase}`) + } + + break + } + + case 'OAUTH_CALLBACK': { + this._log.event(evt) + this._oauthHandler(context, evt.oauthData) + responder.respond({statusCode: 200, oAuthCallbackData: {}}) + break + } + + case 'INSTALL': { + this._log.event(evt) + this._installedHandler(context, evt.installData) + if (this._contextStore) { + this._contextStore.put({ + installedAppId: context.installedAppId, + locationId: context.locationId, + authToken: context.api.client.authToken, + refreshToken: context.api.client.refreshToken, + clientId: this._clientId, + clientSecret: this._clientSecret, + config: context.config + }) + } + + responder.respond({statusCode: 200, installData: {}}) + break + } + + case 'UPDATE': { + this._log.event(evt) + this._updatedHandler(context, evt.updateData) + if (this._contextStore) { + this._contextStore.put({ + installedAppId: context.installedAppId, + locationId: context.locationId, + authToken: context.api.client.authToken, + refreshToken: context.api.client.refreshToken, + clientId: this._clientId, + clientSecret: this._clientSecret, + config: context.config + }) + } + + responder.respond({statusCode: 200, updateData: {}}) + break + } + + case 'UNINSTALL': { + this._log.event(evt) + this._uninstalledHandler(context, evt.uninstallData) + if (this._contextStore) { + this._contextStore.delete(context.installedAppId) + } + + responder.respond({statusCode: 200, uninstallData: {}}) + break + } + + case 'EVENT': { + this._log.event(evt) + for (const event of evt.eventData.events) { + switch (event.eventType) { + case 'DEVICE_EVENT': { + const handlerName = event.deviceEvent.subscriptionName.split('_')[0] + const handler = this._subscribedEventHandlers[handlerName] + handler(context, event.deviceEvent) + break + } + + case 'TIMER_EVENT': { + const handlerName = event.timerEvent.name + const handler = this._scheduledEventHandlers[handlerName] + handler(context, event.timerEvent) + break + } + + case 'DEVICE_COMMANDS_EVENT': { + if (this._deviceCommandHandler) { + this._deviceCommandHandler(context, event.deviceCommandsEvent) + } else { + const {deviceCommandsEvent} = event + for (const cmd of deviceCommandsEvent.commands) { + const compKey = `${cmd.componentId}/${cmd.capability}/${cmd.command}` + const capKey = `${cmd.capability}/${cmd.command}` + let handler = this._deviceCommands[compKey] + if (!handler) { + handler = this._deviceCommands[capKey] + } + + if (handler) { + handler(context, deviceCommandsEvent.deviceId, cmd, deviceCommandsEvent) + } else { + this._defaultDeviceCommandHandler(context, deviceCommandsEvent.deviceId, cmd) + } + } + } + + break + } + + case 'MODE_EVENT': { + // TODO - there is no mode handler name!!! + const handlerName = 'modeChangeHandler' + const handler = this._subscribedEventHandlers[handlerName] + handler(context, event.modeEvent) + break + } + + case 'SECURITY_ARM_STATE_EVENT': { + // TODO - name specified but not returned!!! + // const handlerName = event.securityArmStateEvent.name; + const handlerName = 'securityArmStateHandler' + const handler = this._subscribedEventHandlers[handlerName] + handler(context, event.securityArmStateEvent) + break + } + + default: { + this._log.warn(`Unhandled event of type ${event.eventType}`) + } + } + } + + responder.respond({statusCode: 200, eventData: {}}) + break + } + + case 'EXECUTE': { + this._log.event(evt) + this._executeHandler(context, evt.executeData) + break + } + + default: { + this._log.warn(`Lifecycle ${evt.lifecycle} not supported`) + } + } + } catch (error) { + this._log.error(error.message ? error.message : error.toString()) + } + } +} diff --git a/lib/util/endpoint-context.js b/lib/util/endpoint-context.js index 5ef206e..32fbe02 100644 --- a/lib/util/endpoint-context.js +++ b/lib/util/endpoint-context.js @@ -1,160 +1,159 @@ -'use strict'; +'use strict' -const i18n = require('i18n'); -const SmartThingsApi = require('../api'); +const i18n = require('i18n') +const SmartThingsApi = require('../api') module.exports = class EndpointContext { - - constructor(app, evt, apiMutex) { - this.event = evt; - - let authToken; - let refreshToken; - switch (evt.lifecycle) { - case 'EVENT': - authToken = evt.eventData.authToken; - this.installedAppId = evt.eventData.installedApp.installedAppId; - this.locationId = evt.eventData.installedApp.locationId; - this.config = evt.eventData.installedApp.config; - this.locale = evt.locale; - break; - - case 'INSTALL': - authToken = evt.installData.authToken; - refreshToken = evt.installData.refreshToken; - this.installedAppId = evt.installData.installedApp.installedAppId; - this.locationId = evt.installData.installedApp.locationId; - this.config = evt.installData.installedApp.config; - this.locale = evt.client.language; - break; - - case 'UPDATE': - authToken = evt.updateData.authToken; - refreshToken = evt.updateData.refreshToken; - this.installedAppId = evt.updateData.installedApp.installedAppId; - this.locationId = evt.updateData.installedApp.locationId; - this.config = evt.updateData.installedApp.config; - this.locale = evt.client.language; - break; - - case 'CONFIGURATION': - this.installedAppId = evt.configurationData.installedAppId; - this.locationId = evt.configurationData.locationId; - this.config = evt.configurationData.config; - this.locale = evt.client.language; - break; - - case 'UNINSTALL': - this.installedAppId = evt.uninstallData.installedApp.installedAppId; - this.locationId = evt.uninstallData.installedApp.locationId; - break; - - case 'EXECUTE': - authToken = evt.executeData.authToken; - this.installedAppId = evt.executeData.installedApp.installedAppId; - this.locationId = evt.executeData.installedApp.locationId; - this.config = evt.executeData.installedApp.config; - this.locale = evt.executeData.parameters.locale; - break; - - // For constructing context for proactive API calls not in response to a lifecycle event - default: - authToken = evt.authToken; - refreshToken = evt.refreshToken; - this.installedAppId = evt.installedAppId; - this.locationId = evt.locationId; - this.config = evt.config; - this.locale = evt.locale; - break; - } - - if (app._localizationEnabled) { - if (this.locale) { - this.headers = {'accept-language': this.locale}; - i18n.init(this); - } - } - - this.api = new SmartThingsApi({ - authToken: authToken, - refreshToken: refreshToken, - clientId: app._clientId, - clientSecret: app._clientSecret, - log: app._log, - apiUrl: app._apiUrl, - refreshUrl: app._refreshUrl, - locationId: this.locationId, - installedAppId: this.installedAppId, - contextStore: app._contextStore, - apiMutex: apiMutex - }); - } - - configStringValue(name) { - const entry = this.config[name]; - return entry ? entry[0].stringConfig.value : null; - } - - configBooleanValue(name) { - const entry = this.config[name]; - return entry ? entry[0].stringConfig.value === 'true' : false; - } - - configNumberValue(name) { - const entry = this.config[name]; - return entry ? Number(entry[0].stringConfig.value) : null; - } - - configDateValue(name) { - const entry = this.config[name]; - return entry ? new Date(entry[0].stringConfig.value) : null; - } - - configTimeString(name) { - return this.configDateValue(name).toLocaleTimeString(this.locale, {hour: '2-digit', minute:'2-digit'}); - } - - configModeIds(name) { - const entry = this.config[name]; - return entry.map(it => it.modeConfig.modeId); - } - - configDevices(name) { - let list = []; - this.config[name].forEach((item) => { - const componentId = item.deviceConfig.componentId; - const promise = this.api.devices.get(item.deviceConfig.deviceId).then((device) => { - return { - deviceId: device.deviceId, - name: device.name, - label: device.label, - componentId: componentId - }; - }); - list.push(promise); - }); - return Promise.all(list); - } - - configDevicesWithState(name) { - let list = []; - this.config[name].forEach((item) => { - const componentId = item.deviceConfig.componentId; - const promise = this.api.devices.get(item.deviceConfig.deviceId).then((device) => { - return { - deviceId: device.deviceId, - name: device.name, - label: device.label, - componentId: componentId - }; - }).then((entry) => { - return this.api.devices.getState(entry.deviceId).then((state) => { - entry.state = state.components[componentId]; - return entry; - }); - }); - list.push(promise); - }); - return Promise.all(list); - } -}; \ No newline at end of file + constructor(app, evt, apiMutex) { + this.event = evt + + let authToken + let refreshToken + switch (evt.lifecycle) { + case 'EVENT': + authToken = evt.eventData.authToken + this.installedAppId = evt.eventData.installedApp.installedAppId + this.locationId = evt.eventData.installedApp.locationId + this.config = evt.eventData.installedApp.config + this.locale = evt.locale + break + + case 'INSTALL': + authToken = evt.installData.authToken + refreshToken = evt.installData.refreshToken + this.installedAppId = evt.installData.installedApp.installedAppId + this.locationId = evt.installData.installedApp.locationId + this.config = evt.installData.installedApp.config + this.locale = evt.client.language + break + + case 'UPDATE': + authToken = evt.updateData.authToken + refreshToken = evt.updateData.refreshToken + this.installedAppId = evt.updateData.installedApp.installedAppId + this.locationId = evt.updateData.installedApp.locationId + this.config = evt.updateData.installedApp.config + this.locale = evt.client.language + break + + case 'CONFIGURATION': + this.installedAppId = evt.configurationData.installedAppId + this.locationId = evt.configurationData.locationId + this.config = evt.configurationData.config + this.locale = evt.client.language + break + + case 'UNINSTALL': + this.installedAppId = evt.uninstallData.installedApp.installedAppId + this.locationId = evt.uninstallData.installedApp.locationId + break + + case 'EXECUTE': + authToken = evt.executeData.authToken + this.installedAppId = evt.executeData.installedApp.installedAppId + this.locationId = evt.executeData.installedApp.locationId + this.config = evt.executeData.installedApp.config + this.locale = evt.executeData.parameters.locale + break + + // For constructing context for proactive API calls not in response to a lifecycle event + default: + authToken = evt.authToken + refreshToken = evt.refreshToken + this.installedAppId = evt.installedAppId + this.locationId = evt.locationId + this.config = evt.config + this.locale = evt.locale + break + } + + if (app._localizationEnabled) { + if (this.locale) { + this.headers = {'accept-language': this.locale} + i18n.init(this) + } + } + + this.api = new SmartThingsApi({ + authToken, + refreshToken, + clientId: app._clientId, + clientSecret: app._clientSecret, + log: app._log, + apiUrl: app._apiUrl, + refreshUrl: app._refreshUrl, + locationId: this.locationId, + installedAppId: this.installedAppId, + contextStore: app._contextStore, + apiMutex + }) + } + + configStringValue(name) { + const entry = this.config[name] + return entry ? entry[0].stringConfig.value : null + } + + configBooleanValue(name) { + const entry = this.config[name] + return entry ? entry[0].stringConfig.value === 'true' : false + } + + configNumberValue(name) { + const entry = this.config[name] + return entry ? Number(entry[0].stringConfig.value) : null + } + + configDateValue(name) { + const entry = this.config[name] + return entry ? new Date(entry[0].stringConfig.value) : null + } + + configTimeString(name) { + return this.configDateValue(name).toLocaleTimeString(this.locale, {hour: '2-digit', minute: '2-digit'}) + } + + configModeIds(name) { + const entry = this.config[name] + return entry.map(it => it.modeConfig.modeId) + } + + configDevices(name) { + const list = [] + this.config[name].forEach(item => { + const {componentId} = item.deviceConfig + const promise = this.api.devices.get(item.deviceConfig.deviceId).then(device => { + return { + deviceId: device.deviceId, + name: device.name, + label: device.label, + componentId + } + }) + list.push(promise) + }) + return Promise.all(list) + } + + configDevicesWithState(name) { + const list = [] + this.config[name].forEach(item => { + const {componentId} = item.deviceConfig + const promise = this.api.devices.get(item.deviceConfig.deviceId).then(device => { + return { + deviceId: device.deviceId, + name: device.name, + label: device.label, + componentId + } + }).then(entry => { + return this.api.devices.getState(entry.deviceId).then(state => { + entry.state = state.components[componentId] + return entry + }) + }) + list.push(promise) + }) + return Promise.all(list) + } +} diff --git a/lib/util/log.js b/lib/util/log.js index 30fc2ef..2de694e 100644 --- a/lib/util/log.js +++ b/lib/util/log.js @@ -1,71 +1,68 @@ -'use strict'; +'use strict' -const winston = require('winston'); +const winston = require('winston') /** * Simple wrapper around the console for logging various kinds of information */ module.exports = class Log { + constructor(logger, jsonSpace = null, enableEvents = false) { + this._eventsEnabled = enableEvents + this._jsonSpace = jsonSpace + if (!logger) { + this._logger = winston.createLogger({ + level: 'debug', + transports: [ + new winston.transports.Console({ + format: winston.format.simple() + }) + ] + }) + } + } - constructor(logger, jsonSpace = null, enableEvents = false) { - this._eventsEnabled = enableEvents; - this._jsonSpace = jsonSpace; - if (!logger) { - this._logger = winston.createLogger({ - level: 'debug', - transports: [ - new winston.transports.Console({ - format: winston.format.simple() - }) - ] - }); - } - } + event(evt, suffix = '') { + if (this._eventsEnabled) { + try { + this._logger.log('debug', `${evt.lifecycle}${suffix ? `/${suffix}` : ''}\nREQUEST: ${JSON.stringify(evt, null, this._jsonSpace)}`) + } catch (error) { + this._logger.log('error', `${evt.lifecycle}${suffix ? `/${suffix}` : ''}\nREQUEST: ${error}`) + } + } + } - event (evt, suffix = '') { - if ( this._eventsEnabled) { - try { - this._logger.log ('debug', `${evt.lifecycle}${suffix ? `/${suffix}` : ''}\nREQUEST: ${JSON.stringify(evt, null, this._jsonSpace)}`); - } - catch (e) { - this._logger.log ('error', `${evt.lifecycle}${suffix ? `/${suffix}` : ''}\nREQUEST: ${e}`); - } - } - } + response(data) { + if (this._eventsEnabled) { + try { + this._logger.log('debug', `RESPONSE: ${JSON.stringify(data, null, this._jsonSpace)}`) + } catch (error) { + this._logger.log('debug', `RESPONSE: ${error}`) + } + } + } - response (data) { - if ( this._eventsEnabled) { - try { - this._logger.log('debug', `RESPONSE: ${JSON.stringify(data, null, this._jsonSpace)}`); - } - catch (e) { - this._logger.log('debug', `RESPONSE: ${e}`); - } - } - } + debug(msg) { + this._logger.log('debug', msg) + } - debug (msg) { - this._logger.log ('debug', msg); - } + info(msg) { + this._logger.log('info', msg) + } - info (msg) { - this._logger.log ('info', msg); - } + warn(msg) { + this._logger.log('warn', msg) + } - warn (msg) { - this._logger.log ('warn', msg); - } + error(msg) { + this._logger.log('error', msg) + } - error (msg) { - this._logger.log ('error', msg); - } + apiError(msg) { + this._logger.log('error', msg) + } - apiError (msg) { - this._logger.log ('error', msg); - } - - enableEvents (jsonSpace, enabled = true) { - this._eventsEnabled = enabled; - this._jsonSpace = jsonSpace; - } -}; \ No newline at end of file + enableEvents(jsonSpace, enabled = true) { + this._eventsEnabled = enabled + this._jsonSpace = jsonSpace + } +} diff --git a/lib/util/responders.js b/lib/util/responders.js index 866b249..b81f32a 100644 --- a/lib/util/responders.js +++ b/lib/util/responders.js @@ -1,53 +1,52 @@ -'use strict'; +'use strict' class HttpResponder { - - constructor(response, log) { - this._response = response; - this._log = log; - } - - respond(data) { - this._log.response(data); - if (data && data.statusCode) { - this._response.statusCode = data.statusCode; - } - this._response.json(data); - } + constructor(response, log) { + this._response = response + this._log = log + } + + respond(data) { + this._log.response(data) + if (data && data.statusCode) { + this._response.statusCode = data.statusCode + } + + this._response.json(data) + } } class LambdaResponder { - - constructor(callback, log) { - this._callback = callback; - this._log = log; - } - - respond(data) { - this._callback(null, data); - } + constructor(callback, log) { + this._callback = callback + this._log = log + } + + respond(data) { + this._callback(null, data) + } } class MockResponder { - constructor(log) { - this._log = log; - } + constructor(log) { + this._log = log + } - respond(data) { - this.response = data; - } + respond(data) { + this.response = data + } } module.exports = { - httpResponder(response, log) { - return new HttpResponder(response, log); - }, - - lambdaResponse(callback, log) { - return new LambdaResponder(callback, log); - }, - - mockResponder(log) { - return new MockResponder(log); - } -}; + httpResponder(response, log) { + return new HttpResponder(response, log) + }, + + lambdaResponse(callback, log) { + return new LambdaResponder(callback, log) + }, + + mockResponder(log) { + return new MockResponder(log) + } +} diff --git a/lib/util/signature.js b/lib/util/signature.js index c0ab607..fdf7a88 100644 --- a/lib/util/signature.js +++ b/lib/util/signature.js @@ -1,32 +1,33 @@ -'use strict'; +'use strict' -const fs = require('fs'); -const httpSignature = require('http-signature'); +const fs = require('fs') +const httpSignature = require('http-signature') -let publicKey = 'INVALID'; +let publicKey = 'INVALID' module.exports = { - setPublicKey(key) { - if (key.startsWith('@')) { - publicKey = fs.readFileSync(key.slice(1), 'utf8'); - } - else { - publicKey = key; - } - return publicKey; - }, + setPublicKey(key) { + if (key.startsWith('@')) { + publicKey = fs.readFileSync(key.slice(1), 'utf8') + } else { + publicKey = key + } - signatureIsVerified(req) { - try { - let parsed = httpSignature.parseRequest(req); - if (!httpSignature.verifySignature(parsed, publicKey)) { - console.error('Forbidden - failed verifySignature'); - return false; - } - } catch (err) { - console.error(err); - return false; - } - return true; - } -}; \ No newline at end of file + return publicKey + }, + + signatureIsVerified(req) { + try { + const parsed = httpSignature.parseRequest(req) + if (!httpSignature.verifySignature(parsed, publicKey)) { + console.error('Forbidden - failed verifySignature') + return false + } + } catch (error) { + console.error(error) + return false + } + + return true + } +} diff --git a/package-lock.json b/package-lock.json index 3499f63..f4bafc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@smartthings/smartapp", - "version": "0.3.4", + "version": "1.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -24,15 +24,31 @@ "js-tokens": "^4.0.0" } }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "acorn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.5.tgz", - "integrity": "sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.0.tgz", + "integrity": "sha512-MW/FjM+IvU9CgBzjO3UIPCE2pyEwUsoFl+VGdczOPEdxfGFjuKny/gN54mOuX7Qxmb9Rg9MCn2oKiSUeW+pjrw==", "dev": true }, "acorn-jsx": { @@ -82,10 +98,19 @@ } } }, + "ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "dev": true, + "requires": { + "string-width": "^2.0.0" + } + }, "ansi-escapes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", "dev": true }, "ansi-regex": { @@ -120,6 +145,63 @@ } } }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -139,6 +221,12 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -160,6 +248,12 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -175,6 +269,61 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -188,6 +337,21 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==" }, + "boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "dev": true, + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -197,18 +361,99 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "buf-compare": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buf-compare/-/buf-compare-1.0.1.tgz", + "integrity": "sha1-/vKNqLgROgoNtEMLC2Rntpcws0o=", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, "callsites": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz", "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==", "dev": true }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "capture-stack-trace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", + "dev": true + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -251,12 +496,56 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, + "ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", "dev": true }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", + "integrity": "sha1-jffHquUf02h06PjQW5GAvBGj/tc=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", + "dev": true + }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -266,12 +555,50 @@ "restore-cursor": "^2.0.0" } }, + "cli-table3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "dev": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + } + }, "cli-width": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, "color": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", @@ -336,16 +663,79 @@ "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "configstore": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", + "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", + "dev": true, + "requires": { + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-assert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/core-assert/-/core-assert-0.2.1.tgz", + "integrity": "sha1-+F4s+b/tKPdzzIs/pcW2m9wC/j8=", + "dev": true, + "requires": { + "buf-compare": "^1.0.0", + "is-error": "^2.2.0" + } + }, + "core-js": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.4.tgz", + "integrity": "sha512-05qQ5hXShcqGkPZpXEFLIpxayZscVD2kuMBZewxiIPPEagukO4mqgPA9CWhUvFBJfy3ODdK2p9xyHh7FTU9/7A==", + "dev": true + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", + "dev": true, + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -359,11 +749,26 @@ "which": "^1.2.9" } }, + "crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", + "dev": true + }, "csextends": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/csextends/-/csextends-1.2.0.tgz", "integrity": "sha512-S/8k1bDTJIwuGgQYmsRoE+8P+ohV32WhQ0l4zqrc0XDdxOhjQQD7/wTZwCzoZX53jSX3V/qwjT+OkPTxWQcmjg==" }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -380,6 +785,36 @@ "ms": "^2.1.1" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, "deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", @@ -389,17 +824,79 @@ "type-detect": "^4.0.0" } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "deep-strict-equal": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/deep-strict-equal/-/deep-strict-equal-0.2.0.tgz", + "integrity": "sha1-SgeBR6irV/ag1PVUckPNIvROtOQ=", + "dev": true, + "requires": { + "core-assert": "^0.2.0" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, + "detect-indent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=", + "dev": true + }, "diagnostics": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", @@ -416,6 +913,32 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + }, + "dependencies": { + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -425,6 +948,21 @@ "esutils": "^2.0.2" } }, + "dot-prop": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, "eachr": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/eachr/-/eachr-2.0.4.tgz", @@ -447,6 +985,12 @@ "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==" }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "enabled": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", @@ -455,14 +999,29 @@ "env-variable": "0.0.x" } }, - "env-variable": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", - "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==" - }, - "errlop": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/errlop/-/errlop-1.1.1.tgz", + "enhance-visitors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/enhance-visitors/-/enhance-visitors-1.0.0.tgz", + "integrity": "sha1-qpRdBdpGVnKh69OP7i7T2oUY6Vo=", + "dev": true, + "requires": { + "lodash": "^4.13.1" + } + }, + "env-editor": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/env-editor/-/env-editor-0.3.1.tgz", + "integrity": "sha1-MNBUDCEBQU8lipTUwKUkwGwT48Y=", + "dev": true + }, + "env-variable": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", + "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==" + }, + "errlop": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/errlop/-/errlop-1.1.1.tgz", "integrity": "sha512-WX7QjiPHhsny7/PQvrhS5VMizXXKoKCS3udaBp8gjlARdbn+XmK300eKBAAN0hGyRaTCtRpOaxK+xFVPUJ3zkw==", "requires": { "editions": "^2.1.2" @@ -479,6 +1038,23 @@ } } }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + } + } + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -486,9 +1062,9 @@ "dev": true }, "eslint": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.12.1.tgz", - "integrity": "sha512-54NV+JkTpTu0d8+UYSA8mMKAG4XAsaOrozA9rCW7tgneg1mevcL7wIotPC+fZ0SkWwdhNqoXoxnQCTBp7UvTsg==", + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.13.0.tgz", + "integrity": "sha512-nqD5WQMisciZC5EHZowejLKQjWGuFS5c70fxqSKlnDME+oz9zmE8KTlX+lHSg+/5wsC/kf9Q9eMkC8qS3oM2fg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -520,7 +1096,6 @@ "natural-compare": "^1.4.0", "optionator": "^0.8.2", "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", "progress": "^2.0.0", "regexpp": "^2.0.1", "semver": "^5.5.1", @@ -546,6 +1121,287 @@ } } }, + "eslint-ast-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-ast-utils/-/eslint-ast-utils-1.1.0.tgz", + "integrity": "sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA==", + "dev": true, + "requires": { + "lodash.get": "^4.4.2", + "lodash.zip": "^4.2.0" + } + }, + "eslint-config-prettier": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-3.6.0.tgz", + "integrity": "sha512-ixJ4U3uTLXwJts4rmSVW/lMXjlGwCijhBJHk8iVqKKSifeI0qgFEfWl8L63isfc8Od7EiBALF6BX3jKLluf/jQ==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + } + }, + "eslint-config-xo": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/eslint-config-xo/-/eslint-config-xo-0.26.0.tgz", + "integrity": "sha512-l+93kmBSNr5rMrsqwC6xVWsi8LI4He3z6jSk38e9bAkMNsVsQ8XYO+qzXfJFgFX4i/+hiTswyHtl+nDut9rPaA==", + "dev": true + }, + "eslint-formatter-pretty": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/eslint-formatter-pretty/-/eslint-formatter-pretty-2.1.1.tgz", + "integrity": "sha512-gWfagucSWBn82WxzwFloBTLAcwYDgnpAfiV5pQfyAV5YpZikuLflRU8nc3Ts9wnNvLhwk4blzb42/C495Yw7BA==", + "dev": true, + "requires": { + "ansi-escapes": "^3.1.0", + "chalk": "^2.1.0", + "eslint-rule-docs": "^1.1.5", + "log-symbols": "^2.0.0", + "plur": "^3.0.1", + "string-width": "^2.0.0", + "supports-hyperlinks": "^1.0.1" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", + "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.3.0.tgz", + "integrity": "sha512-lmDJgeOOjk8hObTysjqH7wyMi+nsHwwvfBykwfhjR1LNdd7C2uFJBvx4OpWYpXOw4df1yE1cDEVd1yLHitk34w==", + "dev": true, + "requires": { + "debug": "^2.6.8", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-ava": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-ava/-/eslint-plugin-ava-5.1.1.tgz", + "integrity": "sha512-3N7geVdXTabpngQOl+ih1ejMbFOXCUYROnTIP66KAQoMcEAkPSXYc/Jwo/qC4zpRR7PXMuf5afMzTEBpyZmWzQ==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "deep-strict-equal": "^0.2.0", + "enhance-visitors": "^1.0.0", + "esm": "^3.0.82", + "espree": "^4.0.0", + "espurify": "^1.8.1", + "import-modules": "^1.1.0", + "is-plain-object": "^2.0.4", + "multimatch": "^2.1.0", + "pkg-up": "^2.0.0" + }, + "dependencies": { + "espree": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", + "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", + "dev": true, + "requires": { + "acorn": "^6.0.2", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "multimatch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", + "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", + "dev": true, + "requires": { + "array-differ": "^1.0.0", + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "minimatch": "^3.0.0" + } + } + } + }, + "eslint-plugin-es": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-1.4.0.tgz", + "integrity": "sha512-XfFmgFdIUDgvaRAlaXUkxrRg5JSADoRC8IkKLc/cISeR3yHVMefFHQZpcyXXEUUPHfy5DwviBcrfqlyqEwlQVw==", + "dev": true, + "requires": { + "eslint-utils": "^1.3.0", + "regexpp": "^2.0.1" + } + }, + "eslint-plugin-eslint-comments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.1.0.tgz", + "integrity": "sha512-yvukhLNPZGkwKWe4Zr8gXCl10zfsa2jTksdKVhU0sYrK/RsvUoJ2PUKe4OJOupl1nS0cWlz7XyH4LUEwejTyaQ==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "ignore": "^5.0.5" + }, + "dependencies": { + "ignore": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.0.5.tgz", + "integrity": "sha512-kOC8IUb8HSDMVcYrDVezCxpJkzSQWTAzf3olpKM6o9rM5zpojx23O0Fl8Wr4+qJ6ZbPEHqf1fdwev/DS7v7pmA==", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.16.0.tgz", + "integrity": "sha512-z6oqWlf1x5GkHIFgrSvtmudnqM6Q60KM4KvpWi5ubonMjycLjndvd5+8VAZIsTlHC03djdgJuyKG6XO577px6A==", + "dev": true, + "requires": { + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.3.0", + "has": "^1.0.3", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "read-pkg-up": "^2.0.0", + "resolve": "^1.9.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-no-use-extend-native": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-use-extend-native/-/eslint-plugin-no-use-extend-native-0.4.0.tgz", + "integrity": "sha512-9W2747CwC7aTJknLKY6ftdzj3AZz8DSaa64zONOMIemxH7YRr0+hqrvsNtHK/v9DusPuMxM9y9hBnfHwzKFmww==", + "dev": true, + "requires": { + "is-get-set-prop": "^1.0.0", + "is-js-type": "^2.0.0", + "is-obj-prop": "^1.0.0", + "is-proto-prop": "^2.0.0" + } + }, + "eslint-plugin-node": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-8.0.1.tgz", + "integrity": "sha512-ZjOjbjEi6jd82rIpFSgagv4CHWzG9xsQAVp1ZPlhRnnYxcTgENUVBvhYmkQ7GvT1QFijUSo69RaiOJKhMu6i8w==", + "dev": true, + "requires": { + "eslint-plugin-es": "^1.3.1", + "eslint-utils": "^1.3.1", + "ignore": "^5.0.2", + "minimatch": "^3.0.4", + "resolve": "^1.8.1", + "semver": "^5.5.0" + }, + "dependencies": { + "ignore": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.0.5.tgz", + "integrity": "sha512-kOC8IUb8HSDMVcYrDVezCxpJkzSQWTAzf3olpKM6o9rM5zpojx23O0Fl8Wr4+qJ6ZbPEHqf1fdwev/DS7v7pmA==", + "dev": true + } + } + }, + "eslint-plugin-prettier": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.0.1.tgz", + "integrity": "sha512-/PMttrarPAY78PLvV3xfWibMOdMDl57hmlQ2XqFeA37wd+CJ7WSxV7txqjVPHi/AAFKd2lX0ZqfsOc/i5yFCSQ==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-plugin-promise": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz", + "integrity": "sha512-Si16O0+Hqz1gDHsys6RtFRrW7cCTB6P7p3OJmKp3Y3dxpQE2qwOA7d3xnV+0mBmrPoi0RBnxlCKvqu70te6wjg==", + "dev": true + }, + "eslint-plugin-unicorn": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-7.1.0.tgz", + "integrity": "sha512-lW/ZwGR638V0XuZgR160qVQvPtw8tw3laKT5LjJPt+W+tN7kVf2S2V7x+ZrEEwSjEb3OiEzb3cppzaKuYtgYeg==", + "dev": true, + "requires": { + "clean-regexp": "^1.0.0", + "eslint-ast-utils": "^1.0.0", + "import-modules": "^1.1.0", + "lodash.camelcase": "^4.1.1", + "lodash.kebabcase": "^4.0.1", + "lodash.snakecase": "^4.0.1", + "lodash.upperfirst": "^4.2.0", + "safe-regex": "^2.0.1" + } + }, + "eslint-rule-docs": { + "version": "1.1.60", + "resolved": "https://registry.npmjs.org/eslint-rule-docs/-/eslint-rule-docs-1.1.60.tgz", + "integrity": "sha512-lNd2XSEmpxd0jbscR3fL7D87WmmxzXW5/+7GAPLz40EDCp4i6XpZrW0JAftHvmQtgCvCguBOmWNcyOco/tzuEg==", + "dev": true + }, "eslint-scope": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", @@ -568,6 +1424,12 @@ "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", "dev": true }, + "esm": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.4.tgz", + "integrity": "sha512-wOuWtQCkkwD1WKQN/k3RsyGSSN+AmiUzdKftn8vaC+uV9JesYmQlODJxgXaaRz0LaaFIlUxZaUu5NPiUAjKAAA==", + "dev": true + }, "espree": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.0.tgz", @@ -585,6 +1447,15 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "espurify": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/espurify/-/espurify-1.8.1.tgz", + "integrity": "sha512-ZDko6eY/o+D/gHCWyHTU85mKDgYcS4FJj7S+YD6WIInm7GQ6AnOjmcL4+buFV/JOztVLELi/7MmuGU5NHta0Mg==", + "dev": true, + "requires": { + "core-js": "^2.0.0" + } + }, "esquery": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", @@ -615,11 +1486,110 @@ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "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" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, "extendr": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/extendr/-/extendr-2.1.0.tgz", @@ -646,6 +1616,71 @@ "tmp": "^0.0.33" } }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, "extract-opts": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/extract-opts/-/extract-opts-2.2.0.tgz", @@ -671,6 +1706,26 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-glob": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.6.tgz", + "integrity": "sha512-0BvMaZc1k9F+MeWWMe8pL6YltFzZYcJsYU7D4JyDA6PAczaXvxqQQ/z+mDF7/4Mw01DeUc+i3CTKajnkANkV4w==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + } + }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", @@ -711,10 +1766,107 @@ "object-assign": "^4.0.1" } }, - "flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.0.0.tgz", + "integrity": "sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", + "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", "dev": true, "requires": { "circular-json": "^0.3.1", @@ -723,6 +1875,12 @@ "write": "^0.2.1" } }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -738,6 +1896,15 @@ "mime-types": "^2.1.12" } }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, "fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -754,18 +1921,54 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, + "get-set-props": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-set-props/-/get-set-props-0.1.0.tgz", + "integrity": "sha1-mYR1wXhEVobQsyJG2l3428++jqM=", + "dev": true + }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -786,12 +1989,104 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "dev": true + }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, "globals": { "version": "11.10.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz", "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==", "dev": true }, + "globby": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-9.0.0.tgz", + "integrity": "sha512-q0qiO/p1w/yJ0hk8V9x1UXlgsXUxlGd0AHUOXZVXBO6aznDtpx7M8D1kBrCAItoPm+4l8r6ATXV1JpjY2SBQOw==", + "dev": true, + "requires": { + "array-union": "^1.0.2", + "dir-glob": "^2.2.1", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" + }, + "dependencies": { + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "dev": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + } + }, "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", @@ -817,18 +2112,71 @@ "har-schema": "^2.0.0" } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "has-yarn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-1.0.0.tgz", + "integrity": "sha1-ieJdtgS3Jcj1l2//Ct3JIbgopac=", + "dev": true + }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -891,12 +2239,30 @@ "resolve-from": "^4.0.0" } }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, + "import-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/import-modules/-/import-modules-1.1.0.tgz", + "integrity": "sha1-dI23nFzEK7lwHvq0JPiU5yYA6dw=", + "dev": true + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -911,22 +2277,28 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, "inquirer": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz", - "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz", + "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", - "external-editor": "^3.0.0", + "external-editor": "^3.0.3", "figures": "^2.0.0", - "lodash": "^4.17.10", + "lodash": "^4.17.11", "mute-stream": "0.0.7", "run-async": "^2.2.0", - "rxjs": "^6.1.0", + "rxjs": "^6.4.0", "string-width": "^2.1.0", "strip-ansi": "^5.0.0", "through": "^2.3.6" @@ -949,76 +2321,336 @@ } } }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "irregular-plurals": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-2.0.0.tgz", + "integrity": "sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true + "is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "requires": { + "ci-info": "^1.5.0" + } }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "is-error": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-error/-/is-error-2.2.1.tgz", + "integrity": "sha1-aEqW2EB2V3yY9M20DG0mpRI78Zw=", + "dev": true }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "js-yaml": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", - "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", + "is-get-set-prop": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-get-set-prop/-/is-get-set-prop-1.0.0.tgz", + "integrity": "sha1-JzGHfk14pqae3M5rudaLB3nnYxI=", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "get-set-props": "^0.1.0", + "lowercase-keys": "^1.0.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + "is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "dev": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } }, - "json-schema-traverse": { + "is-js-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-js-type/-/is-js-type-2.0.0.tgz", + "integrity": "sha1-c2FwBtZZtOtHKbunR9KHgt8PfiI=", + "dev": true, + "requires": { + "js-types": "^1.0.0" + } + }, + "is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-obj-prop": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-obj-prop/-/is-obj-prop-1.0.0.tgz", + "integrity": "sha1-s03nnEULjXxzqyzfZ9yHWtuF+A4=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0", + "obj-props": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-proto-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-proto-prop/-/is-proto-prop-2.0.0.tgz", + "integrity": "sha512-jl3NbQ/fGLv5Jhan4uX+Ge9ohnemqyblWVVCpAvtTQzNFvV2xhJq+esnkIbYQ9F1nITXoLfDDQLp7LBw/zzncg==", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0", + "proto-props": "^2.0.0" + } + }, + "is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "dev": true + }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/js-types/-/js-types-1.0.0.tgz", + "integrity": "sha1-0kLmSU7Vcq08koCfyL7X92h8vwM=", + "dev": true + }, + "js-yaml": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", + "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" @@ -1053,6 +2685,12 @@ "verror": "1.10.0" } }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, "kuler": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", @@ -1061,6 +2699,24 @@ "colornames": "^1.1.1" } }, + "latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "dev": true, + "requires": { + "package-json": "^4.0.0" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -1071,11 +2727,96 @@ "type-check": "~0.3.2" } }, + "line-column-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/line-column-path/-/line-column-path-1.0.0.tgz", + "integrity": "sha1-ODuD/KhIj6p6WZQOvyi4IFjBbFU=", + "dev": true + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "dev": true + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true + }, + "lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=", + "dev": true + }, + "lodash.mergewith": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", + "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", + "dev": true + }, + "lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40=", + "dev": true + }, + "lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984=", + "dev": true + }, + "lodash.zip": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", + "integrity": "sha1-7GZi5IlkCO1KtsVCo5kLcswIACA=", + "dev": true + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, "logform": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/logform/-/logform-1.10.0.tgz", @@ -1088,6 +2829,49 @@ "triple-beam": "^1.2.0" } }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, "make-plural": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-3.0.6.tgz", @@ -1096,6 +2880,27 @@ "minimist": "^1.2.0" } }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, "math-interval-parser": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-interval-parser/-/math-interval-parser-1.1.0.tgz", @@ -1104,6 +2909,107 @@ "xregexp": "^2.0.0" } }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "meow": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0", + "yargs-parser": "^10.0.0" + }, + "dependencies": { + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "merge2": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.3.tgz", + "integrity": "sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==", + "dev": true + }, "messageformat": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/messageformat/-/messageformat-0.3.1.tgz", @@ -1116,6 +3022,27 @@ "watchr": "~2.4.13" } }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, "mime-db": { "version": "1.37.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", @@ -1146,8 +3073,38 @@ "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "optional": true + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } }, "mkdirp": { "version": "0.5.1", @@ -1236,6 +3193,26 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, + "multimatch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-3.0.0.tgz", + "integrity": "sha512-22foS/gqQfANZ3o+W7ST2x25ueHDVNWl/b9OlGcLpy/iKxjCpvcNCM51YCenUi7Mt/jAjjqv8JwZRs8YP5sRjA==", + "dev": true, + "requires": { + "array-differ": "^2.0.3", + "array-union": "^1.0.2", + "arrify": "^1.0.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "array-differ": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-2.0.3.tgz", + "integrity": "sha1-AZW7AMzM8nEQbv7kpHhkiLcYBxI=", + "dev": true + } + } + }, "mustache": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mustache/-/mustache-3.0.1.tgz", @@ -1247,6 +3224,25 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -1267,17 +3263,99 @@ "abbrev": "1" } }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, + "obj-props": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/obj-props/-/obj-props-1.1.0.tgz", + "integrity": "sha1-YmMT+qRCvv1KROmgLDy2vek3tRE=", + "dev": true + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1300,6 +3378,26 @@ "mimic-fn": "^1.0.0" } }, + "open-editor": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/open-editor/-/open-editor-1.2.0.tgz", + "integrity": "sha1-dcoj8LdNSz9V7guKTg9cIyXrd18=", + "dev": true, + "requires": { + "env-editor": "^0.3.1", + "line-column-path": "^1.0.0", + "opn": "^5.0.0" + } + }, + "opn": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz", + "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, "optionator": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", @@ -1314,12 +3412,65 @@ "wordwrap": "~1.0.0" } }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "dev": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, "parent-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.0.tgz", @@ -1329,6 +3480,33 @@ "callsites": "^3.0.0" } }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1346,21 +3524,109 @@ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "dependencies": { + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "plur": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/plur/-/plur-3.0.1.tgz", + "integrity": "sha512-lJl0ojUynAM1BZn58Pas2WT/TXeC1+bS+UqShl0x9+49AtOn7DixRXVzaC8qrDOIxNDmepKnLuMTH7NQmkX0PA==", + "dev": true, + "requires": { + "irregular-plurals": "^2.0.0" + } }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, "prelude-ls": { @@ -1369,6 +3635,27 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "prettier": { + "version": "1.16.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.4.tgz", + "integrity": "sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -1380,6 +3667,18 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "proto-props": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/proto-props/-/proto-props-2.0.0.tgz", + "integrity": "sha512-2yma2tog9VaRZY2mn3Wq51uiSW4NcPYT1cQdBagwyrznrilKSZwIZ0UG3ZPL/mx+axEns0hE35T5ufOYZXEnBQ==", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, "psl": { "version": "1.1.31", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", @@ -1395,6 +3694,45 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "dev": true + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", @@ -1409,12 +3747,85 @@ "util-deprecate": "~1.0.1" } }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "dependencies": { + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + } + } + }, + "regexp-tree": { + "version": "0.0.86", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.0.86.tgz", + "integrity": "sha512-xjTX9GmRw7Nn2wxinLoqQXv9Dt5yerP7CJIsYe/5z5gFs0Ltu20wRuZophafi9sf0JpsdVtki5EuICRYpgB1AQ==", + "dev": true, + "requires": { + "cli-table3": "^0.5.0", + "colors": "^1.1.2", + "yargs": "^10.0.3" + } + }, "regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, + "registry-auth-token": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", + "dev": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "dev": true, + "requires": { + "rc": "^1.0.1" + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", @@ -1461,12 +3872,56 @@ "lodash": "^4.13.1" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + } + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", @@ -1477,6 +3932,12 @@ "signal-exit": "^3.0.2" } }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -1512,9 +3973,9 @@ } }, "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", + "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -1525,6 +3986,15 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "safe-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.0.1.tgz", + "integrity": "sha512-4tbOl0xq/cxbhEhdvxKaCZgzwOKeqt2tnHc2OPBkMsVdZ0s0C5oJwI6voRI9XzPSzeN35PECDNDK946x4d/0eA==", + "dev": true, + "requires": { + "regexp-tree": "~0.0.85" + } + }, "safefs": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/safefs/-/safefs-3.2.2.tgz", @@ -1553,6 +4023,44 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" }, + "semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "dev": true, + "requires": { + "semver": "^5.0.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -1582,10 +4090,16 @@ "is-arrayish": "^0.3.1" } }, - "slice-ansi": { + "slash": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.0.0.tgz", - "integrity": "sha512-4j2WTWjp3GsZ+AOagyzVbzp4vWGtZ0hEZ/gDY/uTvm6MTxUfTUIsnMIFb1bn8o0RuXiqUw15H1bue8f22Vw2oQ==", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", "dev": true, "requires": { "ansi-styles": "^3.2.0", @@ -1593,6 +4107,203 @@ "is-fullwidth-code-point": "^2.0.0" } }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sort-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", + "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", + "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, "sprintf-js": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", @@ -1619,6 +4330,27 @@ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, "stealthy-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", @@ -1651,6 +4383,24 @@ "ansi-regex": "^3.0.0" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -1666,16 +4416,74 @@ "has-flag": "^3.0.0" } }, + "supports-hyperlinks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz", + "integrity": "sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==", + "dev": true, + "requires": { + "has-flag": "^2.0.0", + "supports-color": "^5.0.0" + }, + "dependencies": { + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + } + } + }, "table": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/table/-/table-5.2.1.tgz", - "integrity": "sha512-qmhNs2GEHNqY5fd2Mo+8N1r2sw/rvTAAvBZTaTx+Y7PHLypqyrxr1MdIu0pLw6Xvl/Gi4ONu/sdceP8vvUjkyA==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz", + "integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==", "dev": true, "requires": { - "ajv": "^6.6.1", + "ajv": "^6.9.1", "lodash": "^4.17.11", - "slice-ansi": "2.0.0", - "string-width": "^2.1.1" + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", + "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz", + "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==", + "dev": true + }, + "string-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.0.0.tgz", + "integrity": "sha512-rr8CUxBbvOZDUvc5lNIJ+OC1nPVpz+Siw9VBtUjB9b6jZehZLFt0JMCZzShFHIsI8cbhm0EsNIfWJMFV3cu3Ew==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.0.0" + } + }, + "strip-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz", + "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==", + "dev": true, + "requires": { + "ansi-regex": "^4.0.0" + } + } } }, "taskgroup": { @@ -1687,6 +4495,15 @@ "csextends": "^1.0.3" } }, + "term-size": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", + "dev": true, + "requires": { + "execa": "^0.7.0" + } + }, "text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -1698,12 +4515,24 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "the-argv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/the-argv/-/the-argv-1.0.0.tgz", + "integrity": "sha1-AIRwUAVzDdhNt1UlPJMa45jblSI=", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", + "dev": true + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -1713,6 +4542,59 @@ "os-tmpdir": "~1.0.2" } }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "dependencies": { + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + } + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", @@ -1729,6 +4611,12 @@ } } }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true + }, "triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", @@ -1773,11 +4661,119 @@ "resolved": "https://registry.npmjs.org/typechecker/-/typechecker-2.1.0.tgz", "integrity": "sha1-0cIJOlT/ihn1jP+HfuqlTyJC04M=" }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "dev": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "unzip-response": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", + "dev": true + }, + "update-notifier": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "dev": true, + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -1786,6 +4782,27 @@ "punycode": "^2.1.0" } }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -1796,6 +4813,16 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -1830,6 +4857,21 @@ "isexe": "^2.0.0" } }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "widest-line": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "dev": true, + "requires": { + "string-width": "^2.1.1" + } + }, "winston": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/winston/-/winston-3.1.0.tgz", @@ -1871,6 +4913,53 @@ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -1885,10 +4974,242 @@ "mkdirp": "^0.5.1" } }, + "write-file-atomic": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", + "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "write-json-file": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-2.3.0.tgz", + "integrity": "sha1-K2TIozAE1UuGmMdtWFp3zrYdoy8=", + "dev": true, + "requires": { + "detect-indent": "^5.0.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "pify": "^3.0.0", + "sort-keys": "^2.0.0", + "write-file-atomic": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "write-pkg": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/write-pkg/-/write-pkg-3.2.0.tgz", + "integrity": "sha512-tX2ifZ0YqEFOF1wjRW2Pk93NLsj02+n1UP5RvO6rCs0K6R2g1padvf006cY74PQJKMGS2r42NK7FD0dG6Y6paw==", + "dev": true, + "requires": { + "sort-keys": "^2.0.0", + "write-json-file": "^2.2.0" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", + "dev": true + }, + "xo": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/xo/-/xo-0.24.0.tgz", + "integrity": "sha512-eaXWpNtXHbJ+DSiDkdRnDcMYPeUi/MWFUoUgorBhzAueTCM+v4o9Xv6buYgyoL4r7JuTp5EWXx3lGn9Md4dgWA==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "debug": "^4.1.0", + "eslint": "^5.12.0", + "eslint-config-prettier": "^3.3.0", + "eslint-config-xo": "^0.26.0", + "eslint-formatter-pretty": "^2.0.0", + "eslint-plugin-ava": "^5.1.0", + "eslint-plugin-eslint-comments": "^3.0.1", + "eslint-plugin-import": "^2.14.0", + "eslint-plugin-no-use-extend-native": "^0.4.0", + "eslint-plugin-node": "^8.0.0", + "eslint-plugin-prettier": "^3.0.0", + "eslint-plugin-promise": "^4.0.0", + "eslint-plugin-unicorn": "^7.0.0", + "find-cache-dir": "^2.0.0", + "get-stdin": "^6.0.0", + "globby": "^9.0.0", + "has-flag": "^3.0.0", + "lodash.isequal": "^4.5.0", + "lodash.mergewith": "^4.6.1", + "meow": "^5.0.0", + "multimatch": "^3.0.0", + "open-editor": "^1.2.0", + "path-exists": "^3.0.0", + "pkg-conf": "^2.1.0", + "prettier": "^1.15.2", + "resolve-cwd": "^2.0.0", + "resolve-from": "^4.0.0", + "semver": "^5.5.0", + "slash": "^2.0.0", + "update-notifier": "^2.3.0", + "xo-init": "^0.7.0" + } + }, + "xo-init": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/xo-init/-/xo-init-0.7.0.tgz", + "integrity": "sha512-mrrCKMu52vz0u2tiOl8DoG709pBtnSp58bb4/j58a4jeXjrb1gV7dxfOBjOlXitYtfW2QnlxxxfAojoFcpynDg==", + "dev": true, + "requires": { + "arrify": "^1.0.0", + "execa": "^0.9.0", + "has-yarn": "^1.0.0", + "minimist": "^1.1.3", + "path-exists": "^3.0.0", + "read-pkg-up": "^3.0.0", + "the-argv": "^1.0.0", + "write-pkg": "^3.1.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.9.0.tgz", + "integrity": "sha512-BbUMBiX4hqiHZUA5+JujIjNb6TyAlp2D5KLheMjMluwOuzcnylDL4AxZYLLn1n2AGB49eSWwyKvvEQoRpnAtmA==", + "dev": true, + "requires": { + "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" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + } + } + }, "xregexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", + "integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^8.1.0" + } + }, + "yargs-parser": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", + "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } } } } diff --git a/package.json b/package.json index eb2dcd5..e57bb97 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "@smartthings/smartapp", "version": "1.0.1", - "description": "SDK for SmartApps using Node JS", + "description": "NodeJS SDK for SmartApps", + "displayName": "SmartThings SmartApp SDK for NodeJS", "author": "SmartThings", "contributors": [ - "Bob Florian" + "Bob Florian (https://github.com/bflorian)" ], "keywords": [ "smartthings", @@ -12,16 +13,14 @@ ], "main": "index.js", "scripts": { - "test": "mocha test/**/*.js" + "test": "xo && mocha test/**/*.js", + "lint": "xo" }, "engines": { "node": ">=6.12.3 <8.0.0 || >=8.9.4 <10.0.0 || >=10.4.1" }, "license": "Apache-2.0", - "repository": { - "type": "git", - "url": "https://github.com/SmartThingsCommunity/smartapp-sdk-nodejs.git" - }, + "repository": "github:SmartThingsCommunity/smartapp-sdk-nodejs", "bugs": { "url": "https://github.com/SmartThingsCommunity/smartapp-sdk-nodejs/issues" }, @@ -29,6 +28,7 @@ "dependencies": { "async-mutex": "^0.1.3", "fs-extra": "^7.0.1", + "http-signature": "^1.2.0", "i18n": "^0.8.3", "request": "^2.88.0", "request-promise": "^4.2.2", @@ -37,8 +37,15 @@ "devDependencies": { "ajv": "^6.5.3", "chai": "^4.1.2", - "eslint": "^5.6.0", "mocha": "^5.2.0", - "mockery": "^2.1.0" + "mockery": "^2.1.0", + "xo": "^0.24.0" + }, + "xo": { + "space": false, + "semicolon": false, + "rules": { + "no-useless-constructor": 1 + } } } diff --git a/test/pagebuilder-i18n-spec.js b/test/pagebuilder-i18n-spec.js index 3e345ab..bd064e9 100644 --- a/test/pagebuilder-i18n-spec.js +++ b/test/pagebuilder-i18n-spec.js @@ -1,54 +1,54 @@ -/*eslint no-undef: "off"*/ -const {expect} = require('chai'); -const Page = require('../lib/pages/page'); -const i18n = require('i18n'); +/* eslint no-undef: "off" */ +const path = require('path') +const {expect} = require('chai') +const i18n = require('i18n') +const Page = require('../lib/pages/page') i18n.configure({ - locales:['en', 'fr'], - directory: __dirname + '/locales' -}); -describe ('pagebuilder-i18n-spec', () => { + locales: ['en', 'fr'], + directory: path.join(__dirname, 'locales') +}) +describe('pagebuilder-i18n-spec', () => { + it('should process two sections', () => { + const page = new Page('i18nMainPage', 'fr') - it('should process two sections', () => { - const page = new Page('i18nMainPage', 'fr'); + page.section('whenDoorOpensAndCloses', section => { + section.deviceSetting('contactSensor') + .capabilities(['contactSensor']) + }) - page.section('whenDoorOpensAndCloses', (section) => { - section.deviceSetting('contactSensor') - .capabilities(['contactSensor']); - }); + page.section('turnLightsOnAndOff', section => { + section.deviceSetting('lights') + .capabilities(['switch']) + .multiple(true) + .permissions('rx') + }) - page.section('turnLightsOnAndOff', (section) => { - section.deviceSetting('lights') - .capabilities(['switch']) - .multiple(true) - .permissions('rx'); - }); + const json = page.toJson() + // Console.log(JSON.stringify(json, null, 2)) + expect(json.sections.length).to.equal(2) - const json = page.toJson(); - //console.log(JSON.stringify(json, null, 2)) - expect(json.sections.length).to.equal(2); + expect(json.sections[0].name).to.equal('Quand cette porte s\'ouvre et se ferme') + expect(json.sections[0].settings.length).to.equal(1) + expect(json.sections[0].settings[0].id).to.equal('contactSensor') + expect(json.sections[0].settings[0].name).to.equal('Sélectionnez un capteur d\'ouverture / fermeture') + expect(json.sections[0].settings[0].description).to.equal('Appuyez pour définir') + expect(json.sections[0].settings[0].required).to.equal(true) + expect(json.sections[0].settings[0].multiple).to.equal(false) + expect(json.sections[0].settings[0].type).to.equal('DEVICE') + expect(json.sections[0].settings[0].permissions[0]).to.equal('r') + expect(json.sections[0].settings[0].capabilities[0]).to.equal('contactSensor') - expect(json.sections[0].name).to.equal('Quand cette porte s\'ouvre et se ferme'); - expect(json.sections[0].settings.length).to.equal(1); - expect(json.sections[0].settings[0].id).to.equal('contactSensor'); - expect(json.sections[0].settings[0].name).to.equal('Sélectionnez un capteur d\'ouverture / fermeture'); - expect(json.sections[0].settings[0].description).to.equal('Appuyez pour définir'); - expect(json.sections[0].settings[0].required).to.equal(true); - expect(json.sections[0].settings[0].multiple).to.equal(false); - expect(json.sections[0].settings[0].type).to.equal('DEVICE'); - expect(json.sections[0].settings[0].permissions[0]).to.equal('r'); - expect(json.sections[0].settings[0].capabilities[0]).to.equal('contactSensor'); - - expect(json.sections[1].name).to.equal('Allumer et éteindre ces lumières'); - expect(json.sections[1].settings.length).to.equal(1); - expect(json.sections[1].settings[0].id).to.equal('lights'); - expect(json.sections[1].settings[0].name).to.equal('Sélectionnez une ou plusieurs lumières à contrôler'); - expect(json.sections[0].settings[0].description).to.equal('Appuyez pour définir'); - expect(json.sections[1].settings[0].required).to.equal(true); - expect(json.sections[1].settings[0].multiple).to.equal(true); - expect(json.sections[1].settings[0].type).to.equal('DEVICE'); - expect(json.sections[1].settings[0].permissions[0]).to.equal('r'); - expect(json.sections[1].settings[0].permissions[1]).to.equal('x'); - expect(json.sections[1].settings[0].capabilities[0]).to.equal('switch'); - }); -}); \ No newline at end of file + expect(json.sections[1].name).to.equal('Allumer et éteindre ces lumières') + expect(json.sections[1].settings.length).to.equal(1) + expect(json.sections[1].settings[0].id).to.equal('lights') + expect(json.sections[1].settings[0].name).to.equal('Sélectionnez une ou plusieurs lumières à contrôler') + expect(json.sections[0].settings[0].description).to.equal('Appuyez pour définir') + expect(json.sections[1].settings[0].required).to.equal(true) + expect(json.sections[1].settings[0].multiple).to.equal(true) + expect(json.sections[1].settings[0].type).to.equal('DEVICE') + expect(json.sections[1].settings[0].permissions[0]).to.equal('r') + expect(json.sections[1].settings[0].permissions[1]).to.equal('x') + expect(json.sections[1].settings[0].capabilities[0]).to.equal('switch') + }) +}) diff --git a/test/pagebuilder-spec.js b/test/pagebuilder-spec.js index 3cdd876..70082b9 100644 --- a/test/pagebuilder-spec.js +++ b/test/pagebuilder-spec.js @@ -1,206 +1,202 @@ -/*eslint no-undef: "off"*/ -const {expect} = require('chai'); -const Page = require('../lib/pages/page'); - -describe ('pagebuilder', () => { - it('should set page ID', () => { - const page = new Page('mainPage'); - page.name('Page Builder Spec'); - const json = page.toJson(); - expect(json.pageId).to.equal('mainPage'); - expect(json.name).to.equal('Page Builder Spec'); - }); - - it('should process one section', () => { - const page = new Page('mainPage'); - - page.section('When this door opens and closes', (section) => { - section.deviceSetting('contactSensor') - .capabilities(['contactSensor']) - .name('Select an open/close sensor'); - }); - - const json = page.toJson(); - //console.log(JSON.stringify(json, null, 2)) - expect(json.sections.length).to.equal(1); - expect(json.sections[0].name).to.equal('When this door opens and closes'); - expect(json.sections[0].settings.length).to.equal(1); - expect(json.sections[0].settings[0].id).to.equal('contactSensor'); - expect(json.sections[0].settings[0].name).to.equal('Select an open/close sensor'); - expect(json.sections[0].settings[0].description).to.equal('Tap to set'); - expect(json.sections[0].settings[0].required).to.equal(true); - expect(json.sections[0].settings[0].multiple).to.equal(false); - expect(json.sections[0].settings[0].type).to.equal('DEVICE'); - expect(json.sections[0].settings[0].permissions[0]).to.equal('r'); - expect(json.sections[0].settings[0].capabilities[0]).to.equal('contactSensor'); - }); - - it('should process two sections', () => { - const page = new Page('mainPage'); - - page.section('When this door opens and closes', (section) => { - section.deviceSetting('contactSensor') - .capabilities(['contactSensor']) - .name('Select an open/close sensor'); - }); - - page.section('Turn these lights on and off', (section) => { - section.deviceSetting('lights') - .capabilities(['switch']) - .name('Select lights') - .multiple(true) - .permissions('rx'); - }); - - const json = page.toJson(); - //console.log(JSON.stringify(json, null, 2)) - expect(json.sections.length).to.equal(2); - - expect(json.sections[0].name).to.equal('When this door opens and closes'); - expect(json.sections[0].settings.length).to.equal(1); - expect(json.sections[0].settings[0].id).to.equal('contactSensor'); - expect(json.sections[0].settings[0].name).to.equal('Select an open/close sensor'); - expect(json.sections[0].settings[0].description).to.equal('Tap to set'); - expect(json.sections[0].settings[0].required).to.equal(true); - expect(json.sections[0].settings[0].multiple).to.equal(false); - expect(json.sections[0].settings[0].type).to.equal('DEVICE'); - expect(json.sections[0].settings[0].permissions[0]).to.equal('r'); - expect(json.sections[0].settings[0].capabilities[0]).to.equal('contactSensor'); - - expect(json.sections[1].name).to.equal('Turn these lights on and off'); - expect(json.sections[1].settings.length).to.equal(1); - expect(json.sections[1].settings[0].id).to.equal('lights'); - expect(json.sections[1].settings[0].name).to.equal('Select lights'); - expect(json.sections[0].settings[0].description).to.equal('Tap to set'); - expect(json.sections[1].settings[0].required).to.equal(true); - expect(json.sections[1].settings[0].multiple).to.equal(true); - expect(json.sections[1].settings[0].type).to.equal('DEVICE'); - expect(json.sections[1].settings[0].permissions[0]).to.equal('r'); - expect(json.sections[1].settings[0].permissions[1]).to.equal('x'); - expect(json.sections[1].settings[0].capabilities[0]).to.equal('switch'); - }); - - it('should process unnamed section', () => { - const page = new Page('mainPage'); - - page.section((section) => { - section.deviceSetting('contactSensor') - .capabilities(['contactSensor']) - .name('Select an open/close sensor'); - }); - - const json = page.toJson(); - //console.log(JSON.stringify(json, null, 2)) - expect(json.sections.length).to.equal(1); - expect(json.sections[0].name).to.equal(undefined); - expect(json.sections[0].settings.length).to.equal(1); - expect(json.sections[0].settings[0].id).to.equal('contactSensor'); - expect(json.sections[0].settings[0].name).to.equal('Select an open/close sensor'); - expect(json.sections[0].settings[0].required).to.equal(true); - expect(json.sections[0].settings[0].multiple).to.equal(false); - expect(json.sections[0].settings[0].type).to.equal('DEVICE'); - expect(json.sections[0].settings[0].permissions[0]).to.equal('r'); - expect(json.sections[0].settings[0].capabilities[0]).to.equal('contactSensor'); - }); - - it('should detect duplicate setting ID', () => { - const page = new Page('mainPage'); - let caughtError = false; - page.section('When this door opens and closes', (section) => { - section.deviceSetting('sensor') - .capabilities(['contactSensor']) - .name('Select an open/close sensor'); - - try { - section.deviceSetting('sensor') - .capabilities(['motionSensor']) - .name('Select a motion sensor'); - } - catch(e) { - caughtError = true; - } - }); - - expect(caughtError).to.equal(true) - }); - - it('should honor default required', () => { - const page = new Page('mainPage'); - page.defaultRequired(false); - - page.section('When this door opens and closes', (section) => { - section.defaultRequired(true); - section.deviceSetting('contactSensor') - .capabilities(['contactSensor']) - .name('Select an open/close sensor'); - section.deviceSetting('motionSensor') - .capabilities(['motionSensor']) - .name('Select an open/close sensor') - .required(false); - }); - - page.section('Turn these lights on and off', (section) => { - section.deviceSetting('lights') - .capabilities(['switch']) - .name('Select lights') - .multiple(true) - .permissions('rx'); - }); - - const json = page.toJson(); - //console.log(JSON.stringify(json, null, 2)) - expect(json.sections[0].settings[0].required).to.equal(true); - expect(json.sections[0].settings[1].required).to.equal(false); - expect(json.sections[1].settings[0].required).to.equal(false); - }); - - it('options formats', () => { - const page = new Page('mainPage'); - page.defaultRequired(false); - - page.section((section) => { - section.enumSetting('standardOptions').options([{id:'one', name: 'One'}, {id:'two', name: 'Two'}]); - section.enumSetting('mapOptions').options({red:'Red', green:'Green', blue:'Blue'}); - section.enumSetting('simpleOptions').options(['Vanilla','Chocolate','Strawberry']); - }); - - - const json = page.toJson(); - //console.log(JSON.stringify(json, null, 2)) - expect(json.sections[0].settings[0].options[0].id).to.equal('one'); - expect(json.sections[0].settings[0].options[0].name).to.equal('One'); - expect(json.sections[0].settings[0].options[1].id).to.equal('two'); - expect(json.sections[0].settings[0].options[1].name).to.equal('Two'); - - expect(json.sections[0].settings[1].options[0].id).to.equal('red'); - expect(json.sections[0].settings[1].options[0].name).to.equal('Red'); - expect(json.sections[0].settings[1].options[1].id).to.equal('green'); - expect(json.sections[0].settings[1].options[1].name).to.equal('Green'); - expect(json.sections[0].settings[1].options[2].id).to.equal('blue'); - expect(json.sections[0].settings[1].options[2].name).to.equal('Blue'); - - expect(json.sections[0].settings[2].options[0].id).to.equal('Vanilla'); - expect(json.sections[0].settings[2].options[0].name).to.equal('Vanilla'); - expect(json.sections[0].settings[2].options[1].id).to.equal('Chocolate'); - expect(json.sections[0].settings[2].options[1].name).to.equal('Chocolate'); - expect(json.sections[0].settings[2].options[2].id).to.equal('Strawberry'); - expect(json.sections[0].settings[2].options[2].name).to.equal('Strawberry'); - }); - - it('page setting', () => { - const page = new Page('mainPage'); - page.defaultRequired(false); - - page.section((section) => { - section.pageSetting('anotherPage'); - }); - - - const json = page.toJson(); - //console.log(JSON.stringify(json, null, 2)) - expect(json.sections[0].settings[0].id).to.equal('anotherPage'); - expect(json.sections[0].settings[0].page).to.equal('anotherPage'); - expect(json.sections[0].settings[0].required).to.equal(undefined); - }); - -}); \ No newline at end of file +/* eslint no-undef: "off" */ +const {expect} = require('chai') +const Page = require('../lib/pages/page') + +describe('pagebuilder', () => { + it('should set page ID', () => { + const page = new Page('mainPage') + page.name('Page Builder Spec') + const json = page.toJson() + expect(json.pageId).to.equal('mainPage') + expect(json.name).to.equal('Page Builder Spec') + }) + + it('should process one section', () => { + const page = new Page('mainPage') + + page.section('When this door opens and closes', section => { + section.deviceSetting('contactSensor') + .capabilities(['contactSensor']) + .name('Select an open/close sensor') + }) + + const json = page.toJson() + // Console.log(JSON.stringify(json, null, 2)) + expect(json.sections.length).to.equal(1) + expect(json.sections[0].name).to.equal('When this door opens and closes') + expect(json.sections[0].settings.length).to.equal(1) + expect(json.sections[0].settings[0].id).to.equal('contactSensor') + expect(json.sections[0].settings[0].name).to.equal('Select an open/close sensor') + expect(json.sections[0].settings[0].description).to.equal('Tap to set') + expect(json.sections[0].settings[0].required).to.equal(true) + expect(json.sections[0].settings[0].multiple).to.equal(false) + expect(json.sections[0].settings[0].type).to.equal('DEVICE') + expect(json.sections[0].settings[0].permissions[0]).to.equal('r') + expect(json.sections[0].settings[0].capabilities[0]).to.equal('contactSensor') + }) + + it('should process two sections', () => { + const page = new Page('mainPage') + + page.section('When this door opens and closes', section => { + section.deviceSetting('contactSensor') + .capabilities(['contactSensor']) + .name('Select an open/close sensor') + }) + + page.section('Turn these lights on and off', section => { + section.deviceSetting('lights') + .capabilities(['switch']) + .name('Select lights') + .multiple(true) + .permissions('rx') + }) + + const json = page.toJson() + // Console.log(JSON.stringify(json, null, 2)) + expect(json.sections.length).to.equal(2) + + expect(json.sections[0].name).to.equal('When this door opens and closes') + expect(json.sections[0].settings.length).to.equal(1) + expect(json.sections[0].settings[0].id).to.equal('contactSensor') + expect(json.sections[0].settings[0].name).to.equal('Select an open/close sensor') + expect(json.sections[0].settings[0].description).to.equal('Tap to set') + expect(json.sections[0].settings[0].required).to.equal(true) + expect(json.sections[0].settings[0].multiple).to.equal(false) + expect(json.sections[0].settings[0].type).to.equal('DEVICE') + expect(json.sections[0].settings[0].permissions[0]).to.equal('r') + expect(json.sections[0].settings[0].capabilities[0]).to.equal('contactSensor') + + expect(json.sections[1].name).to.equal('Turn these lights on and off') + expect(json.sections[1].settings.length).to.equal(1) + expect(json.sections[1].settings[0].id).to.equal('lights') + expect(json.sections[1].settings[0].name).to.equal('Select lights') + expect(json.sections[0].settings[0].description).to.equal('Tap to set') + expect(json.sections[1].settings[0].required).to.equal(true) + expect(json.sections[1].settings[0].multiple).to.equal(true) + expect(json.sections[1].settings[0].type).to.equal('DEVICE') + expect(json.sections[1].settings[0].permissions[0]).to.equal('r') + expect(json.sections[1].settings[0].permissions[1]).to.equal('x') + expect(json.sections[1].settings[0].capabilities[0]).to.equal('switch') + }) + + it('should process unnamed section', () => { + const page = new Page('mainPage') + + page.section(section => { + section.deviceSetting('contactSensor') + .capabilities(['contactSensor']) + .name('Select an open/close sensor') + }) + + const json = page.toJson() + // Console.log(JSON.stringify(json, null, 2)) + expect(json.sections.length).to.equal(1) + expect(json.sections[0].name).to.equal(undefined) + expect(json.sections[0].settings.length).to.equal(1) + expect(json.sections[0].settings[0].id).to.equal('contactSensor') + expect(json.sections[0].settings[0].name).to.equal('Select an open/close sensor') + expect(json.sections[0].settings[0].required).to.equal(true) + expect(json.sections[0].settings[0].multiple).to.equal(false) + expect(json.sections[0].settings[0].type).to.equal('DEVICE') + expect(json.sections[0].settings[0].permissions[0]).to.equal('r') + expect(json.sections[0].settings[0].capabilities[0]).to.equal('contactSensor') + }) + + it('should detect duplicate setting ID', () => { + const page = new Page('mainPage') + let caughtError = false + page.section('When this door opens and closes', section => { + section.deviceSetting('sensor') + .capabilities(['contactSensor']) + .name('Select an open/close sensor') + + try { + section.deviceSetting('sensor') + .capabilities(['motionSensor']) + .name('Select a motion sensor') + } catch (error) { + caughtError = true + } + }) + + expect(caughtError).to.equal(true) + }) + + it('should honor default required', () => { + const page = new Page('mainPage') + page.defaultRequired(false) + + page.section('When this door opens and closes', section => { + section.defaultRequired(true) + section.deviceSetting('contactSensor') + .capabilities(['contactSensor']) + .name('Select an open/close sensor') + section.deviceSetting('motionSensor') + .capabilities(['motionSensor']) + .name('Select an open/close sensor') + .required(false) + }) + + page.section('Turn these lights on and off', section => { + section.deviceSetting('lights') + .capabilities(['switch']) + .name('Select lights') + .multiple(true) + .permissions('rx') + }) + + const json = page.toJson() + // Console.log(JSON.stringify(json, null, 2)) + expect(json.sections[0].settings[0].required).to.equal(true) + expect(json.sections[0].settings[1].required).to.equal(false) + expect(json.sections[1].settings[0].required).to.equal(false) + }) + + it('options formats', () => { + const page = new Page('mainPage') + page.defaultRequired(false) + + page.section(section => { + section.enumSetting('standardOptions').options([{id: 'one', name: 'One'}, {id: 'two', name: 'Two'}]) + section.enumSetting('mapOptions').options({red: 'Red', green: 'Green', blue: 'Blue'}) + section.enumSetting('simpleOptions').options(['Vanilla', 'Chocolate', 'Strawberry']) + }) + + const json = page.toJson() + // Console.log(JSON.stringify(json, null, 2)) + expect(json.sections[0].settings[0].options[0].id).to.equal('one') + expect(json.sections[0].settings[0].options[0].name).to.equal('One') + expect(json.sections[0].settings[0].options[1].id).to.equal('two') + expect(json.sections[0].settings[0].options[1].name).to.equal('Two') + + expect(json.sections[0].settings[1].options[0].id).to.equal('red') + expect(json.sections[0].settings[1].options[0].name).to.equal('Red') + expect(json.sections[0].settings[1].options[1].id).to.equal('green') + expect(json.sections[0].settings[1].options[1].name).to.equal('Green') + expect(json.sections[0].settings[1].options[2].id).to.equal('blue') + expect(json.sections[0].settings[1].options[2].name).to.equal('Blue') + + expect(json.sections[0].settings[2].options[0].id).to.equal('Vanilla') + expect(json.sections[0].settings[2].options[0].name).to.equal('Vanilla') + expect(json.sections[0].settings[2].options[1].id).to.equal('Chocolate') + expect(json.sections[0].settings[2].options[1].name).to.equal('Chocolate') + expect(json.sections[0].settings[2].options[2].id).to.equal('Strawberry') + expect(json.sections[0].settings[2].options[2].name).to.equal('Strawberry') + }) + + it('page setting', () => { + const page = new Page('mainPage') + page.defaultRequired(false) + + page.section(section => { + section.pageSetting('anotherPage') + }) + + const json = page.toJson() + // Console.log(JSON.stringify(json, null, 2)) + expect(json.sections[0].settings[0].id).to.equal('anotherPage') + expect(json.sections[0].settings[0].page).to.equal('anotherPage') + expect(json.sections[0].settings[0].required).to.equal(undefined) + }) +}) diff --git a/test/smartapp-page-spec.js b/test/smartapp-page-spec.js index 817ed9d..6c39305 100644 --- a/test/smartapp-page-spec.js +++ b/test/smartapp-page-spec.js @@ -1,99 +1,97 @@ -/*eslint no-undef: "off"*/ -const {expect} = require('chai'); -const SmartApp = require('../lib/smart-app'); +/* eslint no-undef: "off" */ +const SmartApp = require('../lib/smart-app') -describe ('smartapp-page-spec', () => { +describe('smartapp-page-spec', () => { + it('should set page ID', () => { + const app = new SmartApp() + app.appId('xxx') + app.page('eaMainPage', (ctx, page) => { + page.section('whenDoorOpensAndCloses', section => { + section.deviceSetting('contactSensor') + .capabilities(['contactSensor']) + }) - it('should set page ID', () => { - const app = new SmartApp(); - app.appId('xxx'); - app.page('eaMainPage', (ctx, page) => { - page.section('whenDoorOpensAndCloses', (section) => { - section.deviceSetting('contactSensor') - .capabilities(['contactSensor']); - }); + page.section('turnLightsOnAndOff', section => { + section.deviceSetting('lights') + .capabilities(['switch']) + .multiple(true) + .permissions('rx') + }) + }) - page.section('turnLightsOnAndOff', (section) => { - section.deviceSetting('lights') - .capabilities(['switch']) - .multiple(true) - .permissions('rx'); - }); - }); + const initResponse = app.handleMockCallback({ + lifecycle: 'CONFIGURATION', + executionId: 'e6903fe6-f88f-da69-4c12-e2802606ccbc', + locale: 'en', + version: '0.1.0', + client: { + os: 'ios', + version: '0.0.0', + language: 'en-US' + }, + configurationData: { + installedAppId: '7d7fa36d-0ad9-4893-985c-6b75858e38e4', + phase: 'INITIALIZE', + pageId: '', + previousPageId: '', + config: {} + }, + settings: {} + }) - const initResponse = app.handleMockCallback({ - 'lifecycle': 'CONFIGURATION', - 'executionId': 'e6903fe6-f88f-da69-4c12-e2802606ccbc', - 'locale': 'en', - 'version': '0.1.0', - 'client': { - 'os': 'ios', - 'version': '0.0.0', - 'language': 'en-US' - }, - 'configurationData': { - 'installedAppId': '7d7fa36d-0ad9-4893-985c-6b75858e38e4', - 'phase': 'INITIALIZE', - 'pageId': '', - 'previousPageId': '', - 'config': {} - }, - 'settings': {} - }); + const pageReponse = app.handleMockCallback({ + lifecycle: 'CONFIGURATION', + executionId: 'abcf6e72-60f4-1f27-341b-449ad9e2192e', + locale: 'en', + version: '0.1.0', + client: { + os: 'ios', + version: '0.0.0', + language: 'fr' + }, + configurationData: { + installedAppId: '702d6539-cde1-4baf-9336-10110a0fd000', + phase: 'PAGE', + pageId: '', + previousPageId: '', + config: {} + }, + settings: {} + }) - const pageReponse = app.handleMockCallback({ - 'lifecycle': 'CONFIGURATION', - 'executionId': 'abcf6e72-60f4-1f27-341b-449ad9e2192e', - 'locale': 'en', - 'version': '0.1.0', - 'client': { - 'os': 'ios', - 'version': '0.0.0', - 'language': 'fr' - }, - 'configurationData': { - 'installedAppId': '702d6539-cde1-4baf-9336-10110a0fd000', - 'phase': 'PAGE', - 'pageId': '', - 'previousPageId': '', - 'config': {} - }, - 'settings': {} - }); + console.log(JSON.stringify(initResponse, null, 2)) + console.log(JSON.stringify(pageReponse, null, 2)) + }) - console.log(JSON.stringify(initResponse, null, 2)); - console.log(JSON.stringify(pageReponse, null, 2)); - }); + it('should configure event logger', () => { + const app = new SmartApp() + app.appId('xxx') + app.enableEventLogging(4) + app.page('eaMainPage', (ctx, page) => { + page.section('whenDoorOpensAndCloses', section => { + section.deviceSetting('contactSensor') + .capabilities(['contactSensor']) + }) + }) - it('should configure event logger', () => { - const app = new SmartApp(); - app.appId('xxx'); - app.enableEventLogging(4); - app.page('eaMainPage', (ctx, page) => { - page.section('whenDoorOpensAndCloses', (section) => { - section.deviceSetting('contactSensor') - .capabilities(['contactSensor']); - }); - }); - - app.handleMockCallback({ - 'lifecycle': 'CONFIGURATION', - 'executionId': 'e6903fe6-f88f-da69-4c12-e2802606ccbc', - 'locale': 'en', - 'version': '0.1.0', - 'client': { - 'os': 'ios', - 'version': '0.0.0', - 'language': 'en-US' - }, - 'configurationData': { - 'installedAppId': '7d7fa36d-0ad9-4893-985c-6b75858e38e4', - 'phase': 'INITIALIZE', - 'pageId': '', - 'previousPageId': '', - 'config': {} - }, - 'settings': {} - }); - }); -}); \ No newline at end of file + app.handleMockCallback({ + lifecycle: 'CONFIGURATION', + executionId: 'e6903fe6-f88f-da69-4c12-e2802606ccbc', + locale: 'en', + version: '0.1.0', + client: { + os: 'ios', + version: '0.0.0', + language: 'en-US' + }, + configurationData: { + installedAppId: '7d7fa36d-0ad9-4893-985c-6b75858e38e4', + phase: 'INITIALIZE', + pageId: '', + previousPageId: '', + config: {} + }, + settings: {} + }) + }) +})