From 0c95a3efd9a8c876d4577114a10d134075939767 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Tue, 10 Mar 2020 22:04:06 +0100 Subject: [PATCH 01/94] Add Sensor device type Signed-off-by: Michael Krug --- functions/devices.js | 63 ++++++++++++++++++++++++ tests/devices.metadata.test.js | 89 ++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) diff --git a/functions/devices.js b/functions/devices.js index b8517a93..8083243e 100644 --- a/functions/devices.js +++ b/functions/devices.js @@ -592,6 +592,68 @@ class AirPurifier extends Fan { } } +/* Sensor items */ + +class Sensor extends GenericDevice { + static get type() { + return 'action.devices.types.SENSOR'; + } + + static get traits() { + return [ + 'action.devices.traits.SensorState' + ]; + } + + static getAttributes(item) { + const config = getConfig(item); + if (!config || !config.sensorName) { + return {}; + } + const attributes = { + sensorStatesSupported: { + name: config.sensorName + } + }; + if (config.valueUnit) { + attributes.sensorStatesSupported.numericCapabilities = {} + attributes.sensorStatesSupported.numericCapabilities.rawValueUnit = config.valueUnit + } + if (config.states) { + attributes.sensorStatesSupported.descriptiveCapabilities = {} + attributes.sensorStatesSupported.descriptiveCapabilities.availableStates = config.states.split(',').map((state) => state.split('=')[0]); + } + return attributes; + } + + static getState(item) { + const config = getConfig(item); + return { + currentSensorStateData: { + name: config.sensorName, + currentSensorState: this.translateStateToGoogle(item), + rawValue: Number(item.state) || 0 + } + }; + } + + static translateStateToGoogle(item) { + const config = getConfig(item); + if ('states' in config) { + const states = config.states.split(',') + for (const state of states) { + const [ key, value ] = state.split('='); + if (value == item.state) { + return key; + } + } + } + return ''; + } +} + +/* Thermostat items formed by a group of items */ + class Thermostat extends GenericDevice { static get type() { return 'action.devices.types.THERMOSTAT'; @@ -743,6 +805,7 @@ const Devices = [ SimpleFan, Fan, SimpleHood, Hood, SimpleAirPurifier, AirPurifier, + Sensor, Thermostat ]; diff --git a/tests/devices.metadata.test.js b/tests/devices.metadata.test.js index 8abbffe5..5d2ee31e 100644 --- a/tests/devices.metadata.test.js +++ b/tests/devices.metadata.test.js @@ -248,6 +248,94 @@ describe('Test Rollershutter Devices with Metadata', () => { }); }); +describe('Test Sensor Device with Metadata', () => { + const item = { + type: 'Number', + metadata: { + ga: { + value: 'Sensor', + config: { + sensorName: 'MySensor', + valueUnit: 'Percent', + states: 'off=0,low=50,high=100' + } + } + }, + state: '100' + }; + const device = Devices.getDeviceForItem(item); + + test('getAttributes', () => { + expect(device.getAttributes({ + metadata: { + ga: { + value: 'Sensor', + config: { + sensorName: 'AirQuality' + } + } + } + })).toStrictEqual({ + 'sensorStatesSupported': { + 'name': 'AirQuality' + } + }); + + expect(device.getAttributes({ + metadata: { + ga: { + value: 'Sensor', + config: { + sensorName: 'MySensor', + valueUnit: 'Percent' + } + } + } + })).toStrictEqual({ + 'sensorStatesSupported': { + 'name': 'MySensor', + 'numericCapabilities': { + 'rawValueUnit': 'Percent' + } + } + }); + + expect(device.getAttributes({ + metadata: { + ga: { + value: 'Sensor', + config: { + sensorName: 'MySensor', + states: 'off=0,low=50,high=100' + } + } + } + })).toStrictEqual({ + 'sensorStatesSupported': { + 'name': 'MySensor', + 'descriptiveCapabilities': { + 'availableStates': ['off', 'low', 'high'] + } + } + }); + }); + + test('translateModeToGoogle', () => { + expect(device.name).toBe('Sensor'); + expect(device.translateStateToGoogle(item)).toBe('high'); + }); + + test('getState', () => { + expect(device.getState(item)).toStrictEqual({ + "currentSensorStateData": { + "currentSensorState": "high", + "name": "MySensor", + "rawValue": 100, + } + }); + }); +}); + describe('Test Thermostat Device with Metadata', () => { test('getDeviceForItem', () => { expect(Devices.getDeviceForItem({ @@ -474,6 +562,7 @@ describe('Test Thermostat Device with Metadata', () => { name: 'MyItem', label: 'MyThermostat', type: 'Group', + groupType: 'Group', metadata: { ga: { value: 'Thermostat', From 5a2068e240012587e3a11a1186d74f4c20b889d1 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 4 Jun 2020 12:49:53 +0200 Subject: [PATCH 02/94] Add support for Contact items for Locks and OpenClose devices Signed-off-by: Michael Krug --- functions/commands.js | 9 ++ tests/openhab.metadata.test.js | 160 ++++++++++++++++++++++++++++++++- 2 files changed, 165 insertions(+), 4 deletions(-) diff --git a/functions/commands.js b/functions/commands.js index 1fdc844f..b929a3c8 100644 --- a/functions/commands.js +++ b/functions/commands.js @@ -180,6 +180,9 @@ class LockUnlockCommand extends GenericCommand { } static convertParamsToValue(params, item, device) { + if (device.customData && device.customData.itemType === 'Contact') { + throw { statusCode: 400 }; + } let lock = params.lock; if (device.customData && device.customData.inverted === true) { lock = !lock; @@ -393,6 +396,9 @@ class OpenCloseCommand extends GenericCommand { } static convertParamsToValue(params, item, device) { + if (device.customData && device.customData.itemType === 'Contact') { + throw { statusCode: 400 }; + } let openPercent = params.openPercent; if (device.customData && device.customData.inverted === true) { openPercent = 100 - openPercent; @@ -422,6 +428,9 @@ class StartStopCommand extends GenericCommand { } static convertParamsToValue(params, item, device) { + if (device.customData && device.customData.itemType === 'Contact') { + throw { statusCode: 400 }; + } let value = params.start ? 'MOVE' : 'STOP'; // item can not handle StartStop --> we will send "ON" / "OFF" if (device.customData && device.customData.itemType !== 'Rollershutter') { diff --git a/tests/openhab.metadata.test.js b/tests/openhab.metadata.test.js index d536aded..5dc3c97f 100644 --- a/tests/openhab.metadata.test.js +++ b/tests/openhab.metadata.test.js @@ -321,6 +321,41 @@ describe('Test QUERY with Metadata', () => { }); }); + test('Lock Device as Contact', async () => { + const item = + { + "state": "CLOSED", + "type": "Contact", + "name": "MyLock", + "metadata": { + "ga": { + "value": "Lock" + } + } + }; + + const getItemMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + + const apiHandler = { + getItem: getItemMock + }; + + const payload = await new OpenHAB(apiHandler).handleQuery([{ + "id": "MyLock" + }]); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(payload).toStrictEqual({ + "devices": { + "MyLock": { + "isLocked": true, + "online": true, + }, + }, + }); + }); + test('Multiple Light Devices', async () => { const item1 = { @@ -484,6 +519,41 @@ describe('Test QUERY with Metadata', () => { }); }); + test('Window as Contact', async () => { + const item = + { + "state": "CLOSED", + "type": "Contact", + "name": "MyWindow", + "metadata": { + "ga": { + "value": "Window" + } + } + }; + + const getItemMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + + const apiHandler = { + getItem: getItemMock + }; + + const payload = await new OpenHAB(apiHandler).handleQuery([{ + "id": "MyWindow" + }]); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(payload).toStrictEqual({ + "devices": { + "MyWindow": { + "openPercent": 0, + "online": true, + }, + }, + }); + }); + test('Fan Device', async () => { const item = { "state": "50", @@ -1141,7 +1211,7 @@ describe('Test EXECUTE with Metadata', () => { }); }); - test('Lock with required acknowledge', async () => { + test('LockUnlock with Lock and required acknowledge', async () => { const item = { "state": "OFF", @@ -1206,7 +1276,7 @@ describe('Test EXECUTE with Metadata', () => { }); }); - test('Lock with acknowledged challenge', async () => { + test('LockUnlock with Lock and acknowledged challenge', async () => { const getItemMock = jest.fn(); const sendCommandMock = jest.fn(); getItemMock.mockReturnValue(Promise.resolve()); @@ -1253,7 +1323,7 @@ describe('Test EXECUTE with Metadata', () => { }); }); - test('Lock inverted', async () => { + test('LockUnlock with Lock inverted', async () => { const getItemMock = jest.fn(); const sendCommandMock = jest.fn(); getItemMock.mockReturnValue(Promise.resolve()); @@ -1274,7 +1344,7 @@ describe('Test EXECUTE with Metadata', () => { "execution": [{ "command": "action.devices.commands.LockUnlock", "params": { - lock: true + "lock": true } }] }]; @@ -1297,6 +1367,88 @@ describe('Test EXECUTE with Metadata', () => { }); }); + test('LockUnlock with Lock as Contact', async () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "customData": { + "itemType": "Contact" + }, + "id": "MyLock" + }], + "execution": [{ + "command": "action.devices.commands.LockUnlock", + "params": { + "lock": true + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(0); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyLock" + ], + "status": "ERROR", + "errorCode": "notSupported" + }] + }); + }); + + test('OpenClose with OpenCloseDevice as Contact', async () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "customData": { + "itemType": "Contact" + }, + "id": "MyLock" + }], + "execution": [{ + "command": "action.devices.commands.OpenClose", + "params": { + "openPercent": 0 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(0); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyLock" + ], + "status": "ERROR", + "errorCode": "notSupported" + }] + }); + }); + test('BrightnessAbsolute with required acknowledge', async () => { const getItemMock = jest.fn(); const sendCommandMock = jest.fn(); From ddb25b87e6fe959f654c152d92579b0ef3e4357f Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 4 Jun 2020 14:58:04 +0200 Subject: [PATCH 03/94] Add attributes for OpenClose devices Signed-off-by: Michael Krug --- functions/devices.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/functions/devices.js b/functions/devices.js index 8083243e..fd6d0291 100644 --- a/functions/devices.js +++ b/functions/devices.js @@ -409,6 +409,21 @@ class GenericOpenCloseDevice extends GenericDevice { ]; } + static getAttributes(item) { + const attributes = {}; + attributes.discreteOnlyOpenClose = getConfig(item).discreteOnlyOpenClose === true; + attributes.queryOnlyOpenClose = getConfig(item).queryOnlyOpenClose === true; + const itemType = item.type === 'Group' && item.groupType ? item.groupType : item.type; + if (itemType === 'Switch') { + attributes.discreteOnlyOpenClose = true; + } + if (itemType === 'Contact') { + attributes.discreteOnlyOpenClose = true; + attributes.queryOnlyOpenClose = true; + } + return attributes; + } + static get requiredItemTypes() { return ['Rollershutter', 'Switch']; } From b1aa4a32889609693e620be4db4f3259317f4e1a Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 4 Jun 2020 14:58:11 +0200 Subject: [PATCH 04/94] Add and update tests Signed-off-by: Michael Krug --- tests/devices.metadata.test.js | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/tests/devices.metadata.test.js b/tests/devices.metadata.test.js index 5d2ee31e..8b486deb 100644 --- a/tests/devices.metadata.test.js +++ b/tests/devices.metadata.test.js @@ -198,7 +198,7 @@ describe('Test Light Devices with Metadata', () => { }); }); -describe('Test Rollershutter Devices with Metadata', () => { +describe('Test OpenClose Devices', () => { test('Invalid Blinds Type', () => { expect(Devices.getDeviceForItem({ type: 'Dimmer', @@ -222,6 +222,10 @@ describe('Test Rollershutter Devices with Metadata', () => { }; const device = Devices.getDeviceForItem(item); expect(device.name).toBe('Blinds'); + expect(device.getAttributes(item)).toStrictEqual({ + discreteOnlyOpenClose: false, + queryOnlyOpenClose: false + }); expect(device.getState(item)).toStrictEqual({ openPercent: 100 }); @@ -235,17 +239,43 @@ describe('Test Rollershutter Devices with Metadata', () => { ga: { value: 'BLINDS', config: { - inverted: true + inverted: true, + discreteOnlyOpenClose: true } } } }; const device = Devices.getDeviceForItem(item); expect(device.name).toBe('Blinds'); + expect(device.getAttributes(item)).toStrictEqual({ + discreteOnlyOpenClose: true, + queryOnlyOpenClose: false + }); expect(device.getState(item)).toStrictEqual({ openPercent: 0 }); }); + + test('Window as Contact', () => { + const item = { + type: 'Contact', + state: 'OPEN', + metadata: { + ga: { + value: 'WINDOW' + } + } + }; + const device = Devices.getDeviceForItem(item); + expect(device.name).toBe('Window'); + expect(device.getAttributes(item)).toStrictEqual({ + discreteOnlyOpenClose: true, + queryOnlyOpenClose: true + }); + expect(device.getState(item)).toStrictEqual({ + openPercent: 100 + }); + }); }); describe('Test Sensor Device with Metadata', () => { From 77e07bfb6d0a41193360873686ca0cb25491a420 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 4 Jun 2020 15:18:51 +0200 Subject: [PATCH 05/94] Remove unnecessary customData, Rename tfaAck & tfaPin Signed-off-by: Michael Krug --- README.md | 8 +++---- docs/USAGE.md | 8 +++---- functions/commands.js | 6 ++--- tests/__snapshots__/openhab.tags.test.js.snap | 24 ------------------- tests/openhab.metadata.test.js | 14 +++++------ 5 files changed, 18 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index e3ed0ddf..573f1de3 100644 --- a/README.md +++ b/README.md @@ -208,8 +208,8 @@ Currently the following metadata values are supported (also depending on Googles * `Switch { ga="Sprinkler" }` * `Switch { ga="Vacuum" }` * `Switch { ga="Scene" }` -* `Switch { ga="Lock" [ tfaAck=true ] }` -* `Switch { ga="SecuritySystem" [ tfaPin="1234" ] }` +* `Switch { ga="Lock" [ ackNeeded=true ] }` +* `Switch { ga="SecuritySystem" [ pinNeeded="1234" ] }` * `Dimmer { ga="Speaker" }` * `Switch / Dimmer { ga="Fan" [ speeds="0=away:zero,50=default:standard:one,100=high:two", lang="en", ordered=true ] }` (for Dimmer the options have to be set) * `Switch / Dimmer { ga="Hood" }` @@ -273,8 +273,8 @@ _pinNeeded_: "A two-factor authentication that requires a personal identificatio Example: ``` -Switch DoorLock "Front Door" { ga="Lock" [ tfaAck=true ] } -Switch HouseAlarm "House Alarm" { ga="SecuritySystem" [ tfaPin="1234" ] } +Switch DoorLock "Front Door" { ga="Lock" [ ackNeeded=true ] } +Switch HouseAlarm "House Alarm" { ga="SecuritySystem" [ pinNeeded="1234" ] } ``` #### Thermostats diff --git a/docs/USAGE.md b/docs/USAGE.md index 2018e88d..cdb86e37 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -36,8 +36,8 @@ Currently the following metadata values are supported (also depending on Googles * `Switch { ga="Sprinkler" }` * `Switch { ga="Vacuum" }` * `Switch { ga="Scene" }` -* `Switch { ga="Lock" [ tfaAck=true ] }` -* `Switch { ga="SecuritySystem" [ tfaPin="1234" ] }` +* `Switch { ga="Lock" [ ackNeeded=true ] }` +* `Switch { ga="SecuritySystem" [ pinNeeded="1234" ] }` * `Dimmer { ga="Speaker" }` * `Switch / Dimmer { ga="Fan" [ speeds="0=away:zero,50=default:standard:one,100=high:two", lang="en", ordered=true ] }` (for Dimmer the options have to be set) * `Switch / Dimmer { ga="Hood" }` @@ -118,8 +118,8 @@ _pinNeeded_: "A two-factor authentication that requires a personal identificatio Example: ``` -Switch DoorLock "Front Door" { ga="Lock" [ tfaAck=true ] } -Switch HouseAlarm "House Alarm" { ga="SecuritySystem" [ tfaPin="1234" ] } +Switch DoorLock "Front Door" { ga="Lock" [ ackNeeded=true ] } +Switch HouseAlarm "House Alarm" { ga="SecuritySystem" [ pinNeeded="1234" ] } ``` #### Thermostats diff --git a/functions/commands.js b/functions/commands.js index b929a3c8..3c9115e9 100644 --- a/functions/commands.js +++ b/functions/commands.js @@ -62,7 +62,7 @@ class GenericCommand { } static handlAuthPin(device = {}, challenge = {}) { - if (!device.customData || !device.customData.tfaPin || challenge.pin === device.customData.tfaPin) { + if (!device.customData || !device.customData.pinNeeded || challenge.pin === device.customData.pinNeeded) { return; } return { @@ -76,7 +76,7 @@ class GenericCommand { } static handlAuthAck(device = {}, challenge = {}, responseStates = {}) { - if (!device.customData || !device.customData.tfaAck || challenge.ack === true) { + if (!device.customData || !device.customData.ackNeeded || challenge.ack === true) { return; } return { @@ -101,7 +101,7 @@ class GenericCommand { return Promise.resolve(); } - const ackWithState = ackSupported.includes(this.type) && device.customData && device.customData.tfaAck && !challenge.ack; + const ackWithState = ackSupported.includes(this.type) && device.customData && device.customData.ackNeeded && !challenge.ack; let getItemPromise = Promise.resolve(({ name: device.id })); if (this.requiresItem || ackWithState) { diff --git a/tests/__snapshots__/openhab.tags.test.js.snap b/tests/__snapshots__/openhab.tags.test.js.snap index 87ac6d04..5dc90e4d 100644 --- a/tests/__snapshots__/openhab.tags.test.js.snap +++ b/tests/__snapshots__/openhab.tags.test.js.snap @@ -6,11 +6,7 @@ Object { Object { "attributes": Object {}, "customData": Object { - "deviceType": "action.devices.types.LIGHT", - "inverted": false, "itemType": "Switch", - "tfaAck": undefined, - "tfaPin": undefined, }, "deviceInfo": Object { "hwVersion": "2.5.0", @@ -39,11 +35,7 @@ Object { Object { "attributes": Object {}, "customData": Object { - "deviceType": "action.devices.types.LIGHT", - "inverted": false, "itemType": "Dimmer", - "tfaAck": undefined, - "tfaPin": undefined, }, "deviceInfo": Object { "hwVersion": "2.5.0", @@ -75,11 +67,7 @@ Object { "colorModel": "hsv", }, "customData": Object { - "deviceType": "action.devices.types.LIGHT", - "inverted": false, "itemType": "Color", - "tfaAck": undefined, - "tfaPin": undefined, }, "deviceInfo": Object { "hwVersion": "2.5.0", @@ -110,11 +98,7 @@ Object { Object { "attributes": Object {}, "customData": Object { - "deviceType": "action.devices.types.LIGHT", - "inverted": false, "itemType": "Switch", - "tfaAck": undefined, - "tfaPin": undefined, }, "deviceInfo": Object { "hwVersion": "2.5.0", @@ -143,11 +127,7 @@ Object { Object { "attributes": Object {}, "customData": Object { - "deviceType": "action.devices.types.LIGHT", - "inverted": false, "itemType": "Dimmer", - "tfaAck": undefined, - "tfaPin": undefined, }, "deviceInfo": Object { "hwVersion": "2.5.0", @@ -179,11 +159,7 @@ Object { "colorModel": "hsv", }, "customData": Object { - "deviceType": "action.devices.types.LIGHT", - "inverted": false, "itemType": "Color", - "tfaAck": undefined, - "tfaPin": undefined, }, "deviceInfo": Object { "hwVersion": "2.5.0", diff --git a/tests/openhab.metadata.test.js b/tests/openhab.metadata.test.js index 5dc3c97f..e05e8914 100644 --- a/tests/openhab.metadata.test.js +++ b/tests/openhab.metadata.test.js @@ -1222,7 +1222,7 @@ describe('Test EXECUTE with Metadata', () => { "ga": { "value": "Lock", "config": { - "tfaAck": true + "ackNeeded": true } } }, @@ -1242,7 +1242,7 @@ describe('Test EXECUTE with Metadata', () => { const commands = [{ "devices": [{ "customData": { - "tfaAck": true + "ackNeeded": true }, "id": "MyLock" }], @@ -1290,7 +1290,7 @@ describe('Test EXECUTE with Metadata', () => { const commands = [{ "devices": [{ "customData": { - "tfaAck": true + "ackNeeded": true }, "id": "MyLock" }], @@ -1463,7 +1463,7 @@ describe('Test EXECUTE with Metadata', () => { const commands = [{ "devices": [{ "customData": { - "tfaAck": true + "ackNeeded": true }, "id": "MyLight" }], @@ -1510,7 +1510,7 @@ describe('Test EXECUTE with Metadata', () => { const commands = [{ "devices": [{ "customData": { - "tfaPin": "1234" + "pinNeeded": "1234" }, "id": "MyAlarm" }], @@ -1553,7 +1553,7 @@ describe('Test EXECUTE with Metadata', () => { const commands = [{ "devices": [{ "customData": { - "tfaPin": "1234" + "pinNeeded": "1234" }, "id": "MyAlarm" }], @@ -1599,7 +1599,7 @@ describe('Test EXECUTE with Metadata', () => { const commands = [{ "devices": [{ "customData": { - "tfaPin": "1234" + "pinNeeded": "1234" }, "id": "MyAlarm" }], From e694ffe0018f9b8030bdfcbe4fecac0e4dd49382 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 4 Jun 2020 15:31:29 +0200 Subject: [PATCH 06/94] Add note about tags to docu Signed-off-by: Michael Krug --- README.md | 3 +++ docs/USAGE.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index 573f1de3..0b839d86 100644 --- a/README.md +++ b/README.md @@ -258,6 +258,9 @@ NOTE: metadata is not (yet?) available via paperUI. Either you create your items } ``` +NOTE: Please be aware that for backward compatibilty also the former usage of tags (ref. [Google Assistant Action Documentation v2.5](https://www.openhab.org/v2.5/docs/ecosystem/google-assistant/)) to specify items to be exposed to Google Assistent is supported and may cause unexpected behavior. +Items that contain tags that refer to a valid Google Assistent device will be exposed regardless of having metadata set. E.g.: `Switch MyBulb ["Lighting"]`. + ### Special item configurations #### Two-Factor-Authentication diff --git a/docs/USAGE.md b/docs/USAGE.md index cdb86e37..ccefee41 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -103,6 +103,9 @@ NOTE: metadata is not (yet?) available via paperUI. Either you create your items } ``` +NOTE: Please be aware that for backward compatibilty also the former usage of tags (ref. [Google Assistant Action Documentation v2.5](https://www.openhab.org/v2.5/docs/ecosystem/google-assistant/)) to specify items to be exposed to Google Assistent is supported and may cause unexpected behavior. +Items that contain tags that refer to a valid Google Assistent device will be exposed regardless of having metadata set. E.g.: `Switch MyBulb ["Lighting"]`. + ### Special item configurations #### Two-Factor-Authentication From 0e1dc7bda0fa798d2fca4fc0cd8b4c02c22533d6 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Fri, 5 Jun 2020 23:04:52 +0200 Subject: [PATCH 07/94] Improve efficiency of getting items, fix synonyms Signed-off-by: Michael Krug --- .../openhab.metadata.test.js.snap | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/__snapshots__/openhab.metadata.test.js.snap b/tests/__snapshots__/openhab.metadata.test.js.snap index 91f0c370..58750938 100644 --- a/tests/__snapshots__/openhab.metadata.test.js.snap +++ b/tests/__snapshots__/openhab.metadata.test.js.snap @@ -383,3 +383,42 @@ Object { ], } `; + +exports[`Test SYNC with Metadata Thermostat Device 1`] = ` +Object { + "devices": Array [ + Object { + "attributes": Object { + "availableThermostatModes": "off,heat,eco,auto", + "thermostatTemperatureUnit": "C", + }, + "customData": Object { + "itemType": "Group", + }, + "deviceInfo": Object { + "hwVersion": "2.5.0", + "manufacturer": "openHAB", + "model": "Group:MyThermostat", + "swVersion": "2.5.0", + }, + "id": "MyThermostat", + "name": Object { + "defaultNames": Array [ + "My Thermostat", + ], + "name": "My Thermostat", + "nicknames": Array [ + "My Thermostat", + ], + }, + "roomHint": undefined, + "structureHint": undefined, + "traits": Array [ + "action.devices.traits.TemperatureSetting", + ], + "type": "action.devices.types.THERMOSTAT", + "willReportState": false, + }, + ], +} +`; From dfd68cbae874a6503f330a8d3362fab080febb58 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 20 Jun 2020 21:46:03 +0200 Subject: [PATCH 08/94] Refactor code, increase robustness Signed-off-by: Michael Krug --- functions/devices.js | 68 +++++++++++++++++++--------------- tests/devices.metadata.test.js | 36 +++++++++++++++++- 2 files changed, 74 insertions(+), 30 deletions(-) diff --git a/functions/devices.js b/functions/devices.js index fd6d0291..e215982a 100644 --- a/functions/devices.js +++ b/functions/devices.js @@ -58,7 +58,7 @@ class GenericDevice { name: { name: config.name || item.label, defaultNames: [config.name || item.label], - nicknames: [config.name || item.label, ...(item.metadata && item.metadata.synonyms ? item.metadata.synonyms.value.split(',') : [])] + nicknames: [config.name || item.label, ...(item.metadata && item.metadata.synonyms ? item.metadata.synonyms.value.split(',').map(s => s.trim()) : [])] }, willReportState: false, roomHint: config.roomHint, @@ -78,6 +78,16 @@ class GenericDevice { tfaPin: config.tfaPin } }; + if (config.inverted === true) { + metadata.customData.inverted = true; + } + if (config.ackNeeded === true) { + metadata.customData.ackNeeded = true; + } + if (typeof (config.pinNeeded) === 'string') { + metadata.customData.pinNeeded = config.pinNeeded; + } + return metadata; } static get requiredItemTypes() { @@ -366,15 +376,15 @@ class ColorLight extends GenericDevice { const attributes = { colorModel: 'hsv' }; - const colorTemperatureRange = getConfig(item).colorTemperatureRange; - if (colorTemperatureRange) { - try { - const range = colorTemperatureRange.split(','); + const config = getConfig(item); + if ('colorTemperatureRange' in config) { + const [min, max] = config.colorTemperatureRange.split(',').map(s => Number(s.trim())); + if (!isNaN(min) && !isNaN(max)) { attributes.colorTemperatureRange = { - temperatureMinK: Number(range[0]), - temperatureMaxK: Number(range[1]) + temperatureMinK: min, + temperatureMaxK: max }; - } catch (err) { } + } } return attributes; } @@ -384,15 +394,15 @@ class ColorLight extends GenericDevice { } static getState(item) { - const hsvArray = item.state.split(",").map((val) => Number(val)); + const [hue, sat, val] = item.state.split(',').map(s => Number(s.trim())); return { - on: hsvArray[2] > 0, - brightness: hsvArray[2], + on: val > 0, + brightness: val, color: { spectrumHSV: { - hue: hsvArray[0], - saturation: hsvArray[1] / 100, - value: hsvArray[2] / 100 + hue: hue, + saturation: sat / 100, + value: val / 100 } } }; @@ -533,7 +543,7 @@ class Camera extends GenericDevice { static getAttributes(item) { return { - cameraStreamSupportedProtocols: (getConfig(item).protocols || 'hls,dash').split(','), + cameraStreamSupportedProtocols: (getConfig(item).protocols || 'hls,dash').split(',').map(s => s.trim()), cameraStreamNeedAuthToken: getConfig(item).token ? true : false, cameraStreamNeedDrmEncryption: false }; @@ -570,11 +580,11 @@ class Fan extends GenericDevice { }; config.speeds.split(',').forEach((speedEntry) => { try { - const [speedName, speedSynonyms] = speedEntry.split('='); + const [speedName, speedSynonyms] = speedEntry.trim().split('=').map(s => s.trim()); attributes.availableFanSpeeds.speeds.push({ speed_name: speedName, speed_values: [{ - speed_synonym: speedSynonyms.split(':'), + speed_synonym: speedSynonyms.split(':').map(s => s.trim()), lang: config.lang || 'en' }] }); @@ -636,7 +646,7 @@ class Sensor extends GenericDevice { } if (config.states) { attributes.sensorStatesSupported.descriptiveCapabilities = {} - attributes.sensorStatesSupported.descriptiveCapabilities.availableStates = config.states.split(',').map((state) => state.split('=')[0]); + attributes.sensorStatesSupported.descriptiveCapabilities.availableStates = config.states.split(',').map(s => s.trim().split('=')[0].trim()); } return attributes; } @@ -647,7 +657,7 @@ class Sensor extends GenericDevice { currentSensorStateData: { name: config.sensorName, currentSensorState: this.translateStateToGoogle(item), - rawValue: Number(item.state) || 0 + rawValue: Number(item.state) || 0 } }; } @@ -655,9 +665,9 @@ class Sensor extends GenericDevice { static translateStateToGoogle(item) { const config = getConfig(item); if ('states' in config) { - const states = config.states.split(',') + const states = config.states.split(',').map(s => s.trim()) for (const state of states) { - const [ key, value ] = state.split('='); + const [key, value] = state.split('=').map(s => s.trim()); if (value == item.state) { return key; } @@ -685,12 +695,12 @@ class Thermostat extends GenericDevice { const attributes = { thermostatTemperatureUnit: this.usesFahrenheit(item) ? 'F' : 'C' }; - if (('thermostatTemperatureRange' in config)) { - const range = config.thermostatTemperatureRange.split(','); - if (range.length > 1) { + if ('thermostatTemperatureRange' in config) { + const [min, max] = config.thermostatTemperatureRange.split(',').map(s => parseFloat(s.trim())); + if (!isNaN(min) && !isNaN(max)) { attributes.thermostatTemperatureRange = { - minThresholdCelsius: parseFloat(range[0]), - maxThresholdCelsius: parseFloat(range[1]) + minThresholdCelsius: min, + maxThresholdCelsius: max } } } @@ -769,12 +779,12 @@ class Thermostat extends GenericDevice { const config = getConfig(item); let modes = ['off', 'heat', 'cool', 'on', 'heatcool', 'auto', 'eco']; if ('modes' in config) { - modes = config.modes.split(','); + modes = config.modes.split(',').map(s => s.trim()); } const modeMap = {}; modes.forEach(pair => { - const [ key, value ] = pair.split('='); - modeMap[key] = value ? value.split(':') : [key]; + const [key, value] = pair.split('=').map(s => s.trim()); + modeMap[key] = value ? value.split(':').map(s => s.trim()) : [key]; }); return modeMap; } diff --git a/tests/devices.metadata.test.js b/tests/devices.metadata.test.js index 8b486deb..00db8d37 100644 --- a/tests/devices.metadata.test.js +++ b/tests/devices.metadata.test.js @@ -181,7 +181,7 @@ describe('Test Light Devices with Metadata', () => { ga: { value: 'LIGHT', config: { - colorTemperatureRange: "1000,4000" + colorTemperatureRange: '1000,4000' } } } @@ -196,6 +196,25 @@ describe('Test Light Devices with Metadata', () => { } }); }); + + test('Color Light Type colorTemperatureRange with invalid value', () => { + const item = { + type: 'Color', + metadata: { + ga: { + value: 'LIGHT', + config: { + colorTemperatureRange: 'a,b' + } + } + } + }; + const device = Devices.getDeviceForItem(item); + expect(device.name).toBe('ColorLight'); + expect(device.getAttributes(item)).toStrictEqual({ + colorModel: 'hsv' + }); + }); }); describe('Test OpenClose Devices', () => { @@ -485,6 +504,21 @@ describe('Test Thermostat Device with Metadata', () => { availableThermostatModes: 'off,heat,cool,on,heatcool,auto,eco', thermostatTemperatureUnit: 'C' }); + + // wrong value for thermostatTemperatureRange should not throw error + expect(Devices.Thermostat.getAttributes({ + metadata: { + ga: { + value: 'Thermostat', + config: { + thermostatTemperatureRange: 'a,b' + } + } + } + })).toStrictEqual({ + availableThermostatModes: 'off,heat,cool,on,heatcool,auto,eco', + thermostatTemperatureUnit: 'C' + }); }); test('getModeMap', () => { From 11e832c18d2f31dae754fb2cb6cf2ccd67336cbb Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 20 Jun 2020 23:22:45 +0200 Subject: [PATCH 09/94] Restructure devices into single files Signed-off-by: Michael Krug --- .eslintrc | 3 + .eslintrc.json | 23 - functions/apihandler.js | 2 +- functions/commands.js | 20 +- functions/devices.js | 816 +------------------------ functions/devices/airpurifier.js | 9 + functions/devices/awning.js | 9 + functions/devices/blinds.js | 9 + functions/devices/camera.js | 28 + functions/devices/coffeemaker.js | 9 + functions/devices/colorlight.js | 53 ++ functions/devices/curtain.js | 9 + functions/devices/default.js | 73 +++ functions/devices/dimmablelight.js | 28 + functions/devices/door.js | 9 + functions/devices/fan.js | 54 ++ functions/devices/fireplace.js | 9 + functions/devices/garage.js | 9 + functions/devices/gate.js | 9 + functions/devices/hood.js | 9 + functions/devices/lock.js | 29 + functions/devices/openclosedevice.js | 44 ++ functions/devices/outlet.js | 9 + functions/devices/pergola.js | 9 + functions/devices/scene.js | 25 + functions/devices/securitysystem.js | 29 + functions/devices/sensor.js | 61 ++ functions/devices/shutter.js | 9 + functions/devices/simpleairpurifier.js | 9 + functions/devices/simplefan.js | 9 + functions/devices/simplehood.js | 9 + functions/devices/simplelight.js | 9 + functions/devices/speaker.js | 27 + functions/devices/sprinkler.js | 9 + functions/devices/startstopswitch.js | 26 + functions/devices/switch.js | 29 + functions/devices/thermostat.js | 144 +++++ functions/devices/vacuum.js | 9 + functions/devices/valve.js | 29 + functions/devices/waterheater.js | 9 + functions/devices/window.js | 9 + functions/index.js | 4 +- functions/openhab.js | 2 +- functions/package-lock.json | 77 +++ functions/package.json | 3 +- package-lock.json | 367 +++++++++++ package.json | 5 + tests/devices.metadata.test.js | 37 +- tests/devices.tags.test.js | 13 +- tests/devices.test.js | 10 +- tests/openhab.metadata.test.js | 3 +- tests/openhab.tags.test.js | 3 +- tests/openhab.test.js | 3 +- 53 files changed, 1382 insertions(+), 877 deletions(-) create mode 100644 .eslintrc delete mode 100644 .eslintrc.json create mode 100644 functions/devices/airpurifier.js create mode 100644 functions/devices/awning.js create mode 100644 functions/devices/blinds.js create mode 100644 functions/devices/camera.js create mode 100644 functions/devices/coffeemaker.js create mode 100644 functions/devices/colorlight.js create mode 100644 functions/devices/curtain.js create mode 100644 functions/devices/default.js create mode 100644 functions/devices/dimmablelight.js create mode 100644 functions/devices/door.js create mode 100644 functions/devices/fan.js create mode 100644 functions/devices/fireplace.js create mode 100644 functions/devices/garage.js create mode 100644 functions/devices/gate.js create mode 100644 functions/devices/hood.js create mode 100644 functions/devices/lock.js create mode 100644 functions/devices/openclosedevice.js create mode 100644 functions/devices/outlet.js create mode 100644 functions/devices/pergola.js create mode 100644 functions/devices/scene.js create mode 100644 functions/devices/securitysystem.js create mode 100644 functions/devices/sensor.js create mode 100644 functions/devices/shutter.js create mode 100644 functions/devices/simpleairpurifier.js create mode 100644 functions/devices/simplefan.js create mode 100644 functions/devices/simplehood.js create mode 100644 functions/devices/simplelight.js create mode 100644 functions/devices/speaker.js create mode 100644 functions/devices/sprinkler.js create mode 100644 functions/devices/startstopswitch.js create mode 100644 functions/devices/switch.js create mode 100644 functions/devices/thermostat.js create mode 100644 functions/devices/vacuum.js create mode 100644 functions/devices/valve.js create mode 100644 functions/devices/waterheater.js create mode 100644 functions/devices/window.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..4307cd08 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "standard" +} diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 9585fe86..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "env": { - "browser": true, - "commonjs": true, - "es6": true, - "node": true - }, - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "sourceType": "module" - }, - "rules": { - "no-const-assign": "warn", - "no-this-before-super": "warn", - "no-undef": "warn", - "no-unreachable": "warn", - "no-unused-vars": "warn", - "constructor-super": "warn", - "valid-typeof": "warn" - } -} \ No newline at end of file diff --git a/functions/apihandler.js b/functions/apihandler.js index d30b0a18..d9db8de5 100644 --- a/functions/apihandler.js +++ b/functions/apihandler.js @@ -104,4 +104,4 @@ class ApiHandler { } } -module.exports = { ApiHandler }; +module.exports = ApiHandler; diff --git a/functions/commands.js b/functions/commands.js index 3c9115e9..4ac64e3c 100644 --- a/functions/commands.js +++ b/functions/commands.js @@ -17,7 +17,7 @@ * @author Michael Krug * */ -const Thermostat = require('./devices.js').Thermostat; +const Thermostat = require('../functions/devices/thermostat.js'); const ackSupported = [ 'action.devices.commands.ArmDisarm', @@ -507,10 +507,10 @@ class ThermostatTemperatureSetpointCommand extends GenericCommand { static getItemName(item) { const members = Thermostat.getMembers(item); - if (!('thermostatTemperatureSetpoint' in members)) { - throw { statusCode: 400 }; + if ('thermostatTemperatureSetpoint' in members) { + return members.thermostatTemperatureSetpoint.name; } - return members.thermostatTemperatureSetpoint.name; + throw { statusCode: 400 }; } static convertParamsToValue(params, item) { @@ -543,10 +543,10 @@ class ThermostatTemperatureSetpointHighCommand extends GenericCommand { static getItemName(item) { const members = Thermostat.getMembers(item); - if (!('thermostatTemperatureSetpointHigh' in members)) { - throw { statusCode: 400 }; + if ('thermostatTemperatureSetpointHigh' in members) { + return members.thermostatTemperatureSetpointHigh.name; } - return members.thermostatTemperatureSetpointHigh.name; + throw { statusCode: 400 }; } static convertParamsToValue(params, item) { @@ -579,10 +579,10 @@ class ThermostatTemperatureSetpointLowCommand extends GenericCommand { static getItemName(item) { const members = Thermostat.getMembers(item); - if (!('thermostatTemperatureSetpointLow' in members)) { - throw { statusCode: 400 }; + if ('thermostatTemperatureSetpointLow' in members) { + return members.thermostatTemperatureSetpointLow.name; } - return members.thermostatTemperatureSetpointLow.name; + throw { statusCode: 400 }; } static convertParamsToValue(params, item) { diff --git a/functions/devices.js b/functions/devices.js index e215982a..6dd41771 100644 --- a/functions/devices.js +++ b/functions/devices.js @@ -18,6 +18,17 @@ * */ +const glob = require('glob'); + +const Devices = []; + +glob.sync('./devices/*.js', { cwd: __dirname }).forEach(file => { + const device = require(file); + if (device.type) { + Devices.push(device); + } +}); + const hasTag = (item = {}, tag = '') => { return item.tags && item.tags.map(t => t.toLowerCase()).includes(tag.toLowerCase()) || false; }; @@ -32,809 +43,6 @@ const getDeviceForItem = (item = {}) => { )); }; -const getConfig = (item = {}) => { - return item && item.metadata && item.metadata.ga && item.metadata.ga.config || {}; -}; - -class GenericDevice { - static get type() { - return ''; - } - - static get traits() { - return []; - } - - static getAttributes(item = {}) { - return {}; - } - - static getMetadata(item = {}) { - const config = getConfig(item); - return { - id: item.name, - type: this.type, - traits: this.traits, - name: { - name: config.name || item.label, - defaultNames: [config.name || item.label], - nicknames: [config.name || item.label, ...(item.metadata && item.metadata.synonyms ? item.metadata.synonyms.value.split(',').map(s => s.trim()) : [])] - }, - willReportState: false, - roomHint: config.roomHint, - structureHint: config.structureHint, - deviceInfo: { - manufacturer: 'openHAB', - model: item.label, - hwVersion: '2.5.0', - swVersion: '2.5.0' - }, - attributes: this.getAttributes(item), - customData: { - itemType: item.type === 'Group' ? item.groupType : item.type, - deviceType: this.type, - inverted: config.inverted === true, - tfaAck: config.tfaAck, - tfaPin: config.tfaPin - } - }; - if (config.inverted === true) { - metadata.customData.inverted = true; - } - if (config.ackNeeded === true) { - metadata.customData.ackNeeded = true; - } - if (typeof (config.pinNeeded) === 'string') { - metadata.customData.pinNeeded = config.pinNeeded; - } - return metadata; - } - - static get requiredItemTypes() { - return []; - } - - static checkItemType(item = {}) { - return ( - !this.requiredItemTypes.length || - this.requiredItemTypes.includes(item.type) || - (item.type === 'Group' && item.groupType && this.requiredItemTypes.includes(item.groupType)) - ); - } - - static getState(item = {}) { - return {}; - } -} - -/* Switch items that act as switch devices */ - -class Switch extends GenericDevice { - static get type() { - return 'action.devices.types.SWITCH'; - } - - static get traits() { - return [ - 'action.devices.traits.OnOff' - ]; - } - - static get requiredItemTypes() { - return ['Switch']; - } - - static getState(item) { - let state = item.state === 'ON'; - if (getConfig(item).inverted === true) { - state = !state; - } - return { - on: state - }; - } -} - -class Outlet extends Switch { - static get type() { - return 'action.devices.types.OUTLET'; - } -} - -class CoffeeMaker extends Switch { - static get type() { - return 'action.devices.types.COFFEE_MAKER'; - } -} - -class WaterHeater extends Switch { - static get type() { - return 'action.devices.types.WATERHEATER'; - } -} - -class Fireplace extends Switch { - static get type() { - return 'action.devices.types.FIREPLACE'; - } -} - -class SimpleFan extends Switch { - static get type() { - return 'action.devices.types.FAN'; - } -} - -class SimpleHood extends Switch { - static get type() { - return 'action.devices.types.HOOD'; - } -} - -class SimpleAirPurifier extends Switch { - static get type() { - return 'action.devices.types.AIRPURIFIER' - } -} - -/* Switch items that act as open-close devices */ - -class Valve extends GenericDevice { - static get type() { - return 'action.devices.types.VALVE'; - } - - static get traits() { - return [ - 'action.devices.traits.OpenClose' - ]; - } - - static get requiredItemTypes() { - return ['Switch']; - } - - static getState(item) { - let state = item.state === 'ON'; - if (getConfig(item).inverted === true) { - state = !state; - } - return { - openPercent: state ? 100 : 0 - }; - } -} - -/* Switch items that act as start-stop devices */ - -class StartStopSwitch extends GenericDevice { - static get traits() { - return [ - 'action.devices.traits.StartStop' - ]; - } - - static get requiredItemTypes() { - return ['Switch']; - } - - static getState(item) { - let state = item.state === 'ON'; - if (getConfig(item).inverted === true) { - state = !state; - } - return { - isRunning: state, - isPaused: !state - }; - } -} - -class Sprinkler extends StartStopSwitch { - static get type() { - return 'action.devices.types.SPRINKLER'; - } -} - -class Vacuum extends StartStopSwitch { - static get type() { - return 'action.devices.types.VACUUM'; - } -} - -/* Switch items that act as scene devices */ - -class Scene extends GenericDevice { - static get type() { - return 'action.devices.types.SCENE'; - } - - static get traits() { - return [ - 'action.devices.traits.Scene' - ]; - } - - static get requiredItemTypes() { - return ['Switch']; - } - - static getAttributes() { - return { - sceneReversible: true - }; - } -} - -/* Switch items that act as lock devices */ - -class Lock extends GenericDevice { - static get type() { - return 'action.devices.types.LOCK'; - } - - static get traits() { - return [ - 'action.devices.traits.LockUnlock' - ]; - } - - static get requiredItemTypes() { - return ['Switch']; - } - - static getState(item) { - let state = item.state === 'ON'; - if (getConfig(item).inverted === true) { - state = !state; - } - return { - isLocked: state - }; - } -} - -/* Switch items that act as security system devices */ - -class SecuritySystem extends GenericDevice { - static get type() { - return 'action.devices.types.SECURITYSYSTEM'; - } - - static get traits() { - return [ - 'action.devices.traits.ArmDisarm' - ]; - } - - static get requiredItemTypes() { - return ['Switch']; - } - - static getState(item) { - let state = item.state === 'ON'; - if (getConfig(item).inverted === true) { - state = !state; - } - return { - isArmed: state - }; - } -} - -/* Switch items that act as lighting devices */ - -class SimpleLight extends Switch { - static get type() { - return 'action.devices.types.LIGHT'; - } -} - -/* Dimmer items that act as lighting devices */ - -class DimmableLight extends GenericDevice { - static get type() { - return 'action.devices.types.LIGHT'; - } - - static get traits() { - return [ - 'action.devices.traits.OnOff', - 'action.devices.traits.Brightness' - ]; - } - - static get requiredItemTypes() { - return ['Dimmer']; - } - - static getState(item) { - let brightness = Number(item.state) || 0; - return { - on: brightness > 0, - brightness: brightness - }; - } -} - -/* Color items that act as lighting devices */ - -class ColorLight extends GenericDevice { - static get type() { - return 'action.devices.types.LIGHT'; - } - - static get traits() { - return [ - 'action.devices.traits.OnOff', - 'action.devices.traits.Brightness', - 'action.devices.traits.ColorSetting' - ]; - } - - static getAttributes(item) { - const attributes = { - colorModel: 'hsv' - }; - const config = getConfig(item); - if ('colorTemperatureRange' in config) { - const [min, max] = config.colorTemperatureRange.split(',').map(s => Number(s.trim())); - if (!isNaN(min) && !isNaN(max)) { - attributes.colorTemperatureRange = { - temperatureMinK: min, - temperatureMaxK: max - }; - } - } - return attributes; - } - - static get requiredItemTypes() { - return ['Color']; - } - - static getState(item) { - const [hue, sat, val] = item.state.split(',').map(s => Number(s.trim())); - return { - on: val > 0, - brightness: val, - color: { - spectrumHSV: { - hue: hue, - saturation: sat / 100, - value: val / 100 - } - } - }; - } -} - -/* Rollershutter items that act as open-close / start-stop devices */ - -class GenericOpenCloseDevice extends GenericDevice { - static get traits() { - return [ - 'action.devices.traits.OpenClose', - 'action.devices.traits.StartStop' - ]; - } - - static getAttributes(item) { - const attributes = {}; - attributes.discreteOnlyOpenClose = getConfig(item).discreteOnlyOpenClose === true; - attributes.queryOnlyOpenClose = getConfig(item).queryOnlyOpenClose === true; - const itemType = item.type === 'Group' && item.groupType ? item.groupType : item.type; - if (itemType === 'Switch') { - attributes.discreteOnlyOpenClose = true; - } - if (itemType === 'Contact') { - attributes.discreteOnlyOpenClose = true; - attributes.queryOnlyOpenClose = true; - } - return attributes; - } - - static get requiredItemTypes() { - return ['Rollershutter', 'Switch']; - } - - static getState(item) { - let state = 0; - const itemType = item.type === 'Group' ? item.groupType : item.type; - if (itemType == 'Switch') { - state = item.state === 'ON' ? 0 : 100; - } else { - state = Number(item.state); - } - return { - openPercent: getConfig(item).inverted !== true ? 100 - state : state - }; - } -} - -class Awning extends GenericOpenCloseDevice { - static get type() { - return 'action.devices.types.AWNING'; - } -} - -class Blinds extends GenericOpenCloseDevice { - static get type() { - return 'action.devices.types.BLINDS'; - } -} - -class Curtain extends GenericOpenCloseDevice { - static get type() { - return 'action.devices.types.CURTAIN'; - } -} - -class Door extends GenericOpenCloseDevice { - static get type() { - return 'action.devices.types.DOOR'; - } -} - -class Garage extends GenericOpenCloseDevice { - static get type() { - return 'action.devices.types.GARAGE'; - } -} - -class Gate extends GenericOpenCloseDevice { - static get type() { - return 'action.devices.types.GATE'; - } -} - -class Pergola extends GenericOpenCloseDevice { - static get type() { - return 'action.devices.types.PERGOLA'; - } -} - -class Shutter extends GenericOpenCloseDevice { - static get type() { - return 'action.devices.types.SHUTTER'; - } -} - -class Window extends GenericOpenCloseDevice { - static get type() { - return 'action.devices.types.WINDOW'; - } -} - -class Speaker extends GenericDevice { - static get type() { - return 'action.devices.types.SPEAKER'; - } - - static get traits() { - return [ - 'action.devices.traits.Volume' - ]; - } - - static get requiredItemTypes() { - return ['Dimmer']; - } - - static getState(item) { - const volume = Number(item.state) || 0; - return { - currentVolume: volume, - isMuted: volume === 0 - }; - } -} - -class Camera extends GenericDevice { - static get type() { - return 'action.devices.types.CAMERA'; - } - - static get traits() { - return [ - 'action.devices.traits.CameraStream' - ]; - } - - static getAttributes(item) { - return { - cameraStreamSupportedProtocols: (getConfig(item).protocols || 'hls,dash').split(',').map(s => s.trim()), - cameraStreamNeedAuthToken: getConfig(item).token ? true : false, - cameraStreamNeedDrmEncryption: false - }; - } - - static get requiredItemTypes() { - return ['String']; - } -} - -class Fan extends GenericDevice { - static get type() { - return 'action.devices.types.FAN'; - } - - static get traits() { - return [ - 'action.devices.traits.OnOff', - 'action.devices.traits.FanSpeed' - ]; - } - - static getAttributes(item) { - const config = getConfig(item); - if (!config || !config.speeds) { - return {}; - } - const attributes = { - availableFanSpeeds: { - speeds: [], - ordered: config.ordered === true - }, - reversible: false - }; - config.speeds.split(',').forEach((speedEntry) => { - try { - const [speedName, speedSynonyms] = speedEntry.trim().split('=').map(s => s.trim()); - attributes.availableFanSpeeds.speeds.push({ - speed_name: speedName, - speed_values: [{ - speed_synonym: speedSynonyms.split(':').map(s => s.trim()), - lang: config.lang || 'en' - }] - }); - } catch (e) { } - }); - return attributes; - } - - static get requiredItemTypes() { - return ['Dimmer']; - } - - static getState(item) { - return { - currentFanSpeedSetting: item.state.toString(), - on: Number(item.state) > 0 - }; - } -} - -class Hood extends Fan { - static get type() { - return 'action.devices.types.HOOD'; - } -} - -class AirPurifier extends Fan { - static get type() { - return 'action.devices.types.AIRPURIFIER'; - } -} - -/* Sensor items */ - -class Sensor extends GenericDevice { - static get type() { - return 'action.devices.types.SENSOR'; - } - - static get traits() { - return [ - 'action.devices.traits.SensorState' - ]; - } - - static getAttributes(item) { - const config = getConfig(item); - if (!config || !config.sensorName) { - return {}; - } - const attributes = { - sensorStatesSupported: { - name: config.sensorName - } - }; - if (config.valueUnit) { - attributes.sensorStatesSupported.numericCapabilities = {} - attributes.sensorStatesSupported.numericCapabilities.rawValueUnit = config.valueUnit - } - if (config.states) { - attributes.sensorStatesSupported.descriptiveCapabilities = {} - attributes.sensorStatesSupported.descriptiveCapabilities.availableStates = config.states.split(',').map(s => s.trim().split('=')[0].trim()); - } - return attributes; - } - - static getState(item) { - const config = getConfig(item); - return { - currentSensorStateData: { - name: config.sensorName, - currentSensorState: this.translateStateToGoogle(item), - rawValue: Number(item.state) || 0 - } - }; - } - - static translateStateToGoogle(item) { - const config = getConfig(item); - if ('states' in config) { - const states = config.states.split(',').map(s => s.trim()) - for (const state of states) { - const [key, value] = state.split('=').map(s => s.trim()); - if (value == item.state) { - return key; - } - } - } - return ''; - } -} - -/* Thermostat items formed by a group of items */ - -class Thermostat extends GenericDevice { - static get type() { - return 'action.devices.types.THERMOSTAT'; - } - - static get traits() { - return [ - 'action.devices.traits.TemperatureSetting' - ]; - } - - static getAttributes(item) { - const config = getConfig(item); - const attributes = { - thermostatTemperatureUnit: this.usesFahrenheit(item) ? 'F' : 'C' - }; - if ('thermostatTemperatureRange' in config) { - const [min, max] = config.thermostatTemperatureRange.split(',').map(s => parseFloat(s.trim())); - if (!isNaN(min) && !isNaN(max)) { - attributes.thermostatTemperatureRange = { - minThresholdCelsius: min, - maxThresholdCelsius: max - } - } - } - const members = this.getMembers(item); - if (('thermostatTemperatureAmbient' in members) && - !('thermostatMode' in members) && - !('thermostatTemperatureSetpoint' in members)) { - attributes.queryOnlyTemperatureSetting = true; - } else { - attributes.availableThermostatModes = Object.keys(this.getModeMap(item)).join(','); - } - return attributes; - } - - static checkItemType(item) { - return item.type === 'Group'; - } - - static getState(item) { - const state = {}; - const members = this.getMembers(item); - for (const member in members) { - if (member == 'thermostatMode') { - state[member] = this.translateModeToGoogle(item, members[member].state); - } else { - state[member] = Number(parseFloat(members[member].state).toFixed(1)); - if (member.indexOf('Temperature') > 0 && this.usesFahrenheit(item)) { - state[member] = this.convertToCelsius(state[member]); - } - } - } - return state; - } - - static getMembers(item) { - const supportedMembers = [ - 'thermostatMode', - 'thermostatTemperatureSetpoint', - 'thermostatTemperatureSetpointHigh', - 'thermostatTemperatureSetpointLow', - 'thermostatTemperatureAmbient', - 'thermostatHumidityAmbient' - ]; - const members = {}; - if (item.members && item.members.length) { - item.members.forEach((member) => { - if (member.metadata && member.metadata.ga) { - const memberType = supportedMembers.find(m => member.metadata.ga.value.toLowerCase() === m.toLowerCase()); - if (memberType) { - members[memberType] = { name: member.name, state: member.state }; - } - } else { - if (hasTag(member, 'HeatingCoolingMode') || hasTag(member, 'homekit:HeatingCoolingMode') || hasTag(member, 'homekit:TargetHeatingCoolingMode') || hasTag(member, 'homekit:CurrentHeatingCoolingMode')) { - members.thermostatMode = { name: member.name, state: member.state }; - } - if (hasTag(member, 'TargetTemperature') || hasTag(member, 'homekit:TargetTemperature')) { - members.thermostatTemperatureSetpoint = { name: member.name, state: member.state }; - } - if (hasTag(member, 'CurrentTemperature')) { - members.thermostatTemperatureAmbient = { name: member.name, state: member.state }; - } - if (hasTag(member, 'CurrentHumidity')) { - members.thermostatHumidityAmbient = { name: member.name, state: member.state }; - } - } - }); - } - return members; - } - - static usesFahrenheit(item) { - return getConfig(item).useFahrenheit === true || hasTag(item, 'Fahrenheit'); - } - - static getModeMap(item) { - const config = getConfig(item); - let modes = ['off', 'heat', 'cool', 'on', 'heatcool', 'auto', 'eco']; - if ('modes' in config) { - modes = config.modes.split(',').map(s => s.trim()); - } - const modeMap = {}; - modes.forEach(pair => { - const [key, value] = pair.split('=').map(s => s.trim()); - modeMap[key] = value ? value.split(':').map(s => s.trim()) : [key]; - }); - return modeMap; - } - - static translateModeToOpenhab(item, mode) { - const modeMap = this.getModeMap(item); - if (mode in modeMap) { - return modeMap[mode][0]; - } - throw { statusCode: 400 }; - } - - static translateModeToGoogle(item, mode) { - const modeMap = this.getModeMap(item); - for (const key in modeMap) { - if (modeMap[key].includes(mode)) { - return key; - } - } - return 'on'; - } - - static convertToFahrenheit(value = 0) { - return Math.round(value * 9 / 5 + 32); - } - - static convertToCelsius(value = 0) { - return Number(((value - 32) * 5 / 9).toFixed(1)); - } -} - -const Devices = [ - Switch, Outlet, CoffeeMaker, WaterHeater, Fireplace, - Valve, - Sprinkler, Vacuum, - Scene, - Lock, - SecuritySystem, - SimpleLight, DimmableLight, ColorLight, - Awning, Blinds, Curtain, Door, Garage, Gate, Shutter, Pergola, Window, - Speaker, - Camera, - SimpleFan, Fan, - SimpleHood, Hood, - SimpleAirPurifier, AirPurifier, - Sensor, - Thermostat -]; - module.exports = { - getDeviceForItem, - Thermostat + getDeviceForItem } diff --git a/functions/devices/airpurifier.js b/functions/devices/airpurifier.js new file mode 100644 index 00000000..f8585a7b --- /dev/null +++ b/functions/devices/airpurifier.js @@ -0,0 +1,9 @@ +const Fan = require('./fan.js'); + +class AirPurifier extends Fan { + static get type() { + return 'action.devices.types.AIRPURIFIER'; + } +} + +module.exports = AirPurifier; diff --git a/functions/devices/awning.js b/functions/devices/awning.js new file mode 100644 index 00000000..f3db4a9c --- /dev/null +++ b/functions/devices/awning.js @@ -0,0 +1,9 @@ +const OpenCloseDevice = require('./openclosedevice.js'); + +class Awning extends OpenCloseDevice { + static get type() { + return 'action.devices.types.AWNING'; + } +} + +module.exports = Awning; diff --git a/functions/devices/blinds.js b/functions/devices/blinds.js new file mode 100644 index 00000000..19d94145 --- /dev/null +++ b/functions/devices/blinds.js @@ -0,0 +1,9 @@ +const OpenCloseDevice = require('./openclosedevice.js'); + +class Blinds extends OpenCloseDevice { + static get type() { + return 'action.devices.types.BLINDS'; + } +} + +module.exports = Blinds; diff --git a/functions/devices/camera.js b/functions/devices/camera.js new file mode 100644 index 00000000..b3a36f45 --- /dev/null +++ b/functions/devices/camera.js @@ -0,0 +1,28 @@ +const DefaultDevice = require('./default.js'); + +class Camera extends DefaultDevice { + static get type() { + return 'action.devices.types.CAMERA'; + } + + static get traits() { + return [ + 'action.devices.traits.CameraStream' + ]; + } + + static getAttributes(item) { + const config = this.getConfig(item); + return { + cameraStreamSupportedProtocols: (config.protocols || 'hls,dash').split(',').map(s => s.trim()), + cameraStreamNeedAuthToken: config.token ? true : false, + cameraStreamNeedDrmEncryption: false + }; + } + + static get requiredItemTypes() { + return ['String']; + } +} + +module.exports = Camera; diff --git a/functions/devices/coffeemaker.js b/functions/devices/coffeemaker.js new file mode 100644 index 00000000..afd39c9b --- /dev/null +++ b/functions/devices/coffeemaker.js @@ -0,0 +1,9 @@ +const Switch = require('./switch.js'); + +class CoffeeMaker extends Switch { + static get type() { + return 'action.devices.types.COFFEE_MAKER'; + } +} + +module.exports = CoffeeMaker; diff --git a/functions/devices/colorlight.js b/functions/devices/colorlight.js new file mode 100644 index 00000000..cfd2f8b1 --- /dev/null +++ b/functions/devices/colorlight.js @@ -0,0 +1,53 @@ +const DefaultDevice = require('./default.js'); + +class ColorLight extends DefaultDevice { + static get type() { + return 'action.devices.types.LIGHT'; + } + + static get traits() { + return [ + 'action.devices.traits.OnOff', + 'action.devices.traits.Brightness', + 'action.devices.traits.ColorSetting' + ]; + } + + static getAttributes(item) { + const attributes = { + colorModel: 'hsv' + }; + const config = this.getConfig(item); + if ('colorTemperatureRange' in config) { + const [min, max] = config.colorTemperatureRange.split(',').map(s => Number(s.trim())); + if (!isNaN(min) && !isNaN(max)) { + attributes.colorTemperatureRange = { + temperatureMinK: min, + temperatureMaxK: max + }; + } + } + return attributes; + } + + static get requiredItemTypes() { + return ['Color']; + } + + static getState(item) { + const [hue, sat, val] = item.state.split(',').map(s => Number(s.trim())); + return { + on: val > 0, + brightness: val, + color: { + spectrumHSV: { + hue: hue, + saturation: sat / 100, + value: val / 100 + } + } + }; + } +} + +module.exports = ColorLight; diff --git a/functions/devices/curtain.js b/functions/devices/curtain.js new file mode 100644 index 00000000..4af25cec --- /dev/null +++ b/functions/devices/curtain.js @@ -0,0 +1,9 @@ +const OpenCloseDevice = require('./openclosedevice.js'); + +class Curtain extends OpenCloseDevice { + static get type() { + return 'action.devices.types.CURTAIN'; + } +} + +module.exports = Curtain; diff --git a/functions/devices/default.js b/functions/devices/default.js new file mode 100644 index 00000000..cf478936 --- /dev/null +++ b/functions/devices/default.js @@ -0,0 +1,73 @@ +class DefaultDevice { + static get type() { + return ''; + } + + static get traits() { + return []; + } + + static getAttributes(item = {}) { + return {}; + } + + static getConfig(item = {}) { + return item && item.metadata && item.metadata.ga && item.metadata.ga.config || {}; + } + + static getMetadata(item = {}) { + const config = this.getConfig(item); + const itemType = item.type === 'Group' && item.groupType ? item.groupType : item.type; + const metadata = { + id: item.name, + type: this.type, + traits: this.traits, + name: { + name: config.name || item.label, + defaultNames: [config.name || item.label], + nicknames: [config.name || item.label, ...(item.metadata && item.metadata.synonyms ? item.metadata.synonyms.value.split(',').map(s => s.trim()) : [])] + }, + willReportState: false, + roomHint: config.roomHint, + structureHint: config.structureHint, + deviceInfo: { + manufacturer: 'openHAB', + model: `${itemType}:${item.name}`, + hwVersion: '2.5.0', + swVersion: '2.5.0' + }, + attributes: this.getAttributes(item), + customData: { + itemType: itemType + } + }; + if (config.inverted === true) { + metadata.customData.inverted = true; + } + if (config.ackNeeded === true) { + metadata.customData.ackNeeded = true; + } + if (typeof (config.pinNeeded) === 'string') { + metadata.customData.pinNeeded = config.pinNeeded; + } + return metadata; + } + + static get requiredItemTypes() { + return []; + } + + static checkItemType(item = {}) { + return ( + !this.requiredItemTypes.length || + this.requiredItemTypes.includes(item.type) || + (item.type === 'Group' && item.groupType && this.requiredItemTypes.includes(item.groupType)) + ); + } + + static getState(item = {}) { + return {}; + } +} + +module.exports = DefaultDevice; diff --git a/functions/devices/dimmablelight.js b/functions/devices/dimmablelight.js new file mode 100644 index 00000000..6b84ea10 --- /dev/null +++ b/functions/devices/dimmablelight.js @@ -0,0 +1,28 @@ +const DefaultDevice = require('./default.js'); + +class DimmableLight extends DefaultDevice { + static get type() { + return 'action.devices.types.LIGHT'; + } + + static get traits() { + return [ + 'action.devices.traits.OnOff', + 'action.devices.traits.Brightness' + ]; + } + + static get requiredItemTypes() { + return ['Dimmer']; + } + + static getState(item) { + let brightness = Number(item.state) || 0; + return { + on: brightness > 0, + brightness: brightness + }; + } +} + +module.exports = DimmableLight; diff --git a/functions/devices/door.js b/functions/devices/door.js new file mode 100644 index 00000000..dc0c94e9 --- /dev/null +++ b/functions/devices/door.js @@ -0,0 +1,9 @@ +const OpenCloseDevice = require('./openclosedevice.js'); + +class Door extends OpenCloseDevice { + static get type() { + return 'action.devices.types.DOOR'; + } +} + +module.exports = Door; diff --git a/functions/devices/fan.js b/functions/devices/fan.js new file mode 100644 index 00000000..3faf8a7e --- /dev/null +++ b/functions/devices/fan.js @@ -0,0 +1,54 @@ +const DefaultDevice = require('./default.js'); + +class Fan extends DefaultDevice { + static get type() { + return 'action.devices.types.FAN'; + } + + static get traits() { + return [ + 'action.devices.traits.OnOff', + 'action.devices.traits.FanSpeed' + ]; + } + + static getAttributes(item) { + const config = this.getConfig(item); + if (!config || !config.speeds) { + return {}; + } + const attributes = { + availableFanSpeeds: { + speeds: [], + ordered: config.ordered === true + }, + reversible: false + }; + config.speeds.split(',').forEach((speedEntry) => { + try { + const [speedName, speedSynonyms] = speedEntry.trim().split('=').map(s => s.trim()); + attributes.availableFanSpeeds.speeds.push({ + speed_name: speedName, + speed_values: [{ + speed_synonym: speedSynonyms.split(':').map(s => s.trim()), + lang: config.lang || 'en' + }] + }); + } catch (e) { } + }); + return attributes; + } + + static get requiredItemTypes() { + return ['Dimmer']; + } + + static getState(item) { + return { + currentFanSpeedSetting: item.state.toString(), + on: Number(item.state) > 0 + }; + } +} + +module.exports = Fan; diff --git a/functions/devices/fireplace.js b/functions/devices/fireplace.js new file mode 100644 index 00000000..ba67860f --- /dev/null +++ b/functions/devices/fireplace.js @@ -0,0 +1,9 @@ +const Switch = require('./switch.js'); + +class Fireplace extends Switch { + static get type() { + return 'action.devices.types.FIREPLACE'; + } +} + +module.exports = Fireplace; diff --git a/functions/devices/garage.js b/functions/devices/garage.js new file mode 100644 index 00000000..9d99bbda --- /dev/null +++ b/functions/devices/garage.js @@ -0,0 +1,9 @@ +const OpenCloseDevice = require('./openclosedevice.js'); + +class Garage extends OpenCloseDevice { + static get type() { + return 'action.devices.types.GARAGE'; + } +} + +module.exports = Garage; diff --git a/functions/devices/gate.js b/functions/devices/gate.js new file mode 100644 index 00000000..3dbfc103 --- /dev/null +++ b/functions/devices/gate.js @@ -0,0 +1,9 @@ +const OpenCloseDevice = require('./openclosedevice.js'); + +class Gate extends OpenCloseDevice { + static get type() { + return 'action.devices.types.GATE'; + } +} + +module.exports = Gate; diff --git a/functions/devices/hood.js b/functions/devices/hood.js new file mode 100644 index 00000000..1c3c3506 --- /dev/null +++ b/functions/devices/hood.js @@ -0,0 +1,9 @@ +const Fan = require('./fan.js'); + +class Hood extends Fan { + static get type() { + return 'action.devices.types.HOOD'; + } +} + +module.exports = Hood; diff --git a/functions/devices/lock.js b/functions/devices/lock.js new file mode 100644 index 00000000..00b47ce7 --- /dev/null +++ b/functions/devices/lock.js @@ -0,0 +1,29 @@ +const DefaultDevice = require('./default.js'); + +class Lock extends DefaultDevice { + static get type() { + return 'action.devices.types.LOCK'; + } + + static get traits() { + return [ + 'action.devices.traits.LockUnlock' + ]; + } + + static get requiredItemTypes() { + return ['Switch', 'Contact']; + } + + static getState(item) { + let state = item.state === 'ON' || item.state === 'CLOSED'; + if (this.getConfig(item).inverted === true) { + state = !state; + } + return { + isLocked: state + }; + } +} + +module.exports = Lock; diff --git a/functions/devices/openclosedevice.js b/functions/devices/openclosedevice.js new file mode 100644 index 00000000..3356d4c2 --- /dev/null +++ b/functions/devices/openclosedevice.js @@ -0,0 +1,44 @@ +const DefaultDevice = require('./default.js'); + +class OpenCloseDevice extends DefaultDevice { + static get traits() { + return [ + 'action.devices.traits.OpenClose', + 'action.devices.traits.StartStop' + ]; + } + + static getAttributes(item) { + const attributes = {}; + attributes.discreteOnlyOpenClose = this.getConfig(item).discreteOnlyOpenClose === true; + attributes.queryOnlyOpenClose = this.getConfig(item).queryOnlyOpenClose === true; + const itemType = item.type === 'Group' && item.groupType ? item.groupType : item.type; + if (itemType === 'Switch') { + attributes.discreteOnlyOpenClose = true; + } + if (itemType === 'Contact') { + attributes.discreteOnlyOpenClose = true; + attributes.queryOnlyOpenClose = true; + } + return attributes; + } + + static get requiredItemTypes() { + return ['Rollershutter', 'Switch', 'Contact']; + } + + static getState(item) { + let state = 0; + const itemType = item.type === 'Group' && item.groupType ? item.groupType : item.type; + if (itemType === 'Rollershutter') { + state = Number(item.state); + } else { + state = item.state === 'ON' || item.state === 'OPEN' ? 0 : 100; + } + return { + openPercent: this.getConfig(item).inverted !== true ? 100 - state : state + }; + } +} + +module.exports = OpenCloseDevice; diff --git a/functions/devices/outlet.js b/functions/devices/outlet.js new file mode 100644 index 00000000..f6f90051 --- /dev/null +++ b/functions/devices/outlet.js @@ -0,0 +1,9 @@ +const Switch = require('./switch.js'); + +class Outlet extends Switch { + static get type() { + return 'action.devices.types.OUTLET'; + } +} + +module.exports = Outlet; diff --git a/functions/devices/pergola.js b/functions/devices/pergola.js new file mode 100644 index 00000000..b0ef6194 --- /dev/null +++ b/functions/devices/pergola.js @@ -0,0 +1,9 @@ +const OpenCloseDevice = require('./openclosedevice.js'); + +class Pergola extends OpenCloseDevice { + static get type() { + return 'action.devices.types.PERGOLA'; + } +} + +module.exports = Pergola; diff --git a/functions/devices/scene.js b/functions/devices/scene.js new file mode 100644 index 00000000..4c884895 --- /dev/null +++ b/functions/devices/scene.js @@ -0,0 +1,25 @@ +const DefaultDevice = require('./default.js'); + +class Scene extends DefaultDevice { + static get type() { + return 'action.devices.types.SCENE'; + } + + static get traits() { + return [ + 'action.devices.traits.Scene' + ]; + } + + static get requiredItemTypes() { + return ['Switch']; + } + + static getAttributes() { + return { + sceneReversible: true + }; + } +} + +module.exports = Scene; diff --git a/functions/devices/securitysystem.js b/functions/devices/securitysystem.js new file mode 100644 index 00000000..00097cda --- /dev/null +++ b/functions/devices/securitysystem.js @@ -0,0 +1,29 @@ +const DefaultDevice = require('./default.js'); + +class SecuritySystem extends DefaultDevice { + static get type() { + return 'action.devices.types.SECURITYSYSTEM'; + } + + static get traits() { + return [ + 'action.devices.traits.ArmDisarm' + ]; + } + + static get requiredItemTypes() { + return ['Switch']; + } + + static getState(item) { + let state = item.state === 'ON'; + if (this.getConfig(item).inverted === true) { + state = !state; + } + return { + isArmed: state + }; + } +} + +module.exports = SecuritySystem; diff --git a/functions/devices/sensor.js b/functions/devices/sensor.js new file mode 100644 index 00000000..7640afeb --- /dev/null +++ b/functions/devices/sensor.js @@ -0,0 +1,61 @@ +const DefaultDevice = require('./default.js'); + +class Sensor extends DefaultDevice { + static get type() { + return 'action.devices.types.SENSOR'; + } + + static get traits() { + return [ + 'action.devices.traits.SensorState' + ]; + } + + static getAttributes(item) { + const config = this.getConfig(item); + if (!config || !config.sensorName) { + return {}; + } + const attributes = { + sensorStatesSupported: { + name: config.sensorName + } + }; + if (config.valueUnit) { + attributes.sensorStatesSupported.numericCapabilities = {} + attributes.sensorStatesSupported.numericCapabilities.rawValueUnit = config.valueUnit + } + if (config.states) { + attributes.sensorStatesSupported.descriptiveCapabilities = {} + attributes.sensorStatesSupported.descriptiveCapabilities.availableStates = config.states.split(',').map(s => s.trim().split('=')[0].trim()); + } + return attributes; + } + + static getState(item) { + const config = this.getConfig(item); + return { + currentSensorStateData: { + name: config.sensorName, + currentSensorState: this.translateStateToGoogle(item), + rawValue: Number(item.state) || 0 + } + }; + } + + static translateStateToGoogle(item) { + const config = this.getConfig(item); + if ('states' in config) { + const states = config.states.split(',').map(s => s.trim()) + for (const state of states) { + const [key, value] = state.split('=').map(s => s.trim()); + if (value == item.state) { + return key; + } + } + } + return ''; + } +} + +module.exports = Sensor; diff --git a/functions/devices/shutter.js b/functions/devices/shutter.js new file mode 100644 index 00000000..fceab2cf --- /dev/null +++ b/functions/devices/shutter.js @@ -0,0 +1,9 @@ +const OpenCloseDevice = require('./openclosedevice.js'); + +class Shutter extends OpenCloseDevice { + static get type() { + return 'action.devices.types.SHUTTER'; + } +} + +module.exports = Shutter; diff --git a/functions/devices/simpleairpurifier.js b/functions/devices/simpleairpurifier.js new file mode 100644 index 00000000..4873845c --- /dev/null +++ b/functions/devices/simpleairpurifier.js @@ -0,0 +1,9 @@ +const Switch = require('./switch.js'); + +class SimpleAirPurifier extends Switch { + static get type() { + return 'action.devices.types.AIRPURIFIER' + } +} + +module.exports = SimpleAirPurifier; diff --git a/functions/devices/simplefan.js b/functions/devices/simplefan.js new file mode 100644 index 00000000..772b9896 --- /dev/null +++ b/functions/devices/simplefan.js @@ -0,0 +1,9 @@ +const Switch = require('./switch.js'); + +class SimpleFan extends Switch { + static get type() { + return 'action.devices.types.FAN'; + } +} + +module.exports = SimpleFan; diff --git a/functions/devices/simplehood.js b/functions/devices/simplehood.js new file mode 100644 index 00000000..fd11aca7 --- /dev/null +++ b/functions/devices/simplehood.js @@ -0,0 +1,9 @@ +const Switch = require('./switch.js'); + +class SimpleHood extends Switch { + static get type() { + return 'action.devices.types.HOOD'; + } +} + +module.exports = SimpleHood; diff --git a/functions/devices/simplelight.js b/functions/devices/simplelight.js new file mode 100644 index 00000000..21035c17 --- /dev/null +++ b/functions/devices/simplelight.js @@ -0,0 +1,9 @@ +const Switch = require('./switch.js'); + +class SimpleLight extends Switch { + static get type() { + return 'action.devices.types.LIGHT'; + } +} + +module.exports = SimpleLight; diff --git a/functions/devices/speaker.js b/functions/devices/speaker.js new file mode 100644 index 00000000..828ddf55 --- /dev/null +++ b/functions/devices/speaker.js @@ -0,0 +1,27 @@ +const DefaultDevice = require('./default.js'); + +class Speaker extends DefaultDevice { + static get type() { + return 'action.devices.types.SPEAKER'; + } + + static get traits() { + return [ + 'action.devices.traits.Volume' + ]; + } + + static get requiredItemTypes() { + return ['Dimmer']; + } + + static getState(item) { + const volume = Number(item.state) || 0; + return { + currentVolume: volume, + isMuted: volume === 0 + }; + } +} + +module.exports = Speaker; diff --git a/functions/devices/sprinkler.js b/functions/devices/sprinkler.js new file mode 100644 index 00000000..4ce490af --- /dev/null +++ b/functions/devices/sprinkler.js @@ -0,0 +1,9 @@ +const StartStopSwitch = require('./startstopswitch.js'); + +class Sprinkler extends StartStopSwitch { + static get type() { + return 'action.devices.types.SPRINKLER'; + } +} + +module.exports = Sprinkler; diff --git a/functions/devices/startstopswitch.js b/functions/devices/startstopswitch.js new file mode 100644 index 00000000..2b9cff56 --- /dev/null +++ b/functions/devices/startstopswitch.js @@ -0,0 +1,26 @@ +const DefaultDevice = require('./default.js'); + +class StartStopSwitch extends DefaultDevice { + static get traits() { + return [ + 'action.devices.traits.StartStop' + ]; + } + + static get requiredItemTypes() { + return ['Switch']; + } + + static getState(item) { + let state = item.state === 'ON'; + if (this.getConfig(item).inverted === true) { + state = !state; + } + return { + isRunning: state, + isPaused: !state + }; + } +} + +module.exports = StartStopSwitch; diff --git a/functions/devices/switch.js b/functions/devices/switch.js new file mode 100644 index 00000000..c3456204 --- /dev/null +++ b/functions/devices/switch.js @@ -0,0 +1,29 @@ +const DefaultDevice = require('./default.js'); + +class Switch extends DefaultDevice { + static get type() { + return 'action.devices.types.SWITCH'; + } + + static get traits() { + return [ + 'action.devices.traits.OnOff' + ]; + } + + static get requiredItemTypes() { + return ['Switch']; + } + + static getState(item) { + let state = item.state === 'ON'; + if (this.getConfig(item).inverted === true) { + state = !state; + } + return { + on: state + }; + } +} + +module.exports = Switch; diff --git a/functions/devices/thermostat.js b/functions/devices/thermostat.js new file mode 100644 index 00000000..04524893 --- /dev/null +++ b/functions/devices/thermostat.js @@ -0,0 +1,144 @@ +const DefaultDevice = require('./default.js'); + +const hasTag = (item = {}, tag = '') => { + return item.tags && item.tags.map(t => t.toLowerCase()).includes(tag.toLowerCase()) || false; +}; + +class Thermostat extends DefaultDevice { + static get type() { + return 'action.devices.types.THERMOSTAT'; + } + + static get traits() { + return [ + 'action.devices.traits.TemperatureSetting' + ]; + } + + static getAttributes(item) { + const config = this.getConfig(item); + const attributes = { + thermostatTemperatureUnit: this.usesFahrenheit(item) ? 'F' : 'C' + }; + if ('thermostatTemperatureRange' in config) { + const [min, max] = config.thermostatTemperatureRange.split(',').map(s => parseFloat(s.trim())); + if (!isNaN(min) && !isNaN(max)) { + attributes.thermostatTemperatureRange = { + minThresholdCelsius: min, + maxThresholdCelsius: max + } + } + } + const members = this.getMembers(item); + if (('thermostatTemperatureAmbient' in members) && + !('thermostatMode' in members) && + !('thermostatTemperatureSetpoint' in members)) { + attributes.queryOnlyTemperatureSetting = true; + } else { + attributes.availableThermostatModes = Object.keys(this.getModeMap(item)).join(','); + } + return attributes; + } + + static checkItemType(item) { + return item.type === 'Group'; + } + + static getState(item) { + const state = {}; + const members = this.getMembers(item); + for (const member in members) { + if (member == 'thermostatMode') { + state[member] = this.translateModeToGoogle(item, members[member].state); + } else { + state[member] = Number(parseFloat(members[member].state).toFixed(1)); + if (member.indexOf('Temperature') > 0 && this.usesFahrenheit(item)) { + state[member] = this.convertToCelsius(state[member]); + } + } + } + return state; + } + + static getMembers(item) { + const supportedMembers = [ + 'thermostatMode', + 'thermostatTemperatureSetpoint', + 'thermostatTemperatureSetpointHigh', + 'thermostatTemperatureSetpointLow', + 'thermostatTemperatureAmbient', + 'thermostatHumidityAmbient' + ]; + const members = Object(); + if (item.members && item.members.length) { + item.members.forEach((member) => { + if (member.metadata && member.metadata.ga) { + const memberType = supportedMembers.find(m => member.metadata.ga.value.toLowerCase() === m.toLowerCase()); + if (memberType) { + members[memberType] = { name: member.name, state: member.state }; + } + } else { + if (hasTag(member, 'HeatingCoolingMode') || hasTag(member, 'homekit:HeatingCoolingMode') || hasTag(member, 'homekit:TargetHeatingCoolingMode') || hasTag(member, 'homekit:CurrentHeatingCoolingMode')) { + members.thermostatMode = { name: member.name, state: member.state }; + } + if (hasTag(member, 'TargetTemperature') || hasTag(member, 'homekit:TargetTemperature')) { + members.thermostatTemperatureSetpoint = { name: member.name, state: member.state }; + } + if (hasTag(member, 'CurrentTemperature')) { + members.thermostatTemperatureAmbient = { name: member.name, state: member.state }; + } + if (hasTag(member, 'CurrentHumidity')) { + members.thermostatHumidityAmbient = { name: member.name, state: member.state }; + } + } + }); + } + return members; + } + + static usesFahrenheit(item) { + return this.getConfig(item).useFahrenheit === true || hasTag(item, 'Fahrenheit'); + } + + static getModeMap(item) { + const config = this.getConfig(item); + let modes = ['off', 'heat', 'cool', 'on', 'heatcool', 'auto', 'eco']; + if ('modes' in config) { + modes = config.modes.split(',').map(s => s.trim()); + } + const modeMap = {}; + modes.forEach(pair => { + const [key, value] = pair.split('=').map(s => s.trim()); + modeMap[key] = value ? value.split(':').map(s => s.trim()) : [key]; + }); + return modeMap; + } + + static translateModeToOpenhab(item, mode) { + const modeMap = this.getModeMap(item); + if (mode in modeMap) { + return modeMap[mode][0]; + } + throw { statusCode: 400 }; + } + + static translateModeToGoogle(item, mode) { + const modeMap = this.getModeMap(item); + for (const key in modeMap) { + if (modeMap[key].includes(mode)) { + return key; + } + } + return 'on'; + } + + static convertToFahrenheit(value = 0) { + return Math.round(value * 9 / 5 + 32); + } + + static convertToCelsius(value = 0) { + return Number(((value - 32) * 5 / 9).toFixed(1)); + } +} + +module.exports = Thermostat; diff --git a/functions/devices/vacuum.js b/functions/devices/vacuum.js new file mode 100644 index 00000000..4a442c90 --- /dev/null +++ b/functions/devices/vacuum.js @@ -0,0 +1,9 @@ +const StartStopSwitch = require('./startstopswitch.js'); + +class Vacuum extends StartStopSwitch { + static get type() { + return 'action.devices.types.VACUUM'; + } +} + +module.exports = Vacuum; diff --git a/functions/devices/valve.js b/functions/devices/valve.js new file mode 100644 index 00000000..eec0c1c4 --- /dev/null +++ b/functions/devices/valve.js @@ -0,0 +1,29 @@ +const DefaultDevice = require('./default.js'); + +class Valve extends DefaultDevice { + static get type() { + return 'action.devices.types.VALVE'; + } + + static get traits() { + return [ + 'action.devices.traits.OpenClose' + ]; + } + + static get requiredItemTypes() { + return ['Switch']; + } + + static getState(item) { + let state = item.state === 'ON'; + if (this.getConfig(item).inverted === true) { + state = !state; + } + return { + openPercent: state ? 100 : 0 + }; + } +} + +module.exports = Valve; diff --git a/functions/devices/waterheater.js b/functions/devices/waterheater.js new file mode 100644 index 00000000..eb6f4861 --- /dev/null +++ b/functions/devices/waterheater.js @@ -0,0 +1,9 @@ +const Switch = require('./switch.js'); + +class WaterHeater extends Switch { + static get type() { + return 'action.devices.types.WATERHEATER'; + } +} + +module.exports = WaterHeater; diff --git a/functions/devices/window.js b/functions/devices/window.js new file mode 100644 index 00000000..0fcbee0f --- /dev/null +++ b/functions/devices/window.js @@ -0,0 +1,9 @@ +const OpenCloseDevice = require('./openclosedevice.js'); + +class Window extends OpenCloseDevice { + static get type() { + return 'action.devices.types.WINDOW'; + } +} + +module.exports = Window; diff --git a/functions/index.js b/functions/index.js index a18eb9d4..f99c86a8 100644 --- a/functions/index.js +++ b/functions/index.js @@ -18,8 +18,8 @@ * @author Michael Krug - Rework * */ -const OpenHAB = require('./openhab.js').OpenHAB; -const ApiHandler = require('./apihandler.js').ApiHandler; +const OpenHAB = require('./openhab.js'); +const ApiHandler = require('./apihandler.js'); const config = require('./config.js'); const app = require('actions-on-google').smarthome(); diff --git a/functions/openhab.js b/functions/openhab.js index 6fc01bac..413696e5 100644 --- a/functions/openhab.js +++ b/functions/openhab.js @@ -115,4 +115,4 @@ class OpenHAB { } } -module.exports = { OpenHAB }; +module.exports = OpenHAB; diff --git a/functions/package-lock.json b/functions/package-lock.json index 376b64ad..564d0daa 100644 --- a/functions/package-lock.json +++ b/functions/package-lock.json @@ -119,6 +119,11 @@ "is-buffer": "^2.0.2" } }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", @@ -129,11 +134,25 @@ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", @@ -201,6 +220,11 @@ } } }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, "gaxios": { "version": "1.8.4", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz", @@ -222,6 +246,19 @@ "retry-axios": "0.3.2" } }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "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" + } + }, "google-auth-library": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-1.6.1.tgz", @@ -368,6 +405,20 @@ "debug": "^3.1.0" } }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, "is-buffer": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", @@ -419,6 +470,14 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -434,6 +493,19 @@ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.5.tgz", "integrity": "sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q==" }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -474,6 +546,11 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", diff --git a/functions/package.json b/functions/package.json index b335c3b6..f5f154db 100644 --- a/functions/package.json +++ b/functions/package.json @@ -12,6 +12,7 @@ ], "license": "EPL-2.0", "dependencies": { - "actions-on-google": "^2.12.0" + "actions-on-google": "^2.12.0", + "glob": "^7.1.6" } } diff --git a/package-lock.json b/package-lock.json index eb6a92e6..de0233a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -531,6 +531,12 @@ "jest-diff": "^24.3.0" } }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -669,12 +675,33 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, "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 }, + "array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -1104,6 +1131,12 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -1313,6 +1346,16 @@ "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", "dev": true }, + "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" + } + }, "domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", @@ -1420,6 +1463,277 @@ "source-map": "~0.6.1" } }, + "eslint-config-standard": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz", + "integrity": "sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "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" + } + }, + "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" + } + }, + "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 + }, + "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" + } + } + } + }, + "eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + } + }, + "eslint-plugin-import": { + "version": "2.21.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.21.2.tgz", + "integrity": "sha512-FEmxeGI6yaz+SnEB6YgNHlQK1Bs2DKLM+YF+vuTk5H8J9CLbJLtlPvRFgZZ2+sXiKAlN5dpdlrWOjK8ZoZJpQA==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.3", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "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" + } + }, + "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" + } + }, + "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 + }, + "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" + } + }, + "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" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "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" + } + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "requires": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-promise": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", + "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", + "dev": true + }, + "eslint-plugin-standard": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", + "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", + "dev": true + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -2594,6 +2908,12 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, "import-local": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", @@ -2793,6 +3113,12 @@ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, "is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", @@ -3879,6 +4205,18 @@ "isobject": "^3.0.1" } }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -4188,6 +4526,12 @@ "safe-regex": "^1.1.0" } }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -4940,6 +5284,29 @@ "punycode": "^2.1.0" } }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", diff --git a/package.json b/package.json index a40d6c2d..6e857cc4 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,11 @@ }, "devDependencies": { "@types/jest": "^24.9.1", + "eslint-config-standard": "^14.1.1", + "eslint-plugin-import": "^2.21.2", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-standard": "^4.0.1", "jest": "^24.9.0" } } diff --git a/tests/devices.metadata.test.js b/tests/devices.metadata.test.js index 00db8d37..7931367c 100644 --- a/tests/devices.metadata.test.js +++ b/tests/devices.metadata.test.js @@ -1,4 +1,5 @@ const Devices = require('../functions/devices.js'); +const Thermostat = require('../functions/devices/thermostat.js'); describe('Test Switch Devices with Metadata', () => { test('Switch Type', () => { @@ -416,7 +417,7 @@ describe('Test Thermostat Device with Metadata', () => { }); test('usesFahrenheit', () => { - expect(Devices.Thermostat.usesFahrenheit({ + expect(Thermostat.usesFahrenheit({ metadata: { ga: { value: 'Thermostat', @@ -429,7 +430,7 @@ describe('Test Thermostat Device with Metadata', () => { }); test('getAttributes', () => { - expect(Devices.Thermostat.getAttributes({ + expect(Thermostat.getAttributes({ metadata: { ga: { value: 'Thermostat', @@ -443,7 +444,7 @@ describe('Test Thermostat Device with Metadata', () => { thermostatTemperatureUnit: 'F', }); - expect(Devices.Thermostat.getAttributes({ + expect(Thermostat.getAttributes({ metadata: { ga: { value: 'Thermostat', @@ -457,7 +458,7 @@ describe('Test Thermostat Device with Metadata', () => { thermostatTemperatureUnit: 'C', }); - expect(Devices.Thermostat.getAttributes({ + expect(Thermostat.getAttributes({ metadata: { ga: { value: 'Thermostat', @@ -472,7 +473,7 @@ describe('Test Thermostat Device with Metadata', () => { }); // test thermostatTemperatureRange attribute - expect(Devices.Thermostat.getAttributes({ + expect(Thermostat.getAttributes({ metadata: { ga: { value: 'Thermostat', @@ -491,7 +492,7 @@ describe('Test Thermostat Device with Metadata', () => { }); // wrong value for thermostatTemperatureRange should not throw error - expect(Devices.Thermostat.getAttributes({ + expect(Thermostat.getAttributes({ metadata: { ga: { value: 'Thermostat', @@ -506,7 +507,7 @@ describe('Test Thermostat Device with Metadata', () => { }); // wrong value for thermostatTemperatureRange should not throw error - expect(Devices.Thermostat.getAttributes({ + expect(Thermostat.getAttributes({ metadata: { ga: { value: 'Thermostat', @@ -522,7 +523,7 @@ describe('Test Thermostat Device with Metadata', () => { }); test('getModeMap', () => { - expect(Devices.Thermostat.getModeMap({ + expect(Thermostat.getModeMap({ metadata: { ga: { value: 'Thermostat' @@ -538,7 +539,7 @@ describe('Test Thermostat Device with Metadata', () => { 'eco': ['eco'] }); - expect(Devices.Thermostat.getModeMap({ + expect(Thermostat.getModeMap({ metadata: { ga: { value: 'Thermostat', @@ -554,7 +555,7 @@ describe('Test Thermostat Device with Metadata', () => { 'cool': ['cool'] }); - expect(Devices.Thermostat.getModeMap({ + expect(Thermostat.getModeMap({ metadata: { ga: { value: 'Thermostat', @@ -573,7 +574,7 @@ describe('Test Thermostat Device with Metadata', () => { }); test('translateModeToGoogle', () => { - expect(Devices.Thermostat.translateModeToGoogle({ + expect(Thermostat.translateModeToGoogle({ metadata: { ga: { value: 'Thermostat', @@ -584,7 +585,7 @@ describe('Test Thermostat Device with Metadata', () => { } }, 'COMFORT')).toBe('heat'); - expect(Devices.Thermostat.translateModeToGoogle({ + expect(Thermostat.translateModeToGoogle({ metadata: { ga: { value: 'Thermostat', @@ -597,7 +598,7 @@ describe('Test Thermostat Device with Metadata', () => { }); test('translateModeToOpenhab', () => { - expect(Devices.Thermostat.translateModeToOpenhab({ + expect(Thermostat.translateModeToOpenhab({ metadata: { ga: { value: 'Thermostat', @@ -608,7 +609,7 @@ describe('Test Thermostat Device with Metadata', () => { } }, 'heat')).toBe('COMFORT'); - expect(Devices.Thermostat.translateModeToOpenhab({ + expect(Thermostat.translateModeToOpenhab({ metadata: { ga: { value: 'Thermostat', @@ -621,7 +622,7 @@ describe('Test Thermostat Device with Metadata', () => { }); test('getMetadata', () => { - expect(Devices.Thermostat.getMetadata( + expect(Thermostat.getMetadata( { name: 'MyItem', label: 'MyThermostat', @@ -703,7 +704,7 @@ describe('Test Thermostat Device with Metadata', () => { }); test('getState', () => { - expect(Devices.Thermostat.getState({ + expect(Thermostat.getState({ type: 'Group', metadata: { ga: { @@ -712,7 +713,7 @@ describe('Test Thermostat Device with Metadata', () => { } })).toStrictEqual({}); - expect(Devices.Thermostat.getState({ + expect(Thermostat.getState({ type: 'Group', metadata: { ga: { @@ -753,7 +754,7 @@ describe('Test Thermostat Device with Metadata', () => { 'thermostatMode': 'off' }); - expect(Devices.Thermostat.getState({ + expect(Thermostat.getState({ type: 'Group', metadata: { ga: { diff --git a/tests/devices.tags.test.js b/tests/devices.tags.test.js index 19fa1131..decbb9a4 100644 --- a/tests/devices.tags.test.js +++ b/tests/devices.tags.test.js @@ -1,4 +1,5 @@ const Devices = require('../functions/devices.js'); +const Thermostat = require('../functions/devices/thermostat.js'); describe('Test Switch Devices with Tags', () => { test('Switch Type', () => { @@ -188,7 +189,7 @@ describe('Test Thermostat Device with Tags', () => { }); test('usesFahrenheit', () => { - expect(Devices.Thermostat.usesFahrenheit({ + expect(Thermostat.usesFahrenheit({ tags: [ 'Thermostat', 'Fahrenheit' @@ -197,7 +198,7 @@ describe('Test Thermostat Device with Tags', () => { }); test('getAttributes', () => { - expect(Devices.Thermostat.getAttributes({ + expect(Thermostat.getAttributes({ tags: [ 'Thermostat', 'Fahrenheit' @@ -207,7 +208,7 @@ describe('Test Thermostat Device with Tags', () => { 'thermostatTemperatureUnit': 'F', }); - expect(Devices.Thermostat.getAttributes({ + expect(Thermostat.getAttributes({ tags: [ 'Thermostat' ] @@ -218,14 +219,14 @@ describe('Test Thermostat Device with Tags', () => { }); test('getState', () => { - expect(Devices.Thermostat.getState({ + expect(Thermostat.getState({ type: 'Group', tags: [ 'Thermostat' ] })).toStrictEqual({}); - expect(Devices.Thermostat.getState({ + expect(Thermostat.getState({ type: 'Group', tags: [ 'Thermostat', @@ -256,7 +257,7 @@ describe('Test Thermostat Device with Tags', () => { 'thermostatMode': 'off' }); - expect(Devices.Thermostat.getState({ + expect(Thermostat.getState({ type: 'Group', tags: [ 'Thermostat' diff --git a/tests/devices.test.js b/tests/devices.test.js index 75a3ab05..7660a09d 100644 --- a/tests/devices.test.js +++ b/tests/devices.test.js @@ -1,13 +1,13 @@ -const Devices = require('../functions/devices.js'); +const Thermostat = require('../functions/devices/thermostat.js'); describe('Test Thermostat Device', () => { test('convertToCelsius', () => { - expect(Devices.Thermostat.convertToCelsius(10.0)).toEqual(-12.2); - expect(Devices.Thermostat.convertToCelsius(0.0)).toEqual(-17.8); + expect(Thermostat.convertToCelsius(10.0)).toEqual(-12.2); + expect(Thermostat.convertToCelsius(0.0)).toEqual(-17.8); }); test('convertToFahrenheit', () => { - expect(Devices.Thermostat.convertToFahrenheit(10.0)).toEqual(50); - expect(Devices.Thermostat.convertToFahrenheit(0.0)).toEqual(32); + expect(Thermostat.convertToFahrenheit(10.0)).toEqual(50); + expect(Thermostat.convertToFahrenheit(0.0)).toEqual(32); }); }); diff --git a/tests/openhab.metadata.test.js b/tests/openhab.metadata.test.js index e05e8914..a7768d89 100644 --- a/tests/openhab.metadata.test.js +++ b/tests/openhab.metadata.test.js @@ -1,5 +1,4 @@ - -const OpenHAB = require('../functions/openhab.js').OpenHAB; +const OpenHAB = require('../functions/openhab.js'); describe('Test SYNC with Metadata', () => { test('Light Devices', async () => { diff --git a/tests/openhab.tags.test.js b/tests/openhab.tags.test.js index c77f4dc5..b1ed1ebc 100644 --- a/tests/openhab.tags.test.js +++ b/tests/openhab.tags.test.js @@ -1,5 +1,4 @@ - -const OpenHAB = require('../functions/openhab.js').OpenHAB; +const OpenHAB = require('../functions/openhab.js'); describe('Test SYNC with Tags', () => { test('Light Devices', async () => { diff --git a/tests/openhab.test.js b/tests/openhab.test.js index 4b38578d..4395fa03 100644 --- a/tests/openhab.test.js +++ b/tests/openhab.test.js @@ -1,5 +1,4 @@ - -const OpenHAB = require('../functions/openhab.js').OpenHAB; +const OpenHAB = require('../functions/openhab.js'); describe('Test EXECUTE', () => { test('OnOff Switch', async () => { From 69c204c668fbc46de8f823ecae48388056f52eed Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sun, 21 Jun 2020 00:10:46 +0200 Subject: [PATCH 10/94] Restructure commands into single files Signed-off-by: Michael Krug --- functions/commands.js | 643 +----------------- functions/commands/activatescene.js | 21 + functions/commands/armdisaarm.js | 27 + functions/commands/brightnessabsolute.js | 23 + functions/commands/colorabsolute.js | 27 + .../commands/colorabsolutetemperature.js | 57 ++ functions/commands/default.js | 124 ++++ functions/commands/getcamerastream.js | 28 + functions/commands/lockunlock.js | 30 + functions/commands/onoff.js | 27 + functions/commands/openclose.js | 35 + functions/commands/setfanspeed.js | 23 + functions/commands/setvolume.js | 24 + functions/commands/startstop.js | 32 + functions/commands/thermostatsetmode.js | 36 + .../commands/thermostattemperaturesetpoint.js | 40 ++ .../thermostattemperaturesetpointhigh.js | 40 ++ .../thermostattemperaturesetpointlow.js | 40 ++ functions/commands/volumerelative.js | 30 + 19 files changed, 675 insertions(+), 632 deletions(-) create mode 100644 functions/commands/activatescene.js create mode 100644 functions/commands/armdisaarm.js create mode 100644 functions/commands/brightnessabsolute.js create mode 100644 functions/commands/colorabsolute.js create mode 100644 functions/commands/colorabsolutetemperature.js create mode 100644 functions/commands/default.js create mode 100644 functions/commands/getcamerastream.js create mode 100644 functions/commands/lockunlock.js create mode 100644 functions/commands/onoff.js create mode 100644 functions/commands/openclose.js create mode 100644 functions/commands/setfanspeed.js create mode 100644 functions/commands/setvolume.js create mode 100644 functions/commands/startstop.js create mode 100644 functions/commands/thermostatsetmode.js create mode 100644 functions/commands/thermostattemperaturesetpoint.js create mode 100644 functions/commands/thermostattemperaturesetpointhigh.js create mode 100644 functions/commands/thermostattemperaturesetpointlow.js create mode 100644 functions/commands/volumerelative.js diff --git a/functions/commands.js b/functions/commands.js index 4ac64e3c..3b05709c 100644 --- a/functions/commands.js +++ b/functions/commands.js @@ -17,641 +17,20 @@ * @author Michael Krug * */ -const Thermostat = require('../functions/devices/thermostat.js'); -const ackSupported = [ - 'action.devices.commands.ArmDisarm', - 'action.devices.commands.Fill', - 'action.devices.commands.LockUnlock', - 'action.devices.commands.OnOff', - 'action.devices.commands.OpenClose', - 'action.devices.commands.ActivateScene', - 'action.devices.commands.ThermostatTemperatureSetpoint', - 'action.devices.commands.ThermostatTemperatureSetRange', - 'action.devices.commands.ThermostatSetMode', - 'action.devices.commands.TemperatureRelative' -]; +const glob = require('glob'); -const getCommandType = (command = '', params = {}) => { - return CommandTypes.find((commandType) => command === commandType.type && commandType.validateParams(params)); -}; - -class GenericCommand { - static get type() { - return ''; - } - - static validateParams(params = {}) { - return false; - } - - static convertParamsToValue(params = {}, item = {}, device = {}) { - return null; - } - - static getResponseStates(params = {}, item = {}) { - return {}; - } - - static getItemName(item = {}) { - return item.name; - } - - static get requiresItem() { - return false; - } - - static handlAuthPin(device = {}, challenge = {}) { - if (!device.customData || !device.customData.pinNeeded || challenge.pin === device.customData.pinNeeded) { - return; - } - return { - ids: [device.id], - status: 'ERROR', - errorCode: 'challengeNeeded', - challengeNeeded: { - type: !challenge.pin ? 'pinNeeded' : 'challengeFailedPinNeeded' - } - }; - } - - static handlAuthAck(device = {}, challenge = {}, responseStates = {}) { - if (!device.customData || !device.customData.ackNeeded || challenge.ack === true) { - return; - } - return { - ids: [device.id], - status: 'ERROR', - states: responseStates, - errorCode: 'challengeNeeded', - challengeNeeded: { - type: 'ackNeeded' - } - }; - } - - static execute(apiHandler = {}, devices = [], params = {}, challenge = {}) { - console.log(`openhabGoogleAssistant - ${this.type}: ${JSON.stringify({ devices: devices, params: params })}`); - const commandsResponse = []; - const promises = devices.map((device) => { - - const authPinResponse = this.handlAuthPin(device, challenge); - if (authPinResponse) { - commandsResponse.push(authPinResponse); - return Promise.resolve(); - } - - const ackWithState = ackSupported.includes(this.type) && device.customData && device.customData.ackNeeded && !challenge.ack; - - let getItemPromise = Promise.resolve(({ name: device.id })); - if (this.requiresItem || ackWithState) { - getItemPromise = apiHandler.getItem(device.id); - } - - return getItemPromise.then((item) => { - const responseStates = this.getResponseStates(params, item); - if (Object.keys(responseStates).length) { - responseStates.online = true; - } - - const authAckResponse = this.handlAuthAck(device, challenge, responseStates); - if (authAckResponse) { - commandsResponse.push(authAckResponse); - return; - } - - const targetItem = this.getItemName(item); - const targetValue = this.convertParamsToValue(params, item, device); - let sendCommandPromise = Promise.resolve(); - if (typeof targetItem === 'string' && typeof targetValue === 'string') { - sendCommandPromise = apiHandler.sendCommand(targetItem, targetValue); - } - return sendCommandPromise.then(() => { - commandsResponse.push({ - ids: [device.id], - status: 'SUCCESS', - states: responseStates - }); - }); - }).catch((error) => { - console.error(`openhabGoogleAssistant - ${this.type}: ERROR ${JSON.stringify(error)}`); - commandsResponse.push({ - ids: [device.id], - status: 'ERROR', - errorCode: error.statusCode == 404 ? 'deviceNotFound' : error.statusCode == 400 ? 'notSupported' : 'deviceOffline' - }); - }); - }); - return Promise.all(promises).then(() => commandsResponse); - } -} - -class OnOffCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.OnOff'; - } - - static validateParams(params) { - return ('on' in params) && typeof params.on === 'boolean'; - } - - static convertParamsToValue(params, item, device) { - let on = params.on; - if (device.customData && device.customData.inverted === true) { - on = !on; - } - return on ? 'ON' : 'OFF'; - } - - static getResponseStates(params) { - return { - on: params.on - }; - } -} - -class LockUnlockCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.LockUnlock'; - } - - static validateParams(params) { - return ('lock' in params) && typeof params.lock === 'boolean'; - } - - static convertParamsToValue(params, item, device) { - if (device.customData && device.customData.itemType === 'Contact') { - throw { statusCode: 400 }; - } - let lock = params.lock; - if (device.customData && device.customData.inverted === true) { - lock = !lock; - } - return lock ? 'ON' : 'OFF'; - } - - static getResponseStates(params) { - return { - isLocked: params.lock - }; - } -} - -class ArmDisarmCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.ArmDisarm'; - } - - static validateParams(params) { - return ('arm' in params) && typeof params.arm === 'boolean'; - } - - static convertParamsToValue(params, item, device) { - let arm = params.arm; - if (device.customData && device.customData.inverted === true) { - arm = !arm; - } - return arm ? 'ON' : 'OFF'; - } - - static getResponseStates(params) { - return { - isArmed: params.arm - }; - } -} - -class ActivateSceneCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.ActivateScene'; - } - - static validateParams(params) { - return (('deactivate' in params) && typeof params.deactivate === 'boolean') || !('deactivate' in params); - } - - static convertParamsToValue(params, item, device) { - let deactivate = params.deactivate; - if (device.customData && device.customData.inverted === true) { - deactivate = !deactivate; - } - return !deactivate ? 'ON' : 'OFF'; - } -} - - -class SetVolumeCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.setVolume'; - } - - static validateParams(params) { - return ('volumeLevel' in params) && typeof params.volumeLevel === 'number'; - } - - static convertParamsToValue(params) { - return params.volumeLevel.toString(); - } - - static getResponseStates(params) { - return { - currentVolume: params.volumeLevel, - isMuted: params.volumeLevel === 0 - }; - } -} - -class VolumeRelativeCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.volumeRelative'; - } - - static validateParams(params) { - return ('volumeRelativeLevel' in params) && typeof params.volumeRelativeLevel === 'number'; - } - - static get requiresItem() { - return true; - } - - static convertParamsToValue(params, item) { - let level = parseInt(item.state) + params.volumeRelativeLevel; - return (level < 0 ? 0 : level > 100 ? 100 : level).toString(); - } - - static getResponseStates(params, item) { - const state = parseInt(this.convertParamsToValue(params, item)); - return { - currentVolume: state, - isMuted: state === 0 - }; - } -} - -class BrightnessAbsoluteCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.BrightnessAbsolute'; - } - - static validateParams(params) { - return ('brightness' in params) && typeof params.brightness === 'number'; - } - - static convertParamsToValue(params) { - return params.brightness.toString(); - } - - static getResponseStates(params) { - return { - brightness: params.brightness - }; - } -} - -class ColorAbsoluteCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.ColorAbsolute'; - } - - static validateParams(params) { - return ('color' in params) && typeof params.color === 'object' && - ('spectrumHSV' in params.color) && typeof params.color.spectrumHSV === 'object'; - } - - static convertParamsToValue(params) { - const hsv = params.color.spectrumHSV; - return [hsv.hue, hsv.saturation * 100, hsv.value * 100].join(','); - } - - static getResponseStates(params) { - return { - color: { - spectrumHsv: params.color.spectrumHSV - } - }; - } -} - -class ColorAbsoluteTemperatureCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.ColorAbsolute'; - } - - static validateParams(params) { - return ('color' in params) && typeof params.color === 'object' && - ('temperature' in params.color) && typeof params.color.temperature === 'number'; - } - - static get requiresItem() { - return true; - } - - static convertParamsToValue(params, item) { - const hsv = this.rgb2hsv(this.kelvin2rgb(params.color.temperature)); - const hsvArray = item.state.split(",").map((val) => Number(val)); - return [Math.round(hsv.hue * 100) / 100, Math.round(hsv.saturation * 1000) / 10, hsvArray[2]].join(','); - } - - static getResponseStates(params) { - return { - color: { - temperatureK: params.color.temperature - } - }; - } - - static kelvin2rgb(kelvin) { - const temp = kelvin / 100; - const r = temp <= 66 ? 255 : 329.698727446 * Math.pow(temp - 60, -0.1332047592); - const g = temp <= 66 ? 99.4708025861 * Math.log(temp) - 161.1195681661 : 288.1221695283 * Math.pow(temp - 60, -0.0755148492); - const b = temp <= 66 ? (temp <= 19 ? 0 : 138.5177312231 * Math.log(temp - 10) - 305.0447927307) : 255; - return { - r: r < 0 ? 0 : r > 255 ? 255 : Math.round(r), - g: g < 0 ? 0 : g > 255 ? 255 : Math.round(g), - b: b < 0 ? 0 : b > 255 ? 255 : Math.round(b), - }; - } - - static rgb2hsv({ r, g, b }) { - r = r / 255; - g = g / 255; - b = b / 255; - let v = Math.max(r, g, b), n = v - Math.min(r, g, b); - let h = n && ((v == r) ? (g - b) / n : ((v == g) ? 2 + (b - r) / n : 4 + (r - g) / n)); - return { - hue: 60 * (h < 0 ? h + 6 : h), - saturation: v && n / v, - value: v - }; - } -} - -class OpenCloseCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.OpenClose'; - } - - static validateParams(params) { - return ('openPercent' in params) && typeof params.openPercent === 'number'; - } - - static convertParamsToValue(params, item, device) { - if (device.customData && device.customData.itemType === 'Contact') { - throw { statusCode: 400 }; - } - let openPercent = params.openPercent; - if (device.customData && device.customData.inverted === true) { - openPercent = 100 - openPercent; - } - let value = openPercent === 0 ? 'DOWN' : openPercent === 100 ? 'UP' : (100 - openPercent).toString(); - // item can not handle OpenClose --> we will send "ON" / "OFF" - if (device.customData && device.customData.itemType !== 'Rollershutter') { - value = openPercent === 0 ? 'OFF' : 'ON'; - } - return value; - } - - static getResponseStates(params) { - return { - openPercent: params.openPercent - }; - } -} - -class StartStopCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.StartStop'; - } - - static validateParams(params) { - return ('start' in params) && typeof params.start === 'boolean'; - } - - static convertParamsToValue(params, item, device) { - if (device.customData && device.customData.itemType === 'Contact') { - throw { statusCode: 400 }; - } - let value = params.start ? 'MOVE' : 'STOP'; - // item can not handle StartStop --> we will send "ON" / "OFF" - if (device.customData && device.customData.itemType !== 'Rollershutter') { - value = params.start ? 'ON' : 'OFF'; - } - return value; - } - - static getResponseStates(params) { - return { - isRunning: params.start, - isPaused: !params.start - }; - } -} - -class SetFanSpeedCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.SetFanSpeed'; - } - - static validateParams(params) { - return ('fanSpeed' in params) && typeof params.fanSpeed === 'string'; - } - - static convertParamsToValue(params) { - return params.fanSpeed.toString(); - } - - static getResponseStates(params) { - return { - currentFanSpeedSetting: params.fanSpeed - }; - } -} - -class GetCameraStreamCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.GetCameraStream'; - } - - static validateParams(params) { - return ('StreamToChromecast' in params) && typeof params.StreamToChromecast === 'boolean' && - ('SupportedStreamProtocols' in params) && typeof params.SupportedStreamProtocols === 'object'; - } - - static get requiresItem() { - return true; - } - - static convertParamsToValue() { - return null; - } - - static getResponseStates(params, item) { - return { - cameraStreamAccessUrl: item.state - }; - } -} - -class ThermostatTemperatureSetpointCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.ThermostatTemperatureSetpoint'; - } +const Commands = []; - static validateParams(params) { - return ('thermostatTemperatureSetpoint' in params) && typeof params.thermostatTemperatureSetpoint === 'number'; +glob.sync('./commands/*.js', { cwd: __dirname }).forEach(file => { + const command = require(file); + if (command.type) { + Commands.push(command); } +}); - static get requiresItem() { - return true; - } - - static getItemName(item) { - const members = Thermostat.getMembers(item); - if ('thermostatTemperatureSetpoint' in members) { - return members.thermostatTemperatureSetpoint.name; - } - throw { statusCode: 400 }; - } - - static convertParamsToValue(params, item) { - let value = params.thermostatTemperatureSetpoint; - if (Thermostat.usesFahrenheit(item)) { - value = Thermostat.convertToFahrenheit(value); - } - return value.toString(); - } - - static getResponseStates(params, item) { - const states = Thermostat.getState(item); - states.thermostatTemperatureSetpoint = params.thermostatTemperatureSetpoint; - return states; - } -} - -class ThermostatTemperatureSetpointHighCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.ThermostatTemperatureSetpointHigh'; - } - - static validateParams(params) { - return ('thermostatTemperatureSetpointHigh' in params) && typeof params.thermostatTemperatureSetpointHigh === 'number'; - } - - static get requiresItem() { - return true; - } - - static getItemName(item) { - const members = Thermostat.getMembers(item); - if ('thermostatTemperatureSetpointHigh' in members) { - return members.thermostatTemperatureSetpointHigh.name; - } - throw { statusCode: 400 }; - } - - static convertParamsToValue(params, item) { - let value = params.thermostatTemperatureSetpointHigh; - if (Thermostat.usesFahrenheit(item)) { - value = Thermostat.convertToFahrenheit(value); - } - return value.toString(); - } - - static getResponseStates(params, item) { - const states = Thermostat.getState(item); - states.thermostatTemperatureSetpointHigh = params.thermostatTemperatureSetpointHigh; - return states; - } -} - -class ThermostatTemperatureSetpointLowCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.ThermostatTemperatureSetpointLow'; - } - - static validateParams(params) { - return ('thermostatTemperatureSetpointLow' in params) && typeof params.thermostatTemperatureSetpointLow === 'number'; - } - - static get requiresItem() { - return true; - } - - static getItemName(item) { - const members = Thermostat.getMembers(item); - if ('thermostatTemperatureSetpointLow' in members) { - return members.thermostatTemperatureSetpointLow.name; - } - throw { statusCode: 400 }; - } - - static convertParamsToValue(params, item) { - let value = params.thermostatTemperatureSetpointLow; - if (Thermostat.usesFahrenheit(item)) { - value = Thermostat.convertToFahrenheit(value); - } - return value.toString(); - } - - static getResponseStates(params, item) { - const states = Thermostat.getState(item); - states.thermostatTemperatureSetpointLow = params.thermostatTemperatureSetpointLow; - return states; - } -} - -class ThermostatSetModeCommand extends GenericCommand { - static get type() { - return 'action.devices.commands.ThermostatSetMode'; - } - - static validateParams(params) { - return ('thermostatMode' in params) && typeof params.thermostatMode === 'string'; - } - - static get requiresItem() { - return true; - } - - static getItemName(item) { - const members = Thermostat.getMembers(item); - if (!('thermostatMode' in members)) { - throw { statusCode: 400 }; - } - return members.thermostatMode.name; - } - - static convertParamsToValue(params, item) { - return Thermostat.translateModeToOpenhab(item, params.thermostatMode); - } - - static getResponseStates(params, item) { - const states = Thermostat.getState(item); - states.thermostatMode = params.thermostatMode; - return states; - } -} - -const CommandTypes = [ - OnOffCommand, - LockUnlockCommand, - ArmDisarmCommand, - ActivateSceneCommand, - BrightnessAbsoluteCommand, - SetVolumeCommand, - VolumeRelativeCommand, - ColorAbsoluteCommand, - ColorAbsoluteTemperatureCommand, - OpenCloseCommand, - StartStopCommand, - SetFanSpeedCommand, - GetCameraStreamCommand, - ThermostatTemperatureSetpointCommand, - ThermostatTemperatureSetpointHighCommand, - ThermostatTemperatureSetpointLowCommand, - ThermostatSetModeCommand -]; - -module.exports = { - getCommandType +const getCommandType = (command = '', params = {}) => { + return Commands.find((commandType) => command === commandType.type && commandType.validateParams(params)); }; + +module.exports = { getCommandType }; diff --git a/functions/commands/activatescene.js b/functions/commands/activatescene.js new file mode 100644 index 00000000..dac738f2 --- /dev/null +++ b/functions/commands/activatescene.js @@ -0,0 +1,21 @@ +const DefaultCommand = require('./default.js'); + +class ActivateScene extends DefaultCommand { + static get type() { + return 'action.devices.commands.ActivateScene'; + } + + static validateParams(params) { + return (('deactivate' in params) && typeof params.deactivate === 'boolean') || !('deactivate' in params); + } + + static convertParamsToValue(params, item, device) { + let deactivate = params.deactivate; + if (device.customData && device.customData.inverted === true) { + deactivate = !deactivate; + } + return !deactivate ? 'ON' : 'OFF'; + } +} + +module.exports = ActivateScene; diff --git a/functions/commands/armdisaarm.js b/functions/commands/armdisaarm.js new file mode 100644 index 00000000..97c0ea7c --- /dev/null +++ b/functions/commands/armdisaarm.js @@ -0,0 +1,27 @@ +const DefaultCommand = require('./default.js'); + +class ArmDisarm extends DefaultCommand { + static get type() { + return 'action.devices.commands.ArmDisarm'; + } + + static validateParams(params) { + return ('arm' in params) && typeof params.arm === 'boolean'; + } + + static convertParamsToValue(params, item, device) { + let arm = params.arm; + if (device.customData && device.customData.inverted === true) { + arm = !arm; + } + return arm ? 'ON' : 'OFF'; + } + + static getResponseStates(params) { + return { + isArmed: params.arm + }; + } +} + +module.exports = ArmDisarm; diff --git a/functions/commands/brightnessabsolute.js b/functions/commands/brightnessabsolute.js new file mode 100644 index 00000000..8b501fd9 --- /dev/null +++ b/functions/commands/brightnessabsolute.js @@ -0,0 +1,23 @@ +const DefaultCommand = require('./default.js'); + +class BrightnessAbsolute extends DefaultCommand { + static get type() { + return 'action.devices.commands.BrightnessAbsolute'; + } + + static validateParams(params) { + return ('brightness' in params) && typeof params.brightness === 'number'; + } + + static convertParamsToValue(params) { + return params.brightness.toString(); + } + + static getResponseStates(params) { + return { + brightness: params.brightness + }; + } +} + +module.exports = BrightnessAbsolute; diff --git a/functions/commands/colorabsolute.js b/functions/commands/colorabsolute.js new file mode 100644 index 00000000..9755ab70 --- /dev/null +++ b/functions/commands/colorabsolute.js @@ -0,0 +1,27 @@ +const DefaultCommand = require('./default.js'); + +class ColorAbsolute extends DefaultCommand { + static get type() { + return 'action.devices.commands.ColorAbsolute'; + } + + static validateParams(params) { + return ('color' in params) && typeof params.color === 'object' && + ('spectrumHSV' in params.color) && typeof params.color.spectrumHSV === 'object'; + } + + static convertParamsToValue(params) { + const hsv = params.color.spectrumHSV; + return [hsv.hue, hsv.saturation * 100, hsv.value * 100].join(','); + } + + static getResponseStates(params) { + return { + color: { + spectrumHsv: params.color.spectrumHSV + } + }; + } +} + +module.exports = ColorAbsolute; diff --git a/functions/commands/colorabsolutetemperature.js b/functions/commands/colorabsolutetemperature.js new file mode 100644 index 00000000..a71eb26b --- /dev/null +++ b/functions/commands/colorabsolutetemperature.js @@ -0,0 +1,57 @@ +const DefaultCommand = require('./default.js'); + +class ColorAbsoluteTemperature extends DefaultCommand { + static get type() { + return 'action.devices.commands.ColorAbsolute'; + } + + static validateParams(params) { + return ('color' in params) && typeof params.color === 'object' && + ('temperature' in params.color) && typeof params.color.temperature === 'number'; + } + + static get requiresItem() { + return true; + } + + static convertParamsToValue(params, item) { + const hsv = this.rgb2hsv(this.kelvin2rgb(params.color.temperature)); + const hsvArray = item.state.split(",").map((val) => Number(val)); + return [Math.round(hsv.hue * 100) / 100, Math.round(hsv.saturation * 1000) / 10, hsvArray[2]].join(','); + } + + static getResponseStates(params) { + return { + color: { + temperatureK: params.color.temperature + } + }; + } + + static kelvin2rgb(kelvin) { + const temp = kelvin / 100; + const r = temp <= 66 ? 255 : 329.698727446 * Math.pow(temp - 60, -0.1332047592); + const g = temp <= 66 ? 99.4708025861 * Math.log(temp) - 161.1195681661 : 288.1221695283 * Math.pow(temp - 60, -0.0755148492); + const b = temp <= 66 ? (temp <= 19 ? 0 : 138.5177312231 * Math.log(temp - 10) - 305.0447927307) : 255; + return { + r: r < 0 ? 0 : r > 255 ? 255 : Math.round(r), + g: g < 0 ? 0 : g > 255 ? 255 : Math.round(g), + b: b < 0 ? 0 : b > 255 ? 255 : Math.round(b), + }; + } + + static rgb2hsv({ r, g, b }) { + r = r / 255; + g = g / 255; + b = b / 255; + let v = Math.max(r, g, b), n = v - Math.min(r, g, b); + let h = n && ((v == r) ? (g - b) / n : ((v == g) ? 2 + (b - r) / n : 4 + (r - g) / n)); + return { + hue: 60 * (h < 0 ? h + 6 : h), + saturation: v && n / v, + value: v + }; + } +} + +module.exports = ColorAbsoluteTemperature; diff --git a/functions/commands/default.js b/functions/commands/default.js new file mode 100644 index 00000000..51769635 --- /dev/null +++ b/functions/commands/default.js @@ -0,0 +1,124 @@ +const ackSupported = [ + 'action.devices.commands.ArmDisarm', + 'action.devices.commands.Fill', + 'action.devices.commands.LockUnlock', + 'action.devices.commands.OnOff', + 'action.devices.commands.OpenClose', + 'action.devices.commands.ActivateScene', + 'action.devices.commands.ThermostatTemperatureSetpoint', + 'action.devices.commands.ThermostatTemperatureSetRange', + 'action.devices.commands.ThermostatSetMode', + 'action.devices.commands.TemperatureRelative' +]; + +class DefaultCommand { + static get type() { + return ''; + } + + static validateParams(params = {}) { + return false; + } + + static convertParamsToValue(params = {}, item = {}, device = {}) { + return null; + } + + static getResponseStates(params = {}, item = {}) { + return {}; + } + + static getItemName(item = {}) { + return item.name; + } + + static get requiresItem() { + return false; + } + + static handlAuthPin(device = {}, challenge = {}) { + if (!device.customData || !device.customData.pinNeeded || challenge.pin === device.customData.pinNeeded) { + return; + } + return { + ids: [device.id], + status: 'ERROR', + errorCode: 'challengeNeeded', + challengeNeeded: { + type: !challenge.pin ? 'pinNeeded' : 'challengeFailedPinNeeded' + } + }; + } + + static handlAuthAck(device = {}, challenge = {}, responseStates = {}) { + if (!device.customData || !device.customData.ackNeeded || challenge.ack === true) { + return; + } + return { + ids: [device.id], + status: 'ERROR', + states: responseStates, + errorCode: 'challengeNeeded', + challengeNeeded: { + type: 'ackNeeded' + } + }; + } + + static execute(apiHandler = {}, devices = [], params = {}, challenge = {}) { + console.log(`openhabGoogleAssistant - ${this.type}: ${JSON.stringify({ devices: devices, params: params })}`); + const commandsResponse = []; + const promises = devices.map((device) => { + + const authPinResponse = this.handlAuthPin(device, challenge); + if (authPinResponse) { + commandsResponse.push(authPinResponse); + return Promise.resolve(); + } + + const ackWithState = ackSupported.includes(this.type) && device.customData && device.customData.ackNeeded && !challenge.ack; + + let getItemPromise = Promise.resolve(({ name: device.id })); + if (this.requiresItem || ackWithState) { + getItemPromise = apiHandler.getItem(device.id); + } + + return getItemPromise.then((item) => { + const responseStates = this.getResponseStates(params, item); + if (Object.keys(responseStates).length) { + responseStates.online = true; + } + + const authAckResponse = this.handlAuthAck(device, challenge, responseStates); + if (authAckResponse) { + commandsResponse.push(authAckResponse); + return; + } + + const targetItem = this.getItemName(item); + const targetValue = this.convertParamsToValue(params, item, device); + let sendCommandPromise = Promise.resolve(); + if (typeof targetItem === 'string' && typeof targetValue === 'string') { + sendCommandPromise = apiHandler.sendCommand(targetItem, targetValue); + } + return sendCommandPromise.then(() => { + commandsResponse.push({ + ids: [device.id], + status: 'SUCCESS', + states: responseStates + }); + }); + }).catch((error) => { + console.error(`openhabGoogleAssistant - ${this.type}: ERROR ${JSON.stringify(error)}`); + commandsResponse.push({ + ids: [device.id], + status: 'ERROR', + errorCode: error.statusCode == 404 ? 'deviceNotFound' : error.statusCode == 400 ? 'notSupported' : 'deviceOffline' + }); + }); + }); + return Promise.all(promises).then(() => commandsResponse); + } +} + +module.exports = DefaultCommand; diff --git a/functions/commands/getcamerastream.js b/functions/commands/getcamerastream.js new file mode 100644 index 00000000..e4611879 --- /dev/null +++ b/functions/commands/getcamerastream.js @@ -0,0 +1,28 @@ +const DefaultCommand = require('./default.js'); + +class GetCameraStream extends DefaultCommand { + static get type() { + return 'action.devices.commands.GetCameraStream'; + } + + static validateParams(params) { + return ('StreamToChromecast' in params) && typeof params.StreamToChromecast === 'boolean' && + ('SupportedStreamProtocols' in params) && typeof params.SupportedStreamProtocols === 'object'; + } + + static get requiresItem() { + return true; + } + + static convertParamsToValue() { + return null; + } + + static getResponseStates(params, item) { + return { + cameraStreamAccessUrl: item.state + }; + } +} + +module.exports = GetCameraStream; diff --git a/functions/commands/lockunlock.js b/functions/commands/lockunlock.js new file mode 100644 index 00000000..fc29ee5b --- /dev/null +++ b/functions/commands/lockunlock.js @@ -0,0 +1,30 @@ +const DefaultCommand = require('./default.js'); + +class LockUnlock extends DefaultCommand { + static get type() { + return 'action.devices.commands.LockUnlock'; + } + + static validateParams(params) { + return ('lock' in params) && typeof params.lock === 'boolean'; + } + + static convertParamsToValue(params, item, device) { + if (device.customData && device.customData.itemType === 'Contact') { + throw { statusCode: 400 }; + } + let lock = params.lock; + if (device.customData && device.customData.inverted === true) { + lock = !lock; + } + return lock ? 'ON' : 'OFF'; + } + + static getResponseStates(params) { + return { + isLocked: params.lock + }; + } +} + +module.exports = LockUnlock; diff --git a/functions/commands/onoff.js b/functions/commands/onoff.js new file mode 100644 index 00000000..9f7358a7 --- /dev/null +++ b/functions/commands/onoff.js @@ -0,0 +1,27 @@ +const DefaultCommand = require('./default.js'); + +class OnOff extends DefaultCommand { + static get type() { + return 'action.devices.commands.OnOff'; + } + + static validateParams(params) { + return ('on' in params) && typeof params.on === 'boolean'; + } + + static convertParamsToValue(params, item, device) { + let on = params.on; + if (device.customData && device.customData.inverted === true) { + on = !on; + } + return on ? 'ON' : 'OFF'; + } + + static getResponseStates(params) { + return { + on: params.on + }; + } +} + +module.exports = OnOff; diff --git a/functions/commands/openclose.js b/functions/commands/openclose.js new file mode 100644 index 00000000..3ba3487f --- /dev/null +++ b/functions/commands/openclose.js @@ -0,0 +1,35 @@ +const DefaultCommand = require('./default.js'); + +class OpenClose extends DefaultCommand { + static get type() { + return 'action.devices.commands.OpenClose'; + } + + static validateParams(params) { + return ('openPercent' in params) && typeof params.openPercent === 'number'; + } + + static convertParamsToValue(params, item, device) { + if (device.customData && device.customData.itemType === 'Contact') { + throw { statusCode: 400 }; + } + let openPercent = params.openPercent; + if (device.customData && device.customData.inverted === true) { + openPercent = 100 - openPercent; + } + let value = openPercent === 0 ? 'DOWN' : openPercent === 100 ? 'UP' : (100 - openPercent).toString(); + // item can not handle OpenClose --> we will send "ON" / "OFF" + if (device.customData && device.customData.itemType !== 'Rollershutter') { + value = openPercent === 0 ? 'OFF' : 'ON'; + } + return value; + } + + static getResponseStates(params) { + return { + openPercent: params.openPercent + }; + } +} + +module.exports = OpenClose; diff --git a/functions/commands/setfanspeed.js b/functions/commands/setfanspeed.js new file mode 100644 index 00000000..600bd0d9 --- /dev/null +++ b/functions/commands/setfanspeed.js @@ -0,0 +1,23 @@ +const DefaultCommand = require('./default.js'); + +class SetFanSpeed extends DefaultCommand { + static get type() { + return 'action.devices.commands.SetFanSpeed'; + } + + static validateParams(params) { + return ('fanSpeed' in params) && typeof params.fanSpeed === 'string'; + } + + static convertParamsToValue(params) { + return params.fanSpeed.toString(); + } + + static getResponseStates(params) { + return { + currentFanSpeedSetting: params.fanSpeed + }; + } +} + +module.exports = SetFanSpeed; diff --git a/functions/commands/setvolume.js b/functions/commands/setvolume.js new file mode 100644 index 00000000..14362bbc --- /dev/null +++ b/functions/commands/setvolume.js @@ -0,0 +1,24 @@ +const DefaultCommand = require('./default.js'); + +class SetVolume extends DefaultCommand { + static get type() { + return 'action.devices.commands.setVolume'; + } + + static validateParams(params) { + return ('volumeLevel' in params) && typeof params.volumeLevel === 'number'; + } + + static convertParamsToValue(params) { + return params.volumeLevel.toString(); + } + + static getResponseStates(params) { + return { + currentVolume: params.volumeLevel, + isMuted: params.volumeLevel === 0 + }; + } +} + +module.exports = SetVolume; diff --git a/functions/commands/startstop.js b/functions/commands/startstop.js new file mode 100644 index 00000000..da4a1971 --- /dev/null +++ b/functions/commands/startstop.js @@ -0,0 +1,32 @@ +const DefaultCommand = require('./default.js'); + +class StartStop extends DefaultCommand { + static get type() { + return 'action.devices.commands.StartStop'; + } + + static validateParams(params) { + return ('start' in params) && typeof params.start === 'boolean'; + } + + static convertParamsToValue(params, item, device) { + if (device.customData && device.customData.itemType === 'Contact') { + throw { statusCode: 400 }; + } + let value = params.start ? 'MOVE' : 'STOP'; + // item can not handle StartStop --> we will send "ON" / "OFF" + if (device.customData && device.customData.itemType !== 'Rollershutter') { + value = params.start ? 'ON' : 'OFF'; + } + return value; + } + + static getResponseStates(params) { + return { + isRunning: params.start, + isPaused: !params.start + }; + } +} + +module.exports = StartStop; diff --git a/functions/commands/thermostatsetmode.js b/functions/commands/thermostatsetmode.js new file mode 100644 index 00000000..b3abd006 --- /dev/null +++ b/functions/commands/thermostatsetmode.js @@ -0,0 +1,36 @@ +const DefaultCommand = require('./default.js'); +const Thermostat = require('../devices/thermostat.js'); + +class ThermostatSetMode extends DefaultCommand { + static get type() { + return 'action.devices.commands.ThermostatSetMode'; + } + + static validateParams(params) { + return ('thermostatMode' in params) && typeof params.thermostatMode === 'string'; + } + + static get requiresItem() { + return true; + } + + static getItemName(item) { + const members = Thermostat.getMembers(item); + if ('thermostatMode' in members) { + return members.thermostatMode.name; + } + throw { statusCode: 400 }; + } + + static convertParamsToValue(params, item) { + return Thermostat.translateModeToOpenhab(item, params.thermostatMode); + } + + static getResponseStates(params, item) { + const states = Thermostat.getState(item); + states.thermostatMode = params.thermostatMode; + return states; + } +} + +module.exports = ThermostatSetMode; diff --git a/functions/commands/thermostattemperaturesetpoint.js b/functions/commands/thermostattemperaturesetpoint.js new file mode 100644 index 00000000..49206d8b --- /dev/null +++ b/functions/commands/thermostattemperaturesetpoint.js @@ -0,0 +1,40 @@ +const DefaultCommand = require('./default.js'); +const Thermostat = require('../devices/thermostat.js'); + +class ThermostatTemperatureSetpoint extends DefaultCommand { + static get type() { + return 'action.devices.commands.ThermostatTemperatureSetpoint'; + } + + static validateParams(params) { + return ('thermostatTemperatureSetpoint' in params) && typeof params.thermostatTemperatureSetpoint === 'number'; + } + + static get requiresItem() { + return true; + } + + static getItemName(item) { + const members = Thermostat.getMembers(item); + if ('thermostatTemperatureSetpoint' in members) { + return members.thermostatTemperatureSetpoint.name; + } + throw { statusCode: 400 }; + } + + static convertParamsToValue(params, item) { + let value = params.thermostatTemperatureSetpoint; + if (Thermostat.usesFahrenheit(item)) { + value = Thermostat.convertToFahrenheit(value); + } + return value.toString(); + } + + static getResponseStates(params, item) { + const states = Thermostat.getState(item); + states.thermostatTemperatureSetpoint = params.thermostatTemperatureSetpoint; + return states; + } +} + +module.exports = ThermostatTemperatureSetpoint; diff --git a/functions/commands/thermostattemperaturesetpointhigh.js b/functions/commands/thermostattemperaturesetpointhigh.js new file mode 100644 index 00000000..bc0371e9 --- /dev/null +++ b/functions/commands/thermostattemperaturesetpointhigh.js @@ -0,0 +1,40 @@ +const DefaultCommand = require('./default.js'); +const Thermostat = require('../devices/thermostat.js'); + +class ThermostatTemperatureSetpointHigh extends DefaultCommand { + static get type() { + return 'action.devices.commands.ThermostatTemperatureSetpointHigh'; + } + + static validateParams(params) { + return ('thermostatTemperatureSetpointHigh' in params) && typeof params.thermostatTemperatureSetpointHigh === 'number'; + } + + static get requiresItem() { + return true; + } + + static getItemName(item) { + const members = Thermostat.getMembers(item); + if ('thermostatTemperatureSetpointHigh' in members) { + return members.thermostatTemperatureSetpointHigh.name; + } + throw { statusCode: 400 }; + } + + static convertParamsToValue(params, item) { + let value = params.thermostatTemperatureSetpointHigh; + if (Thermostat.usesFahrenheit(item)) { + value = Thermostat.convertToFahrenheit(value); + } + return value.toString(); + } + + static getResponseStates(params, item) { + const states = Thermostat.getState(item); + states.thermostatTemperatureSetpointHigh = params.thermostatTemperatureSetpointHigh; + return states; + } +} + +module.exports = ThermostatTemperatureSetpointHigh; diff --git a/functions/commands/thermostattemperaturesetpointlow.js b/functions/commands/thermostattemperaturesetpointlow.js new file mode 100644 index 00000000..4089811d --- /dev/null +++ b/functions/commands/thermostattemperaturesetpointlow.js @@ -0,0 +1,40 @@ +const DefaultCommand = require('./default.js'); +const Thermostat = require('../devices/thermostat.js'); + +class ThermostatTemperatureSetpointLow extends DefaultCommand { + static get type() { + return 'action.devices.commands.ThermostatTemperatureSetpointLow'; + } + + static validateParams(params) { + return ('thermostatTemperatureSetpointLow' in params) && typeof params.thermostatTemperatureSetpointLow === 'number'; + } + + static get requiresItem() { + return true; + } + + static getItemName(item) { + const members = Thermostat.getMembers(item); + if ('thermostatTemperatureSetpointLow' in members) { + return members.thermostatTemperatureSetpointLow.name; + } + throw { statusCode: 400 }; + } + + static convertParamsToValue(params, item) { + let value = params.thermostatTemperatureSetpointLow; + if (Thermostat.usesFahrenheit(item)) { + value = Thermostat.convertToFahrenheit(value); + } + return value.toString(); + } + + static getResponseStates(params, item) { + const states = Thermostat.getState(item); + states.thermostatTemperatureSetpointLow = params.thermostatTemperatureSetpointLow; + return states; + } +} + +module.exports = ThermostatTemperatureSetpointLow; diff --git a/functions/commands/volumerelative.js b/functions/commands/volumerelative.js new file mode 100644 index 00000000..39cab3fd --- /dev/null +++ b/functions/commands/volumerelative.js @@ -0,0 +1,30 @@ +const DefaultCommand = require('./default.js'); + +class VolumeRelative extends DefaultCommand { + static get type() { + return 'action.devices.commands.volumeRelative'; + } + + static validateParams(params) { + return ('volumeRelativeLevel' in params) && typeof params.volumeRelativeLevel === 'number'; + } + + static get requiresItem() { + return true; + } + + static convertParamsToValue(params, item) { + let level = parseInt(item.state) + params.volumeRelativeLevel; + return (level < 0 ? 0 : level > 100 ? 100 : level).toString(); + } + + static getResponseStates(params, item) { + const state = parseInt(this.convertParamsToValue(params, item)); + return { + currentVolume: state, + isMuted: state === 0 + }; + } +} + +module.exports = VolumeRelative; From 55be6347ef12260552feecaa45dab62621f2fd9b Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 1 Aug 2020 03:01:41 +0200 Subject: [PATCH 11/94] Update dependencies, increase version Signed-off-by: Michael Krug --- functions/package.json | 2 +- package-lock.json | 3691 ++++++++++++++++++++-------------------- package.json | 8 +- 3 files changed, 1882 insertions(+), 1819 deletions(-) diff --git a/functions/package.json b/functions/package.json index f5f154db..d84dd5b0 100644 --- a/functions/package.json +++ b/functions/package.json @@ -1,6 +1,6 @@ { "name": "openhab.google-assistant-smarthome.cloud-function", - "version": "2.0.22", + "version": "2.1.0", "description": "A Google Assistant, Actions on Google based implementation for openHAB", "repository": { "type": "git", diff --git a/package-lock.json b/package-lock.json index de0233a7..c95d2229 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,37 +1,37 @@ { "name": "openhab.google-assistant-smarthome", - "version": "2.0.22", + "version": "2.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", "dev": true, "requires": { - "@babel/highlight": "^7.8.3" + "@babel/highlight": "^7.10.4" } }, "@babel/core": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz", - "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.0", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helpers": "^7.9.0", - "@babel/parser": "^7.9.0", - "@babel/template": "^7.8.6", - "@babel/traverse": "^7.9.0", - "@babel/types": "^7.9.0", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.0.tgz", + "integrity": "sha512-mkLq8nwaXmDtFmRkQ8ED/eA2CnVw4zr7dCztKalZXBvdK5EeNUAesrrwUqjQEzFgomJssayzB0aqlOsP1vGLqg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.11.0", + "@babel/helper-module-transforms": "^7.11.0", + "@babel/helpers": "^7.10.4", + "@babel/parser": "^7.11.0", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.11.0", + "@babel/types": "^7.11.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", "json5": "^2.1.2", - "lodash": "^4.17.13", + "lodash": "^4.17.19", "resolve": "^1.3.2", "semver": "^5.4.1", "source-map": "^0.5.0" @@ -61,14 +61,13 @@ } }, "@babel/generator": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.5.tgz", - "integrity": "sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz", + "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==", "dev": true, "requires": { - "@babel/types": "^7.9.5", + "@babel/types": "^7.11.0", "jsesc": "^2.5.1", - "lodash": "^4.17.13", "source-map": "^0.5.0" }, "dependencies": { @@ -81,138 +80,262 @@ } }, "@babel/helper-function-name": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz", - "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.9.5" + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.4" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", - "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", + "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.11.0" } }, "@babel/helper-module-imports": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", - "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", + "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.4" } }, "@babel/helper-module-transforms": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", - "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", + "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-simple-access": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.6", - "@babel/types": "^7.9.0", - "lodash": "^4.17.13" + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/template": "^7.10.4", + "@babel/types": "^7.11.0", + "lodash": "^4.17.19" } }, "@babel/helper-optimise-call-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", - "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.4" } }, "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", "dev": true }, "@babel/helper-replace-supers": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz", - "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", + "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.8.6", - "@babel/types": "^7.8.6" + "@babel/helper-member-expression-to-functions": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-simple-access": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", - "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", + "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", "dev": true, "requires": { - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.11.0" } }, "@babel/helper-validator-identifier": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", - "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", "dev": true }, "@babel/helpers": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz", - "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", + "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", "dev": true, "requires": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.9.0", - "@babel/types": "^7.9.0" + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/highlight": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", - "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.9.0", + "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/parser": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", - "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz", + "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==", "dev": true }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", + "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, "@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", @@ -222,32 +345,50 @@ "@babel/helper-plugin-utils": "^7.8.0" } }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, "@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/traverse": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.5.tgz", - "integrity": "sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", + "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.5", - "@babel/helper-function-name": "^7.9.5", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.9.0", - "@babel/types": "^7.9.5", + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.11.0", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.11.0", + "@babel/types": "^7.11.0", "debug": "^4.1.0", "globals": "^11.1.0", - "lodash": "^4.17.13" + "lodash": "^4.17.19" }, "dependencies": { "debug": { @@ -268,16 +409,22 @@ } }, "@babel/types": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz", - "integrity": "sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", + "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.9.5", - "lodash": "^4.17.13", + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, "@cnakazawa/watch": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", @@ -288,178 +435,279 @@ "minimist": "^1.2.0" } }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.2.0.tgz", + "integrity": "sha512-mXQfx3nSLwiHm1i7jbu+uvi+vvpVjNGzIQYLCfsat9rapC+MJkS4zBseNrgJE0vU921b3P67bQzhduphjY3Tig==", "dev": true, "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "@jest/types": "^26.2.0", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.2.0", + "jest-util": "^26.2.0", + "slash": "^3.0.0" } }, "@jest/core": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz", - "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/reporters": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.2.2.tgz", + "integrity": "sha512-UwA8gNI8aeV4FHGfGAUfO/DHjrFVvlBravF1Tm9Kt6qFE+6YHR47kFhgdepOFpADEKstyO+MVdPvkV6/dyt9sA==", + "dev": true, + "requires": { + "@jest/console": "^26.2.0", + "@jest/reporters": "^26.2.2", + "@jest/test-result": "^26.2.0", + "@jest/transform": "^26.2.2", + "@jest/types": "^26.2.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-changed-files": "^24.9.0", - "jest-config": "^24.9.0", - "jest-haste-map": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.9.0", - "jest-resolve-dependencies": "^24.9.0", - "jest-runner": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-snapshot": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "jest-watcher": "^24.9.0", - "micromatch": "^3.1.10", - "p-each-series": "^1.0.0", - "realpath-native": "^1.1.0", - "rimraf": "^2.5.4", - "slash": "^2.0.0", - "strip-ansi": "^5.0.0" + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.2.0", + "jest-config": "^26.2.2", + "jest-haste-map": "^26.2.2", + "jest-message-util": "^26.2.0", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.2.2", + "jest-resolve-dependencies": "^26.2.2", + "jest-runner": "^26.2.2", + "jest-runtime": "^26.2.2", + "jest-snapshot": "^26.2.2", + "jest-util": "^26.2.0", + "jest-validate": "^26.2.0", + "jest-watcher": "^26.2.0", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } } }, "@jest/environment": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", - "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.2.0.tgz", + "integrity": "sha512-oCgp9NmEiJ5rbq9VI/v/yYLDpladAAVvFxZgNsnJxOETuzPZ0ZcKKHYjKYwCtPOP1WCrM5nmyuOhMStXFGHn+g==", "dev": true, "requires": { - "@jest/fake-timers": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0" + "@jest/fake-timers": "^26.2.0", + "@jest/types": "^26.2.0", + "@types/node": "*", + "jest-mock": "^26.2.0" } }, "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.2.0.tgz", + "integrity": "sha512-45Gfe7YzYTKqTayBrEdAF0qYyAsNRBzfkV0IyVUm3cx7AsCWlnjilBM4T40w7IXT5VspOgMPikQlV0M6gHwy/g==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" + "@jest/types": "^26.2.0", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.2.0", + "jest-mock": "^26.2.0", + "jest-util": "^26.2.0" } }, - "@jest/reporters": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz", - "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==", + "@jest/globals": { + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.2.0.tgz", + "integrity": "sha512-Hoc6ScEIPaym7RNytIL2ILSUWIGKlwEv+JNFof9dGYOdvPjb2evEURSslvCMkNuNg1ECEClTE8PH7ULlMJntYA==", "dev": true, "requires": { - "@jest/environment": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", + "@jest/environment": "^26.2.0", + "@jest/types": "^26.2.0", + "expect": "^26.2.0" + } + }, + "@jest/reporters": { + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.2.2.tgz", + "integrity": "sha512-7854GPbdFTAorWVh+RNHyPO9waRIN6TcvCezKVxI1khvFq9YjINTW7J3WU+tbR038Ynn6WjYred6vtT0YmIWVQ==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.2.0", + "@jest/test-result": "^26.2.0", + "@jest/transform": "^26.2.2", + "@jest/types": "^26.2.0", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.2", - "istanbul-lib-coverage": "^2.0.2", - "istanbul-lib-instrument": "^3.0.1", - "istanbul-lib-report": "^2.0.4", - "istanbul-lib-source-maps": "^3.0.1", - "istanbul-reports": "^2.2.6", - "jest-haste-map": "^24.9.0", - "jest-resolve": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.6.0", - "node-notifier": "^5.4.2", - "slash": "^2.0.0", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.2.2", + "jest-resolve": "^26.2.2", + "jest-util": "^26.2.0", + "jest-worker": "^26.2.1", + "node-notifier": "^7.0.0", + "slash": "^3.0.0", "source-map": "^0.6.0", - "string-length": "^2.0.0" + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^4.1.3" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } } }, "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.1.0.tgz", + "integrity": "sha512-XYRPYx4eEVX15cMT9mstnO7hkHP3krNtKfxUYd8L7gbtia8JvZZ6bMzSwa6IQJENbudTwKMw5R1BePRD+bkEmA==", "dev": true, "requires": { "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", + "graceful-fs": "^4.2.4", "source-map": "^0.6.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } } }, "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.2.0.tgz", + "integrity": "sha512-kgPlmcVafpmfyQEu36HClK+CWI6wIaAWDHNxfQtGuKsgoa2uQAYdlxjMDBEa3CvI40+2U3v36gQF6oZBkoKatw==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "@jest/console": "^26.2.0", + "@jest/types": "^26.2.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" } }, "@jest/test-sequencer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz", - "integrity": "sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.2.2.tgz", + "integrity": "sha512-SliZWon5LNqV/lVXkeowSU6L8++FGOu3f43T01L1Gv6wnFDP00ER0utV9jyK9dVNdXqfMNCN66sfcyar/o7BNw==", "dev": true, "requires": { - "@jest/test-result": "^24.9.0", - "jest-haste-map": "^24.9.0", - "jest-runner": "^24.9.0", - "jest-runtime": "^24.9.0" + "@jest/test-result": "^26.2.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.2.2", + "jest-runner": "^26.2.2", + "jest-runtime": "^26.2.2" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } } }, "@jest/transform": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", - "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.2.2.tgz", + "integrity": "sha512-c1snhvi5wRVre1XyoO3Eef5SEWpuBCH/cEbntBUd9tI5sNYiBDmO0My/lc5IuuGYKp/HFIHV1eZpSx5yjdkhKw==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^24.9.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", + "@jest/types": "^26.2.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.9.0", - "jest-regex-util": "^24.9.0", - "jest-util": "^24.9.0", - "micromatch": "^3.1.10", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.2.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.2.0", + "micromatch": "^4.0.2", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", + "slash": "^3.0.0", "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" + "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } } }, "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz", + "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@sinonjs/commons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" } }, "@types/babel__core": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz", - "integrity": "sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw==", + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz", + "integrity": "sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -489,18 +737,33 @@ } }, "@types/babel__traverse": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.10.tgz", - "integrity": "sha512-74fNdUGrWsgIB/V9kTO5FGHPWYY6Eqn+3Z7L6Hc4e/BxjYV7puvBqp5HwsVYYfLm6iURYBNCx4Ut37OF9yitCw==", + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.13.tgz", + "integrity": "sha512-i+zS7t6/s9cdQvbqKDARrcbrPvtJGlbYsMkazo03nTAK3RX9FNrLllXys22uiTGJapPOTZTQ35nHh4ISph4SLQ==", "dev": true, "requires": { "@babel/types": "^7.3.0" } }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/graceful-fs": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.3.tgz", + "integrity": "sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/istanbul-lib-coverage": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", - "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", "dev": true }, "@types/istanbul-lib-report": { @@ -513,30 +776,39 @@ } }, "@types/istanbul-reports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", - "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "*", "@types/istanbul-lib-report": "*" } }, - "@types/jest": { - "version": "24.9.1", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.9.1.tgz", - "integrity": "sha512-Fb38HkXSVA4L8fGKEZ6le5bB8r6MRWlOCZbVuWZcmOMSCd2wCYOwN1ibj8daIoV9naq7aaOZjrLCoCMptKU/4Q==", - "dev": true, - "requires": { - "jest-diff": "^24.3.0" - } - }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "@types/node": { + "version": "14.0.27", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz", + "integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/prettier": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.0.2.tgz", + "integrity": "sha512-IkVfat549ggtkZUthUzEX49562eGikhSYeVGX97SkMFn+sTZrgRewXjQ4tPKFPCykZHkX1Zfd9OoELGqKU2jJA==", + "dev": true + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -544,9 +816,9 @@ "dev": true }, "@types/yargs": { - "version": "13.0.8", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz", - "integrity": "sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA==", + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz", + "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -574,39 +846,31 @@ } }, "acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", "dev": true }, "acorn-globals": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", - "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", "dev": true, "requires": { - "acorn": "^6.0.1", - "acorn-walk": "^6.0.1" - }, - "dependencies": { - "acorn": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", - "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", - "dev": true - } + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" } }, "acorn-walk": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", - "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", "dev": true }, "ajv": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", - "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -616,34 +880,55 @@ } }, "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } }, "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "dev": true, "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" } }, "arr-diff": { @@ -664,12 +949,6 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, - "array-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", - "dev": true - }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -723,18 +1002,6 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -754,55 +1021,87 @@ "dev": true }, "aws4": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", "dev": true }, "babel-jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", - "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", - "dev": true, - "requires": { - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/babel__core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.9.0", - "chalk": "^2.4.2", - "slash": "^2.0.0" + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.2.2.tgz", + "integrity": "sha512-JmLuePHgA+DSOdOL8lPxCgD2LhPPm+rdw1vnxR73PpIrnmKCS2/aBhtkAcxQWuUcW2hBrH8MJ3LKXE7aWpNZyA==", + "dev": true, + "requires": { + "@jest/transform": "^26.2.2", + "@jest/types": "^26.2.0", + "@types/babel__core": "^7.1.7", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^26.2.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } } }, "babel-plugin-istanbul": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", - "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "find-up": "^3.0.0", - "istanbul-lib-instrument": "^3.3.0", - "test-exclude": "^5.2.3" + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" } }, "babel-plugin-jest-hoist": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", - "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.2.0.tgz", + "integrity": "sha512-B/hVMRv8Nh1sQ1a3EY8I0n4Y1Wty3NrR5ebOyVT302op+DOAau+xNEImGMsUWOC3++ZlMooCytKz+NgN8aKGbA==", "dev": true, "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", "@types/babel__traverse": "^7.0.6" } }, + "babel-preset-current-node-syntax": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.3.tgz", + "integrity": "sha512-uyexu1sVwcdFnyq9o8UQYsXwXflIh8LvrF5+cKrYam93ned1CStffB3+BEcsxGSgagoA3GEyjDqO4a/58hyPYQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, "babel-preset-jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", - "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.2.0.tgz", + "integrity": "sha512-R1k8kdP3R9phYQugXeNnK/nvCGlBzG4m3EoIIukC80GXb6wCv2XiwPhK6K9MAkQcMszWBYvl2Wm+yigyXFQqXg==", "dev": true, "requires": { - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.9.0" + "babel-plugin-jest-hoist": "^26.2.0", + "babel-preset-current-node-syntax": "^0.1.2" } }, "balanced-match": { @@ -875,16 +1174,6 @@ "tweetnacl": "^0.14.3" } }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "optional": true, - "requires": { - "file-uri-to-path": "1.0.0" - } - }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -913,32 +1202,12 @@ } }, "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "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" - } - } + "fill-range": "^7.0.1" } }, "browser-process-hrtime": { @@ -947,23 +1216,6 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "requires": { - "resolve": "1.1.7" - }, - "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, "bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -1029,16 +1281,21 @@ "dev": true }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, "ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -1069,14 +1326,14 @@ } }, "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" } }, "co": { @@ -1085,6 +1342,12 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -1096,18 +1359,18 @@ } }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "combined-stream": { @@ -1195,18 +1458,26 @@ } }, "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", "dev": true }, "cssstyle": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", - "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", "dev": true, "requires": { - "cssom": "0.3.x" + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } } }, "dashdash": { @@ -1219,27 +1490,14 @@ } }, "data-urls": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", - "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", "dev": true, "requires": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.2.0", - "whatwg-url": "^7.0.0" - }, - "dependencies": { - "whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - } + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" } }, "debug": { @@ -1256,6 +1514,12 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "decimal.js": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", + "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==", + "dev": true + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -1268,6 +1532,12 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -1335,15 +1605,15 @@ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, "detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, "diff-sequences": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", - "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.0.0.tgz", + "integrity": "sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg==", "dev": true }, "doctrine": { @@ -1357,12 +1627,20 @@ } }, "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", "dev": true, "requires": { - "webidl-conversions": "^4.0.2" + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } } }, "ecc-jsbn": { @@ -1380,10 +1658,16 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emittery": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.1.tgz", + "integrity": "sha512-d34LN4L6h18Bzz9xpoku2nPwKxCPlPMr3EEKTkoEBi+1/+b0lcRkRJ1UVyyZaKNeqGR3swcGl6s390DNO4YVgQ==", + "dev": true + }, "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "encodeurl": { @@ -1451,9 +1735,9 @@ "dev": true }, "escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", "dev": true, "requires": { "esprima": "^4.0.1", @@ -1554,9 +1838,9 @@ } }, "eslint-plugin-import": { - "version": "2.21.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.21.2.tgz", - "integrity": "sha512-FEmxeGI6yaz+SnEB6YgNHlQK1Bs2DKLM+YF+vuTk5H8J9CLbJLtlPvRFgZZ2+sXiKAlN5dpdlrWOjK8ZoZJpQA==", + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz", + "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==", "dev": true, "requires": { "array-includes": "^3.1.1", @@ -1820,17 +2104,17 @@ } }, "expect": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", - "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.2.0.tgz", + "integrity": "sha512-8AMBQ9UVcoUXt0B7v+5/U5H6yiUR87L6eKCfjE3spx7Ya5lF+ebUo37MCFBML2OiLfkX1sxmQOZhIDonyVTkcw==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-regex-util": "^24.9.0" + "@jest/types": "^26.2.0", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.0.0", + "jest-matcher-utils": "^26.2.0", + "jest-message-util": "^26.2.0", + "jest-regex-util": "^26.0.0" } }, "express": { @@ -1969,9 +2253,9 @@ "dev": true }, "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-json-stable-stringify": { @@ -1995,34 +2279,13 @@ "bser": "2.1.1" } }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "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" - } - } + "to-regex-range": "^5.0.1" } }, "finalhandler": { @@ -2040,12 +2303,21 @@ } }, "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "dependencies": { + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } } }, "for-in": { @@ -2091,627 +2363,17 @@ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", - "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", - "dev": true, - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1", - "node-pre-gyp": "*" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true, - "optional": true - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "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, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.6.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true, - "optional": 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, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", - "dev": true, - "optional": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, - "optional": true - }, - "needle": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.3.tgz", - "integrity": "sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw==", - "dev": true, - "optional": true, - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz", - "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==", - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4.4.2" - } - }, - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "dev": true, - "optional": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": 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, - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true, - "optional": true - }, - "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, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true, - "optional": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.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, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "optional": true - } - } + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true }, "function-bind": { "version": "1.1.1", @@ -2731,6 +2393,12 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -2785,7 +2453,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true + "dev": true, + "optional": true }, "har-schema": { "version": "2.0.0", @@ -2794,12 +2463,12 @@ "dev": true }, "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "dev": true, "requires": { - "ajv": "^6.5.5", + "ajv": "^6.12.3", "har-schema": "^2.0.0" } }, @@ -2813,9 +2482,9 @@ } }, "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "has-symbols": { @@ -2845,6 +2514,26 @@ "kind-of": "^4.0.0" }, "dependencies": { + "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" + } + } + } + }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", @@ -2863,12 +2552,12 @@ "dev": true }, "html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", "dev": true, "requires": { - "whatwg-encoding": "^1.0.1" + "whatwg-encoding": "^1.0.5" } }, "html-escaper": { @@ -2900,6 +2589,12 @@ "sshpk": "^1.7.0" } }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -2915,13 +2610,13 @@ "dev": true }, "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", "dev": true, "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" } }, "imurmurhash": { @@ -2945,14 +2640,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true }, "ipaddr.js": { "version": "1.9.1", @@ -3051,6 +2743,13 @@ } } }, + "is-docker": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", + "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", + "dev": true, + "optional": true + }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -3058,9 +2757,9 @@ "dev": true }, "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "is-generator-fn": { @@ -3070,24 +2769,10 @@ "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" - } - } - } + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, "is-plain-object": { "version": "2.0.4", @@ -3098,6 +2783,12 @@ "isobject": "^3.0.1" } }, + "is-potential-custom-element-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", + "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", + "dev": true + }, "is-regex": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", @@ -3141,10 +2832,14 @@ "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 + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "optional": true, + "requires": { + "is-docker": "^2.0.0" + } }, "isarray": { "version": "1.0.0", @@ -3171,24 +2866,21 @@ "dev": true }, "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", "dev": true }, "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", "dev": true, "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" }, "dependencies": { "semver": { @@ -3200,37 +2892,24 @@ } }, "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", "dev": true, "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" } }, "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", "dev": true, "requires": { "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", + "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" }, "dependencies": { @@ -3252,438 +2931,628 @@ } }, "istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", "dev": true, "requires": { - "html-escaper": "^2.0.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" } }, "jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", - "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.2.2.tgz", + "integrity": "sha512-EkJNyHiAG1+A8pqSz7cXttoVa34hOEzN/MrnJhYnfp5VHxflVcf2pu3oJSrhiy6LfIutLdWo+n6q63tjcoIeig==", "dev": true, "requires": { - "import-local": "^2.0.0", - "jest-cli": "^24.9.0" + "@jest/core": "^26.2.2", + "import-local": "^3.0.2", + "jest-cli": "^26.2.2" }, "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, "jest-cli": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", - "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.2.2.tgz", + "integrity": "sha512-vVcly0n/ijZvdy6gPQiQt0YANwX2hLTPQZHtW7Vi3gcFdKTtif7YpI85F8R8JYy5DFSWz4x1OW0arnxlziu5Lw==", "dev": true, "requires": { - "@jest/core": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", + "@jest/core": "^26.2.2", + "@jest/test-result": "^26.2.0", + "@jest/types": "^26.2.0", + "chalk": "^4.0.0", "exit": "^0.1.2", - "import-local": "^2.0.0", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", "is-ci": "^2.0.0", - "jest-config": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", + "jest-config": "^26.2.2", + "jest-util": "^26.2.0", + "jest-validate": "^26.2.0", "prompts": "^2.0.1", - "realpath-native": "^1.1.0", - "yargs": "^13.3.0" + "yargs": "^15.3.1" } } } }, "jest-changed-files": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", - "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.2.0.tgz", + "integrity": "sha512-+RyJb+F1K/XBLIYiL449vo5D+CvlHv29QveJUWNPXuUicyZcq+tf1wNxmmFeRvAU1+TzhwqczSjxnCCFt7+8iA==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "execa": "^1.0.0", - "throat": "^4.0.0" + "@jest/types": "^26.2.0", + "execa": "^4.0.0", + "throat": "^5.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", + "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "jest-config": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", - "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.2.2.tgz", + "integrity": "sha512-2lhxH0y4YFOijMJ65usuf78m7+9/8+hAb1PZQtdRdgnQpAb4zP6KcVDDktpHEkspBKnc2lmFu+RQdHukUUbiTg==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.9.0", - "@jest/types": "^24.9.0", - "babel-jest": "^24.9.0", - "chalk": "^2.0.1", + "@jest/test-sequencer": "^26.2.2", + "@jest/types": "^26.2.0", + "babel-jest": "^26.2.2", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", "glob": "^7.1.1", - "jest-environment-jsdom": "^24.9.0", - "jest-environment-node": "^24.9.0", - "jest-get-type": "^24.9.0", - "jest-jasmine2": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.9.0", - "realpath-native": "^1.1.0" + "graceful-fs": "^4.2.4", + "jest-environment-jsdom": "^26.2.0", + "jest-environment-node": "^26.2.0", + "jest-get-type": "^26.0.0", + "jest-jasmine2": "^26.2.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.2.2", + "jest-util": "^26.2.0", + "jest-validate": "^26.2.0", + "micromatch": "^4.0.2", + "pretty-format": "^26.2.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } } }, "jest-diff": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", - "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.2.0.tgz", + "integrity": "sha512-Wu4Aopi2nzCsHWLBlD48TgRy3Z7OsxlwvHNd1YSnHc7q1NJfrmyCPoUXrTIrydQOG5ApaYpsAsdfnMbJqV1/wQ==", "dev": true, "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "chalk": "^4.0.0", + "diff-sequences": "^26.0.0", + "jest-get-type": "^26.0.0", + "pretty-format": "^26.2.0" } }, "jest-docblock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", - "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", + "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", "dev": true, "requires": { - "detect-newline": "^2.1.0" + "detect-newline": "^3.0.0" } }, "jest-each": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", - "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.2.0.tgz", + "integrity": "sha512-gHPCaho1twWHB5bpcfnozlc6mrMi+VAewVPNgmwf81x2Gzr6XO4dl+eOrwPWxbkYlgjgrYjWK2xgKnixbzH3Ew==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.9.0", - "jest-util": "^24.9.0", - "pretty-format": "^24.9.0" + "@jest/types": "^26.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.0.0", + "jest-util": "^26.2.0", + "pretty-format": "^26.2.0" } }, "jest-environment-jsdom": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", - "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.2.0.tgz", + "integrity": "sha512-sDG24+5M4NuIGzkI3rJW8XUlrpkvIdE9Zz4jhD8OBnVxAw+Y1jUk9X+lAOD48nlfUTlnt3lbAI3k2Ox+WF3S0g==", "dev": true, "requires": { - "@jest/environment": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-util": "^24.9.0", - "jsdom": "^11.5.1" + "@jest/environment": "^26.2.0", + "@jest/fake-timers": "^26.2.0", + "@jest/types": "^26.2.0", + "@types/node": "*", + "jest-mock": "^26.2.0", + "jest-util": "^26.2.0", + "jsdom": "^16.2.2" } }, "jest-environment-node": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", - "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.2.0.tgz", + "integrity": "sha512-4M5ExTYkJ19efBzkiXtBi74JqKLDciEk4CEsp5tTjWGYMrlKFQFtwIVG3tW1OGE0AlXhZjuHPwubuRYY4j4uOw==", "dev": true, "requires": { - "@jest/environment": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-util": "^24.9.0" + "@jest/environment": "^26.2.0", + "@jest/fake-timers": "^26.2.0", + "@jest/types": "^26.2.0", + "@types/node": "*", + "jest-mock": "^26.2.0", + "jest-util": "^26.2.0" } }, "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", + "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==", "dev": true }, "jest-haste-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", - "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.2.2.tgz", + "integrity": "sha512-3sJlMSt+NHnzCB+0KhJ1Ut4zKJBiJOlbrqEYNdRQGlXTv8kqzZWjUKQRY3pkjmlf+7rYjAV++MQ4D6g4DhAyOg==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "anymatch": "^2.0.0", + "@jest/types": "^26.2.0", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.9.0", - "micromatch": "^3.1.10", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.2.0", + "jest-util": "^26.2.0", + "jest-worker": "^26.2.1", + "micromatch": "^4.0.2", "sane": "^4.0.3", "walker": "^1.0.7" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } } }, "jest-jasmine2": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", - "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.2.2.tgz", + "integrity": "sha512-Q8AAHpbiZMVMy4Hz9j1j1bg2yUmPa1W9StBvcHqRaKa9PHaDUMwds8LwaDyzP/2fkybcTQE4+pTMDOG9826tEw==", "dev": true, "requires": { "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", + "@jest/environment": "^26.2.0", + "@jest/source-map": "^26.1.0", + "@jest/test-result": "^26.2.0", + "@jest/types": "^26.2.0", + "@types/node": "*", + "chalk": "^4.0.0", "co": "^4.6.0", - "expect": "^24.9.0", + "expect": "^26.2.0", "is-generator-fn": "^2.0.0", - "jest-each": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-snapshot": "^24.9.0", - "jest-util": "^24.9.0", - "pretty-format": "^24.9.0", - "throat": "^4.0.0" + "jest-each": "^26.2.0", + "jest-matcher-utils": "^26.2.0", + "jest-message-util": "^26.2.0", + "jest-runtime": "^26.2.2", + "jest-snapshot": "^26.2.2", + "jest-util": "^26.2.0", + "pretty-format": "^26.2.0", + "throat": "^5.0.0" } }, "jest-leak-detector": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", - "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.2.0.tgz", + "integrity": "sha512-aQdzTX1YiufkXA1teXZu5xXOJgy7wZQw6OJ0iH5CtQlOETe6gTSocaYKUNui1SzQ91xmqEUZ/WRavg9FD82rtQ==", "dev": true, "requires": { - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "jest-get-type": "^26.0.0", + "pretty-format": "^26.2.0" } }, "jest-matcher-utils": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", - "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.2.0.tgz", + "integrity": "sha512-2cf/LW2VFb3ayPHrH36ZDjp9+CAeAe/pWBAwsV8t3dKcrINzXPVxq8qMWOxwt5BaeBCx4ZupVGH7VIgB8v66vQ==", "dev": true, "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "chalk": "^4.0.0", + "jest-diff": "^26.2.0", + "jest-get-type": "^26.0.0", + "pretty-format": "^26.2.0" } }, "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.2.0.tgz", + "integrity": "sha512-g362RhZaJuqeqG108n1sthz5vNpzTNy926eNDszo4ncRbmmcMRIUAZibnd6s5v2XSBCChAxQtCoN25gnzp7JbQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", + "@jest/types": "^26.2.0", "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } } }, "jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.2.0.tgz", + "integrity": "sha512-XeC7yWtWmWByoyVOHSsE7NYsbXJLtJNgmhD7z4MKumKm6ET0si81bsSLbQ64L5saK3TgsHo2B/UqG5KNZ1Sp/Q==", "dev": true, "requires": { - "@jest/types": "^24.9.0" + "@jest/types": "^26.2.0", + "@types/node": "*" } }, "jest-pnp-resolver": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", - "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", "dev": true }, "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", "dev": true }, "jest-resolve": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", - "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.2.2.tgz", + "integrity": "sha512-ye9Tj/ILn/0OgFPE/3dGpQPUqt4dHwIocxt5qSBkyzxQD8PbL0bVxBogX2FHxsd3zJA7V2H/cHXnBnNyyT9YoQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" + "@jest/types": "^26.2.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.2.0", + "read-pkg-up": "^7.0.1", + "resolve": "^1.17.0", + "slash": "^3.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } } }, "jest-resolve-dependencies": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", - "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.2.2.tgz", + "integrity": "sha512-S5vufDmVbQXnpP7435gr710xeBGUFcKNpNswke7RmFvDQtmqPjPVU/rCeMlEU0p6vfpnjhwMYeaVjKZAy5QYJA==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-snapshot": "^24.9.0" + "@jest/types": "^26.2.0", + "jest-regex-util": "^26.0.0", + "jest-snapshot": "^26.2.2" } }, "jest-runner": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", - "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.4.2", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.2.2.tgz", + "integrity": "sha512-/qb6ptgX+KQ+aNMohJf1We695kaAfuu3u3ouh66TWfhTpLd9WbqcF6163d/tMoEY8GqPztXPLuyG0rHRVDLxCA==", + "dev": true, + "requires": { + "@jest/console": "^26.2.0", + "@jest/environment": "^26.2.0", + "@jest/test-result": "^26.2.0", + "@jest/types": "^26.2.0", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.7.1", "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-config": "^24.9.0", - "jest-docblock": "^24.3.0", - "jest-haste-map": "^24.9.0", - "jest-jasmine2": "^24.9.0", - "jest-leak-detector": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-resolve": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.6.0", + "graceful-fs": "^4.2.4", + "jest-config": "^26.2.2", + "jest-docblock": "^26.0.0", + "jest-haste-map": "^26.2.2", + "jest-leak-detector": "^26.2.0", + "jest-message-util": "^26.2.0", + "jest-resolve": "^26.2.2", + "jest-runtime": "^26.2.2", + "jest-util": "^26.2.0", + "jest-worker": "^26.2.1", "source-map-support": "^0.5.6", - "throat": "^4.0.0" + "throat": "^5.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } } }, "jest-runtime": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", - "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.9.0", - "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/yargs": "^13.0.0", - "chalk": "^2.0.1", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.2.2.tgz", + "integrity": "sha512-a8VXM3DxCDnCIdl9+QucWFfQ28KdqmyVFqeKLigHdErtsx56O2ZIdQkhFSuP1XtVrG9nTNHbKxjh5XL1UaFDVQ==", + "dev": true, + "requires": { + "@jest/console": "^26.2.0", + "@jest/environment": "^26.2.0", + "@jest/fake-timers": "^26.2.0", + "@jest/globals": "^26.2.0", + "@jest/source-map": "^26.1.0", + "@jest/test-result": "^26.2.0", + "@jest/transform": "^26.2.2", + "@jest/types": "^26.2.0", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "jest-config": "^24.9.0", - "jest-haste-map": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.9.0", - "jest-snapshot": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "strip-bom": "^3.0.0", - "yargs": "^13.3.0" + "graceful-fs": "^4.2.4", + "jest-config": "^26.2.2", + "jest-haste-map": "^26.2.2", + "jest-message-util": "^26.2.0", + "jest-mock": "^26.2.0", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.2.2", + "jest-snapshot": "^26.2.2", + "jest-util": "^26.2.0", + "jest-validate": "^26.2.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.3.1" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + } } }, "jest-serializer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", - "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", - "dev": true + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.2.0.tgz", + "integrity": "sha512-V7snZI9IVmyJEu0Qy0inmuXgnMWDtrsbV2p9CRAcmlmPVwpC2ZM8wXyYpiugDQnwLHx0V4+Pnog9Exb3UO8M6Q==", + "dev": true, + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } + } }, "jest-snapshot": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", - "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.2.2.tgz", + "integrity": "sha512-NdjD8aJS7ePu268Wy/n/aR1TUisG0BOY+QOW4f6h46UHEKOgYmmkvJhh2BqdVZQ0BHSxTMt04WpCf9njzx8KtA==", "dev": true, "requires": { "@babel/types": "^7.0.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "expect": "^24.9.0", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-resolve": "^24.9.0", - "mkdirp": "^0.5.1", + "@jest/types": "^26.2.0", + "@types/prettier": "^2.0.0", + "chalk": "^4.0.0", + "expect": "^26.2.0", + "graceful-fs": "^4.2.4", + "jest-diff": "^26.2.0", + "jest-get-type": "^26.0.0", + "jest-haste-map": "^26.2.2", + "jest-matcher-utils": "^26.2.0", + "jest-message-util": "^26.2.0", + "jest-resolve": "^26.2.2", "natural-compare": "^1.4.0", - "pretty-format": "^24.9.0", - "semver": "^6.2.0" + "pretty-format": "^26.2.0", + "semver": "^7.3.2" }, "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true } } }, "jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.2.0.tgz", + "integrity": "sha512-YmDwJxLZ1kFxpxPfhSJ0rIkiZOM0PQbRcfH0TzJOhqCisCAsI1WcmoQqO83My9xeVA2k4n+rzg2UuexVKzPpig==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", + "@jest/types": "^26.2.0", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" + "micromatch": "^4.0.2" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } } }, "jest-validate": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", - "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.2.0.tgz", + "integrity": "sha512-8XKn3hM6VIVmLNuyzYLCPsRCT83o8jMZYhbieh4dAyKLc4Ypr36rVKC+c8WMpWkfHHpGnEkvWUjjIAyobEIY/Q==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "camelcase": "^5.3.1", - "chalk": "^2.0.1", - "jest-get-type": "^24.9.0", + "@jest/types": "^26.2.0", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.0.0", "leven": "^3.1.0", - "pretty-format": "^24.9.0" + "pretty-format": "^26.2.0" + }, + "dependencies": { + "camelcase": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz", + "integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==", + "dev": true + } } }, "jest-watcher": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", - "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.2.0.tgz", + "integrity": "sha512-674Boco4Joe0CzgKPL6K4Z9LgyLx+ZvW2GilbpYb8rFEUkmDGgsZdv1Hv5rxsRpb1HLgKUOL/JfbttRCuFdZXQ==", "dev": true, "requires": { - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/yargs": "^13.0.0", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "jest-util": "^24.9.0", - "string-length": "^2.0.0" + "@jest/test-result": "^26.2.0", + "@jest/types": "^26.2.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^26.2.0", + "string-length": "^4.0.1" } }, "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "version": "26.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.2.1.tgz", + "integrity": "sha512-+XcGMMJDTeEGncRb5M5Zq9P7K4sQ1sirhjdOxsN1462h6lFo9w59bl2LVQmdGEEeU3m+maZCkS2Tcc9SfCHO4A==", "dev": true, "requires": { + "@types/node": "*", "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "supports-color": "^7.0.0" } }, "js-tokens": { @@ -3692,6 +3561,16 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "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", @@ -3699,36 +3578,36 @@ "dev": true }, "jsdom": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", - "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "acorn": "^5.5.3", - "acorn-globals": "^4.1.0", - "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": "^1.0.0", - "data-urls": "^1.0.0", - "domexception": "^1.0.1", - "escodegen": "^1.9.1", - "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.3.0", - "nwsapi": "^2.0.7", - "parse5": "4.0.0", - "pn": "^1.1.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.4", - "w3c-hr-time": "^1.0.1", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^5.2.0", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.3.0.tgz", + "integrity": "sha512-zggeX5UuEknpdZzv15+MS1dPYG0J/TftiiNunOeNxSl3qr8Z6cIlQpN0IdJa44z9aFxZRIVqRncvEhQ7X5DtZg==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "acorn": "^7.1.1", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.2.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.0", + "domexception": "^2.0.1", + "escodegen": "^1.14.1", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", + "nwsapi": "^2.2.0", + "parse5": "5.1.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.8", + "saxes": "^5.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^3.0.1", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0", + "ws": "^7.2.3", "xml-name-validator": "^3.0.0" } }, @@ -3795,12 +3674,6 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, - "left-pad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "dev": true - }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -3817,32 +3690,25 @@ "type-check": "~0.3.2" } }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true }, "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "^4.1.0" } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "lodash.sortby": { @@ -3851,29 +3717,19 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "semver": "^6.0.0" }, "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } @@ -3924,24 +3780,13 @@ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", "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" + "braces": "^3.0.1", + "picomatch": "^2.0.5" } }, "mime": { @@ -3962,6 +3807,12 @@ "mime-db": "1.43.0" } }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -3998,26 +3849,10 @@ } } }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, "ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true, - "optional": true + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "nanomatch": { "version": "1.2.13", @@ -4068,16 +3903,37 @@ "dev": true }, "node-notifier": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", - "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-7.0.2.tgz", + "integrity": "sha512-ux+n4hPVETuTL8+daJXTOC6uKLgMsl1RYfFv7DKRzyvzBapqco0rZZ9g72ZN8VS6V+gvNYHYa/ofcCY8fkJWsA==", "dev": true, + "optional": true, "requires": { "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", "shellwords": "^0.1.1", - "which": "^1.3.0" + "uuid": "^8.2.0", + "which": "^2.0.2" + }, + "dependencies": { + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true, + "optional": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "optional": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "normalize-package-data": { @@ -4093,13 +3949,10 @@ } }, "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, "npm-run-path": { "version": "2.0.2", @@ -4186,16 +4039,6 @@ "object-keys": "^1.0.11" } }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -4234,6 +4077,15 @@ "wrappy": "1" } }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -4249,13 +4101,10 @@ } }, "p-each-series": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", - "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", - "dev": true, - "requires": { - "p-reduce": "^1.0.0" - } + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", + "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", + "dev": true }, "p-finally": { "version": "1.0.0", @@ -4273,20 +4122,14 @@ } }, "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==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^2.2.0" } }, - "p-reduce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", - "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", - "dev": true - }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -4294,19 +4137,21 @@ "dev": true }, "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.1.tgz", + "integrity": "sha512-ztoZ4/DYeXQq4E21v169sC8qWINGpcosGv9XhTDvg9/hWvx/zrFkc9BiWxR58OJLHGk28j5BL0SDLeV2WmFZlQ==", "dev": true, "requires": { + "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" } }, "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", "dev": true }, "parseurl": { @@ -4349,25 +4194,16 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, "pirates": { @@ -4380,20 +4216,14 @@ } }, "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==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { - "find-up": "^3.0.0" + "find-up": "^4.0.0" } }, - "pn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true - }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -4407,15 +4237,15 @@ "dev": true }, "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.2.0.tgz", + "integrity": "sha512-qi/8IuBu2clY9G7qCXgCdD1Bf9w+sXakdHTRToknzMtVy0g7c4MBWaZy7MfB7ndKZovRO6XRwJiAYqq+MC7SDA==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "@jest/types": "^26.2.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" } }, "prompts": { @@ -4487,33 +4317,34 @@ "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=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "dev": true, "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } } }, "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, - "realpath-native": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", - "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", "dev": true, "requires": { - "util.promisify": "^1.0.0" + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" } }, "regex-not": { @@ -4583,27 +4414,55 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true } } }, "request-promise-core": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", - "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", "dev": true, "requires": { - "lodash": "^4.17.15" + "lodash": "^4.17.19" } }, "request-promise-native": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", - "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", "dev": true, "requires": { - "request-promise-core": "1.1.3", + "request-promise-core": "1.1.4", "stealthy-require": "^1.1.1", "tough-cookie": "^2.3.3" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } } }, "require-directory": { @@ -4628,18 +4487,18 @@ } }, "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "requires": { - "resolve-from": "^3.0.0" + "resolve-from": "^5.0.0" } }, "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, "resolve-url": { @@ -4655,9 +4514,9 @@ "dev": true }, "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" @@ -4703,13 +4562,140 @@ "micromatch": "^3.1.4", "minimist": "^1.1.1", "walker": "~1.0.5" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "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" + } + } + } + }, + "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" + } + } + } + }, + "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" + } + } + } + }, + "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" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "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" + } + } } }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } }, "semver": { "version": "5.7.1", @@ -4808,7 +4794,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true + "dev": true, + "optional": true }, "signal-exit": { "version": "3.0.3", @@ -4823,9 +4810,9 @@ "dev": true }, "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, "snapdragon": { @@ -4961,9 +4948,9 @@ } }, "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -5017,6 +5004,12 @@ "extend-shallow": "^3.0.0" } }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -5035,10 +5028,21 @@ } }, "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", - "dev": true + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.2.tgz", + "integrity": "sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } }, "static-extend": { "version": "0.1.2", @@ -5073,41 +5077,24 @@ "dev": true }, "string-length": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", - "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", + "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", "dev": true, "requires": { - "astral-regex": "^1.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" } }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "string.prototype.trimend": { @@ -5153,12 +5140,12 @@ } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.0" } }, "strip-bom": { @@ -5173,13 +5160,29 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" } }, "symbol-tree": { @@ -5188,22 +5191,31 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" } }, "throat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", - "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "dev": true }, "tmpl": { @@ -5251,13 +5263,12 @@ } }, "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=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "^7.0.0" } }, "toidentifier": { @@ -5266,22 +5277,23 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", "dev": true, "requires": { + "ip-regex": "^2.1.0", "psl": "^1.1.28", "punycode": "^2.1.1" } }, "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", "dev": true, "requires": { - "punycode": "^2.1.0" + "punycode": "^2.1.1" } }, "tsconfig-paths": { @@ -5331,6 +5343,18 @@ "prelude-ls": "~1.1.2" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -5340,6 +5364,15 @@ "mime-types": "~2.1.24" } }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -5418,28 +5451,36 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, - "util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - } - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==", + "dev": true, + "optional": true + }, + "v8-to-istanbul": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz", + "integrity": "sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } }, "validate-npm-package-license": { "version": "3.0.4", @@ -5476,6 +5517,15 @@ "browser-process-hrtime": "^1.0.0" } }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "requires": { + "xml-name-validator": "^3.0.0" + } + }, "walker": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", @@ -5486,9 +5536,9 @@ } }, "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", "dev": true }, "whatwg-encoding": { @@ -5507,14 +5557,22 @@ "dev": true }, "whatwg-url": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", - "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.1.0.tgz", + "integrity": "sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw==", "dev": true, "requires": { "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" + "tr46": "^2.0.2", + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } } }, "which": { @@ -5539,14 +5597,14 @@ "dev": true }, "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, "wrappy": { @@ -5556,24 +5614,22 @@ "dev": true }, "write-file-atomic": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", - "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==", + "dev": true }, "xml-name-validator": { "version": "3.0.0", @@ -5581,6 +5637,12 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "y18n": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", @@ -5588,27 +5650,28 @@ "dev": true }, "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^3.0.0", + "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "yargs-parser": "^18.1.2" } }, "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { "camelcase": "^5.0.0", diff --git a/package.json b/package.json index 6e857cc4..cadecd54 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openhab.google-assistant-smarthome", - "version": "2.0.22", + "version": "2.1.0", "description": "A Google Assistant, Actions on Google based implementation for openHAB", "main": "functions/index.js", "repository": { @@ -25,12 +25,12 @@ "express": "^4.17.1" }, "devDependencies": { - "@types/jest": "^24.9.1", + "@types/jest": "^26.0.8", "eslint-config-standard": "^14.1.1", - "eslint-plugin-import": "^2.21.2", + "eslint-plugin-import": "^2.22.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1", - "jest": "^24.9.0" + "jest": "^26.2.2" } } From 458fa03d2f164ac08420475477d2697cdc84f766 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 1 Aug 2020 03:02:00 +0200 Subject: [PATCH 12/94] Minor fixes Signed-off-by: Michael Krug --- functions/commands/default.js | 2 +- functions/devices/fan.js | 2 +- functions/devices/thermostat.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/functions/commands/default.js b/functions/commands/default.js index 51769635..f760c5f5 100644 --- a/functions/commands/default.js +++ b/functions/commands/default.js @@ -17,7 +17,7 @@ class DefaultCommand { } static validateParams(params = {}) { - return false; + return true; } static convertParamsToValue(params = {}, item = {}, device = {}) { diff --git a/functions/devices/fan.js b/functions/devices/fan.js index 3faf8a7e..c250e296 100644 --- a/functions/devices/fan.js +++ b/functions/devices/fan.js @@ -34,7 +34,7 @@ class Fan extends DefaultDevice { lang: config.lang || 'en' }] }); - } catch (e) { } + } catch { } }); return attributes; } diff --git a/functions/devices/thermostat.js b/functions/devices/thermostat.js index 04524893..e97b9387 100644 --- a/functions/devices/thermostat.js +++ b/functions/devices/thermostat.js @@ -71,7 +71,7 @@ class Thermostat extends DefaultDevice { ]; const members = Object(); if (item.members && item.members.length) { - item.members.forEach((member) => { + item.members.forEach(member => { if (member.metadata && member.metadata.ga) { const memberType = supportedMembers.find(m => member.metadata.ga.value.toLowerCase() === m.toLowerCase()); if (memberType) { From bfe28feb4c32e96f7ebcc53a9e13e5c750f3263b Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 1 Aug 2020 03:03:00 +0200 Subject: [PATCH 13/94] Add TV device Signed-off-by: Michael Krug --- functions/commands/medianext.js | 26 +++ functions/commands/mediapause.js | 26 +++ functions/commands/mediaprevious.js | 26 +++ functions/commands/mediaresume.js | 26 +++ functions/commands/selectchannel.js | 48 ++++ functions/commands/setinput.js | 36 +++ functions/commands/setvolume.js | 16 ++ functions/commands/volumerelative.js | 22 +- functions/devices/tv.js | 114 ++++++++++ tests/devices.metadata.test.js | 164 ++++++++++++++ tests/openhab.metadata.test.js | 326 +++++++++++++++++++++++++++ tests/openhab.test.js | 16 +- 12 files changed, 844 insertions(+), 2 deletions(-) create mode 100644 functions/commands/medianext.js create mode 100644 functions/commands/mediapause.js create mode 100644 functions/commands/mediaprevious.js create mode 100644 functions/commands/mediaresume.js create mode 100644 functions/commands/selectchannel.js create mode 100644 functions/commands/setinput.js create mode 100644 functions/devices/tv.js diff --git a/functions/commands/medianext.js b/functions/commands/medianext.js new file mode 100644 index 00000000..91051205 --- /dev/null +++ b/functions/commands/medianext.js @@ -0,0 +1,26 @@ +const DefaultCommand = require('./default.js'); +const TV = require('../devices/tv.js'); + +class MediaNext extends DefaultCommand { + static get type() { + return 'action.devices.commands.mediaNext'; + } + + static get requiresItem() { + return true; + } + + static getItemName(item) { + const members = TV.getMembers(item); + if ('transport' in members) { + return members.transport.name; + } + throw { statusCode: 400 }; + } + + static convertParamsToValue() { + return 'NEXT'; + } +} + +module.exports = MediaNext; diff --git a/functions/commands/mediapause.js b/functions/commands/mediapause.js new file mode 100644 index 00000000..e995768f --- /dev/null +++ b/functions/commands/mediapause.js @@ -0,0 +1,26 @@ +const DefaultCommand = require('./default.js'); +const TV = require('../devices/tv.js'); + +class MediaPause extends DefaultCommand { + static get type() { + return 'action.devices.commands.mediaPause'; + } + + static get requiresItem() { + return true; + } + + static getItemName(item) { + const members = TV.getMembers(item); + if ('transport' in members) { + return members.transport.name; + } + throw { statusCode: 400 }; + } + + static convertParamsToValue() { + return 'PAUSE'; + } +} + +module.exports = MediaPause; diff --git a/functions/commands/mediaprevious.js b/functions/commands/mediaprevious.js new file mode 100644 index 00000000..2e5a10ab --- /dev/null +++ b/functions/commands/mediaprevious.js @@ -0,0 +1,26 @@ +const DefaultCommand = require('./default.js'); +const TV = require('../devices/tv.js'); + +class MediaPrevious extends DefaultCommand { + static get type() { + return 'action.devices.commands.mediaPrevious'; + } + + static get requiresItem() { + return true; + } + + static getItemName(item) { + const members = TV.getMembers(item); + if ('transport' in members) { + return members.transport.name; + } + throw { statusCode: 400 }; + } + + static convertParamsToValue() { + return 'PREVIOUS'; + } +} + +module.exports = MediaPrevious; diff --git a/functions/commands/mediaresume.js b/functions/commands/mediaresume.js new file mode 100644 index 00000000..f7c20019 --- /dev/null +++ b/functions/commands/mediaresume.js @@ -0,0 +1,26 @@ +const DefaultCommand = require('./default.js'); +const TV = require('../devices/tv.js'); + +class MediaResume extends DefaultCommand { + static get type() { + return 'action.devices.commands.mediaResume'; + } + + static get requiresItem() { + return true; + } + + static getItemName(item) { + const members = TV.getMembers(item); + if ('transport' in members) { + return members.transport.name; + } + throw { statusCode: 400 }; + } + + static convertParamsToValue() { + return 'PLAY'; + } +} + +module.exports = MediaResume; diff --git a/functions/commands/selectchannel.js b/functions/commands/selectchannel.js new file mode 100644 index 00000000..3f00f0f9 --- /dev/null +++ b/functions/commands/selectchannel.js @@ -0,0 +1,48 @@ +const DefaultCommand = require('./default.js'); +const TV = require('../devices/tv.js'); + +class SelectChannel extends DefaultCommand { + static get type() { + return 'action.devices.commands.SelectChannel'; + } + + static validateParams(params) { + return (('channelCode' in params) && typeof params.channelCode === 'string') || + (('channelName' in params) && typeof params.channelName === 'string') || + (('channelNumber' in params) && typeof params.channelNumber === 'string'); + } + + static get requiresItem() { + return true; + } + + static getItemName(item) { + const members = TV.getMembers(item); + if ('channel' in members) { + return members.channel.name; + } + throw { statusCode: 400 }; + } + + static convertParamsToValue(params, item) { + if (params.channelNumber) { + return params.channelNumber; + } + const search = params.channelName || params.channelCode; + const channelMap = TV.getChannelMap(item); + for (const number in channelMap) { + if (channelMap[number].includes(search)) { + return number; + } + } + } + + static getResponseStates(params, item) { + const state = this.convertParamsToValue(params, item); + return { + channelNumber: state + }; + } +} + +module.exports = SelectChannel; diff --git a/functions/commands/setinput.js b/functions/commands/setinput.js new file mode 100644 index 00000000..a1ee14f5 --- /dev/null +++ b/functions/commands/setinput.js @@ -0,0 +1,36 @@ +const DefaultCommand = require('./default.js'); +const TV = require('../devices/tv.js'); + +class SetInput extends DefaultCommand { + static get type() { + return 'action.devices.commands.SetInput'; + } + + static validateParams(params) { + return ('newInput' in params) && typeof params.newInput === 'string'; + } + + static get requiresItem() { + return true; + } + + static getItemName(item) { + const members = TV.getMembers(item); + if ('input' in members) { + return members.input.name; + } + throw { statusCode: 400 }; + } + + static convertParamsToValue(params) { + return params.newInput; + } + + static getResponseStates(params) { + return { + currentInput: params.newInput + }; + } +} + +module.exports = SetInput; diff --git a/functions/commands/setvolume.js b/functions/commands/setvolume.js index 14362bbc..cbc3b7be 100644 --- a/functions/commands/setvolume.js +++ b/functions/commands/setvolume.js @@ -1,4 +1,5 @@ const DefaultCommand = require('./default.js'); +const TV = require('../devices/tv.js'); class SetVolume extends DefaultCommand { static get type() { @@ -9,6 +10,21 @@ class SetVolume extends DefaultCommand { return ('volumeLevel' in params) && typeof params.volumeLevel === 'number'; } + static get requiresItem() { + return true; + } + + static getItemName(item) { + if (item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() == 'tv') { + const members = TV.getMembers(item); + if ('volume' in members) { + return members.volume.name; + } + throw { statusCode: 400 }; + } + return item.name; + } + static convertParamsToValue(params) { return params.volumeLevel.toString(); } diff --git a/functions/commands/volumerelative.js b/functions/commands/volumerelative.js index 39cab3fd..1612a187 100644 --- a/functions/commands/volumerelative.js +++ b/functions/commands/volumerelative.js @@ -1,4 +1,5 @@ const DefaultCommand = require('./default.js'); +const TV = require('../devices/tv.js'); class VolumeRelative extends DefaultCommand { static get type() { @@ -13,8 +14,27 @@ class VolumeRelative extends DefaultCommand { return true; } + static getItemName(item) { + if (item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() == 'tv') { + const members = TV.getMembers(item); + if ('volume' in members) { + return members.volume.name; + } + throw { statusCode: 400 }; + } + return item.name; + } + static convertParamsToValue(params, item) { - let level = parseInt(item.state) + params.volumeRelativeLevel; + let state = item.state; + if (item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() == 'tv') { + const members = TV.getMembers(item); + if ('volume' in members) { + state = members.volume.state; + } + throw { statusCode: 400 }; + } + let level = parseInt(state) + params.volumeRelativeLevel; return (level < 0 ? 0 : level > 100 ? 100 : level).toString(); } diff --git a/functions/devices/tv.js b/functions/devices/tv.js new file mode 100644 index 00000000..79a1f43e --- /dev/null +++ b/functions/devices/tv.js @@ -0,0 +1,114 @@ +const DefaultDevice = require('./default.js'); + +class TV extends DefaultDevice { + static get type() { + return 'action.devices.types.TV'; + } + + static get traits() { + return [ + 'action.devices.traits.Channel', + 'action.devices.traits.Volume', + 'action.devices.traits.InputSelector', + 'action.devices.traits.TransportControl' + ]; + } + + static getAttributes(item) { + const config = this.getConfig(item); + const attributes = { + transportControlSupportedCommands: ['NEXT', 'PREVIOUS', 'PAUSE', 'RESUME'] + }; + if ('transportControlSupportedCommands' in config) { + attributes.transportControlSupportedCommands = config.transportControlSupportedCommands.split(',').map(s => s.toUpperCase()); + } + if ('availableInputs' in config) { + attributes.availableInputs = []; + config.availableInputs.split(',').forEach(input => { + const [key, synonyms] = input.split('='); + attributes.availableInputs.push({ + key: key, + names: [{ + name_synonym: synonyms.split(':'), + lang: config.lang || 'en' + }] + }); + }); + attributes.orderedInputs = config.orderedInputs === true; + } + if ('availableChannels' in config) { + attributes.availableChannels = []; + config.availableChannels.split(',').forEach(channel => { + const [number, key, names] = channel.split('='); + attributes.availableChannels.push({ + key: key, + names: names.split(':'), + number: number + }); + }); + } + return attributes; + } + + static checkItemType(item) { + return item.type === 'Group'; + } + + static getState(item) { + const state = {}; + const members = this.getMembers(item); + for (const member in members) { + switch (member) { + case 'input': + state.currentInput = members[member].state; + break; + case 'volume': + state.currentVolume = Number(members[member].state) || 0; + state.isMuted = state.currentInput === 0; + break; + case 'channel': + state.channelNumber = members[member].state; + try { + state.channelName = this.getChannelMap(item)[members[member].state][0]; + } catch { } + break; + } + } + return state; + } + + static getMembers(item) { + const supportedMembers = [ + 'channel', + 'volume', + 'input', + 'transport' + ]; + const members = Object(); + if (item.members && item.members.length) { + item.members.forEach(member => { + if (member.metadata && member.metadata.ga) { + const memberType = supportedMembers.find(m => member.metadata.ga.value.toLowerCase() === m.toLowerCase()); + if (memberType) { + members[memberType] = { name: member.name, state: member.state }; + } + } + }); + } + return members; + } + + static getChannelMap(item) { + const config = this.getConfig(item); + const channelMap = {}; + if ('availableChannels' in config) { + config.availableChannels.split(',').forEach(channel => { + const [number, key, names] = channel.split('='); + channelMap[number] = [...names.split(':'), key]; + }); + } + return channelMap; + } +} + +module.exports = TV; diff --git a/tests/devices.metadata.test.js b/tests/devices.metadata.test.js index 7931367c..afa84b73 100644 --- a/tests/devices.metadata.test.js +++ b/tests/devices.metadata.test.js @@ -1,5 +1,6 @@ const Devices = require('../functions/devices.js'); const Thermostat = require('../functions/devices/thermostat.js'); +const TV = require('../functions/devices/tv.js'); describe('Test Switch Devices with Metadata', () => { test('Switch Type', () => { @@ -822,4 +823,167 @@ describe('Test Thermostat Device with Metadata', () => { 'thermostatHumidityAmbient': 50, }); }); + + describe('Test TV Device with Metadata', () => { + test('getAttributes', () => { + expect(TV.getAttributes({ + metadata: { + ga: { + value: 'TV', + config: { + availableInputs: 'tv=TV,hdmi1=HDMI1,hdmi2=HDMI2', + availableChannels: '20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2' + } + } + } + })).toStrictEqual({ + "availableInputs": [ + { + "key": "tv", + "names": [ + { + "lang": "en", + "name_synonym": [ + "TV", + ], + }, + ], + }, + { + "key": "hdmi1", + "names": [ + { + "lang": "en", + "name_synonym": [ + "HDMI1", + ], + }, + ], + }, + { + "key": "hdmi2", + "names": [ + { + "lang": "en", + "name_synonym": [ + "HDMI2", + ], + }, + ], + }, + ], + "orderedInputs": false, + "availableChannels": [ + { + "key": "channel1", + "names": [ + "Channel 1", + "Kanal 1", + ], + "number": "20", + }, + { + "key": "channel2", + "names": [ + "Channel 2", + "Kanal 2", + ], + "number": "10", + }, + ], + "transportControlSupportedCommands": [ + "NEXT", + "PREVIOUS", + "PAUSE", + "RESUME", + ], + }); + }); + + test('getChannelMap', () => { + expect(TV.getChannelMap({ + metadata: { + ga: { + value: 'TV', + config: { + availableChannels: '20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2' + } + } + } + })).toStrictEqual({ + "10": [ + "Channel 2", + "Kanal 2", + "channel2", + ], + "20": [ + "Channel 1", + "Kanal 1", + "channel1", + ], + }); + }); + + test('getState', () => { + expect(TV.getState({ + type: 'Group', + metadata: { + ga: { + value: 'TV' + } + } + })).toStrictEqual({}); + + expect(TV.getState({ + type: 'Group', + metadata: { + ga: { + value: 'TV', + config: { + availableChannels: '20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2' + } + } + }, + members: [{ + type: 'String', + metadata: { + ga: { + value: 'input' + } + }, + state: 'tv' + }, { + type: 'Number', + metadata: { + ga: { + value: 'channel' + } + }, + state: '20' + }, { + type: 'Number', + metadata: { + ga: { + value: 'volume' + } + }, + state: '50' + }, { + type: 'String', + metadata: { + ga: { + value: 'transport' + } + }, + state: 'PLAY' + }] + })).toStrictEqual({ + 'channelName': 'Channel 1', + 'channelNumber': '20', + 'currentInput': 'tv', + 'currentVolume': 50, + 'isMuted': false + }); + }); + }); }); diff --git a/tests/openhab.metadata.test.js b/tests/openhab.metadata.test.js index a7768d89..6c6fe04f 100644 --- a/tests/openhab.metadata.test.js +++ b/tests/openhab.metadata.test.js @@ -1717,4 +1717,330 @@ describe('Test EXECUTE with Metadata', () => { }] }); }); + + + test('SelectChannel with Number for TV', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV", + "config": { + "availableChannels": "20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2" + } + } + }, + "members": [{ + type: 'Number', + name: 'MyChannel', + metadata: { + ga: { + value: 'channel' + } + }, + state: '10' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV" + }], + "execution": [{ + "command": "action.devices.commands.SelectChannel", + "params": { + "channelNumber": "20" + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyChannel', '20'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'channelNumber': '20', + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); + + test('SelectChannel with Name for TV', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV", + "config": { + "availableChannels": "20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2" + } + } + }, + "members": [{ + type: 'Number', + name: 'MyChannel', + metadata: { + ga: { + value: 'channel' + } + }, + state: '10' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV" + }], + "execution": [{ + "command": "action.devices.commands.SelectChannel", + "params": { + "channelName": "Channel 1" + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyChannel', '20'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'channelNumber': '20', + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); + + test('SelectInput for TV', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV", + "config": { + "availableInputs": "tv=TV,hdmi1=HDMI1,hdmi2=HDMI2" + } + } + }, + "members": [{ + type: 'String', + name: 'MyInput', + metadata: { + ga: { + value: 'input' + } + }, + state: 'tv' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV" + }], + "execution": [{ + "command": "action.devices.commands.SetInput", + "params": { + "newInput": "hdmi1" + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyInput', 'hdmi1'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'currentInput': 'hdmi1', + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); + + test('SetVolume for TV', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV" + } + }, + "members": [{ + type: 'Dimmer', + name: 'MyVolume', + metadata: { + ga: { + value: 'volume' + } + }, + state: '40' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV" + }], + "execution": [{ + "command": "action.devices.commands.setVolume", + "params": { + "volumeLevel": 10 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyVolume', '10'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'currentVolume': 10, + 'isMuted': false, + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); + + test('MediaPause for TV', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV" + } + }, + "members": [{ + type: 'Player', + name: 'MyTransport', + metadata: { + ga: { + value: 'transport' + } + }, + state: 'PLAY' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV" + }], + "execution": [{ + "command": "action.devices.commands.mediaPause" + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyTransport', 'PAUSE'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + }, + "status": "SUCCESS" + }] + }); + }); }); diff --git a/tests/openhab.test.js b/tests/openhab.test.js index 4395fa03..91e2705c 100644 --- a/tests/openhab.test.js +++ b/tests/openhab.test.js @@ -45,7 +45,21 @@ describe('Test EXECUTE', () => { }); test('setVolume Dimmer', async () => { + const item = + { + "state": "10", + "type": "Dimmer", + "name": "MySpeaker", + "metadata": { + "ga": { + "value": "Speaker" + } + } + }; + const getItemMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + const sendCommandMock = jest.fn(); sendCommandMock.mockReturnValue(Promise.resolve()); @@ -71,7 +85,7 @@ describe('Test EXECUTE', () => { const payload = await new OpenHAB(apiHandler).handleExecute(commands); - expect(getItemMock).not.toHaveBeenCalled(); + expect(getItemMock).toHaveBeenCalledTimes(1); expect(sendCommandMock).toBeCalledWith('MySpeaker', '40'); expect(payload).toStrictEqual({ "commands": [{ From a75d781c0d5f62d5a156be4df09852ce92ab3601 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 1 Aug 2020 03:33:36 +0200 Subject: [PATCH 14/94] Fix select channel Signed-off-by: Michael Krug --- functions/commands/selectchannel.js | 2 +- package-lock.json | 70 +++++++++++++++++++++++++++++ tests/openhab.metadata.test.js | 2 +- 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/functions/commands/selectchannel.js b/functions/commands/selectchannel.js index 3f00f0f9..13852348 100644 --- a/functions/commands/selectchannel.js +++ b/functions/commands/selectchannel.js @@ -3,7 +3,7 @@ const TV = require('../devices/tv.js'); class SelectChannel extends DefaultCommand { static get type() { - return 'action.devices.commands.SelectChannel'; + return 'action.devices.commands.selectChannel'; } static validateParams(params) { diff --git a/package-lock.json b/package-lock.json index c95d2229..5eb2a0e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -785,6 +785,76 @@ "@types/istanbul-lib-report": "*" } }, + "@types/jest": { + "version": "26.0.8", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.8.tgz", + "integrity": "sha512-eo3VX9jGASSuv680D4VQ89UmuLZneNxv2MCZjfwlInav05zXVJTzfc//lavdV0GPwSxsXJTy2jALscB7Acqg0g==", + "dev": true, + "requires": { + "jest-diff": "^25.2.1", + "pretty-format": "^25.2.1" + }, + "dependencies": { + "@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "diff-sequences": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz", + "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", + "dev": true + }, + "jest-diff": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", + "integrity": "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "diff-sequences": "^25.2.6", + "jest-get-type": "^25.2.6", + "pretty-format": "^25.5.0" + } + }, + "jest-get-type": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", + "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", + "dev": true + }, + "pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "dev": true, + "requires": { + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + } + } + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", diff --git a/tests/openhab.metadata.test.js b/tests/openhab.metadata.test.js index 6c6fe04f..bd280525 100644 --- a/tests/openhab.metadata.test.js +++ b/tests/openhab.metadata.test.js @@ -1761,7 +1761,7 @@ describe('Test EXECUTE with Metadata', () => { "id": "MyTV" }], "execution": [{ - "command": "action.devices.commands.SelectChannel", + "command": "action.devices.commands.selectChannel", "params": { "channelNumber": "20" } From 99feb95b9d6366aa30fea5a3818dcfde87506182 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 1 Aug 2020 03:52:25 +0200 Subject: [PATCH 15/94] Fix volume relative, add onoff for TV Signed-off-by: Michael Krug --- functions/commands/setvolume.js | 2 +- functions/commands/volumerelative.js | 7 +-- functions/devices/tv.js | 1 + tests/openhab.metadata.test.js | 67 +++++++++++++++++++++++++++- 4 files changed, 72 insertions(+), 5 deletions(-) diff --git a/functions/commands/setvolume.js b/functions/commands/setvolume.js index cbc3b7be..a833cb73 100644 --- a/functions/commands/setvolume.js +++ b/functions/commands/setvolume.js @@ -15,7 +15,7 @@ class SetVolume extends DefaultCommand { } static getItemName(item) { - if (item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() == 'tv') { + if (item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() === 'tv') { const members = TV.getMembers(item); if ('volume' in members) { return members.volume.name; diff --git a/functions/commands/volumerelative.js b/functions/commands/volumerelative.js index 1612a187..a4e8093e 100644 --- a/functions/commands/volumerelative.js +++ b/functions/commands/volumerelative.js @@ -15,7 +15,7 @@ class VolumeRelative extends DefaultCommand { } static getItemName(item) { - if (item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() == 'tv') { + if (item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() === 'tv') { const members = TV.getMembers(item); if ('volume' in members) { return members.volume.name; @@ -27,12 +27,13 @@ class VolumeRelative extends DefaultCommand { static convertParamsToValue(params, item) { let state = item.state; - if (item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() == 'tv') { + if (item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() === 'tv') { const members = TV.getMembers(item); if ('volume' in members) { state = members.volume.state; + } else { + throw { statusCode: 400 }; } - throw { statusCode: 400 }; } let level = parseInt(state) + params.volumeRelativeLevel; return (level < 0 ? 0 : level > 100 ? 100 : level).toString(); diff --git a/functions/devices/tv.js b/functions/devices/tv.js index 79a1f43e..7aa8c266 100644 --- a/functions/devices/tv.js +++ b/functions/devices/tv.js @@ -7,6 +7,7 @@ class TV extends DefaultDevice { static get traits() { return [ + 'action.devices.traits.OnOff', 'action.devices.traits.Channel', 'action.devices.traits.Volume', 'action.devices.traits.InputSelector', diff --git a/tests/openhab.metadata.test.js b/tests/openhab.metadata.test.js index bd280525..bf5edd97 100644 --- a/tests/openhab.metadata.test.js +++ b/tests/openhab.metadata.test.js @@ -1828,7 +1828,7 @@ describe('Test EXECUTE with Metadata', () => { "id": "MyTV" }], "execution": [{ - "command": "action.devices.commands.SelectChannel", + "command": "action.devices.commands.selectChannel", "params": { "channelName": "Channel 1" } @@ -1985,6 +1985,71 @@ describe('Test EXECUTE with Metadata', () => { }); }); + test('volumeRelative for TV', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV" + } + }, + "members": [{ + type: 'Dimmer', + name: 'MyVolume', + metadata: { + ga: { + value: 'volume' + } + }, + state: '40' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV" + }], + "execution": [{ + "command": "action.devices.commands.volumeRelative", + "params": { + "volumeRelativeLevel": 1 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyVolume', '41'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'currentVolume': 41, + 'isMuted': false, + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); + test('MediaPause for TV', async () => { const item = { From 78cc7542398c8cbfcb779369c05be7666e9156f6 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 13 Aug 2020 21:38:18 +0200 Subject: [PATCH 16/94] Update documentation Signed-off-by: Michael Krug --- README.md | 14 ++++++++++++-- docs/USAGE.md | 12 ++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0b839d86..ede9b902 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,7 @@ Currently the following metadata values are supported (also depending on Googles * `Switch { ga="Sprinkler" }` * `Switch { ga="Vacuum" }` * `Switch { ga="Scene" }` -* `Switch { ga="Lock" [ ackNeeded=true ] }` +* `Switch / Contact { ga="Lock" [ ackNeeded=true ] }` * `Switch { ga="SecuritySystem" [ pinNeeded="1234" ] }` * `Dimmer { ga="Speaker" }` * `Switch / Dimmer { ga="Fan" [ speeds="0=away:zero,50=default:standard:one,100=high:two", lang="en", ordered=true ] }` (for Dimmer the options have to be set) @@ -230,7 +230,7 @@ Currently the following metadata values are supported (also depending on Googles * `Number / String { ga="thermostatMode" }` as part of Thermostat group * `String { ga="Camera" [ protocols="hls,dash" ] }` -_\* All Rollershutter devices can also be used with a Switch item with the limitation of only supporting open and close states._ +_\* All Rollershutter devices can also be used with a Switch or Contact item with the limitation of only supporting open and close states._ Item labels are not mandatory in openHAB, but for the Google Assistant Action they are absolutely necessary! @@ -242,6 +242,11 @@ Furthermore, you can state synonyms for the device name: `Switch KitchenLight "K To ease setting up new devices you can add a room hint: `[ roomHint="Living Room" ]`. +For devices supporting the OpenClose trait, the attributes `[ discreteOnlyOpenClose=false, queryOnlyOpenClose=false ]` can be configured. +- discreteOnlyOpenClose defaults to false. When set to true, this indicates that the device must either be fully open or fully closed (that is, it does not support values between 0% and 100%). An example of such a device may be a valve. +- queryOnlyOpenClose defaults to false. Is set to true for `Contact` items. Indicates if the device can only be queried for state information and cannot be controlled. Sensors that can only report open state should set this field to true. + +--- NOTE: metadata is not (yet?) available via paperUI. Either you create your items via ".items" files, or you can: - add metadata via console: @@ -322,6 +327,11 @@ Blinds should always use the `Rollershutter` item type. Since Google and openHAB use the oposite percentage value for "opened" or "closed", the action will tranlate this automatically. If the values are still inverted in your case, you can state the `[ inverted=true ]` option for all `Rollershutter` items. +Since Google only tells the open percentage (and not the verb "close" or "down"), it can not be differentiated between saying "set blind to 100%" or "open blind". +Therefore, it is not possible to "not invert" the verbs, if the user chooses to invert the numbers. + +--- + More details about the setup and the service linkage (https://myopenhab.org) procedure within the Google App can be found in the [USAGE documentation](docs/USAGE.md). ## Example Voice Commands diff --git a/docs/USAGE.md b/docs/USAGE.md index ccefee41..2d56b831 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -36,7 +36,7 @@ Currently the following metadata values are supported (also depending on Googles * `Switch { ga="Sprinkler" }` * `Switch { ga="Vacuum" }` * `Switch { ga="Scene" }` -* `Switch { ga="Lock" [ ackNeeded=true ] }` +* `Switch / Contact { ga="Lock" [ ackNeeded=true ] }` * `Switch { ga="SecuritySystem" [ pinNeeded="1234" ] }` * `Dimmer { ga="Speaker" }` * `Switch / Dimmer { ga="Fan" [ speeds="0=away:zero,50=default:standard:one,100=high:two", lang="en", ordered=true ] }` (for Dimmer the options have to be set) @@ -58,7 +58,7 @@ Currently the following metadata values are supported (also depending on Googles * `Number / String { ga="thermostatMode" }` as part of Thermostat group * `String { ga="Camera" [ protocols="hls,dash" ] }` -_\* All Rollershutter devices can also be used with a Switch item with the limitation of only supporting open and close states._ +_\* All Rollershutter devices can also be used with a Switch or Contact item with the limitation of only supporting open and close states._ Example item configuration: ``` @@ -87,6 +87,11 @@ Furthermore, you can state synonyms for the device name: `Switch KitchenLight "K To ease setting up new devices you can add a room hint: `[ roomHint="Living Room" ]`. +For devices supporting the OpenClose trait, the attributes `[ discreteOnlyOpenClose=false, queryOnlyOpenClose=false ]` can be configured. +- discreteOnlyOpenClose defaults to false. When set to true, this indicates that the device must either be fully open or fully closed (that is, it does not support values between 0% and 100%). An example of such a device may be a valve. +- queryOnlyOpenClose defaults to false. Is set to true for `Contact` items. Indicates if the device can only be queried for state information and cannot be controlled. Sensors that can only report open state should set this field to true. + +--- NOTE: metadata is not (yet?) available via paperUI. Either you create your items via ".items" files, or you can: - add metadata via console: @@ -167,6 +172,9 @@ Blinds should always use the `Rollershutter` item type. Since Google and openHAB use the oposite percentage value for "opened" or "closed", the action will tranlate this automatically. If the values are still inverted in your case, you can state the `[ inverted=true ]` option for all `Rollershutter` items. +Since Google only tells the open percentage (and not the verb "close" or "down"), it can not be differentiated between saying "set blind to 100%" or "open blind". +Therefore, it is not possible to "not invert" the verbs, if the user chooses to invert the numbers. + ## Setup & Usage on Google Assistant App From d391504f233ddcde8287b7db3f7a2e07bfb67a39 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sun, 30 Aug 2020 14:38:25 +0200 Subject: [PATCH 17/94] Add temperature sensor Signed-off-by: Michael Krug --- functions/devices/temperaturesensor.js | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 functions/devices/temperaturesensor.js diff --git a/functions/devices/temperaturesensor.js b/functions/devices/temperaturesensor.js new file mode 100644 index 00000000..3530d148 --- /dev/null +++ b/functions/devices/temperaturesensor.js @@ -0,0 +1,36 @@ +const DefaultDevice = require('./default.js'); + +class TemperatureSensor extends DefaultDevice { + static get type() { + return 'action.devices.types.SENSOR'; + } + + static get traits() { + return [ + 'action.devices.traits.TemperatureControl' + ]; + } + + static getAttributes(item) { + return { + queryOnlyTemperatureControl: true, + temperatureUnitForUX: this.getConfig(item).useFahrenheit === true ? 'F' : 'C' + }; + } + + static getState(item) { + var state = Number(parseFloat(item.state).toFixed(1)); + if (this.getConfig(item).useFahrenheit === true) { + state = this.convertToCelsius(state); + } + return { + temperatureAmbientCelsius: state, + }; + } + + static convertToCelsius(value = 0) { + return Number(((value - 32) * 5 / 9).toFixed(1)); + } +} + +module.exports = TemperatureSensor; From 15803400131346f5d03ac3b83e6f44f34e7614a5 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sun, 30 Aug 2020 14:52:36 +0200 Subject: [PATCH 18/94] Check type attribute for sensors Signed-off-by: Michael Krug --- functions/devices/sensor.js | 5 +++++ functions/devices/temperaturesensor.js | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/functions/devices/sensor.js b/functions/devices/sensor.js index 7640afeb..c86aaab8 100644 --- a/functions/devices/sensor.js +++ b/functions/devices/sensor.js @@ -32,6 +32,11 @@ class Sensor extends DefaultDevice { return attributes; } + static checkItemType(item = {}) { + const config = this.getConfig(item); + return config && config.type && config.type.toLowerCase() === 'sensor'; + } + static getState(item) { const config = this.getConfig(item); return { diff --git a/functions/devices/temperaturesensor.js b/functions/devices/temperaturesensor.js index 3530d148..aad6335f 100644 --- a/functions/devices/temperaturesensor.js +++ b/functions/devices/temperaturesensor.js @@ -18,6 +18,11 @@ class TemperatureSensor extends DefaultDevice { }; } + static checkItemType(item = {}) { + const config = this.getConfig(item); + return config && config.type && config.type.toLowerCase() === 'temperature'; + } + static getState(item) { var state = Number(parseFloat(item.state).toFixed(1)); if (this.getConfig(item).useFahrenheit === true) { From f396de0bcfa9dce24cfed4abe0057eacaaf7107c Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sun, 30 Aug 2020 21:36:51 +0200 Subject: [PATCH 19/94] Change device filter logic Signed-off-by: Michael Krug --- functions/devices.js | 12 +----------- functions/devices/default.js | 12 +++++++++++- functions/devices/sensor.js | 5 ----- functions/devices/temperaturesensor.js | 10 +++++----- functions/devices/thermostat.js | 2 +- functions/devices/tv.js | 2 +- 6 files changed, 19 insertions(+), 24 deletions(-) diff --git a/functions/devices.js b/functions/devices.js index 6dd41771..0cc73394 100644 --- a/functions/devices.js +++ b/functions/devices.js @@ -29,18 +29,8 @@ glob.sync('./devices/*.js', { cwd: __dirname }).forEach(file => { } }); -const hasTag = (item = {}, tag = '') => { - return item.tags && item.tags.map(t => t.toLowerCase()).includes(tag.toLowerCase()) || false; -}; - const getDeviceForItem = (item = {}) => { - return Devices.find((device) => ( - ( - item.metadata && item.metadata.ga && - device.type.toLowerCase() === `action.devices.types.${item.metadata.ga.value}`.toLowerCase() || - hasTag(item, device.type.substr(21).replace('SWITCH', 'SWITCHABLE').replace('LIGHT', 'LIGHTING')) - ) && device.checkItemType(item) - )); + return Devices.find((device) => device.matchesItemType(item) && device.isCompatible(item)); }; module.exports = { diff --git a/functions/devices/default.js b/functions/devices/default.js index cf478936..42901fec 100644 --- a/functions/devices/default.js +++ b/functions/devices/default.js @@ -57,7 +57,13 @@ class DefaultDevice { return []; } - static checkItemType(item = {}) { + static isCompatible(item = {}) { + return item.metadata && item.metadata.ga && + this.type.toLowerCase() === `action.devices.types.${item.metadata.ga.value}`.toLowerCase() || + this.hasTag(item, this.type.substr(21).replace('SWITCH', 'SWITCHABLE').replace('LIGHT', 'LIGHTING')) + } + + static matchesItemType(item = {}) { return ( !this.requiredItemTypes.length || this.requiredItemTypes.includes(item.type) || @@ -68,6 +74,10 @@ class DefaultDevice { static getState(item = {}) { return {}; } + + static hasTag = (item = {}, tag = '') => { + return item.tags && item.tags.map(t => t.toLowerCase()).includes(tag.toLowerCase()) || false; + }; } module.exports = DefaultDevice; diff --git a/functions/devices/sensor.js b/functions/devices/sensor.js index c86aaab8..7640afeb 100644 --- a/functions/devices/sensor.js +++ b/functions/devices/sensor.js @@ -32,11 +32,6 @@ class Sensor extends DefaultDevice { return attributes; } - static checkItemType(item = {}) { - const config = this.getConfig(item); - return config && config.type && config.type.toLowerCase() === 'sensor'; - } - static getState(item) { const config = this.getConfig(item); return { diff --git a/functions/devices/temperaturesensor.js b/functions/devices/temperaturesensor.js index aad6335f..3d13edcf 100644 --- a/functions/devices/temperaturesensor.js +++ b/functions/devices/temperaturesensor.js @@ -18,18 +18,18 @@ class TemperatureSensor extends DefaultDevice { }; } - static checkItemType(item = {}) { - const config = this.getConfig(item); - return config && config.type && config.type.toLowerCase() === 'temperature'; + static isCompatible(item = {}) { + return item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() == 'temperaturesensor' } static getState(item) { - var state = Number(parseFloat(item.state).toFixed(1)); + let state = Number(parseFloat(item.state).toFixed(1)); if (this.getConfig(item).useFahrenheit === true) { state = this.convertToCelsius(state); } return { - temperatureAmbientCelsius: state, + temperatureSetpointCelsius: state, + temperatureAmbientCelsius: state }; } diff --git a/functions/devices/thermostat.js b/functions/devices/thermostat.js index e97b9387..b69f1984 100644 --- a/functions/devices/thermostat.js +++ b/functions/devices/thermostat.js @@ -40,7 +40,7 @@ class Thermostat extends DefaultDevice { return attributes; } - static checkItemType(item) { + static matchesItemType(item) { return item.type === 'Group'; } diff --git a/functions/devices/tv.js b/functions/devices/tv.js index 7aa8c266..f2422198 100644 --- a/functions/devices/tv.js +++ b/functions/devices/tv.js @@ -51,7 +51,7 @@ class TV extends DefaultDevice { return attributes; } - static checkItemType(item) { + static matchesItemType(item) { return item.type === 'Group'; } From 86bbfb44467ae41d52812faa39862581544efda1 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sun, 30 Aug 2020 21:37:59 +0200 Subject: [PATCH 20/94] Fix copy paste error Signed-off-by: Michael Krug --- functions/devices/default.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/functions/devices/default.js b/functions/devices/default.js index 42901fec..aab7eb14 100644 --- a/functions/devices/default.js +++ b/functions/devices/default.js @@ -75,9 +75,9 @@ class DefaultDevice { return {}; } - static hasTag = (item = {}, tag = '') => { + static hasTag(item = {}, tag = '') { return item.tags && item.tags.map(t => t.toLowerCase()).includes(tag.toLowerCase()) || false; - }; + } } module.exports = DefaultDevice; From 8bc7021b5b3cd2c4eb8342a9e642cdf21038906e Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sun, 30 Aug 2020 22:20:46 +0200 Subject: [PATCH 21/94] Move conversions to utilities file Signed-off-by: Michael Krug --- .../commands/thermostattemperaturesetpoint.js | 3 +- .../thermostattemperaturesetpointhigh.js | 3 +- .../thermostattemperaturesetpointlow.js | 3 +- functions/devices/temperaturesensor.js | 7 +- functions/devices/thermostat.js | 25 +-- functions/utilities.js | 31 +++ tests/devices.test.js | 13 -- tests/openhab.metadata.test.js | 208 ++++++++++++++++++ tests/utilities.test.js | 13 ++ 9 files changed, 271 insertions(+), 35 deletions(-) create mode 100644 functions/utilities.js delete mode 100644 tests/devices.test.js create mode 100644 tests/utilities.test.js diff --git a/functions/commands/thermostattemperaturesetpoint.js b/functions/commands/thermostattemperaturesetpoint.js index 49206d8b..3d202c5d 100644 --- a/functions/commands/thermostattemperaturesetpoint.js +++ b/functions/commands/thermostattemperaturesetpoint.js @@ -1,5 +1,6 @@ const DefaultCommand = require('./default.js'); const Thermostat = require('../devices/thermostat.js'); +const convertToFahrenheit = require('../utilities.js').convertToFahrenheit; class ThermostatTemperatureSetpoint extends DefaultCommand { static get type() { @@ -25,7 +26,7 @@ class ThermostatTemperatureSetpoint extends DefaultCommand { static convertParamsToValue(params, item) { let value = params.thermostatTemperatureSetpoint; if (Thermostat.usesFahrenheit(item)) { - value = Thermostat.convertToFahrenheit(value); + value = convertToFahrenheit(value); } return value.toString(); } diff --git a/functions/commands/thermostattemperaturesetpointhigh.js b/functions/commands/thermostattemperaturesetpointhigh.js index bc0371e9..11c41108 100644 --- a/functions/commands/thermostattemperaturesetpointhigh.js +++ b/functions/commands/thermostattemperaturesetpointhigh.js @@ -1,5 +1,6 @@ const DefaultCommand = require('./default.js'); const Thermostat = require('../devices/thermostat.js'); +const convertToFahrenheit = require('../utilities.js').convertToFahrenheit; class ThermostatTemperatureSetpointHigh extends DefaultCommand { static get type() { @@ -25,7 +26,7 @@ class ThermostatTemperatureSetpointHigh extends DefaultCommand { static convertParamsToValue(params, item) { let value = params.thermostatTemperatureSetpointHigh; if (Thermostat.usesFahrenheit(item)) { - value = Thermostat.convertToFahrenheit(value); + value = convertToFahrenheit(value); } return value.toString(); } diff --git a/functions/commands/thermostattemperaturesetpointlow.js b/functions/commands/thermostattemperaturesetpointlow.js index 4089811d..85866cb4 100644 --- a/functions/commands/thermostattemperaturesetpointlow.js +++ b/functions/commands/thermostattemperaturesetpointlow.js @@ -1,5 +1,6 @@ const DefaultCommand = require('./default.js'); const Thermostat = require('../devices/thermostat.js'); +const convertToFahrenheit = require('../utilities.js').convertToFahrenheit; class ThermostatTemperatureSetpointLow extends DefaultCommand { static get type() { @@ -25,7 +26,7 @@ class ThermostatTemperatureSetpointLow extends DefaultCommand { static convertParamsToValue(params, item) { let value = params.thermostatTemperatureSetpointLow; if (Thermostat.usesFahrenheit(item)) { - value = Thermostat.convertToFahrenheit(value); + value = convertToFahrenheit(value); } return value.toString(); } diff --git a/functions/devices/temperaturesensor.js b/functions/devices/temperaturesensor.js index 3d13edcf..f4aa88c4 100644 --- a/functions/devices/temperaturesensor.js +++ b/functions/devices/temperaturesensor.js @@ -1,4 +1,5 @@ const DefaultDevice = require('./default.js'); +const convertToCelsius = require('../utilities.js').convertToCelsius; class TemperatureSensor extends DefaultDevice { static get type() { @@ -18,6 +19,10 @@ class TemperatureSensor extends DefaultDevice { }; } + static get requiredItemTypes() { + return ['Number']; + } + static isCompatible(item = {}) { return item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() == 'temperaturesensor' } @@ -25,7 +30,7 @@ class TemperatureSensor extends DefaultDevice { static getState(item) { let state = Number(parseFloat(item.state).toFixed(1)); if (this.getConfig(item).useFahrenheit === true) { - state = this.convertToCelsius(state); + state = convertToCelsius(state); } return { temperatureSetpointCelsius: state, diff --git a/functions/devices/thermostat.js b/functions/devices/thermostat.js index b69f1984..b058b153 100644 --- a/functions/devices/thermostat.js +++ b/functions/devices/thermostat.js @@ -1,8 +1,5 @@ const DefaultDevice = require('./default.js'); - -const hasTag = (item = {}, tag = '') => { - return item.tags && item.tags.map(t => t.toLowerCase()).includes(tag.toLowerCase()) || false; -}; +const convertToCelsius = require('../utilities.js').convertToCelsius; class Thermostat extends DefaultDevice { static get type() { @@ -53,7 +50,7 @@ class Thermostat extends DefaultDevice { } else { state[member] = Number(parseFloat(members[member].state).toFixed(1)); if (member.indexOf('Temperature') > 0 && this.usesFahrenheit(item)) { - state[member] = this.convertToCelsius(state[member]); + state[member] = convertToCelsius(state[member]); } } } @@ -78,16 +75,16 @@ class Thermostat extends DefaultDevice { members[memberType] = { name: member.name, state: member.state }; } } else { - if (hasTag(member, 'HeatingCoolingMode') || hasTag(member, 'homekit:HeatingCoolingMode') || hasTag(member, 'homekit:TargetHeatingCoolingMode') || hasTag(member, 'homekit:CurrentHeatingCoolingMode')) { + if (this.hasTag(member, 'HeatingCoolingMode') || this.hasTag(member, 'homekit:HeatingCoolingMode') || this.hasTag(member, 'homekit:TargetHeatingCoolingMode') || this.hasTag(member, 'homekit:CurrentHeatingCoolingMode')) { members.thermostatMode = { name: member.name, state: member.state }; } - if (hasTag(member, 'TargetTemperature') || hasTag(member, 'homekit:TargetTemperature')) { + if (this.hasTag(member, 'TargetTemperature') || this.hasTag(member, 'homekit:TargetTemperature')) { members.thermostatTemperatureSetpoint = { name: member.name, state: member.state }; } - if (hasTag(member, 'CurrentTemperature')) { + if (this.hasTag(member, 'CurrentTemperature')) { members.thermostatTemperatureAmbient = { name: member.name, state: member.state }; } - if (hasTag(member, 'CurrentHumidity')) { + if (this.hasTag(member, 'CurrentHumidity')) { members.thermostatHumidityAmbient = { name: member.name, state: member.state }; } } @@ -97,7 +94,7 @@ class Thermostat extends DefaultDevice { } static usesFahrenheit(item) { - return this.getConfig(item).useFahrenheit === true || hasTag(item, 'Fahrenheit'); + return this.getConfig(item).useFahrenheit === true || this.hasTag(item, 'Fahrenheit'); } static getModeMap(item) { @@ -131,14 +128,6 @@ class Thermostat extends DefaultDevice { } return 'on'; } - - static convertToFahrenheit(value = 0) { - return Math.round(value * 9 / 5 + 32); - } - - static convertToCelsius(value = 0) { - return Number(((value - 32) * 5 / 9).toFixed(1)); - } } module.exports = Thermostat; diff --git a/functions/utilities.js b/functions/utilities.js new file mode 100644 index 00000000..970932b0 --- /dev/null +++ b/functions/utilities.js @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +/** + * Various reusable utilify functions + * + * @author Michael Krug + * + */ + +module.exports = { + + convertToCelsius: (value = 0) => { + return Number(((value - 32) * 5 / 9).toFixed(1)); + }, + + convertToFahrenheit: (value = 0) => { + return Math.round(value * 9 / 5 + 32); + } + +} diff --git a/tests/devices.test.js b/tests/devices.test.js deleted file mode 100644 index 7660a09d..00000000 --- a/tests/devices.test.js +++ /dev/null @@ -1,13 +0,0 @@ -const Thermostat = require('../functions/devices/thermostat.js'); - -describe('Test Thermostat Device', () => { - test('convertToCelsius', () => { - expect(Thermostat.convertToCelsius(10.0)).toEqual(-12.2); - expect(Thermostat.convertToCelsius(0.0)).toEqual(-17.8); - }); - - test('convertToFahrenheit', () => { - expect(Thermostat.convertToFahrenheit(10.0)).toEqual(50); - expect(Thermostat.convertToFahrenheit(0.0)).toEqual(32); - }); -}); diff --git a/tests/openhab.metadata.test.js b/tests/openhab.metadata.test.js index bf5edd97..77043284 100644 --- a/tests/openhab.metadata.test.js +++ b/tests/openhab.metadata.test.js @@ -849,6 +849,214 @@ describe('Test EXECUTE with Metadata', () => { }); }); + test('ThermostatTemperatureSetpointHigh', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyThermostat", + "label": "Thermostat", + "metadata": { + "ga": { + "value": "Thermostat", + "config": { + "useFahrenheit": true + } + } + }, + "members": [{ + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' + } + }, + state: '10' + }, { + name: 'MyTargetTemperatureHigh', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointHigh' + } + }, + state: '20' + }, { + name: 'MyTargetTemperatureLow', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointLow' + } + }, + state: '10' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatMode' + } + }, + state: 'heat' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatHumidityAmbient' + } + }, + state: '50' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyThermostat" + }], + "execution": [{ + "command": "action.devices.commands.ThermostatTemperatureSetpointHigh", + "params": { + "thermostatTemperatureSetpointHigh": 25 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyTargetTemperatureHigh', '77'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyThermostat" + ], + "states": { + "online": true, + "thermostatHumidityAmbient": 50, + "thermostatMode": "heat", + "thermostatTemperatureAmbient": -12.2, + "thermostatTemperatureSetpointHigh": 25, + "thermostatTemperatureSetpointLow": -12.2, + }, + "status": "SUCCESS" + }] + }); + }); + + test('ThermostatTemperatureSetpointLow', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyThermostat", + "label": "Thermostat", + "metadata": { + "ga": { + "value": "Thermostat", + "config": { + "useFahrenheit": true + } + } + }, + "members": [{ + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' + } + }, + state: '10' + }, { + name: 'MyTargetTemperatureHigh', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointHigh' + } + }, + state: '20' + }, { + name: 'MyTargetTemperatureLow', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointLow' + } + }, + state: '10' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatMode' + } + }, + state: 'heat' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatHumidityAmbient' + } + }, + state: '50' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyThermostat" + }], + "execution": [{ + "command": "action.devices.commands.ThermostatTemperatureSetpointLow", + "params": { + "thermostatTemperatureSetpointLow": 5 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyTargetTemperatureLow', '41'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyThermostat" + ], + "states": { + "online": true, + "thermostatHumidityAmbient": 50, + "thermostatMode": "heat", + "thermostatTemperatureAmbient": -12.2, + "thermostatTemperatureSetpointHigh": -6.7, + "thermostatTemperatureSetpointLow": 5 + }, + "status": "SUCCESS" + }] + }); + }); + test('ThermostatTemperatureSetRange', async () => { const item1 = { diff --git a/tests/utilities.test.js b/tests/utilities.test.js new file mode 100644 index 00000000..84b5be29 --- /dev/null +++ b/tests/utilities.test.js @@ -0,0 +1,13 @@ +const Utilities = require('../functions/utilities.js'); + +describe('Test Utilities', () => { + test('convertToCelsius', () => { + expect(Utilities.convertToCelsius(10.0)).toEqual(-12.2); + expect(Utilities.convertToCelsius(0.0)).toEqual(-17.8); + }); + + test('convertToFahrenheit', () => { + expect(Utilities.convertToFahrenheit(10.0)).toEqual(50); + expect(Utilities.convertToFahrenheit(0.0)).toEqual(32); + }); +}); From ecf8e1f4460c3a1e3b0fc5a33cd66f91ed83b4cd Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sun, 30 Aug 2020 22:25:35 +0200 Subject: [PATCH 22/94] Add tests for temperature sensor Signed-off-by: Michael Krug --- .../openhab.metadata.test.js.snap | 39 +++++++ tests/openhab.metadata.test.js | 105 +++++++++++++++++- 2 files changed, 141 insertions(+), 3 deletions(-) diff --git a/tests/__snapshots__/openhab.metadata.test.js.snap b/tests/__snapshots__/openhab.metadata.test.js.snap index 58750938..5e7f6b75 100644 --- a/tests/__snapshots__/openhab.metadata.test.js.snap +++ b/tests/__snapshots__/openhab.metadata.test.js.snap @@ -384,6 +384,45 @@ Object { } `; +exports[`Test SYNC with Metadata Temperature Sensor Device 1`] = ` +Object { + "devices": Array [ + Object { + "attributes": Object { + "queryOnlyTemperatureControl": true, + "temperatureUnitForUX": "F", + }, + "customData": Object { + "itemType": "Number", + }, + "deviceInfo": Object { + "hwVersion": "2.5.0", + "manufacturer": "openHAB", + "model": "Number:MySensor", + "swVersion": "2.5.0", + }, + "id": "MySensor", + "name": Object { + "defaultNames": Array [ + "My Sensor", + ], + "name": "My Sensor", + "nicknames": Array [ + "My Sensor", + ], + }, + "roomHint": undefined, + "structureHint": undefined, + "traits": Array [ + "action.devices.traits.TemperatureControl", + ], + "type": "action.devices.types.SENSOR", + "willReportState": false, + }, + ], +} +`; + exports[`Test SYNC with Metadata Thermostat Device 1`] = ` Object { "devices": Array [ diff --git a/tests/openhab.metadata.test.js b/tests/openhab.metadata.test.js index 77043284..0f1e0a9a 100644 --- a/tests/openhab.metadata.test.js +++ b/tests/openhab.metadata.test.js @@ -157,6 +157,69 @@ describe('Test SYNC with Metadata', () => { expect(payload).toMatchSnapshot(); }); + test('Sensor Device', async () => { + const items = [ + { + "state": "14", + "metadata": { + "ga": { + "value": "Sensor", + "config": { + "sensorName": "AirQuality", + "states": "healthy=0,moderate=10,unhealthy=50,very unhealthy=100" + } + } + }, + "type": "Number", + "name": "MySensor", + "label": "My Sensor", + "tags": [] + } + ]; + const getItemsMock = jest.fn(); + getItemsMock.mockReturnValue(Promise.resolve(items)); + + const apiHandler = { + getItems: getItemsMock + }; + + const payload = await new OpenHAB(apiHandler).handleSync(); + + expect(getItemsMock).toHaveBeenCalledTimes(1); + expect(payload).toMatchSnapshot(); + }); + + test('Temperature Sensor Device', async () => { + const items = [ + { + "state": "14", + "metadata": { + "ga": { + "value": "TemperatureSensor", + "config": { + "useFahrenheit": true + } + } + }, + "type": "Number", + "name": "MySensor", + "label": "My Sensor", + "tags": [] + } + ]; + const getItemsMock = jest.fn(); + getItemsMock.mockReturnValue(Promise.resolve(items)); + + const apiHandler = { + getItems: getItemsMock + }; + + const payload = await new OpenHAB(apiHandler).handleSync(); + + expect(getItemsMock).toHaveBeenCalledTimes(1); + expect(payload).toMatchSnapshot(); + }); + test('Thermostat Device', async () => { const items = [ { @@ -512,7 +575,43 @@ describe('Test QUERY with Metadata', () => { "devices": { "MyBlinds": { "openPercent": 0, - "online": true, + "online": true + }, + }, + }); + }); + + test('Temperature Sensor', async () => { + const item = + { + "state": "20", + "type": "Number", + "name": "MySensor", + "metadata": { + "ga": { + "value": "TemperatureSensor" + } + } + }; + + const getItemMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + + const apiHandler = { + getItem: getItemMock + }; + + const payload = await new OpenHAB(apiHandler).handleQuery([{ + "id": "MySensor" + }]); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(payload).toStrictEqual({ + "devices": { + "MySensor": { + "temperatureAmbientCelsius": 20, + "temperatureSetpointCelsius": 20, + "online": true }, }, }); @@ -547,7 +646,7 @@ describe('Test QUERY with Metadata', () => { "devices": { "MyWindow": { "openPercent": 0, - "online": true, + "online": true }, }, }); @@ -586,7 +685,7 @@ describe('Test QUERY with Metadata', () => { "MyFan": { "on": true, "currentFanSpeedSetting": "50", - "online": true, + "online": true }, }, }); From 42cca6072f111803c867ca02be53b6a3f89af96a Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Mon, 31 Aug 2020 16:58:44 +0200 Subject: [PATCH 23/94] Update documentation Signed-off-by: Michael Krug --- README.md | 4 +++- docs/USAGE.md | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ede9b902..09050d56 100644 --- a/README.md +++ b/README.md @@ -223,11 +223,12 @@ Currently the following metadata values are supported (also depending on Googles * `Rollershutter { ga="Pergola" }` * `Rollershutter { ga="Shutter" }` * `Rollershutter { ga="Window" }` -* `Group { ga="Thermostat" [ modes="..." ] }` +* `Group { ga="Thermostat" [ modes="...", useFahrenheit=true ] }` * `Number { ga="thermostatTemperatureAmbient" }` as part of Thermostat group * `Number { ga="thermostatHumidityAmbient" }` as part of Thermostat group * `Number { ga="thermostatTemperatureSetpoint" }` as part of Thermostat group * `Number / String { ga="thermostatMode" }` as part of Thermostat group +* `Number { ga="TemperatureSensor" } [ useFahrenheit=true ] ` * `String { ga="Camera" [ protocols="hls,dash" ] }` _\* All Rollershutter devices can also be used with a Switch or Contact item with the limitation of only supporting open and close states._ @@ -308,6 +309,7 @@ E.g. `[ modes="off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto" ]` wil By default the integration will provide `"off,heat,cool,on,heatcool,auto,eco"`. You can also set up a Thermostat for using it as a temperature sensor. To do so, create a Thermostat group and only add one item member as "thermostatTemperatureAmbient". +However, it is recommended to prefer the `TemperatureSensor` type for simple temperature reports (but currently no UI support in Google Assistant). #### Fans diff --git a/docs/USAGE.md b/docs/USAGE.md index 2d56b831..f478d03d 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -51,11 +51,12 @@ Currently the following metadata values are supported (also depending on Googles * `Rollershutter { ga="Pergola" }` * `Rollershutter { ga="Shutter" }` * `Rollershutter { ga="Window" }` -* `Group { ga="Thermostat" [ modes="..." ] }` +* `Group { ga="Thermostat" [ modes="...", useFahrenheit=true ] }` * `Number { ga="thermostatTemperatureAmbient" }` as part of Thermostat group * `Number { ga="thermostatHumidityAmbient" }` as part of Thermostat group * `Number { ga="thermostatTemperatureSetpoint" }` as part of Thermostat group * `Number / String { ga="thermostatMode" }` as part of Thermostat group +* `Number { ga="TemperatureSensor" } [ useFahrenheit=true ] ` * `String { ga="Camera" [ protocols="hls,dash" ] }` _\* All Rollershutter devices can also be used with a Switch or Contact item with the limitation of only supporting open and close states._ @@ -153,6 +154,7 @@ E.g. `[ modes="off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto" ]` wil By default the integration will provide `"off,heat,cool,on,heatcool,auto,eco"`. You can also set up a Thermostat for using it as a temperature sensor. To do so, create a Thermostat group and only add one item member as "thermostatTemperatureAmbient". +However, it is recommended to prefer the `TemperatureSensor` type for simple temperature reports (but currently no UI support in Google Assistant). #### Fans From 661a763b3b02a9b73a6738d77a5f6ed43acab9ae Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Wed, 2 Sep 2020 01:56:09 +0200 Subject: [PATCH 24/94] Add special color light device Signed-off-by: Michael Krug --- functions/commands/brightnessabsolute.js | 16 ++++ functions/commands/colorabsolute.js | 5 +- .../commands/colorabsolutetemperature.js | 22 ++++- functions/commands/default.js | 4 +- functions/commands/setvolume.js | 4 +- functions/devices/default.js | 1 + functions/devices/specialcolorlight.js | 55 +++++++++++ .../openhab.metadata.test.js.snap | 93 ++++++++----------- tests/__snapshots__/openhab.tags.test.js.snap | 6 ++ tests/devices.metadata.test.js | 86 +++++++++++++++-- tests/openhab.metadata.test.js | 26 +++++- tests/openhab.test.js | 87 ++++++++++++++++- 12 files changed, 328 insertions(+), 77 deletions(-) create mode 100644 functions/devices/specialcolorlight.js diff --git a/functions/commands/brightnessabsolute.js b/functions/commands/brightnessabsolute.js index 8b501fd9..a11eb4c1 100644 --- a/functions/commands/brightnessabsolute.js +++ b/functions/commands/brightnessabsolute.js @@ -1,4 +1,5 @@ const DefaultCommand = require('./default.js'); +const SpecialColorLight = require('../devices/specialcolorlight.js'); class BrightnessAbsolute extends DefaultCommand { static get type() { @@ -9,6 +10,21 @@ class BrightnessAbsolute extends DefaultCommand { return ('brightness' in params) && typeof params.brightness === 'number'; } + static get requiresItem() { + return true; + } + + static getItemName(item, device) { + if (device.customData && device.customData.itemType === 'SpecialColorLight') { + const members = SpecialColorLight.getMembers(item); + if ('lightBrightness' in members) { + return members.lightBrightness.name; + } + throw { statusCode: 400 }; + } + return item.name; + } + static convertParamsToValue(params) { return params.brightness.toString(); } diff --git a/functions/commands/colorabsolute.js b/functions/commands/colorabsolute.js index 9755ab70..a967bfce 100644 --- a/functions/commands/colorabsolute.js +++ b/functions/commands/colorabsolute.js @@ -10,7 +10,10 @@ class ColorAbsolute extends DefaultCommand { ('spectrumHSV' in params.color) && typeof params.color.spectrumHSV === 'object'; } - static convertParamsToValue(params) { + static convertParamsToValue(params, item, device) { + if (device.customData && device.customData.deviceType !== 'ColorLight') { + throw { statusCode: 400 }; + } const hsv = params.color.spectrumHSV; return [hsv.hue, hsv.saturation * 100, hsv.value * 100].join(','); } diff --git a/functions/commands/colorabsolutetemperature.js b/functions/commands/colorabsolutetemperature.js index a71eb26b..aae34208 100644 --- a/functions/commands/colorabsolutetemperature.js +++ b/functions/commands/colorabsolutetemperature.js @@ -1,4 +1,5 @@ const DefaultCommand = require('./default.js'); +const SpecialColorLight = require('../devices/specialcolorlight.js'); class ColorAbsoluteTemperature extends DefaultCommand { static get type() { @@ -14,7 +15,26 @@ class ColorAbsoluteTemperature extends DefaultCommand { return true; } - static convertParamsToValue(params, item) { + static getItemName(item, device) { + if (device.customData && device.customData.deviceType === 'SpecialColorLight') { + const members = SpecialColorLight.getMembers(item); + if ('lightColorTemperature' in members) { + return members.lightColorTemperature.name; + } + throw { statusCode: 400 }; + } + return item.name; + } + + static convertParamsToValue(params, item, device) { + if (device.customData && device.customData.deviceType === 'SpecialColorLight') { + try { + const { temperatureMinK, temperatureMaxK } = SpecialColorLight.getAttributes(item).colorTemperatureRange; + return ((params.color.temperature - temperatureMinK) / (temperatureMaxK - temperatureMinK) * 100).toString(); + } catch { + return '0'; + } + } const hsv = this.rgb2hsv(this.kelvin2rgb(params.color.temperature)); const hsvArray = item.state.split(",").map((val) => Number(val)); return [Math.round(hsv.hue * 100) / 100, Math.round(hsv.saturation * 1000) / 10, hsvArray[2]].join(','); diff --git a/functions/commands/default.js b/functions/commands/default.js index f760c5f5..97ef8f6e 100644 --- a/functions/commands/default.js +++ b/functions/commands/default.js @@ -28,7 +28,7 @@ class DefaultCommand { return {}; } - static getItemName(item = {}) { + static getItemName(item = {}, device = {}) { return item.name; } @@ -95,7 +95,7 @@ class DefaultCommand { return; } - const targetItem = this.getItemName(item); + const targetItem = this.getItemName(item, device); const targetValue = this.convertParamsToValue(params, item, device); let sendCommandPromise = Promise.resolve(); if (typeof targetItem === 'string' && typeof targetValue === 'string') { diff --git a/functions/commands/setvolume.js b/functions/commands/setvolume.js index a833cb73..4c11fd66 100644 --- a/functions/commands/setvolume.js +++ b/functions/commands/setvolume.js @@ -14,8 +14,8 @@ class SetVolume extends DefaultCommand { return true; } - static getItemName(item) { - if (item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() === 'tv') { + static getItemName(item, device) { + if (device.customData && device.customData.deviceType === 'TV') { const members = TV.getMembers(item); if ('volume' in members) { return members.volume.name; diff --git a/functions/devices/default.js b/functions/devices/default.js index aab7eb14..603527ef 100644 --- a/functions/devices/default.js +++ b/functions/devices/default.js @@ -38,6 +38,7 @@ class DefaultDevice { }, attributes: this.getAttributes(item), customData: { + deviceType: this.name, itemType: itemType } }; diff --git a/functions/devices/specialcolorlight.js b/functions/devices/specialcolorlight.js new file mode 100644 index 00000000..b027a61b --- /dev/null +++ b/functions/devices/specialcolorlight.js @@ -0,0 +1,55 @@ +const ColorLight = require('./colorlight.js'); + +class SpecialColorLight extends ColorLight { + static matchesItemType(item) { + return item.type === 'Group' && Object.keys(this.getMembers(item)).length > 1 && this.getAttributes(item).colorTemperatureRange; + } + + static getAttributes(item) { + const attributes = super.getAttributes(item); + delete attributes.colorModel; + return attributes; + } + + static getState(item) { + const state = {}; + const members = this.getMembers(item); + for (const member in members) { + switch (member) { + case 'lightBrightness': + state.brightness = Number(members[member].state) || 0; + state.on = state.brightness > 0; + break; + case 'lightColorTemperature': + try { + const { temperatureMinK, temperatureMaxK } = this.getAttributes(item).colorTemperatureRange; + state.color = {}; + state.color.temperatureK = temperatureMinK + ((temperatureMaxK - temperatureMinK) / 100 * Number(members[member].state) || 0); + } catch { } + break; + } + } + return state; + } + + static getMembers(item) { + const supportedMembers = [ + 'lightBrightness', + 'lightColorTemperature' + ]; + const members = Object(); + if (item.members && item.members.length) { + item.members.forEach(member => { + if (member.metadata && member.metadata.ga) { + const memberType = supportedMembers.find(m => member.metadata.ga.value.toLowerCase() === m.toLowerCase()); + if (memberType) { + members[memberType] = { name: member.name, state: member.state }; + } + } + }); + } + return members; + } +} + +module.exports = SpecialColorLight; diff --git a/tests/__snapshots__/openhab.metadata.test.js.snap b/tests/__snapshots__/openhab.metadata.test.js.snap index 5e7f6b75..936043fe 100644 --- a/tests/__snapshots__/openhab.metadata.test.js.snap +++ b/tests/__snapshots__/openhab.metadata.test.js.snap @@ -48,16 +48,13 @@ Object { "reversible": false, }, "customData": Object { - "deviceType": "action.devices.types.FAN", - "inverted": false, + "deviceType": "Fan", "itemType": "Dimmer", - "tfaAck": undefined, - "tfaPin": undefined, }, "deviceInfo": Object { "hwVersion": "2.5.0", "manufacturer": "openHAB", - "model": "My Fan", + "model": "Dimmer:MyFan", "swVersion": "2.5.0", }, "id": "MyFan", @@ -89,16 +86,13 @@ Object { Object { "attributes": Object {}, "customData": Object { - "deviceType": "action.devices.types.LIGHT", - "inverted": false, + "deviceType": "SimpleLight", "itemType": "Switch", - "tfaAck": undefined, - "tfaPin": undefined, }, "deviceInfo": Object { "hwVersion": "2.5.0", "manufacturer": "openHAB", - "model": "SwitchLight", + "model": "Switch:MySwitch", "swVersion": "2.5.0", }, "id": "MySwitch", @@ -124,16 +118,13 @@ Object { Object { "attributes": Object {}, "customData": Object { - "deviceType": "action.devices.types.LIGHT", - "inverted": false, + "deviceType": "DimmableLight", "itemType": "Dimmer", - "tfaAck": undefined, - "tfaPin": undefined, }, "deviceInfo": Object { "hwVersion": "2.5.0", "manufacturer": "openHAB", - "model": "DimmLight", + "model": "Dimmer:MyDimmer", "swVersion": "2.5.0", }, "id": "MyDimmer", @@ -160,16 +151,13 @@ Object { "colorModel": "hsv", }, "customData": Object { - "deviceType": "action.devices.types.LIGHT", - "inverted": false, + "deviceType": "ColorLight", "itemType": "Color", - "tfaAck": undefined, - "tfaPin": undefined, }, "deviceInfo": Object { "hwVersion": "2.5.0", "manufacturer": "openHAB", - "model": "ColorLight", + "model": "Color:MyLight", "swVersion": "2.5.0", }, "id": "MyLight", @@ -195,16 +183,13 @@ Object { Object { "attributes": Object {}, "customData": Object { - "deviceType": "action.devices.types.LIGHT", - "inverted": false, + "deviceType": "SimpleLight", "itemType": "Switch", - "tfaAck": undefined, - "tfaPin": undefined, }, "deviceInfo": Object { "hwVersion": "2.5.0", "manufacturer": "openHAB", - "model": "GroupLight", + "model": "Switch:MyLightGroup", "swVersion": "2.5.0", }, "id": "MyLightGroup", @@ -228,16 +213,13 @@ Object { Object { "attributes": Object {}, "customData": Object { - "deviceType": "action.devices.types.LIGHT", - "inverted": false, + "deviceType": "DimmableLight", "itemType": "Dimmer", - "tfaAck": undefined, - "tfaPin": undefined, }, "deviceInfo": Object { "hwVersion": "2.5.0", "manufacturer": "openHAB", - "model": "GroupDimmer", + "model": "Dimmer:MyDimmerGroup", "swVersion": "2.5.0", }, "id": "MyDimmerGroup", @@ -264,16 +246,13 @@ Object { "colorModel": "hsv", }, "customData": Object { - "deviceType": "action.devices.types.LIGHT", - "inverted": false, + "deviceType": "ColorLight", "itemType": "Color", - "tfaAck": undefined, - "tfaPin": undefined, }, "deviceInfo": Object { "hwVersion": "2.5.0", "manufacturer": "openHAB", - "model": "GroupColor", + "model": "Color:MyColorGroup", "swVersion": "2.5.0", }, "id": "MyColorGroup", @@ -308,16 +287,13 @@ Object { "sceneReversible": true, }, "customData": Object { - "deviceType": "action.devices.types.SCENE", - "inverted": false, + "deviceType": "Scene", "itemType": "Switch", - "tfaAck": undefined, - "tfaPin": undefined, }, "deviceInfo": Object { "hwVersion": "2.5.0", "manufacturer": "openHAB", - "model": "My Scene", + "model": "Switch:MyScene", "swVersion": "2.5.0", }, "id": "MyScene", @@ -342,42 +318,49 @@ Object { } `; -exports[`Test SYNC with Metadata Thermostat Device 1`] = ` +exports[`Test SYNC with Metadata Sensor Device 1`] = ` Object { "devices": Array [ Object { "attributes": Object { - "availableThermostatModes": "off,heat,eco,auto", - "thermostatTemperatureUnit": "C", + "sensorStatesSupported": Object { + "descriptiveCapabilities": Object { + "availableStates": Array [ + "healthy", + "moderate", + "unhealthy", + "very unhealthy", + ], + }, + "name": "AirQuality", + }, }, "customData": Object { - "deviceType": "action.devices.types.THERMOSTAT", - "itemType": undefined, - "tfaAck": undefined, - "tfaPin": undefined, + "deviceType": "Sensor", + "itemType": "Number", }, "deviceInfo": Object { "hwVersion": "2.5.0", "manufacturer": "openHAB", - "model": "My Thermostat", + "model": "Number:MySensor", "swVersion": "2.5.0", }, - "id": "MyThermostat", + "id": "MySensor", "name": Object { "defaultNames": Array [ - "My Thermostat", + "My Sensor", ], - "name": "My Thermostat", + "name": "My Sensor", "nicknames": Array [ - "My Thermostat", + "My Sensor", ], }, "roomHint": undefined, "structureHint": undefined, "traits": Array [ - "action.devices.traits.TemperatureSetting", + "action.devices.traits.SensorState", ], - "type": "action.devices.types.THERMOSTAT", + "type": "action.devices.types.SENSOR", "willReportState": false, }, ], @@ -393,6 +376,7 @@ Object { "temperatureUnitForUX": "F", }, "customData": Object { + "deviceType": "TemperatureSensor", "itemType": "Number", }, "deviceInfo": Object { @@ -432,6 +416,7 @@ Object { "thermostatTemperatureUnit": "C", }, "customData": Object { + "deviceType": "Thermostat", "itemType": "Group", }, "deviceInfo": Object { diff --git a/tests/__snapshots__/openhab.tags.test.js.snap b/tests/__snapshots__/openhab.tags.test.js.snap index 5dc90e4d..8316f50d 100644 --- a/tests/__snapshots__/openhab.tags.test.js.snap +++ b/tests/__snapshots__/openhab.tags.test.js.snap @@ -6,6 +6,7 @@ Object { Object { "attributes": Object {}, "customData": Object { + "deviceType": "SimpleLight", "itemType": "Switch", }, "deviceInfo": Object { @@ -35,6 +36,7 @@ Object { Object { "attributes": Object {}, "customData": Object { + "deviceType": "DimmableLight", "itemType": "Dimmer", }, "deviceInfo": Object { @@ -67,6 +69,7 @@ Object { "colorModel": "hsv", }, "customData": Object { + "deviceType": "ColorLight", "itemType": "Color", }, "deviceInfo": Object { @@ -98,6 +101,7 @@ Object { Object { "attributes": Object {}, "customData": Object { + "deviceType": "SimpleLight", "itemType": "Switch", }, "deviceInfo": Object { @@ -127,6 +131,7 @@ Object { Object { "attributes": Object {}, "customData": Object { + "deviceType": "DimmableLight", "itemType": "Dimmer", }, "deviceInfo": Object { @@ -159,6 +164,7 @@ Object { "colorModel": "hsv", }, "customData": Object { + "deviceType": "ColorLight", "itemType": "Color", }, "deviceInfo": Object { diff --git a/tests/devices.metadata.test.js b/tests/devices.metadata.test.js index afa84b73..ba06db27 100644 --- a/tests/devices.metadata.test.js +++ b/tests/devices.metadata.test.js @@ -81,12 +81,17 @@ describe('Test Switch Devices with Metadata', () => { state: 'ON', metadata: { ga: { - value: 'Lock' + value: 'Lock', + config: { + ackNeeded: true + } } } }; const device = Devices.getDeviceForItem(item); expect(device.name).toBe('Lock'); + expect(device.getMetadata(item).customData.ackNeeded).toBe(true); + expect(device.getMetadata(item).customData.pinNeeded).toBeUndefined(); expect(device.getState(item)).toStrictEqual({ isLocked: true }); @@ -98,12 +103,17 @@ describe('Test Switch Devices with Metadata', () => { state: 'ON', metadata: { ga: { - value: 'SecuritySystem' + value: 'SecuritySystem', + config: { + pinNeeded: '1234' + } } } }; const device = Devices.getDeviceForItem(item); expect(device.name).toBe('SecuritySystem'); + expect(device.getMetadata(item).customData.ackNeeded).toBeUndefined(); + expect(device.getMetadata(item).customData.pinNeeded).toBe('1234'); expect(device.getState(item)).toStrictEqual({ isArmed: true }); @@ -186,7 +196,8 @@ describe('Test Light Devices with Metadata', () => { colorTemperatureRange: '1000,4000' } } - } + }, + state: '100,50,20' }; const device = Devices.getDeviceForItem(item); expect(device.name).toBe('ColorLight'); @@ -197,6 +208,17 @@ describe('Test Light Devices with Metadata', () => { temperatureMaxK: 4000 } }); + expect(device.getState(item)).toStrictEqual({ + brightness: 20, + color: { + spectrumHSV: { + hue: 100, + saturation: 0.5, + value: 0.2, + } + }, + on: true + }); }); test('Color Light Type colorTemperatureRange with invalid value', () => { @@ -217,6 +239,54 @@ describe('Test Light Devices with Metadata', () => { colorModel: 'hsv' }); }); + + test('Special Color Light Type', () => { + const item = { + type: 'Group', + metadata: { + ga: { + value: 'LIGHT', + config: { + colorTemperatureRange: '1000,4000' + } + } + }, + members: [{ + name: 'Brightness', + type: 'Dimmer', + metadata: { + ga: { + value: 'lightBrightness' + } + }, + state: '10' + }, { + name: 'ColorTemp', + type: 'Dimmer', + metadata: { + ga: { + value: 'lightColorTemperature' + } + }, + state: '50' + }] + }; + const device = Devices.getDeviceForItem(item); + expect(device.name).toBe('SpecialColorLight'); + expect(device.getAttributes(item)).toStrictEqual({ + colorTemperatureRange: { + temperatureMinK: 1000, + temperatureMaxK: 4000 + } + }); + expect(device.getState(item)).toStrictEqual({ + brightness: 10, + color: { + temperatureK: 2500 + }, + on: true + }); + }); }); describe('Test OpenClose Devices', () => { @@ -628,7 +698,6 @@ describe('Test Thermostat Device with Metadata', () => { name: 'MyItem', label: 'MyThermostat', type: 'Group', - groupType: 'Group', metadata: { ga: { value: 'Thermostat', @@ -672,18 +741,15 @@ describe('Test Thermostat Device with Metadata', () => { "thermostatTemperatureUnit": "C" }, "customData": { - "deviceType": "action.devices.types.THERMOSTAT", - "itemType": undefined, - "inverted": false, - "tfaAck": undefined, - "tfaPin": undefined + "deviceType": "Thermostat", + "itemType": "Group" }, "roomHint": undefined, "structureHint": undefined, "deviceInfo": { "hwVersion": "2.5.0", "manufacturer": "openHAB", - "model": "MyThermostat", + "model": "Group:MyItem", "swVersion": "2.5.0", }, "id": "MyItem", diff --git a/tests/openhab.metadata.test.js b/tests/openhab.metadata.test.js index 0f1e0a9a..4f160ac1 100644 --- a/tests/openhab.metadata.test.js +++ b/tests/openhab.metadata.test.js @@ -1783,7 +1783,7 @@ describe('Test EXECUTE with Metadata', () => { const payload = await new OpenHAB(apiHandler).handleExecute(commands); - expect(getItemMock).toHaveBeenCalledTimes(0); + expect(getItemMock).toHaveBeenCalledTimes(1); expect(payload).toStrictEqual({ "commands": [{ "ids": [ @@ -2071,6 +2071,9 @@ describe('Test EXECUTE with Metadata', () => { "command": "action.devices.commands.selectChannel", "params": { "channelNumber": "20" + }, + "customData": { + "deviceType": "TV" } }] }]; @@ -2138,6 +2141,9 @@ describe('Test EXECUTE with Metadata', () => { "command": "action.devices.commands.selectChannel", "params": { "channelName": "Channel 1" + }, + "customData": { + "deviceType": "TV" } }] }]; @@ -2205,6 +2211,9 @@ describe('Test EXECUTE with Metadata', () => { "command": "action.devices.commands.SetInput", "params": { "newInput": "hdmi1" + }, + "customData": { + "deviceType": "TV" } }] }]; @@ -2263,7 +2272,10 @@ describe('Test EXECUTE with Metadata', () => { const commands = [{ "devices": [{ - "id": "MyTV" + "id": "MyTV", + "customData": { + "deviceType": "TV" + } }], "execution": [{ "command": "action.devices.commands.setVolume", @@ -2328,7 +2340,10 @@ describe('Test EXECUTE with Metadata', () => { const commands = [{ "devices": [{ - "id": "MyTV" + "id": "MyTV", + "customData": { + "deviceType": "TV" + } }], "execution": [{ "command": "action.devices.commands.volumeRelative", @@ -2393,7 +2408,10 @@ describe('Test EXECUTE with Metadata', () => { const commands = [{ "devices": [{ - "id": "MyTV" + "id": "MyTV", + "customData": { + "deviceType": "TV" + } }], "execution": [{ "command": "action.devices.commands.mediaPause" diff --git a/tests/openhab.test.js b/tests/openhab.test.js index 91e2705c..d6280445 100644 --- a/tests/openhab.test.js +++ b/tests/openhab.test.js @@ -459,13 +459,12 @@ describe('Test EXECUTE', () => { }); }); - test('ColorAbsolute Temperature', async () => { + test('ColorAbsolute Temperature ColorLight', async () => { const item = { "state": "50,50,50", - "type": "Switch", + "type": "Color", "name": "MyColor", - "label": "ColorLight", "metadata": { "ga": { "value": "LIGHT" @@ -516,4 +515,86 @@ describe('Test EXECUTE', () => { }] }); }); + + + test('ColorAbsolute Temperature SpecialColorLight', async () => { + const item = + { + "type": "Group", + "name": "MyColor", + "metadata": { + "ga": { + "value": "LIGHT", + "config": { + "colorTemperatureRange": "1000,4000" + } + } + }, + "members": [{ + "type": "Dimmer", + "name": "MyBrightness", + "metadata": { + "ga": { + "value": "lightBrightness" + } + }, + "state": "10" + }, { + "type": "Dimmer", + "name": "MyTemperature", + "metadata": { + "ga": { + "value": "lightColorTemperature" + } + }, + "state": "50" + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyColor", + "customData": { + "deviceType": "SpecialColorLight" + } + }], + "execution": [{ + "command": "action.devices.commands.ColorAbsolute", + "params": { + "color": { + "temperature": 4000 + } + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalled(); + expect(sendCommandMock).toBeCalledWith('MyTemperature', '100'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyColor" + ], + "states": { + "online": true, + "color": { + "temperatureK": 4000 + } + }, + "status": "SUCCESS" + }] + }); + }); }); From 31035828b3fc28b5fc9ba9aebdce0cd7d4c4ca52 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Wed, 2 Sep 2020 01:56:26 +0200 Subject: [PATCH 25/94] Fix typo Signed-off-by: Michael Krug --- functions/commands/default.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/functions/commands/default.js b/functions/commands/default.js index 97ef8f6e..9a56fa99 100644 --- a/functions/commands/default.js +++ b/functions/commands/default.js @@ -36,7 +36,7 @@ class DefaultCommand { return false; } - static handlAuthPin(device = {}, challenge = {}) { + static handleAuthPin(device = {}, challenge = {}) { if (!device.customData || !device.customData.pinNeeded || challenge.pin === device.customData.pinNeeded) { return; } @@ -50,7 +50,7 @@ class DefaultCommand { }; } - static handlAuthAck(device = {}, challenge = {}, responseStates = {}) { + static handleAuthAck(device = {}, challenge = {}, responseStates = {}) { if (!device.customData || !device.customData.ackNeeded || challenge.ack === true) { return; } @@ -70,7 +70,7 @@ class DefaultCommand { const commandsResponse = []; const promises = devices.map((device) => { - const authPinResponse = this.handlAuthPin(device, challenge); + const authPinResponse = this.handleAuthPin(device, challenge); if (authPinResponse) { commandsResponse.push(authPinResponse); return Promise.resolve(); @@ -89,7 +89,7 @@ class DefaultCommand { responseStates.online = true; } - const authAckResponse = this.handlAuthAck(device, challenge, responseStates); + const authAckResponse = this.handleAuthAck(device, challenge, responseStates); if (authAckResponse) { commandsResponse.push(authAckResponse); return; From 02e5861839ef440426f48c7fcee5bd6b19e8948d Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Wed, 2 Sep 2020 01:56:46 +0200 Subject: [PATCH 26/94] Minor refactoring Signed-off-by: Michael Krug --- functions/commands.js | 8 ++++---- functions/devices.js | 10 ++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/functions/commands.js b/functions/commands.js index 3b05709c..3ad32e7f 100644 --- a/functions/commands.js +++ b/functions/commands.js @@ -29,8 +29,8 @@ glob.sync('./commands/*.js', { cwd: __dirname }).forEach(file => { } }); -const getCommandType = (command = '', params = {}) => { - return Commands.find((commandType) => command === commandType.type && commandType.validateParams(params)); +module.exports = { + getCommandType: (command = '', params = {}) => { + return Commands.find((commandType) => command === commandType.type && commandType.validateParams(params)); + } }; - -module.exports = { getCommandType }; diff --git a/functions/devices.js b/functions/devices.js index 0cc73394..052383a8 100644 --- a/functions/devices.js +++ b/functions/devices.js @@ -29,10 +29,8 @@ glob.sync('./devices/*.js', { cwd: __dirname }).forEach(file => { } }); -const getDeviceForItem = (item = {}) => { - return Devices.find((device) => device.matchesItemType(item) && device.isCompatible(item)); -}; - module.exports = { - getDeviceForItem -} + getDeviceForItem: (item = {}) => { + return Devices.find((device) => device.matchesItemType(item) && device.isCompatible(item)); + } +}; From 6c757cfea4fa2438d478462cb9b8d6e4762bbf99 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Wed, 2 Sep 2020 02:07:45 +0200 Subject: [PATCH 27/94] Fix brightness command Signed-off-by: Michael Krug --- functions/commands/brightnessabsolute.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/commands/brightnessabsolute.js b/functions/commands/brightnessabsolute.js index a11eb4c1..37545203 100644 --- a/functions/commands/brightnessabsolute.js +++ b/functions/commands/brightnessabsolute.js @@ -15,7 +15,7 @@ class BrightnessAbsolute extends DefaultCommand { } static getItemName(item, device) { - if (device.customData && device.customData.itemType === 'SpecialColorLight') { + if (device.customData && device.customData.deviceType === 'SpecialColorLight') { const members = SpecialColorLight.getMembers(item); if ('lightBrightness' in members) { return members.lightBrightness.name; From 59fd3c2e1098fd6e5d3722fd3c2ffdfcf7050dd7 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Wed, 2 Sep 2020 02:08:07 +0200 Subject: [PATCH 28/94] Invert percentage of color temperature Signed-off-by: Michael Krug --- functions/commands/colorabsolutetemperature.js | 2 +- functions/devices/specialcolorlight.js | 2 +- tests/openhab.test.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/functions/commands/colorabsolutetemperature.js b/functions/commands/colorabsolutetemperature.js index aae34208..56056d65 100644 --- a/functions/commands/colorabsolutetemperature.js +++ b/functions/commands/colorabsolutetemperature.js @@ -30,7 +30,7 @@ class ColorAbsoluteTemperature extends DefaultCommand { if (device.customData && device.customData.deviceType === 'SpecialColorLight') { try { const { temperatureMinK, temperatureMaxK } = SpecialColorLight.getAttributes(item).colorTemperatureRange; - return ((params.color.temperature - temperatureMinK) / (temperatureMaxK - temperatureMinK) * 100).toString(); + return (100 - ((params.color.temperature - temperatureMinK) / (temperatureMaxK - temperatureMinK) * 100)).toString(); } catch { return '0'; } diff --git a/functions/devices/specialcolorlight.js b/functions/devices/specialcolorlight.js index b027a61b..4e4a06e8 100644 --- a/functions/devices/specialcolorlight.js +++ b/functions/devices/specialcolorlight.js @@ -24,7 +24,7 @@ class SpecialColorLight extends ColorLight { try { const { temperatureMinK, temperatureMaxK } = this.getAttributes(item).colorTemperatureRange; state.color = {}; - state.color.temperatureK = temperatureMinK + ((temperatureMaxK - temperatureMinK) / 100 * Number(members[member].state) || 0); + state.color.temperatureK = temperatureMinK + ((temperatureMaxK - temperatureMinK) / 100 * (100 - Number(members[member].state)) || 0); } catch { } break; } diff --git a/tests/openhab.test.js b/tests/openhab.test.js index d6280445..44a029e9 100644 --- a/tests/openhab.test.js +++ b/tests/openhab.test.js @@ -581,7 +581,7 @@ describe('Test EXECUTE', () => { const payload = await new OpenHAB(apiHandler).handleExecute(commands); expect(getItemMock).toHaveBeenCalled(); - expect(sendCommandMock).toBeCalledWith('MyTemperature', '100'); + expect(sendCommandMock).toBeCalledWith('MyTemperature', '0'); expect(payload).toStrictEqual({ "commands": [{ "ids": [ From 323bcacf1b1c3d62db6a17f3e44436ff6f98185e Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Wed, 2 Sep 2020 02:24:59 +0200 Subject: [PATCH 29/94] Make requiresItem dynamic Signed-off-by: Michael Krug --- functions/commands/brightnessabsolute.js | 4 +- .../commands/colorabsolutetemperature.js | 2 +- functions/commands/default.js | 4 +- functions/commands/getcamerastream.js | 2 +- functions/commands/medianext.js | 2 +- functions/commands/mediapause.js | 2 +- functions/commands/mediaprevious.js | 2 +- functions/commands/mediaresume.js | 2 +- functions/commands/onoff.js | 16 ++++ functions/commands/selectchannel.js | 2 +- functions/commands/setinput.js | 2 +- functions/commands/setvolume.js | 4 +- functions/commands/thermostatsetmode.js | 2 +- .../commands/thermostattemperaturesetpoint.js | 2 +- .../thermostattemperaturesetpointhigh.js | 2 +- .../thermostattemperaturesetpointlow.js | 2 +- functions/commands/volumerelative.js | 2 +- tests/openhab.metadata.test.js | 2 +- tests/openhab.test.js | 95 +++++++++++++++---- 19 files changed, 115 insertions(+), 36 deletions(-) diff --git a/functions/commands/brightnessabsolute.js b/functions/commands/brightnessabsolute.js index 37545203..a7fe5b52 100644 --- a/functions/commands/brightnessabsolute.js +++ b/functions/commands/brightnessabsolute.js @@ -10,8 +10,8 @@ class BrightnessAbsolute extends DefaultCommand { return ('brightness' in params) && typeof params.brightness === 'number'; } - static get requiresItem() { - return true; + static requiresItem(device) { + return device.customData && device.customData.deviceType === 'SpecialColorLight'; } static getItemName(item, device) { diff --git a/functions/commands/colorabsolutetemperature.js b/functions/commands/colorabsolutetemperature.js index 56056d65..103cbf74 100644 --- a/functions/commands/colorabsolutetemperature.js +++ b/functions/commands/colorabsolutetemperature.js @@ -11,7 +11,7 @@ class ColorAbsoluteTemperature extends DefaultCommand { ('temperature' in params.color) && typeof params.color.temperature === 'number'; } - static get requiresItem() { + static requiresItem() { return true; } diff --git a/functions/commands/default.js b/functions/commands/default.js index 9a56fa99..fb3b3dbe 100644 --- a/functions/commands/default.js +++ b/functions/commands/default.js @@ -32,7 +32,7 @@ class DefaultCommand { return item.name; } - static get requiresItem() { + static requiresItem(device = {}) { return false; } @@ -79,7 +79,7 @@ class DefaultCommand { const ackWithState = ackSupported.includes(this.type) && device.customData && device.customData.ackNeeded && !challenge.ack; let getItemPromise = Promise.resolve(({ name: device.id })); - if (this.requiresItem || ackWithState) { + if (this.requiresItem(device) || ackWithState) { getItemPromise = apiHandler.getItem(device.id); } diff --git a/functions/commands/getcamerastream.js b/functions/commands/getcamerastream.js index e4611879..35c8769d 100644 --- a/functions/commands/getcamerastream.js +++ b/functions/commands/getcamerastream.js @@ -10,7 +10,7 @@ class GetCameraStream extends DefaultCommand { ('SupportedStreamProtocols' in params) && typeof params.SupportedStreamProtocols === 'object'; } - static get requiresItem() { + static requiresItem() { return true; } diff --git a/functions/commands/medianext.js b/functions/commands/medianext.js index 91051205..c79b5c46 100644 --- a/functions/commands/medianext.js +++ b/functions/commands/medianext.js @@ -6,7 +6,7 @@ class MediaNext extends DefaultCommand { return 'action.devices.commands.mediaNext'; } - static get requiresItem() { + static requiresItem() { return true; } diff --git a/functions/commands/mediapause.js b/functions/commands/mediapause.js index e995768f..3c612df2 100644 --- a/functions/commands/mediapause.js +++ b/functions/commands/mediapause.js @@ -6,7 +6,7 @@ class MediaPause extends DefaultCommand { return 'action.devices.commands.mediaPause'; } - static get requiresItem() { + static requiresItem() { return true; } diff --git a/functions/commands/mediaprevious.js b/functions/commands/mediaprevious.js index 2e5a10ab..575d0b8f 100644 --- a/functions/commands/mediaprevious.js +++ b/functions/commands/mediaprevious.js @@ -6,7 +6,7 @@ class MediaPrevious extends DefaultCommand { return 'action.devices.commands.mediaPrevious'; } - static get requiresItem() { + static requiresItem() { return true; } diff --git a/functions/commands/mediaresume.js b/functions/commands/mediaresume.js index f7c20019..fa9647e2 100644 --- a/functions/commands/mediaresume.js +++ b/functions/commands/mediaresume.js @@ -6,7 +6,7 @@ class MediaResume extends DefaultCommand { return 'action.devices.commands.mediaResume'; } - static get requiresItem() { + static requiresItem() { return true; } diff --git a/functions/commands/onoff.js b/functions/commands/onoff.js index 9f7358a7..a51027b4 100644 --- a/functions/commands/onoff.js +++ b/functions/commands/onoff.js @@ -1,4 +1,5 @@ const DefaultCommand = require('./default.js'); +const SpecialColorLight = require('../devices/specialcolorlight.js'); class OnOff extends DefaultCommand { static get type() { @@ -9,6 +10,21 @@ class OnOff extends DefaultCommand { return ('on' in params) && typeof params.on === 'boolean'; } + static requiresItem(device) { + return device.customData && device.customData.deviceType === 'SpecialColorLight'; + } + + static getItemName(item, device) { + if (device.customData && device.customData.deviceType === 'SpecialColorLight') { + const members = SpecialColorLight.getMembers(item); + if ('lightBrightness' in members) { + return members.lightBrightness.name; + } + throw { statusCode: 400 }; + } + return item.name; + } + static convertParamsToValue(params, item, device) { let on = params.on; if (device.customData && device.customData.inverted === true) { diff --git a/functions/commands/selectchannel.js b/functions/commands/selectchannel.js index 13852348..6607e8a1 100644 --- a/functions/commands/selectchannel.js +++ b/functions/commands/selectchannel.js @@ -12,7 +12,7 @@ class SelectChannel extends DefaultCommand { (('channelNumber' in params) && typeof params.channelNumber === 'string'); } - static get requiresItem() { + static requiresItem() { return true; } diff --git a/functions/commands/setinput.js b/functions/commands/setinput.js index a1ee14f5..80137258 100644 --- a/functions/commands/setinput.js +++ b/functions/commands/setinput.js @@ -10,7 +10,7 @@ class SetInput extends DefaultCommand { return ('newInput' in params) && typeof params.newInput === 'string'; } - static get requiresItem() { + static requiresItem() { return true; } diff --git a/functions/commands/setvolume.js b/functions/commands/setvolume.js index 4c11fd66..e954c239 100644 --- a/functions/commands/setvolume.js +++ b/functions/commands/setvolume.js @@ -10,8 +10,8 @@ class SetVolume extends DefaultCommand { return ('volumeLevel' in params) && typeof params.volumeLevel === 'number'; } - static get requiresItem() { - return true; + static requiresItem(device) { + return device.customData && device.customData.deviceType === 'TV'; } static getItemName(item, device) { diff --git a/functions/commands/thermostatsetmode.js b/functions/commands/thermostatsetmode.js index b3abd006..d0db344b 100644 --- a/functions/commands/thermostatsetmode.js +++ b/functions/commands/thermostatsetmode.js @@ -10,7 +10,7 @@ class ThermostatSetMode extends DefaultCommand { return ('thermostatMode' in params) && typeof params.thermostatMode === 'string'; } - static get requiresItem() { + static requiresItem() { return true; } diff --git a/functions/commands/thermostattemperaturesetpoint.js b/functions/commands/thermostattemperaturesetpoint.js index 3d202c5d..863fb4cc 100644 --- a/functions/commands/thermostattemperaturesetpoint.js +++ b/functions/commands/thermostattemperaturesetpoint.js @@ -11,7 +11,7 @@ class ThermostatTemperatureSetpoint extends DefaultCommand { return ('thermostatTemperatureSetpoint' in params) && typeof params.thermostatTemperatureSetpoint === 'number'; } - static get requiresItem() { + static requiresItem() { return true; } diff --git a/functions/commands/thermostattemperaturesetpointhigh.js b/functions/commands/thermostattemperaturesetpointhigh.js index 11c41108..71af26df 100644 --- a/functions/commands/thermostattemperaturesetpointhigh.js +++ b/functions/commands/thermostattemperaturesetpointhigh.js @@ -11,7 +11,7 @@ class ThermostatTemperatureSetpointHigh extends DefaultCommand { return ('thermostatTemperatureSetpointHigh' in params) && typeof params.thermostatTemperatureSetpointHigh === 'number'; } - static get requiresItem() { + static requiresItem() { return true; } diff --git a/functions/commands/thermostattemperaturesetpointlow.js b/functions/commands/thermostattemperaturesetpointlow.js index 85866cb4..d79202c1 100644 --- a/functions/commands/thermostattemperaturesetpointlow.js +++ b/functions/commands/thermostattemperaturesetpointlow.js @@ -11,7 +11,7 @@ class ThermostatTemperatureSetpointLow extends DefaultCommand { return ('thermostatTemperatureSetpointLow' in params) && typeof params.thermostatTemperatureSetpointLow === 'number'; } - static get requiresItem() { + static requiresItem() { return true; } diff --git a/functions/commands/volumerelative.js b/functions/commands/volumerelative.js index a4e8093e..7d904b18 100644 --- a/functions/commands/volumerelative.js +++ b/functions/commands/volumerelative.js @@ -10,7 +10,7 @@ class VolumeRelative extends DefaultCommand { return ('volumeRelativeLevel' in params) && typeof params.volumeRelativeLevel === 'number'; } - static get requiresItem() { + static requiresItem() { return true; } diff --git a/tests/openhab.metadata.test.js b/tests/openhab.metadata.test.js index 4f160ac1..7ea74579 100644 --- a/tests/openhab.metadata.test.js +++ b/tests/openhab.metadata.test.js @@ -1783,7 +1783,7 @@ describe('Test EXECUTE with Metadata', () => { const payload = await new OpenHAB(apiHandler).handleExecute(commands); - expect(getItemMock).toHaveBeenCalledTimes(1); + expect(getItemMock).toHaveBeenCalledTimes(0); expect(payload).toStrictEqual({ "commands": [{ "ids": [ diff --git a/tests/openhab.test.js b/tests/openhab.test.js index 44a029e9..02a18fb2 100644 --- a/tests/openhab.test.js +++ b/tests/openhab.test.js @@ -45,21 +45,8 @@ describe('Test EXECUTE', () => { }); test('setVolume Dimmer', async () => { - const item = - { - "state": "10", - "type": "Dimmer", - "name": "MySpeaker", - "metadata": { - "ga": { - "value": "Speaker" - } - } - }; - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - + getItemMock.mockReturnValue(Promise.resolve()); const sendCommandMock = jest.fn(); sendCommandMock.mockReturnValue(Promise.resolve()); @@ -85,7 +72,7 @@ describe('Test EXECUTE', () => { const payload = await new OpenHAB(apiHandler).handleExecute(commands); - expect(getItemMock).toHaveBeenCalledTimes(1); + expect(getItemMock).toHaveBeenCalledTimes(0); expect(sendCommandMock).toBeCalledWith('MySpeaker', '40'); expect(payload).toStrictEqual({ "commands": [{ @@ -516,7 +503,6 @@ describe('Test EXECUTE', () => { }); }); - test('ColorAbsolute Temperature SpecialColorLight', async () => { const item = { @@ -597,4 +583,81 @@ describe('Test EXECUTE', () => { }] }); }); + + test('OnOff SpecialColorLight', async () => { + const item = + { + "type": "Group", + "name": "MyColor", + "metadata": { + "ga": { + "value": "LIGHT", + "config": { + "colorTemperatureRange": "1000,4000" + } + } + }, + "members": [{ + "type": "Dimmer", + "name": "MyBrightness", + "metadata": { + "ga": { + "value": "lightBrightness" + } + }, + "state": "10" + }, { + "type": "Dimmer", + "name": "MyTemperature", + "metadata": { + "ga": { + "value": "lightColorTemperature" + } + }, + "state": "50" + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyColor", + "customData": { + "deviceType": "SpecialColorLight" + } + }], + "execution": [{ + "command": "action.devices.commands.OnOff", + "params": { + "on": false + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyBrightness', 'OFF'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyColor" + ], + "states": { + "online": true, + "on": false + }, + "status": "SUCCESS" + }] + }); + }); }); From 2f308dfda4534fce34487705958effa199036556 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 26 Sep 2020 03:11:04 +0200 Subject: [PATCH 30/94] SpecialColorLight extends Default instead of ColorLight Signed-off-by: Michael Krug --- functions/devices/specialcolorlight.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/functions/devices/specialcolorlight.js b/functions/devices/specialcolorlight.js index 4e4a06e8..41c50497 100644 --- a/functions/devices/specialcolorlight.js +++ b/functions/devices/specialcolorlight.js @@ -1,6 +1,18 @@ -const ColorLight = require('./colorlight.js'); +const DefaultDevice = require('./default.js'); + +class SpecialColorLight extends DefaultDevice { + static get type() { + return 'action.devices.types.LIGHT'; + } + + static get traits() { + return [ + 'action.devices.traits.OnOff', + 'action.devices.traits.Brightness', + 'action.devices.traits.ColorSetting' + ]; + } -class SpecialColorLight extends ColorLight { static matchesItemType(item) { return item.type === 'Group' && Object.keys(this.getMembers(item)).length > 1 && this.getAttributes(item).colorTemperatureRange; } From e31f8eea59d0c54a5451c3ca38f36b5d50c02f92 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 26 Sep 2020 03:11:23 +0200 Subject: [PATCH 31/94] Move color conversion functions to utilities Signed-off-by: Michael Krug --- .../commands/colorabsolutetemperature.js | 29 ++----------------- functions/utilities.js | 25 ++++++++++++++++ 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/functions/commands/colorabsolutetemperature.js b/functions/commands/colorabsolutetemperature.js index 103cbf74..167b2503 100644 --- a/functions/commands/colorabsolutetemperature.js +++ b/functions/commands/colorabsolutetemperature.js @@ -1,5 +1,7 @@ const DefaultCommand = require('./default.js'); const SpecialColorLight = require('../devices/specialcolorlight.js'); +const rgb2hsv = require('../utilities.js').rgb2hsv; +const kelvin2rgb = require('../utilities.js').kelvin2rgb; class ColorAbsoluteTemperature extends DefaultCommand { static get type() { @@ -35,7 +37,7 @@ class ColorAbsoluteTemperature extends DefaultCommand { return '0'; } } - const hsv = this.rgb2hsv(this.kelvin2rgb(params.color.temperature)); + const hsv = rgb2hsv(kelvin2rgb(params.color.temperature)); const hsvArray = item.state.split(",").map((val) => Number(val)); return [Math.round(hsv.hue * 100) / 100, Math.round(hsv.saturation * 1000) / 10, hsvArray[2]].join(','); } @@ -47,31 +49,6 @@ class ColorAbsoluteTemperature extends DefaultCommand { } }; } - - static kelvin2rgb(kelvin) { - const temp = kelvin / 100; - const r = temp <= 66 ? 255 : 329.698727446 * Math.pow(temp - 60, -0.1332047592); - const g = temp <= 66 ? 99.4708025861 * Math.log(temp) - 161.1195681661 : 288.1221695283 * Math.pow(temp - 60, -0.0755148492); - const b = temp <= 66 ? (temp <= 19 ? 0 : 138.5177312231 * Math.log(temp - 10) - 305.0447927307) : 255; - return { - r: r < 0 ? 0 : r > 255 ? 255 : Math.round(r), - g: g < 0 ? 0 : g > 255 ? 255 : Math.round(g), - b: b < 0 ? 0 : b > 255 ? 255 : Math.round(b), - }; - } - - static rgb2hsv({ r, g, b }) { - r = r / 255; - g = g / 255; - b = b / 255; - let v = Math.max(r, g, b), n = v - Math.min(r, g, b); - let h = n && ((v == r) ? (g - b) / n : ((v == g) ? 2 + (b - r) / n : 4 + (r - g) / n)); - return { - hue: 60 * (h < 0 ? h + 6 : h), - saturation: v && n / v, - value: v - }; - } } module.exports = ColorAbsoluteTemperature; diff --git a/functions/utilities.js b/functions/utilities.js index 970932b0..dc3034b9 100644 --- a/functions/utilities.js +++ b/functions/utilities.js @@ -26,6 +26,31 @@ module.exports = { convertToFahrenheit: (value = 0) => { return Math.round(value * 9 / 5 + 32); + }, + + kelvin2rgb: (kelvin) => { + const temp = kelvin / 100; + const r = temp <= 66 ? 255 : 329.698727446 * Math.pow(temp - 60, -0.1332047592); + const g = temp <= 66 ? 99.4708025861 * Math.log(temp) - 161.1195681661 : 288.1221695283 * Math.pow(temp - 60, -0.0755148492); + const b = temp <= 66 ? (temp <= 19 ? 0 : 138.5177312231 * Math.log(temp - 10) - 305.0447927307) : 255; + return { + r: r < 0 ? 0 : r > 255 ? 255 : Math.round(r), + g: g < 0 ? 0 : g > 255 ? 255 : Math.round(g), + b: b < 0 ? 0 : b > 255 ? 255 : Math.round(b), + }; + }, + + rgb2hsv: ({ r, g, b }) => { + r = r / 255; + g = g / 255; + b = b / 255; + let v = Math.max(r, g, b), n = v - Math.min(r, g, b); + let h = n && ((v == r) ? (g - b) / n : ((v == g) ? 2 + (b - r) / n : 4 + (r - g) / n)); + return { + hue: 60 * (h < 0 ? h + 6 : h), + saturation: v && n / v, + value: v + }; } } From f4aee7a9520dcd51befa1323f07ebd6bd3c7a873 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 26 Sep 2020 03:33:06 +0200 Subject: [PATCH 32/94] Support OnOff for TV Signed-off-by: Michael Krug --- functions/commands/medianext.js | 4 ++-- functions/commands/mediapause.js | 4 ++-- functions/commands/mediaprevious.js | 4 ++-- functions/commands/mediaresume.js | 4 ++-- functions/commands/onoff.js | 12 +++++++++++- functions/commands/selectchannel.js | 4 ++-- functions/commands/setinput.js | 4 ++-- functions/commands/setvolume.js | 4 ++-- functions/commands/volumerelative.js | 16 ++++++++-------- functions/devices/tv.js | 18 +++++++++++------- 10 files changed, 44 insertions(+), 30 deletions(-) diff --git a/functions/commands/medianext.js b/functions/commands/medianext.js index c79b5c46..8d66943b 100644 --- a/functions/commands/medianext.js +++ b/functions/commands/medianext.js @@ -12,8 +12,8 @@ class MediaNext extends DefaultCommand { static getItemName(item) { const members = TV.getMembers(item); - if ('transport' in members) { - return members.transport.name; + if ('tvTransport' in members) { + return members.tvTransport.name; } throw { statusCode: 400 }; } diff --git a/functions/commands/mediapause.js b/functions/commands/mediapause.js index 3c612df2..b97b5dce 100644 --- a/functions/commands/mediapause.js +++ b/functions/commands/mediapause.js @@ -12,8 +12,8 @@ class MediaPause extends DefaultCommand { static getItemName(item) { const members = TV.getMembers(item); - if ('transport' in members) { - return members.transport.name; + if ('tvTransport' in members) { + return members.tvTransport.name; } throw { statusCode: 400 }; } diff --git a/functions/commands/mediaprevious.js b/functions/commands/mediaprevious.js index 575d0b8f..edf54cbd 100644 --- a/functions/commands/mediaprevious.js +++ b/functions/commands/mediaprevious.js @@ -12,8 +12,8 @@ class MediaPrevious extends DefaultCommand { static getItemName(item) { const members = TV.getMembers(item); - if ('transport' in members) { - return members.transport.name; + if ('tvTransport' in members) { + return members.tvTransport.name; } throw { statusCode: 400 }; } diff --git a/functions/commands/mediaresume.js b/functions/commands/mediaresume.js index fa9647e2..74ad0a6b 100644 --- a/functions/commands/mediaresume.js +++ b/functions/commands/mediaresume.js @@ -12,8 +12,8 @@ class MediaResume extends DefaultCommand { static getItemName(item) { const members = TV.getMembers(item); - if ('transport' in members) { - return members.transport.name; + if ('tvTransport' in members) { + return members.tvTransport.name; } throw { statusCode: 400 }; } diff --git a/functions/commands/onoff.js b/functions/commands/onoff.js index a51027b4..44f03e31 100644 --- a/functions/commands/onoff.js +++ b/functions/commands/onoff.js @@ -1,5 +1,6 @@ const DefaultCommand = require('./default.js'); const SpecialColorLight = require('../devices/specialcolorlight.js'); +const TV = require('../devices/tv.js'); class OnOff extends DefaultCommand { static get type() { @@ -11,7 +12,9 @@ class OnOff extends DefaultCommand { } static requiresItem(device) { - return device.customData && device.customData.deviceType === 'SpecialColorLight'; + return device.customData && ( + device.customData.deviceType === 'SpecialColorLight' && device.customData.deviceType === 'TV' + ); } static getItemName(item, device) { @@ -22,6 +25,13 @@ class OnOff extends DefaultCommand { } throw { statusCode: 400 }; } + if (device.customData && device.customData.deviceType === 'TV') { + const members = TV.getMembers(item); + if ('tvPower' in members) { + return members.tvPower.name; + } + throw { statusCode: 400 }; + } return item.name; } diff --git a/functions/commands/selectchannel.js b/functions/commands/selectchannel.js index 6607e8a1..6d7adc23 100644 --- a/functions/commands/selectchannel.js +++ b/functions/commands/selectchannel.js @@ -18,8 +18,8 @@ class SelectChannel extends DefaultCommand { static getItemName(item) { const members = TV.getMembers(item); - if ('channel' in members) { - return members.channel.name; + if ('tvChannel' in members) { + return members.tvChannel.name; } throw { statusCode: 400 }; } diff --git a/functions/commands/setinput.js b/functions/commands/setinput.js index 80137258..c2677b86 100644 --- a/functions/commands/setinput.js +++ b/functions/commands/setinput.js @@ -16,8 +16,8 @@ class SetInput extends DefaultCommand { static getItemName(item) { const members = TV.getMembers(item); - if ('input' in members) { - return members.input.name; + if ('tvInput' in members) { + return members.tvInput.name; } throw { statusCode: 400 }; } diff --git a/functions/commands/setvolume.js b/functions/commands/setvolume.js index e954c239..6a6c6444 100644 --- a/functions/commands/setvolume.js +++ b/functions/commands/setvolume.js @@ -17,8 +17,8 @@ class SetVolume extends DefaultCommand { static getItemName(item, device) { if (device.customData && device.customData.deviceType === 'TV') { const members = TV.getMembers(item); - if ('volume' in members) { - return members.volume.name; + if ('tvVolume' in members) { + return members.tvVolume.name; } throw { statusCode: 400 }; } diff --git a/functions/commands/volumerelative.js b/functions/commands/volumerelative.js index 7d904b18..13d474ab 100644 --- a/functions/commands/volumerelative.js +++ b/functions/commands/volumerelative.js @@ -14,23 +14,23 @@ class VolumeRelative extends DefaultCommand { return true; } - static getItemName(item) { - if (item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() === 'tv') { + static getItemName(item, device) { + if (device.customData && device.customData.deviceType === 'TV') { const members = TV.getMembers(item); - if ('volume' in members) { - return members.volume.name; + if ('tvVolume' in members) { + return members.tvVolume.name; } throw { statusCode: 400 }; } return item.name; } - static convertParamsToValue(params, item) { + static convertParamsToValue(params, item, device) { let state = item.state; - if (item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() === 'tv') { + if (device.customData && device.customData.deviceType === 'TV') { const members = TV.getMembers(item); - if ('volume' in members) { - state = members.volume.state; + if ('tvVolume' in members) { + state = members.tvVolume.state; } else { throw { statusCode: 400 }; } diff --git a/functions/devices/tv.js b/functions/devices/tv.js index f2422198..d37d756c 100644 --- a/functions/devices/tv.js +++ b/functions/devices/tv.js @@ -60,14 +60,17 @@ class TV extends DefaultDevice { const members = this.getMembers(item); for (const member in members) { switch (member) { - case 'input': + case 'tvPower': + state.on = members[member].state === 'ON'; + break; + case 'tvInput': state.currentInput = members[member].state; break; - case 'volume': + case 'tvVolume': state.currentVolume = Number(members[member].state) || 0; state.isMuted = state.currentInput === 0; break; - case 'channel': + case 'tvChannel': state.channelNumber = members[member].state; try { state.channelName = this.getChannelMap(item)[members[member].state][0]; @@ -80,10 +83,11 @@ class TV extends DefaultDevice { static getMembers(item) { const supportedMembers = [ - 'channel', - 'volume', - 'input', - 'transport' + 'tvChannel', + 'tvVolume', + 'tvInput', + 'tvTransport', + 'tvPower' ]; const members = Object(); if (item.members && item.members.length) { From 4a70128bb6e33007c001d7c59e55e1af5f3ec870 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 26 Sep 2020 03:40:30 +0200 Subject: [PATCH 33/94] Fix typo Signed-off-by: Michael Krug --- functions/commands/onoff.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/commands/onoff.js b/functions/commands/onoff.js index 44f03e31..57383a6b 100644 --- a/functions/commands/onoff.js +++ b/functions/commands/onoff.js @@ -13,7 +13,7 @@ class OnOff extends DefaultCommand { static requiresItem(device) { return device.customData && ( - device.customData.deviceType === 'SpecialColorLight' && device.customData.deviceType === 'TV' + device.customData.deviceType === 'SpecialColorLight' || device.customData.deviceType === 'TV' ); } From 69470c602d5be2df54bee4e4b77fc556a296cff9 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 26 Sep 2020 20:55:02 +0200 Subject: [PATCH 34/94] Adjust volumeRelative parameter Signed-off-by: Michael Krug --- functions/commands/default.js | 4 ++-- functions/commands/setvolume.js | 3 +-- functions/commands/volumerelative.js | 11 +++++------ tests/openhab.metadata.test.js | 3 +-- tests/openhab.test.js | 10 +++------- 5 files changed, 12 insertions(+), 19 deletions(-) diff --git a/functions/commands/default.js b/functions/commands/default.js index fb3b3dbe..a2bde00a 100644 --- a/functions/commands/default.js +++ b/functions/commands/default.js @@ -24,7 +24,7 @@ class DefaultCommand { return null; } - static getResponseStates(params = {}, item = {}) { + static getResponseStates(params = {}, item = {}, device = {}) { return {}; } @@ -84,7 +84,7 @@ class DefaultCommand { } return getItemPromise.then((item) => { - const responseStates = this.getResponseStates(params, item); + const responseStates = this.getResponseStates(params, item, device); if (Object.keys(responseStates).length) { responseStates.online = true; } diff --git a/functions/commands/setvolume.js b/functions/commands/setvolume.js index 6a6c6444..9b9261d3 100644 --- a/functions/commands/setvolume.js +++ b/functions/commands/setvolume.js @@ -31,8 +31,7 @@ class SetVolume extends DefaultCommand { static getResponseStates(params) { return { - currentVolume: params.volumeLevel, - isMuted: params.volumeLevel === 0 + currentVolume: params.volumeLevel }; } } diff --git a/functions/commands/volumerelative.js b/functions/commands/volumerelative.js index 13d474ab..e5e38f21 100644 --- a/functions/commands/volumerelative.js +++ b/functions/commands/volumerelative.js @@ -7,7 +7,7 @@ class VolumeRelative extends DefaultCommand { } static validateParams(params) { - return ('volumeRelativeLevel' in params) && typeof params.volumeRelativeLevel === 'number'; + return ('relativeSteps' in params) && typeof params.relativeSteps === 'number'; } static requiresItem() { @@ -35,15 +35,14 @@ class VolumeRelative extends DefaultCommand { throw { statusCode: 400 }; } } - let level = parseInt(state) + params.volumeRelativeLevel; + let level = parseInt(state) + params.relativeSteps; return (level < 0 ? 0 : level > 100 ? 100 : level).toString(); } - static getResponseStates(params, item) { - const state = parseInt(this.convertParamsToValue(params, item)); + static getResponseStates(params, item, device) { + const state = parseInt(this.convertParamsToValue(params, item, device)); return { - currentVolume: state, - isMuted: state === 0 + currentVolume: state }; } } diff --git a/tests/openhab.metadata.test.js b/tests/openhab.metadata.test.js index 7ea74579..38d204d6 100644 --- a/tests/openhab.metadata.test.js +++ b/tests/openhab.metadata.test.js @@ -2348,7 +2348,7 @@ describe('Test EXECUTE with Metadata', () => { "execution": [{ "command": "action.devices.commands.volumeRelative", "params": { - "volumeRelativeLevel": 1 + "relativeSteps": 1 } }] }]; @@ -2364,7 +2364,6 @@ describe('Test EXECUTE with Metadata', () => { ], "states": { 'currentVolume': 41, - 'isMuted': false, 'online': true }, "status": "SUCCESS" diff --git a/tests/openhab.test.js b/tests/openhab.test.js index 02a18fb2..f07bbe33 100644 --- a/tests/openhab.test.js +++ b/tests/openhab.test.js @@ -81,7 +81,6 @@ describe('Test EXECUTE', () => { ], "states": { "currentVolume": 40, - "isMuted": false, "online": true }, "status": "SUCCESS" @@ -123,7 +122,7 @@ describe('Test EXECUTE', () => { "execution": [{ "command": "action.devices.commands.volumeRelative", "params": { - "volumeRelativeLevel": 20 + "relativeSteps": 20 } }] }]; @@ -139,7 +138,6 @@ describe('Test EXECUTE', () => { ], "states": { "currentVolume": 60, - "isMuted": false, "online": true }, "status": "SUCCESS" @@ -182,7 +180,7 @@ describe('Test EXECUTE', () => { "execution": [{ "command": "action.devices.commands.volumeRelative", "params": { - "volumeRelativeLevel": 20 + "relativeSteps": 20 } }] }]; @@ -198,7 +196,6 @@ describe('Test EXECUTE', () => { ], "states": { "currentVolume": 100, - "isMuted": false, "online": true }, "status": "SUCCESS" @@ -240,7 +237,7 @@ describe('Test EXECUTE', () => { "execution": [{ "command": "action.devices.commands.volumeRelative", "params": { - "volumeRelativeLevel": -20 + "relativeSteps": -20 } }] }]; @@ -256,7 +253,6 @@ describe('Test EXECUTE', () => { ], "states": { "currentVolume": 0, - "isMuted": true, "online": true }, "status": "SUCCESS" From 57158041d9985a7ae82c8e6991c2c219a8cc83ae Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 26 Sep 2020 20:55:43 +0200 Subject: [PATCH 35/94] Fix SpecialColorLight attributes Signed-off-by: Michael Krug --- functions/devices/specialcolorlight.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/functions/devices/specialcolorlight.js b/functions/devices/specialcolorlight.js index 41c50497..8f5870b4 100644 --- a/functions/devices/specialcolorlight.js +++ b/functions/devices/specialcolorlight.js @@ -18,8 +18,17 @@ class SpecialColorLight extends DefaultDevice { } static getAttributes(item) { - const attributes = super.getAttributes(item); - delete attributes.colorModel; + const attributes = {}; + const config = this.getConfig(item); + if ('colorTemperatureRange' in config) { + const [min, max] = config.colorTemperatureRange.split(',').map(s => Number(s.trim())); + if (!isNaN(min) && !isNaN(max)) { + attributes.colorTemperatureRange = { + temperatureMinK: min, + temperatureMaxK: max + }; + } + } return attributes; } From f08f7bc22ffbbc2f81deb6e7f55628b3eb6aeb56 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 26 Sep 2020 21:11:17 +0200 Subject: [PATCH 36/94] Add mute command Signed-off-by: Michael Krug --- functions/commands/mute.js | 54 ++++++++ functions/devices/tv.js | 7 +- tests/devices.metadata.test.js | 27 +++- tests/openhab.metadata.test.js | 241 ++++++++++++++++++++++++++++++--- tests/openhab.test.js | 44 ++++++ 5 files changed, 345 insertions(+), 28 deletions(-) create mode 100644 functions/commands/mute.js diff --git a/functions/commands/mute.js b/functions/commands/mute.js new file mode 100644 index 00000000..7d43bc32 --- /dev/null +++ b/functions/commands/mute.js @@ -0,0 +1,54 @@ +const DefaultCommand = require('./default.js'); +const TV = require('../devices/tv.js'); + +class Mute extends DefaultCommand { + static get type() { + return 'action.devices.commands.mute'; + } + + static validateParams(params) { + return ('mute' in params) && typeof params.mute === 'boolean'; + } + + static requiresItem(device) { + return device.customData && device.customData.deviceType === 'TV'; + } + + static getItemName(item, device) { + if (device.customData && device.customData.deviceType === 'TV') { + const members = TV.getMembers(item); + if ('tvMute' in members) { + return members.tvMute.name; + } + if ('tvVolume' in members) { + return members.tvVolume.name; + } + throw { statusCode: 400 }; + } + return item.name; + } + + static convertParamsToValue(params, item, device) { + let itemType = device.customData && device.customData.itemType; + if (device.customData && device.customData.deviceType === 'TV') { + const members = TV.getMembers(item); + if ('tvMute' in members) { + itemType = 'Switch'; + } else if ('tvVolume' in members) { + itemType = 'Dimmer'; + } + } + if (itemType !== 'Switch') { + return params.mute ? '0' : undefined; + } + return params.mute ? 'ON' : 'OFF'; + } + + static getResponseStates(params) { + return { + isMuted: params.mute + }; + } +} + +module.exports = Mute; diff --git a/functions/devices/tv.js b/functions/devices/tv.js index d37d756c..f0657c2c 100644 --- a/functions/devices/tv.js +++ b/functions/devices/tv.js @@ -63,12 +63,14 @@ class TV extends DefaultDevice { case 'tvPower': state.on = members[member].state === 'ON'; break; + case 'tvMute': + state.isMuted = members[member].state === 'ON'; + break; case 'tvInput': state.currentInput = members[member].state; break; case 'tvVolume': state.currentVolume = Number(members[member].state) || 0; - state.isMuted = state.currentInput === 0; break; case 'tvChannel': state.channelNumber = members[member].state; @@ -87,7 +89,8 @@ class TV extends DefaultDevice { 'tvVolume', 'tvInput', 'tvTransport', - 'tvPower' + 'tvPower', + 'tvMute' ]; const members = Object(); if (item.members && item.members.length) { diff --git a/tests/devices.metadata.test.js b/tests/devices.metadata.test.js index ba06db27..68253db3 100644 --- a/tests/devices.metadata.test.js +++ b/tests/devices.metadata.test.js @@ -1011,10 +1011,26 @@ describe('Test Thermostat Device with Metadata', () => { } }, members: [{ + type: 'Switch', + metadata: { + ga: { + value: 'tvPower' + } + }, + state: 'ON' + }, { + type: 'Switch', + metadata: { + ga: { + value: 'tvMute' + } + }, + state: 'OFF' + }, { type: 'String', metadata: { ga: { - value: 'input' + value: 'tvInput' } }, state: 'tv' @@ -1022,7 +1038,7 @@ describe('Test Thermostat Device with Metadata', () => { type: 'Number', metadata: { ga: { - value: 'channel' + value: 'tvChannel' } }, state: '20' @@ -1030,7 +1046,7 @@ describe('Test Thermostat Device with Metadata', () => { type: 'Number', metadata: { ga: { - value: 'volume' + value: 'tvVolume' } }, state: '50' @@ -1038,7 +1054,7 @@ describe('Test Thermostat Device with Metadata', () => { type: 'String', metadata: { ga: { - value: 'transport' + value: 'tvTransport' } }, state: 'PLAY' @@ -1048,7 +1064,8 @@ describe('Test Thermostat Device with Metadata', () => { 'channelNumber': '20', 'currentInput': 'tv', 'currentVolume': 50, - 'isMuted': false + 'isMuted': false, + 'on': true }); }); }); diff --git a/tests/openhab.metadata.test.js b/tests/openhab.metadata.test.js index 38d204d6..99e4d50c 100644 --- a/tests/openhab.metadata.test.js +++ b/tests/openhab.metadata.test.js @@ -2025,6 +2025,206 @@ describe('Test EXECUTE with Metadata', () => { }); }); + test('OnOff for TV', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV" + } + }, + "members": [{ + type: 'Switch', + name: 'MyPower', + metadata: { + ga: { + value: 'tvPower' + } + }, + state: 'ON' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV", + "customData": { + "deviceType": "TV" + } + }], + "execution": [{ + "command": "action.devices.commands.OnOff", + "params": { + "on": false + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyPower', 'OFF'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'on': false, + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); + + test('Mute for TV with Switch', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV" + } + }, + "members": [{ + type: 'Switch', + name: 'MyMute', + metadata: { + ga: { + value: 'tvMute' + } + }, + state: 'OFF' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV", + "customData": { + "deviceType": "TV" + } + }], + "execution": [{ + "command": "action.devices.commands.mute", + "params": { + "mute": true + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyMute', 'ON'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'isMuted': true, + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); + + test('Mute for TV without Switch', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV" + } + }, + "members": [{ + type: 'Dimmer', + name: 'MyVolume', + metadata: { + ga: { + value: 'tvVolume' + } + }, + state: '12' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV", + "customData": { + "deviceType": "TV" + } + }], + "execution": [{ + "command": "action.devices.commands.mute", + "params": { + "mute": true + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyVolume', '0'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'isMuted': true, + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); test('SelectChannel with Number for TV', async () => { const item = @@ -2046,7 +2246,7 @@ describe('Test EXECUTE with Metadata', () => { name: 'MyChannel', metadata: { ga: { - value: 'channel' + value: 'tvChannel' } }, state: '10' @@ -2065,15 +2265,15 @@ describe('Test EXECUTE with Metadata', () => { const commands = [{ "devices": [{ - "id": "MyTV" + "id": "MyTV", + "customData": { + "deviceType": "TV" + } }], "execution": [{ "command": "action.devices.commands.selectChannel", "params": { "channelNumber": "20" - }, - "customData": { - "deviceType": "TV" } }] }]; @@ -2116,7 +2316,7 @@ describe('Test EXECUTE with Metadata', () => { name: 'MyChannel', metadata: { ga: { - value: 'channel' + value: 'tvChannel' } }, state: '10' @@ -2135,15 +2335,15 @@ describe('Test EXECUTE with Metadata', () => { const commands = [{ "devices": [{ - "id": "MyTV" + "id": "MyTV", + "customData": { + "deviceType": "TV" + } }], "execution": [{ "command": "action.devices.commands.selectChannel", "params": { "channelName": "Channel 1" - }, - "customData": { - "deviceType": "TV" } }] }]; @@ -2186,7 +2386,7 @@ describe('Test EXECUTE with Metadata', () => { name: 'MyInput', metadata: { ga: { - value: 'input' + value: 'tvInput' } }, state: 'tv' @@ -2205,15 +2405,15 @@ describe('Test EXECUTE with Metadata', () => { const commands = [{ "devices": [{ - "id": "MyTV" + "id": "MyTV", + "customData": { + "deviceType": "TV" + } }], "execution": [{ "command": "action.devices.commands.SetInput", "params": { "newInput": "hdmi1" - }, - "customData": { - "deviceType": "TV" } }] }]; @@ -2253,7 +2453,7 @@ describe('Test EXECUTE with Metadata', () => { name: 'MyVolume', metadata: { ga: { - value: 'volume' + value: 'tvVolume' } }, state: '40' @@ -2295,9 +2495,8 @@ describe('Test EXECUTE with Metadata', () => { "MyTV" ], "states": { - 'currentVolume': 10, - 'isMuted': false, - 'online': true + "currentVolume": 10, + "online": true }, "status": "SUCCESS" }] @@ -2321,7 +2520,7 @@ describe('Test EXECUTE with Metadata', () => { name: 'MyVolume', metadata: { ga: { - value: 'volume' + value: 'tvVolume' } }, state: '40' @@ -2388,7 +2587,7 @@ describe('Test EXECUTE with Metadata', () => { name: 'MyTransport', metadata: { ga: { - value: 'transport' + value: 'tvTransport' } }, state: 'PLAY' diff --git a/tests/openhab.test.js b/tests/openhab.test.js index f07bbe33..3cb88dde 100644 --- a/tests/openhab.test.js +++ b/tests/openhab.test.js @@ -44,6 +44,50 @@ describe('Test EXECUTE', () => { }); }); + test('mute Dimmer', async () => { + const getItemMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + const sendCommandMock = jest.fn(); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "customData": { + "itemType": "Dimmer" + }, + "id": "MySpeaker" + }], + "execution": [{ + "command": "action.devices.commands.mute", + "params": { + "mute": true + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toBeCalledWith('MySpeaker', '0'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MySpeaker" + ], + "states": { + "isMuted": true, + "online": true + }, + "status": "SUCCESS" + }] + }); + }); + test('setVolume Dimmer', async () => { const getItemMock = jest.fn(); getItemMock.mockReturnValue(Promise.resolve()); From 149a0191cc126805f860118a30e000f25add5e28 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 26 Sep 2020 21:18:46 +0200 Subject: [PATCH 37/94] Add volumeCanMuteAndUnmute for TV Signed-off-by: Michael Krug --- functions/devices/tv.js | 4 +++- tests/devices.metadata.test.js | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/functions/devices/tv.js b/functions/devices/tv.js index f0657c2c..7aedc3e0 100644 --- a/functions/devices/tv.js +++ b/functions/devices/tv.js @@ -17,8 +17,10 @@ class TV extends DefaultDevice { static getAttributes(item) { const config = this.getConfig(item); + const members = this.getMembers(item); const attributes = { - transportControlSupportedCommands: ['NEXT', 'PREVIOUS', 'PAUSE', 'RESUME'] + transportControlSupportedCommands: ['NEXT', 'PREVIOUS', 'PAUSE', 'RESUME'], + volumeCanMuteAndUnmute: 'tvMute' in members }; if ('transportControlSupportedCommands' in config) { attributes.transportControlSupportedCommands = config.transportControlSupportedCommands.split(',').map(s => s.toUpperCase()); diff --git a/tests/devices.metadata.test.js b/tests/devices.metadata.test.js index 68253db3..1aa30ba8 100644 --- a/tests/devices.metadata.test.js +++ b/tests/devices.metadata.test.js @@ -963,6 +963,7 @@ describe('Test Thermostat Device with Metadata', () => { "PAUSE", "RESUME", ], + "volumeCanMuteAndUnmute": false }); }); From 93fe4de5d81977fa7a7a7a831400318c908b5539 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Wed, 30 Sep 2020 00:14:13 +0200 Subject: [PATCH 38/94] Rework test structure (WIP) Signed-off-by: Michael Krug --- functions/commands.js | 36 - functions/devices.js | 36 - functions/openhab.js | 40 +- ...data.test.js.snap => openhab.test.js.snap} | 121 +- tests/devices/colorlight.test.js | 73 + tests/devices/default.test.js | 73 + tests/{ => devices}/devices.metadata.test.js | 230 +- tests/{ => devices}/devices.tags.test.js | 36 +- tests/devices/dimmablelight.test.js | 38 + tests/devices/fan.test.js | 95 + tests/devices/openclosedevice.test.js | 66 + tests/devices/simplelight.test.js | 20 + tests/devices/switch.test.js | 52 + tests/openhab.metadata.test.js | 2634 ----------------- tests/openhab.test.js | 2394 +++++++++++++++ 15 files changed, 2911 insertions(+), 3033 deletions(-) delete mode 100644 functions/commands.js delete mode 100644 functions/devices.js rename tests/__snapshots__/{openhab.metadata.test.js.snap => openhab.test.js.snap} (85%) create mode 100644 tests/devices/colorlight.test.js create mode 100644 tests/devices/default.test.js rename tests/{ => devices}/devices.metadata.test.js (78%) rename tests/{ => devices}/devices.tags.test.js (87%) create mode 100644 tests/devices/dimmablelight.test.js create mode 100644 tests/devices/fan.test.js create mode 100644 tests/devices/openclosedevice.test.js create mode 100644 tests/devices/simplelight.test.js create mode 100644 tests/devices/switch.test.js delete mode 100644 tests/openhab.metadata.test.js diff --git a/functions/commands.js b/functions/commands.js deleted file mode 100644 index 3ad32e7f..00000000 --- a/functions/commands.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ - -/** - * Command definitions for translating Google Actions Commands to openHAB Item Updates - * - * @author Michael Krug - * - */ - -const glob = require('glob'); - -const Commands = []; - -glob.sync('./commands/*.js', { cwd: __dirname }).forEach(file => { - const command = require(file); - if (command.type) { - Commands.push(command); - } -}); - -module.exports = { - getCommandType: (command = '', params = {}) => { - return Commands.find((commandType) => command === commandType.type && commandType.validateParams(params)); - } -}; diff --git a/functions/devices.js b/functions/devices.js deleted file mode 100644 index 052383a8..00000000 --- a/functions/devices.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ - -/** - * Device definitions for translating between Google Action Devices to openHAB Items - * - * @author Michael Krug - * - */ - -const glob = require('glob'); - -const Devices = []; - -glob.sync('./devices/*.js', { cwd: __dirname }).forEach(file => { - const device = require(file); - if (device.type) { - Devices.push(device); - } -}); - -module.exports = { - getDeviceForItem: (item = {}) => { - return Devices.find((device) => device.matchesItemType(item) && device.isCompatible(item)); - } -}; diff --git a/functions/openhab.js b/functions/openhab.js index 413696e5..7bd11d9d 100644 --- a/functions/openhab.js +++ b/functions/openhab.js @@ -18,24 +18,48 @@ * @author Michael Krug - Rework * */ -const getCommandType = require('./commands.js').getCommandType; -const getDeviceForItem = require('./devices.js').getDeviceForItem; +const glob = require('glob'); + +const Commands = []; +const Devices = []; + +glob.sync('./commands/*.js', { cwd: __dirname }).forEach(file => { + const command = require(file); + if (command.type) { + Commands.push(command); + } +}); + +glob.sync('./devices/*.js', { cwd: __dirname }).forEach(file => { + const device = require(file); + if (device.type) { + Devices.push(device); + } +}); class OpenHAB { /** * @param {object} apiHandler */ - constructor(apiHandler) { + constructor(apiHandler = {}) { this._apiHandler = apiHandler; } + static getCommandType(command = '', params = {}) { + return Commands.find((commandType) => command === commandType.type && commandType.validateParams(params)); + } + + static getDeviceForItem(item = {}) { + return Devices.find((device) => device.matchesItemType(item) && device.isCompatible(item)); + } + handleSync() { console.log('openhabGoogleAssistant - handleSync'); return this._apiHandler.getItems().then((items) => { let discoveredDevicesList = []; items.forEach((item) => { item.members = items.filter((member) => member.groupNames && member.groupNames.includes(item.name)); - const DeviceType = getDeviceForItem(item); + const DeviceType = OpenHAB.getDeviceForItem(item); if (DeviceType) { console.log(`openhabGoogleAssistant - handleSync - SYNC is adding: ${item.type}:${item.name} with type: ${DeviceType.type}`); discoveredDevicesList.push(DeviceType.getMetadata(item)); @@ -55,7 +79,7 @@ class OpenHAB { }; const promises = devices.map((device) => { return this._apiHandler.getItem(device.id).then((item) => { - const DeviceType = getDeviceForItem(item); + const DeviceType = OpenHAB.getDeviceForItem(item); if (!DeviceType) { throw { statusCode: 404 }; } @@ -87,8 +111,8 @@ class OpenHAB { command.execution.forEach((execution) => { // Special handling of ThermostatTemperatureSetRange that requires updating two values if (execution.command === 'action.devices.commands.ThermostatTemperatureSetRange') { - const SetHigh = getCommandType('action.devices.commands.ThermostatTemperatureSetpointHigh', execution.params); - const SetLow = getCommandType('action.devices.commands.ThermostatTemperatureSetpointLow', execution.params); + const SetHigh = OpenHAB.getCommandType('action.devices.commands.ThermostatTemperatureSetpointHigh', execution.params); + const SetLow = OpenHAB.getCommandType('action.devices.commands.ThermostatTemperatureSetpointLow', execution.params); if (SetHigh && SetLow) { promises.push(SetHigh.execute(this._apiHandler, command.devices, execution.params, execution.challenge).then(() => { return SetLow.execute(this._apiHandler, command.devices, execution.params, execution.challenge); @@ -96,7 +120,7 @@ class OpenHAB { return; } } - const CommandType = getCommandType(execution.command, execution.params); + const CommandType = OpenHAB.getCommandType(execution.command, execution.params); if (!CommandType) { promises.push(Promise.resolve({ ids: command.devices.map((device) => device.id), diff --git a/tests/__snapshots__/openhab.metadata.test.js.snap b/tests/__snapshots__/openhab.test.js.snap similarity index 85% rename from tests/__snapshots__/openhab.metadata.test.js.snap rename to tests/__snapshots__/openhab.test.js.snap index 936043fe..756d86e7 100644 --- a/tests/__snapshots__/openhab.metadata.test.js.snap +++ b/tests/__snapshots__/openhab.test.js.snap @@ -1,86 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Test SYNC with Metadata Fan Device 1`] = ` -Object { - "devices": Array [ - Object { - "attributes": Object { - "availableFanSpeeds": Object { - "ordered": true, - "speeds": Array [ - Object { - "speed_name": "0", - "speed_values": Array [ - Object { - "lang": "en", - "speed_synonym": Array [ - "null", - "off", - ], - }, - ], - }, - Object { - "speed_name": "50", - "speed_values": Array [ - Object { - "lang": "en", - "speed_synonym": Array [ - "slow", - ], - }, - ], - }, - Object { - "speed_name": "100", - "speed_values": Array [ - Object { - "lang": "en", - "speed_synonym": Array [ - "full", - "fast", - ], - }, - ], - }, - ], - }, - "reversible": false, - }, - "customData": Object { - "deviceType": "Fan", - "itemType": "Dimmer", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Dimmer:MyFan", - "swVersion": "2.5.0", - }, - "id": "MyFan", - "name": Object { - "defaultNames": Array [ - "My Fan", - ], - "name": "My Fan", - "nicknames": Array [ - "My Fan", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.OnOff", - "action.devices.traits.FanSpeed", - ], - "type": "action.devices.types.FAN", - "willReportState": false, - }, - ], -} -`; - -exports[`Test SYNC with Metadata Light Devices 1`] = ` +exports[`Test SYNC Light Devices 1`] = ` Object { "devices": Array [ Object { @@ -275,11 +195,42 @@ Object { "type": "action.devices.types.LIGHT", "willReportState": false, }, + Object { + "attributes": Object {}, + "customData": Object { + "deviceType": "Fan", + "itemType": "Dimmer", + }, + "deviceInfo": Object { + "hwVersion": "2.5.0", + "manufacturer": "openHAB", + "model": "Dimmer:MyFan", + "swVersion": "2.5.0", + }, + "id": "MyFan", + "name": Object { + "defaultNames": Array [ + "Fan", + ], + "name": "Fan", + "nicknames": Array [ + "Fan", + ], + }, + "roomHint": undefined, + "structureHint": undefined, + "traits": Array [ + "action.devices.traits.OnOff", + "action.devices.traits.FanSpeed", + ], + "type": "action.devices.types.FAN", + "willReportState": false, + }, ], } `; -exports[`Test SYNC with Metadata Scene Device 1`] = ` +exports[`Test SYNC Scene Device 1`] = ` Object { "devices": Array [ Object { @@ -318,7 +269,7 @@ Object { } `; -exports[`Test SYNC with Metadata Sensor Device 1`] = ` +exports[`Test SYNC Sensor Device 1`] = ` Object { "devices": Array [ Object { @@ -367,7 +318,7 @@ Object { } `; -exports[`Test SYNC with Metadata Temperature Sensor Device 1`] = ` +exports[`Test SYNC Temperature Sensor Device 1`] = ` Object { "devices": Array [ Object { @@ -407,7 +358,7 @@ Object { } `; -exports[`Test SYNC with Metadata Thermostat Device 1`] = ` +exports[`Test SYNC Thermostat Device 1`] = ` Object { "devices": Array [ Object { diff --git a/tests/devices/colorlight.test.js b/tests/devices/colorlight.test.js new file mode 100644 index 00000000..fcffe0c1 --- /dev/null +++ b/tests/devices/colorlight.test.js @@ -0,0 +1,73 @@ +const Device = require('../../functions/devices/colorlight.js'); + +describe('Test ColorLight Device', () => { + test('Test isCompatible', async () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "LIGHT" + } + } + })).toBe(true); + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "SOMETHING" + } + } + })).toBe(false); + }); + + test('Test matchesItemType', async () => { + expect(Device.matchesItemType({ "type": "Color" })).toBe(true); + expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Color" })).toBe(true); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(false); + }); + + test('Test getAttributes', async () => { + const item1 = { + "metadata": { + "ga": { + "config": { + "colorTemperatureRange": "a,b" + } + } + } + }; + expect(Device.getAttributes(item1)).toStrictEqual({ + "colorModel": "hsv" + }); + + const item2 = { + "metadata": { + "ga": { + "config": { + "colorTemperatureRange": "1000,2000" + } + } + } + }; + expect(Device.getAttributes(item2)).toStrictEqual({ + "colorModel": "hsv", + "colorTemperatureRange": { + "temperatureMinK": 1000, + "temperatureMaxK": 2000 + } + }); + }); + + test('Test getState', async () => { + expect(Device.getState({ "state": "100,50,10" })).toStrictEqual({ + "on": true, + "brightness": 10, + "color": { + "spectrumHSV": { + "hue": 100, + "saturation": 0.5, + "value": 0.1 + } + } + }); + }); +}); diff --git a/tests/devices/default.test.js b/tests/devices/default.test.js new file mode 100644 index 00000000..49910594 --- /dev/null +++ b/tests/devices/default.test.js @@ -0,0 +1,73 @@ +const Device = require('../../functions/devices/default.js'); + +describe('Test Fan Device', () => { + const item = { + "type": "Number", + "state": "50", + "name": "DefaultDevice", + "label": "Default Device", + "metadata": { + "ga": { + "value": "", + "config": { + "inverted": true, + "ackNeeded": true, + "pinNeeded": "1234" + } + } + } + }; + + test('Test getConfig', async () => { + expect(Device.getConfig(item)).toStrictEqual({ + "ackNeeded": true, + "inverted": true, + "pinNeeded": "1234" + }); + }); + + test('Test getState', async () => { + expect(Device.getState(item)).toStrictEqual({}); + }); + + test('Test getMetadata', async () => { + expect(Device.getMetadata(item)).toStrictEqual({ + "attributes": {}, + "customData": { + "ackNeeded": true, + "deviceType": "DefaultDevice", + "inverted": true, + "itemType": "Number", + "pinNeeded": "1234" + }, + "deviceInfo": { + "hwVersion": "2.5.0", + "manufacturer": "openHAB", + "model": "Number:DefaultDevice", + "swVersion": "2.5.0", + }, + "id": "DefaultDevice", + "name": { + "defaultNames": [ + "Default Device", + ], + "name": "Default Device", + "nicknames": [ + "Default Device", + ], + }, + "roomHint": undefined, + "structureHint": undefined, + "traits": [], + "type": "", + "willReportState": false + }); + }); + + test('Test hasTag', async () => { + expect(Device.hasTag({}, "testtag")).toBe(false); + expect(Device.hasTag({ "tags": ["test"] }, "testtag")).toBe(false); + expect(Device.hasTag({ "tags": ["testtag"] }, "testtag")).toBe(true); + expect(Device.hasTag({ "tags": ["TestTag"] }, "testtag")).toBe(true); + }); +}); diff --git a/tests/devices.metadata.test.js b/tests/devices/devices.metadata.test.js similarity index 78% rename from tests/devices.metadata.test.js rename to tests/devices/devices.metadata.test.js index 1aa30ba8..e9b6e359 100644 --- a/tests/devices.metadata.test.js +++ b/tests/devices/devices.metadata.test.js @@ -1,45 +1,8 @@ -const Devices = require('../functions/devices.js'); -const Thermostat = require('../functions/devices/thermostat.js'); -const TV = require('../functions/devices/tv.js'); +const OpenHAB = require('../../functions/openhab.js'); +const Thermostat = require('../../functions/devices/thermostat.js'); +const TV = require('../../functions/devices/tv.js'); describe('Test Switch Devices with Metadata', () => { - test('Switch Type', () => { - const item = { - type: 'Switch', - state: 'ON', - metadata: { - ga: { - value: 'Switch' - } - } - }; - const device = Devices.getDeviceForItem(item); - expect(device.name).toBe('Switch'); - expect(device.getState(item)).toStrictEqual({ - on: true - }); - }); - - test('Inverted Switch Type', () => { - const item = { - type: 'Switch', - state: 'ON', - metadata: { - ga: { - value: 'Switch', - config: { - inverted: true - } - } - } - }; - const device = Devices.getDeviceForItem(item); - expect(device.name).toBe('Switch'); - expect(device.getState(item)).toStrictEqual({ - on: false - }); - }); - test('Valve Switch Type', () => { const item = { type: 'Switch', @@ -50,7 +13,7 @@ describe('Test Switch Devices with Metadata', () => { } } }; - const device = Devices.getDeviceForItem(item); + const device = OpenHAB.getDeviceForItem(item); expect(device.name).toBe('Valve'); expect(device.getState(item)).toStrictEqual({ openPercent: 100 @@ -67,7 +30,7 @@ describe('Test Switch Devices with Metadata', () => { } } }; - const device = Devices.getDeviceForItem(item); + const device = OpenHAB.getDeviceForItem(item); expect(device.name).toBe('Sprinkler'); expect(device.getState(item)).toStrictEqual({ isRunning: true, @@ -88,7 +51,7 @@ describe('Test Switch Devices with Metadata', () => { } } }; - const device = Devices.getDeviceForItem(item); + const device = OpenHAB.getDeviceForItem(item); expect(device.name).toBe('Lock'); expect(device.getMetadata(item).customData.ackNeeded).toBe(true); expect(device.getMetadata(item).customData.pinNeeded).toBeUndefined(); @@ -110,7 +73,7 @@ describe('Test Switch Devices with Metadata', () => { } } }; - const device = Devices.getDeviceForItem(item); + const device = OpenHAB.getDeviceForItem(item); expect(device.name).toBe('SecuritySystem'); expect(device.getMetadata(item).customData.ackNeeded).toBeUndefined(); expect(device.getMetadata(item).customData.pinNeeded).toBe('1234'); @@ -121,125 +84,6 @@ describe('Test Switch Devices with Metadata', () => { }); describe('Test Light Devices with Metadata', () => { - test('Switch Light Type', () => { - const item = { - type: 'Switch', - state: 'ON', - metadata: { - ga: { - value: 'LIGHT' - } - } - }; - const device = Devices.getDeviceForItem(item); - expect(device.name).toBe('SimpleLight'); - expect(device.getAttributes(item)).toStrictEqual({}); - expect(device.getState(item)).toStrictEqual({ - on: true - }); - }); - - test('Dimmer Light Type', () => { - const item = { - type: 'Dimmer', - state: '40', - metadata: { - ga: { - value: 'LIGHT' - } - } - }; - const device = Devices.getDeviceForItem(item); - expect(device.name).toBe('DimmableLight'); - expect(device.getAttributes(item)).toStrictEqual({}); - expect(device.getState(item)).toStrictEqual({ - brightness: 40, - on: true - }); - }); - - test('Color Light Type', () => { - const item = { - type: 'Color', - state: '100,50,20', - metadata: { - ga: { - value: 'LIGHT' - } - } - }; - const device = Devices.getDeviceForItem(item); - expect(device.name).toBe('ColorLight'); - expect(device.getAttributes(item)).toStrictEqual({ - colorModel: 'hsv' - }); - expect(device.getState(item)).toStrictEqual({ - brightness: 20, - color: { - spectrumHSV: { - hue: 100, - saturation: 0.5, - value: 0.2, - }, - }, - on: true - }); - }); - - test('Color Light Type colorTemperatureRange', () => { - const item = { - type: 'Color', - metadata: { - ga: { - value: 'LIGHT', - config: { - colorTemperatureRange: '1000,4000' - } - } - }, - state: '100,50,20' - }; - const device = Devices.getDeviceForItem(item); - expect(device.name).toBe('ColorLight'); - expect(device.getAttributes(item)).toStrictEqual({ - colorModel: 'hsv', - colorTemperatureRange: { - temperatureMinK: 1000, - temperatureMaxK: 4000 - } - }); - expect(device.getState(item)).toStrictEqual({ - brightness: 20, - color: { - spectrumHSV: { - hue: 100, - saturation: 0.5, - value: 0.2, - } - }, - on: true - }); - }); - - test('Color Light Type colorTemperatureRange with invalid value', () => { - const item = { - type: 'Color', - metadata: { - ga: { - value: 'LIGHT', - config: { - colorTemperatureRange: 'a,b' - } - } - } - }; - const device = Devices.getDeviceForItem(item); - expect(device.name).toBe('ColorLight'); - expect(device.getAttributes(item)).toStrictEqual({ - colorModel: 'hsv' - }); - }); - test('Special Color Light Type', () => { const item = { type: 'Group', @@ -271,7 +115,7 @@ describe('Test Light Devices with Metadata', () => { state: '50' }] }; - const device = Devices.getDeviceForItem(item); + const device = OpenHAB.getDeviceForItem(item); expect(device.name).toBe('SpecialColorLight'); expect(device.getAttributes(item)).toStrictEqual({ colorTemperatureRange: { @@ -291,7 +135,7 @@ describe('Test Light Devices with Metadata', () => { describe('Test OpenClose Devices', () => { test('Invalid Blinds Type', () => { - expect(Devices.getDeviceForItem({ + expect(OpenHAB.getDeviceForItem({ type: 'Dimmer', metadata: { ga: { @@ -301,52 +145,6 @@ describe('Test OpenClose Devices', () => { })).toBe(undefined); }); - test('Blinds Rollershutter Type', () => { - const item = { - type: 'Rollershutter', - state: '0', - metadata: { - ga: { - value: 'BLINDS' - } - } - }; - const device = Devices.getDeviceForItem(item); - expect(device.name).toBe('Blinds'); - expect(device.getAttributes(item)).toStrictEqual({ - discreteOnlyOpenClose: false, - queryOnlyOpenClose: false - }); - expect(device.getState(item)).toStrictEqual({ - openPercent: 100 - }); - }); - - test('Blinds Rollershutter Type Inverted', () => { - const item = { - type: 'Rollershutter', - state: '0', - metadata: { - ga: { - value: 'BLINDS', - config: { - inverted: true, - discreteOnlyOpenClose: true - } - } - } - }; - const device = Devices.getDeviceForItem(item); - expect(device.name).toBe('Blinds'); - expect(device.getAttributes(item)).toStrictEqual({ - discreteOnlyOpenClose: true, - queryOnlyOpenClose: false - }); - expect(device.getState(item)).toStrictEqual({ - openPercent: 0 - }); - }); - test('Window as Contact', () => { const item = { type: 'Contact', @@ -357,7 +155,7 @@ describe('Test OpenClose Devices', () => { } } }; - const device = Devices.getDeviceForItem(item); + const device = OpenHAB.getDeviceForItem(item); expect(device.name).toBe('Window'); expect(device.getAttributes(item)).toStrictEqual({ discreteOnlyOpenClose: true, @@ -384,7 +182,7 @@ describe('Test Sensor Device with Metadata', () => { }, state: '100' }; - const device = Devices.getDeviceForItem(item); + const device = OpenHAB.getDeviceForItem(item); test('getAttributes', () => { expect(device.getAttributes({ @@ -459,7 +257,7 @@ describe('Test Sensor Device with Metadata', () => { describe('Test Thermostat Device with Metadata', () => { test('getDeviceForItem', () => { - expect(Devices.getDeviceForItem({ + expect(OpenHAB.getDeviceForItem({ type: 'Group', metadata: { ga: { @@ -468,7 +266,7 @@ describe('Test Thermostat Device with Metadata', () => { } }).name).toBe('Thermostat'); - expect(Devices.getDeviceForItem({ + expect(OpenHAB.getDeviceForItem({ type: 'Switch', metadata: { ga: { @@ -477,7 +275,7 @@ describe('Test Thermostat Device with Metadata', () => { } })).toBe(undefined); - expect(Devices.getDeviceForItem({ + expect(OpenHAB.getDeviceForItem({ type: 'Group', metadata: { ga: { diff --git a/tests/devices.tags.test.js b/tests/devices/devices.tags.test.js similarity index 87% rename from tests/devices.tags.test.js rename to tests/devices/devices.tags.test.js index decbb9a4..e879599f 100644 --- a/tests/devices.tags.test.js +++ b/tests/devices/devices.tags.test.js @@ -1,5 +1,5 @@ -const Devices = require('../functions/devices.js'); -const Thermostat = require('../functions/devices/thermostat.js'); +const OpenHAB = require('../../functions/openhab.js'); +const Thermostat = require('../../functions/devices/thermostat.js'); describe('Test Switch Devices with Tags', () => { test('Switch Type', () => { @@ -10,7 +10,7 @@ describe('Test Switch Devices with Tags', () => { 'Switchable' ] }; - const device = Devices.getDeviceForItem(item); + const device = OpenHAB.getDeviceForItem(item); expect(device.name).toBe('Switch'); expect(device.getState(item)).toStrictEqual({ on: true @@ -25,7 +25,7 @@ describe('Test Switch Devices with Tags', () => { 'Valve' ] }; - const device = Devices.getDeviceForItem(item); + const device = OpenHAB.getDeviceForItem(item); expect(device.name).toBe('Valve'); expect(device.getState(item)).toStrictEqual({ openPercent: 100 @@ -40,7 +40,7 @@ describe('Test Switch Devices with Tags', () => { 'Sprinkler' ] }; - const device = Devices.getDeviceForItem(item); + const device = OpenHAB.getDeviceForItem(item); expect(device.name).toBe('Sprinkler'); expect(device.getState(item)).toStrictEqual({ isRunning: true, @@ -56,7 +56,7 @@ describe('Test Switch Devices with Tags', () => { 'Lock' ] }; - const device = Devices.getDeviceForItem(item); + const device = OpenHAB.getDeviceForItem(item); expect(device.name).toBe('Lock'); expect(device.getState(item)).toStrictEqual({ isLocked: true @@ -71,7 +71,7 @@ describe('Test Switch Devices with Tags', () => { 'SecuritySystem' ] }; - const device = Devices.getDeviceForItem(item); + const device = OpenHAB.getDeviceForItem(item); expect(device.name).toBe('SecuritySystem'); expect(device.getState(item)).toStrictEqual({ isArmed: true @@ -81,7 +81,7 @@ describe('Test Switch Devices with Tags', () => { describe('Test Light Devices with Tags', () => { test('Switch Light Type', () => { - expect(Devices.getDeviceForItem({ + expect(OpenHAB.getDeviceForItem({ type: 'Switch', tags: [ 'Lighting' @@ -90,7 +90,7 @@ describe('Test Light Devices with Tags', () => { }); test('Switch Group Light Type', () => { - expect(Devices.getDeviceForItem({ + expect(OpenHAB.getDeviceForItem({ type: 'Group', groupType: 'Switch', tags: [ @@ -100,7 +100,7 @@ describe('Test Light Devices with Tags', () => { }); test('Dimmer Light Type', () => { - expect(Devices.getDeviceForItem({ + expect(OpenHAB.getDeviceForItem({ type: 'Dimmer', tags: [ 'Lighting' @@ -109,7 +109,7 @@ describe('Test Light Devices with Tags', () => { }); test('Dimmer Group Light Type', () => { - expect(Devices.getDeviceForItem({ + expect(OpenHAB.getDeviceForItem({ type: 'Group', groupType: 'Dimmer', tags: [ @@ -119,7 +119,7 @@ describe('Test Light Devices with Tags', () => { }); test('Color Light Type', () => { - expect(Devices.getDeviceForItem({ + expect(OpenHAB.getDeviceForItem({ type: 'Color', tags: [ 'Lighting' @@ -128,7 +128,7 @@ describe('Test Light Devices with Tags', () => { }); test('Color Group Light Type', () => { - expect(Devices.getDeviceForItem({ + expect(OpenHAB.getDeviceForItem({ type: 'Group', groupType: 'Color', tags: [ @@ -140,7 +140,7 @@ describe('Test Light Devices with Tags', () => { describe('Test Rollershutter Devices with Tags', () => { test('Invalid Blinds Type', () => { - expect(Devices.getDeviceForItem({ + expect(OpenHAB.getDeviceForItem({ type: 'Dimmer', tags: [ 'Blinds' @@ -156,7 +156,7 @@ describe('Test Rollershutter Devices with Tags', () => { 'Blinds' ] }; - const device = Devices.getDeviceForItem(item); + const device = OpenHAB.getDeviceForItem(item); expect(device.name).toBe('Blinds'); expect(device.getState(item)).toStrictEqual({ openPercent: 100 @@ -166,21 +166,21 @@ describe('Test Rollershutter Devices with Tags', () => { describe('Test Thermostat Device with Tags', () => { test('getDeviceForItem', () => { - expect(Devices.getDeviceForItem({ + expect(OpenHAB.getDeviceForItem({ type: 'Group', tags: [ 'Thermostat' ] }).name).toBe('Thermostat'); - expect(Devices.getDeviceForItem({ + expect(OpenHAB.getDeviceForItem({ type: 'Switch', tags: [ 'Thermostat' ] })).toBe(undefined); - expect(Devices.getDeviceForItem({ + expect(OpenHAB.getDeviceForItem({ type: 'Group', tags: [ 'Something' diff --git a/tests/devices/dimmablelight.test.js b/tests/devices/dimmablelight.test.js new file mode 100644 index 00000000..a3842656 --- /dev/null +++ b/tests/devices/dimmablelight.test.js @@ -0,0 +1,38 @@ +const Device = require('../../functions/devices/dimmablelight.js'); + +describe('Test DimmableLight Device', () => { + test('Test isCompatible', async () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "LIGHT" + } + } + })).toBe(true); + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "SOMETHING" + } + } + })).toBe(false); + }); + + test('Test matchesItemType', async () => { + expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(true); + expect(Device.matchesItemType({ "type": "String" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(true); + expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); + }); + + test('Test getState', async () => { + expect(Device.getState({ "state": "50" })).toStrictEqual({ + "on": true, + "brightness": 50 + }); + expect(Device.getState({ "state": "NULL" })).toStrictEqual({ + "on": false, + "brightness": 0 + }); + }); +}); diff --git a/tests/devices/fan.test.js b/tests/devices/fan.test.js new file mode 100644 index 00000000..2d683f3d --- /dev/null +++ b/tests/devices/fan.test.js @@ -0,0 +1,95 @@ +const Device = require('../../functions/devices/fan.js'); + +describe('Test Fan Device', () => { + test('Test isCompatible', async () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "FAN" + } + } + })).toBe(true); + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "SOMETHING" + } + } + })).toBe(false); + }); + + test('Test matchesItemType', async () => { + expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(true); + expect(Device.matchesItemType({ "type": "String" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(true); + expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); + }); + + test('Test getAttributes', async () => { + const item1 = { + "metadata": { + "ga": { + "config": {} + } + } + }; + expect(Device.getAttributes(item1)).toStrictEqual({}); + + const item2 = { + "metadata": { + "ga": { + "config": { + "ordered": true, + "speeds": "0=null:off,50=slow,100=full:fast", + "lang": "en" + } + } + } + }; + expect(Device.getAttributes(item2)).toStrictEqual({ + "availableFanSpeeds": { + "speeds": [ + { + "speed_name": "0", + "speed_values": [ + { + "speed_synonym": ["null", "off"], + "lang": "en" + } + ] + }, + { + "speed_name": "50", + "speed_values": [ + { + "speed_synonym": ["slow"], + "lang": "en" + } + ] + }, + { + "speed_name": "100", + "speed_values": [ + { + "speed_synonym": ["full", "fast"], + "lang": "en" + } + ] + } + ], + "ordered": true + }, + "reversible": false + }); + }); + + test('Test getState', async () => { + const item = { + "state": "50" + }; + expect(Device.getState(item)).toStrictEqual({ + "currentFanSpeedSetting": "50", + "on": true + }); + }); +}); diff --git a/tests/devices/openclosedevice.test.js b/tests/devices/openclosedevice.test.js new file mode 100644 index 00000000..312e804b --- /dev/null +++ b/tests/devices/openclosedevice.test.js @@ -0,0 +1,66 @@ +const Device = require('../../functions/devices/openclosedevice.js'); + +describe('Test OpenCloseDevice Device', () => { + test('Test getAttributes', async () => { + expect(Device.getAttributes({ "type": "Rollershutter" })).toStrictEqual({ + "discreteOnlyOpenClose": false, + "queryOnlyOpenClose": false + }); + expect(Device.getAttributes({ "type": "Switch", })).toStrictEqual({ + "discreteOnlyOpenClose": true, + "queryOnlyOpenClose": false + }); + expect(Device.getAttributes({ "type": "Contact" })).toStrictEqual({ + "discreteOnlyOpenClose": true, + "queryOnlyOpenClose": true + }); + }); + + test('Test getState', async () => { + const item1 = { + "type": "Switch", + "state": "ON" + }; + expect(Device.getState(item1)).toStrictEqual({ + "openPercent": 100 + }); + const item2 = { + "type": "Rollershutter", + "state": "25" + }; + expect(Device.getState(item2)).toStrictEqual({ + "openPercent": 75 + }); + }); + + test('Test getState inverted', async () => { + const item = { + "type": "Switch", + "state": "ON", + "metadata": { + "ga": { + "config": { + "inverted": true + } + } + } + }; + expect(Device.getState(item)).toStrictEqual({ + "openPercent": 0 + }); + const item2 = { + "type": "Rollershutter", + "state": "25", + "metadata": { + "ga": { + "config": { + "inverted": true + } + } + } + }; + expect(Device.getState(item2)).toStrictEqual({ + "openPercent": 25 + }); + }); +}); diff --git a/tests/devices/simplelight.test.js b/tests/devices/simplelight.test.js new file mode 100644 index 00000000..e4b620ff --- /dev/null +++ b/tests/devices/simplelight.test.js @@ -0,0 +1,20 @@ +const Device = require('../../functions/devices/simplelight.js'); + +describe('Test SimpleLight Device', () => { + test('Test isCompatible', async () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "LIGHT" + } + } + })).toBe(true); + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "SOMETHING" + } + } + })).toBe(false); + }); +}); diff --git a/tests/devices/switch.test.js b/tests/devices/switch.test.js new file mode 100644 index 00000000..547a78fe --- /dev/null +++ b/tests/devices/switch.test.js @@ -0,0 +1,52 @@ +const Device = require('../../functions/devices/switch.js'); + +describe('Test Switch Device', () => { + test('Test isCompatible', async () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "SWITCH" + } + } + })).toBe(true); + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "SOMETHING" + } + } + })).toBe(false); + }); + + test('Test matchesItemType', async () => { + expect(Device.matchesItemType({ "type": "Switch" })).toBe(true); + expect(Device.matchesItemType({ "type": "String" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Switch" })).toBe(true); + expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); + }); + + test('Test getState', async () => { + expect(Device.getState({ "state": "ON" })).toStrictEqual({ + "on": true + }); + expect(Device.getState({ "state": "OFF" })).toStrictEqual({ + "on": false + }); + }); + + test('Test getState inverted', async () => { + const item = { + "state": "ON", + "metadata": { + "ga": { + "config": { + "inverted": true + } + } + } + }; + expect(Device.getState(item)).toStrictEqual({ + "on": false + }); + }); +}); diff --git a/tests/openhab.metadata.test.js b/tests/openhab.metadata.test.js deleted file mode 100644 index 99e4d50c..00000000 --- a/tests/openhab.metadata.test.js +++ /dev/null @@ -1,2634 +0,0 @@ -const OpenHAB = require('../functions/openhab.js'); - -describe('Test SYNC with Metadata', () => { - test('Light Devices', async () => { - const items = [ - { - "state": "OFF", - "type": "Switch", - "name": "MySwitch", - "label": "SwitchLight", - "metadata": { - "ga": { - "value": "Light", - "config": { - "name": "Light Switch" - } - }, - "synonyms": { - "value": "Testlight,Cool Light" - } - } - }, - { - "state": "0", - "type": "Dimmer", - "name": "MyDimmer", - "label": "DimmLight", - "metadata": { - "ga": { - "value": "Light" - } - } - }, - { - "state": "0,0,0", - "type": "Color", - "name": "MyLight", - "label": "ColorLight", - "metadata": { - "ga": { - "value": "Light" - } - } - }, - { - "members": [], - "state": "NULL", - "type": "Group", - "groupType": "Switch", - "name": "MyLightGroup", - "label": "GroupLight", - "metadata": { - "ga": { - "value": "Light" - } - } - }, - { - "members": [], - "state": "NULL", - "type": "Group", - "groupType": "Dimmer", - "name": "MyDimmerGroup", - "label": "GroupDimmer", - "metadata": { - "ga": { - "value": "Light" - } - } - }, - { - "members": [], - "state": "NULL", - "type": "Group", - "groupType": "Color", - "name": "MyColorGroup", - "label": "GroupColor", - "metadata": { - "ga": { - "value": "Light" - } - } - } - ]; - - const getItemsMock = jest.fn(); - getItemsMock.mockReturnValue(Promise.resolve(items)); - - const apiHandler = { - getItems: getItemsMock - }; - - const payload = await new OpenHAB(apiHandler).handleSync(); - - expect(getItemsMock).toHaveBeenCalledTimes(1); - expect(payload).toMatchSnapshot(); - }); - - test('Fan Device', async () => { - const items = [ - { - "state": "50", - "metadata": { - "ga": { - "value": "FAN", - "config": { - "ordered": true, - "speeds": "0\u003dnull:off,50\u003dslow,100\u003dfull:fast", - "lang": "en" - } - } - }, - "type": "Dimmer", - "name": "MyFan", - "label": "My Fan", - "tags": [] - } - ]; - const getItemsMock = jest.fn(); - getItemsMock.mockReturnValue(Promise.resolve(items)); - - const apiHandler = { - getItems: getItemsMock - }; - - const payload = await new OpenHAB(apiHandler).handleSync(); - - expect(getItemsMock).toHaveBeenCalledTimes(1); - expect(payload).toMatchSnapshot(); - }); - - test('Scene Device', async () => { - const items = [ - { - "state": "OFF", - "metadata": { - "ga": { - "value": "Scene" - } - }, - "type": "Switch", - "name": "MyScene", - "label": "My Scene", - "tags": [] - } - ]; - const getItemsMock = jest.fn(); - getItemsMock.mockReturnValue(Promise.resolve(items)); - - const apiHandler = { - getItems: getItemsMock - }; - - const payload = await new OpenHAB(apiHandler).handleSync(); - - expect(getItemsMock).toHaveBeenCalledTimes(1); - expect(payload).toMatchSnapshot(); - }); - - test('Sensor Device', async () => { - const items = [ - { - "state": "14", - "metadata": { - "ga": { - "value": "Sensor", - "config": { - "sensorName": "AirQuality", - "states": "healthy=0,moderate=10,unhealthy=50,very unhealthy=100" - } - } - }, - "type": "Number", - "name": "MySensor", - "label": "My Sensor", - "tags": [] - } - ]; - const getItemsMock = jest.fn(); - getItemsMock.mockReturnValue(Promise.resolve(items)); - - const apiHandler = { - getItems: getItemsMock - }; - - const payload = await new OpenHAB(apiHandler).handleSync(); - - expect(getItemsMock).toHaveBeenCalledTimes(1); - expect(payload).toMatchSnapshot(); - }); - - test('Temperature Sensor Device', async () => { - const items = [ - { - "state": "14", - "metadata": { - "ga": { - "value": "TemperatureSensor", - "config": { - "useFahrenheit": true - } - } - }, - "type": "Number", - "name": "MySensor", - "label": "My Sensor", - "tags": [] - } - ]; - const getItemsMock = jest.fn(); - getItemsMock.mockReturnValue(Promise.resolve(items)); - - const apiHandler = { - getItems: getItemsMock - }; - - const payload = await new OpenHAB(apiHandler).handleSync(); - - expect(getItemsMock).toHaveBeenCalledTimes(1); - expect(payload).toMatchSnapshot(); - }); - - test('Thermostat Device', async () => { - const items = [ - { - "state": "NULL", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "modes": "off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST:ON,eco=ECO,auto=AUTOMATIC" - } - } - }, - "type": "Group", - "name": "MyThermostat", - "label": "My Thermostat", - "tags": [], - "groupNames": [], - }, - { - "state": "AUTOMATIC", - "type": "String", - "name": "MyThermostat_Mode", - "label": "My Thermostat Mode", - "tags": [], - "groupNames": [ - "MyThermostat" - ], - }, - { - "state": "OFF", - "metadata": { - "ga": { - "value": "thermostatMode" - } - }, - "type": "String", - "name": "MyThermostat_RadiatorMode", - "label": "My Thermostat Mode", - "tags": [], - "groupNames": [ - "MyThermostat" - ], - }, - { - "state": "6.0 °C", - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpoint" - } - }, - "type": "Number:Temperature", - "name": "MyThermostat_SetpointTemperature", - "label": "My Thermostat Setpoint Temperature", - "tags": [], - "groupNames": [ - "MyThermostat" - ], - }, - { - "state": "22.5 °C", - "metadata": { - "ga": { - "value": "thermostatTemperatureAmbient" - } - }, - "type": "Number:Temperature", - "name": "MyThermostat_CurrentTemperature", - "label": "My Thermostat Current Temperature", - "tags": [], - "groupNames": [ - "MyThermostat" - ], - } - ]; - const getItemsMock = jest.fn(); - getItemsMock.mockReturnValue(Promise.resolve(items)); - - const apiHandler = { - getItems: getItemsMock - }; - - const payload = await new OpenHAB(apiHandler).handleSync(); - - expect(getItemsMock).toHaveBeenCalledTimes(1); - expect(payload).toMatchSnapshot(); - }); - -}); - -describe('Test QUERY with Metadata', () => { - test('Switch Device', async () => { - const item = - { - "state": "OFF", - "type": "Switch", - "name": "MySwitch", - "metadata": { - "ga": { - "value": "Switch" - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MySwitch" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MySwitch": { - "on": false, - "online": true, - }, - }, - }); - }); - - test('Inverted Switch Device', async () => { - const item = - { - "state": "OFF", - "type": "Switch", - "name": "MySwitch", - "metadata": { - "ga": { - "value": "Switch", - "config": { - "inverted": true - } - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MySwitch" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MySwitch": { - "on": true, - "online": true, - }, - }, - }); - }); - - test('Lock Device as Contact', async () => { - const item = - { - "state": "CLOSED", - "type": "Contact", - "name": "MyLock", - "metadata": { - "ga": { - "value": "Lock" - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MyLock" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MyLock": { - "isLocked": true, - "online": true, - }, - }, - }); - }); - - test('Multiple Light Devices', async () => { - const item1 = - { - "state": "OFF", - "type": "Switch", - "name": "MySwitch", - "metadata": { - "ga": { - "value": "Light" - } - } - }; - - const item2 = - { - "state": "20", - "type": "Dimmer", - "name": "MyDimmer", - "metadata": { - "ga": { - "value": "Light" - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValueOnce(Promise.resolve(item1)) - .mockReturnValueOnce(Promise.resolve(item2)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MySwitch" - }, { - "id": "MyDimmer" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(2); - expect(payload).toStrictEqual({ - "devices": { - "MySwitch": { - "on": false, - "online": true, - }, - "MyDimmer": { - "on": true, - "brightness": 20, - "online": true, - }, - }, - }); - }); - - test('Blinds as Rollershutter Device', async () => { - const item = - { - "state": "20", - "type": "Rollershutter", - "name": "MyBlinds", - "metadata": { - "ga": { - "value": "Blinds" - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MyBlinds" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MyBlinds": { - "openPercent": 80, - "online": true, - }, - }, - }); - }); - - test('Inverted Blinds as Rollershutter Device', async () => { - const item = - { - "state": "20", - "type": "Rollershutter", - "name": "MyBlinds", - "metadata": { - "ga": { - "value": "Blinds", - "config": { - "inverted": true - } - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MyBlinds" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MyBlinds": { - "openPercent": 20, - "online": true, - }, - }, - }); - }); - - test('Blinds as Switch Device', async () => { - const item = - { - "state": "OFF", - "type": "Switch", - "name": "MyBlinds", - "metadata": { - "ga": { - "value": "Blinds" - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MyBlinds" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MyBlinds": { - "openPercent": 0, - "online": true - }, - }, - }); - }); - - test('Temperature Sensor', async () => { - const item = - { - "state": "20", - "type": "Number", - "name": "MySensor", - "metadata": { - "ga": { - "value": "TemperatureSensor" - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MySensor" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MySensor": { - "temperatureAmbientCelsius": 20, - "temperatureSetpointCelsius": 20, - "online": true - }, - }, - }); - }); - - test('Window as Contact', async () => { - const item = - { - "state": "CLOSED", - "type": "Contact", - "name": "MyWindow", - "metadata": { - "ga": { - "value": "Window" - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MyWindow" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MyWindow": { - "openPercent": 0, - "online": true - }, - }, - }); - }); - - test('Fan Device', async () => { - const item = { - "state": "50", - "metadata": { - "ga": { - "value": "FAN", - "config": { - "ordered": true, - "speeds": "0\u003dnull:off,50\u003dslow,100\u003dfull:fast", - "lang": "en" - } - } - }, - "type": "Dimmer", - "name": "MyFan", - }; - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MyFan" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MyFan": { - "on": true, - "currentFanSpeedSetting": "50", - "online": true - }, - }, - }); - }); - - test('Thermostat Device', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "useFahrenheit": true, - "modes": "off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto=AUTOMATIC" - } - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - name: 'MyTargetTemperature', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '10' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: 'COMFORT' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MyThermostat" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MyThermostat": { - "thermostatHumidityAmbient": 50, - "thermostatMode": "heat", - "thermostatTemperatureAmbient": -12.2, - "thermostatTemperatureSetpoint": -12.2, - "online": true - }, - }, - }); - }); -}); - -describe('Test EXECUTE with Metadata', () => { - test('OnOff with Switch Device', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MySwitch" - }], - "execution": [{ - "command": "action.devices.commands.OnOff", - "params": { - "on": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toBeCalledWith('MySwitch', 'ON'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MySwitch" - ], - "states": { - "online": true, - "on": true - }, - "status": "SUCCESS" - }] - }); - }); - - test('OnOff with Inverted Switch Device', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MySwitch", - "customData": { - "inverted": true - } - }], - "execution": [{ - "command": "action.devices.commands.OnOff", - "params": { - "on": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toBeCalledWith('MySwitch', 'OFF'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MySwitch" - ], - "states": { - "online": true, - "on": true - }, - "status": "SUCCESS" - }] - }); - }); - - test('ThermostatTemperatureSetpoint', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "useFahrenheit": true - } - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - name: 'MyTargetTemperature', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '10' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: 'heat' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyThermostat" - }], - "execution": [{ - "command": "action.devices.commands.ThermostatTemperatureSetpoint", - "params": { - "thermostatTemperatureSetpoint": 25 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyTargetTemperature', '77'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyThermostat" - ], - "states": { - "online": true, - "thermostatHumidityAmbient": 50, - "thermostatMode": "heat", - "thermostatTemperatureAmbient": -12.2, - "thermostatTemperatureSetpoint": 25 - }, - "status": "SUCCESS" - }] - }); - }); - - test('ThermostatTemperatureSetpointHigh', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "useFahrenheit": true - } - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - name: 'MyTargetTemperatureHigh', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointHigh' - } - }, - state: '20' - }, { - name: 'MyTargetTemperatureLow', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointLow' - } - }, - state: '10' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: 'heat' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyThermostat" - }], - "execution": [{ - "command": "action.devices.commands.ThermostatTemperatureSetpointHigh", - "params": { - "thermostatTemperatureSetpointHigh": 25 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyTargetTemperatureHigh', '77'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyThermostat" - ], - "states": { - "online": true, - "thermostatHumidityAmbient": 50, - "thermostatMode": "heat", - "thermostatTemperatureAmbient": -12.2, - "thermostatTemperatureSetpointHigh": 25, - "thermostatTemperatureSetpointLow": -12.2, - }, - "status": "SUCCESS" - }] - }); - }); - - test('ThermostatTemperatureSetpointLow', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "useFahrenheit": true - } - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - name: 'MyTargetTemperatureHigh', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointHigh' - } - }, - state: '20' - }, { - name: 'MyTargetTemperatureLow', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointLow' - } - }, - state: '10' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: 'heat' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyThermostat" - }], - "execution": [{ - "command": "action.devices.commands.ThermostatTemperatureSetpointLow", - "params": { - "thermostatTemperatureSetpointLow": 5 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyTargetTemperatureLow', '41'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyThermostat" - ], - "states": { - "online": true, - "thermostatHumidityAmbient": 50, - "thermostatMode": "heat", - "thermostatTemperatureAmbient": -12.2, - "thermostatTemperatureSetpointHigh": -6.7, - "thermostatTemperatureSetpointLow": 5 - }, - "status": "SUCCESS" - }] - }); - }); - - test('ThermostatTemperatureSetRange', async () => { - const item1 = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat" - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '10' - }, { - name: 'MyTargetTemperatureHigh', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointHigh' - } - }, - state: '13' - }, { - name: 'MyTargetTemperatureLow', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointLow' - } - }, - state: '7' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: 'heatcool' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - - const item2 = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat" - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '10' - }, { - name: 'MyTargetTemperatureHigh', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointHigh' - } - }, - state: '25' - }, { - name: 'MyTargetTemperatureLow', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointLow' - } - }, - state: '7' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: 'heatcool' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValueOnce(Promise.resolve(item1)) - .mockReturnValueOnce(Promise.resolve(item2)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyThermostat" - }], - "execution": [{ - "command": "action.devices.commands.ThermostatTemperatureSetRange", - "params": { - "thermostatTemperatureSetpointHigh": 25, - "thermostatTemperatureSetpointLow": 15 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(2); - expect(sendCommandMock).toBeCalledWith('MyTargetTemperatureHigh', '25'); - expect(sendCommandMock).toBeCalledWith('MyTargetTemperatureLow', '15'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyThermostat" - ], - "states": { - "online": true, - "thermostatHumidityAmbient": 50, - "thermostatMode": "heatcool", - "thermostatTemperatureAmbient": 10, - "thermostatTemperatureSetpoint": 10, - "thermostatTemperatureSetpointHigh": 25, - "thermostatTemperatureSetpointLow": 15 - }, - "status": "SUCCESS" - }] - }); - }); - - test('ThermostatSetMode invalid', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "modes": "on=3,heat=5" - } - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '20' - }, { - name: 'MyTargetTemperature', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '10' - }, { - name: 'MyMode', - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: '3' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyThermostat" - }], - "execution": [{ - "command": "action.devices.commands.ThermostatSetMode", - "params": { - "thermostatMode": "off" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyThermostat" - ], - "errorCode": "notSupported", - "status": "ERROR" - }] - }); - }); - - test('ThermostatSetMode', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "modes": "on=1,off=5" - } - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '20' - }, { - name: 'MyTargetTemperature', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '10' - }, { - name: 'MyMode', - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: '1' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyThermostat" - }], - "execution": [{ - "command": "action.devices.commands.ThermostatSetMode", - "params": { - "thermostatMode": "off" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyMode', '5'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyThermostat" - ], - "states": { - "online": true, - "thermostatHumidityAmbient": 50, - "thermostatMode": "off", - "thermostatTemperatureAmbient": 20, - "thermostatTemperatureSetpoint": 10 - }, - "status": "SUCCESS" - }] - }); - }); - - test('LockUnlock with Lock and required acknowledge', async () => { - const item = - { - "state": "OFF", - "type": "Switch", - "name": "MyLock", - "label": "My Lock", - "metadata": { - "ga": { - "value": "Lock", - "config": { - "ackNeeded": true - } - } - }, - "tags": [] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "ackNeeded": true - }, - "id": "MyLock" - }], - "execution": [{ - "command": "action.devices.commands.LockUnlock", - "params": { - "lock": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyLock" - ], - "states": { - "online": true, - "isLocked": true - }, - "status": "ERROR", - "errorCode": "challengeNeeded", - "challengeNeeded": { - "type": "ackNeeded", - } - }] - }); - }); - - test('LockUnlock with Lock and acknowledged challenge', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "ackNeeded": true - }, - "id": "MyLock" - }], - "execution": [{ - "command": "action.devices.commands.LockUnlock", - "params": { - "lock": true - }, - "challenge": { - "ack": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toBeCalledWith('MyLock', 'ON'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyLock" - ], - "states": { - "online": true, - "isLocked": true - }, - "status": "SUCCESS" - }] - }); - }); - - test('LockUnlock with Lock inverted', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "inverted": true - }, - "id": "MyLock" - }], - "execution": [{ - "command": "action.devices.commands.LockUnlock", - "params": { - "lock": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toBeCalledWith('MyLock', 'OFF'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyLock" - ], - "states": { - "online": true, - "isLocked": true - }, - "status": "SUCCESS" - }] - }); - }); - - test('LockUnlock with Lock as Contact', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "itemType": "Contact" - }, - "id": "MyLock" - }], - "execution": [{ - "command": "action.devices.commands.LockUnlock", - "params": { - "lock": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyLock" - ], - "status": "ERROR", - "errorCode": "notSupported" - }] - }); - }); - - test('OpenClose with OpenCloseDevice as Contact', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "itemType": "Contact" - }, - "id": "MyLock" - }], - "execution": [{ - "command": "action.devices.commands.OpenClose", - "params": { - "openPercent": 0 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyLock" - ], - "status": "ERROR", - "errorCode": "notSupported" - }] - }); - }); - - test('BrightnessAbsolute with required acknowledge', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "ackNeeded": true - }, - "id": "MyLight" - }], - "execution": [{ - "command": "action.devices.commands.BrightnessAbsolute", - "params": { - "brightness": 30 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyLight" - ], - "states": { - "online": true, - "brightness": 30 - }, - "status": "ERROR", - "errorCode": "challengeNeeded", - "challengeNeeded": { - "type": "ackNeeded", - } - }] - }); - }); - - test('Arm with required pin', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "pinNeeded": "1234" - }, - "id": "MyAlarm" - }], - "execution": [{ - "command": "action.devices.commands.ArmDisarm", - "params": { - arm: true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyAlarm" - ], - "status": "ERROR", - "errorCode": "challengeNeeded", - "challengeNeeded": { - "type": "pinNeeded", - } - }] - }); - }); - - test('Arm with wrong pin', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "pinNeeded": "1234" - }, - "id": "MyAlarm" - }], - "execution": [{ - "command": "action.devices.commands.ArmDisarm", - "params": { - "arm": true - }, - "challenge": { - "pin": "3456" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyAlarm" - ], - "status": "ERROR", - "errorCode": "challengeNeeded", - "challengeNeeded": { - "type": "challengeFailedPinNeeded" - } - }] - }); - }); - - test('Arm with correct pin', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "pinNeeded": "1234" - }, - "id": "MyAlarm" - }], - "execution": [{ - "command": "action.devices.commands.ArmDisarm", - "params": { - "arm": true - }, - "challenge": { - "pin": "1234" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyAlarm" - ], - "states": { - "online": true, - "isArmed": true - }, - "status": "SUCCESS" - }] - }); - }); - - test('OpenClose Blinds Group as Rollershutter', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyBlinds", - "customData": { - "itemType": "Rollershutter" - } - }], - "execution": [{ - "command": "action.devices.commands.OpenClose", - "params": { - "openPercent": 0 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toBeCalledWith('MyBlinds', 'DOWN'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyBlinds" - ], - "states": { - "online": true, - "openPercent": 0 - }, - "status": "SUCCESS" - }] - }); - }); - - test('OpenClose Blinds Group as Switch', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyBlinds", - "customData": { - "itemType": "Switch" - } - }], - "execution": [{ - "command": "action.devices.commands.OpenClose", - "params": { - "openPercent": 0 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toBeCalledWith('MyBlinds', 'OFF'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyBlinds" - ], - "states": { - "online": true, - "openPercent": 0 - }, - "status": "SUCCESS" - }] - }); - }); - - test('OnOff for TV', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV" - } - }, - "members": [{ - type: 'Switch', - name: 'MyPower', - metadata: { - ga: { - value: 'tvPower' - } - }, - state: 'ON' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.OnOff", - "params": { - "on": false - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyPower', 'OFF'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - 'on': false, - 'online': true - }, - "status": "SUCCESS" - }] - }); - }); - - test('Mute for TV with Switch', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV" - } - }, - "members": [{ - type: 'Switch', - name: 'MyMute', - metadata: { - ga: { - value: 'tvMute' - } - }, - state: 'OFF' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.mute", - "params": { - "mute": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyMute', 'ON'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - 'isMuted': true, - 'online': true - }, - "status": "SUCCESS" - }] - }); - }); - - test('Mute for TV without Switch', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV" - } - }, - "members": [{ - type: 'Dimmer', - name: 'MyVolume', - metadata: { - ga: { - value: 'tvVolume' - } - }, - state: '12' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.mute", - "params": { - "mute": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyVolume', '0'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - 'isMuted': true, - 'online': true - }, - "status": "SUCCESS" - }] - }); - }); - - test('SelectChannel with Number for TV', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV", - "config": { - "availableChannels": "20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2" - } - } - }, - "members": [{ - type: 'Number', - name: 'MyChannel', - metadata: { - ga: { - value: 'tvChannel' - } - }, - state: '10' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.selectChannel", - "params": { - "channelNumber": "20" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyChannel', '20'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - 'channelNumber': '20', - 'online': true - }, - "status": "SUCCESS" - }] - }); - }); - - test('SelectChannel with Name for TV', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV", - "config": { - "availableChannels": "20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2" - } - } - }, - "members": [{ - type: 'Number', - name: 'MyChannel', - metadata: { - ga: { - value: 'tvChannel' - } - }, - state: '10' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.selectChannel", - "params": { - "channelName": "Channel 1" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyChannel', '20'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - 'channelNumber': '20', - 'online': true - }, - "status": "SUCCESS" - }] - }); - }); - - test('SelectInput for TV', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV", - "config": { - "availableInputs": "tv=TV,hdmi1=HDMI1,hdmi2=HDMI2" - } - } - }, - "members": [{ - type: 'String', - name: 'MyInput', - metadata: { - ga: { - value: 'tvInput' - } - }, - state: 'tv' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.SetInput", - "params": { - "newInput": "hdmi1" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyInput', 'hdmi1'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - 'currentInput': 'hdmi1', - 'online': true - }, - "status": "SUCCESS" - }] - }); - }); - - test('SetVolume for TV', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV" - } - }, - "members": [{ - type: 'Dimmer', - name: 'MyVolume', - metadata: { - ga: { - value: 'tvVolume' - } - }, - state: '40' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.setVolume", - "params": { - "volumeLevel": 10 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyVolume', '10'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - "currentVolume": 10, - "online": true - }, - "status": "SUCCESS" - }] - }); - }); - - test('volumeRelative for TV', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV" - } - }, - "members": [{ - type: 'Dimmer', - name: 'MyVolume', - metadata: { - ga: { - value: 'tvVolume' - } - }, - state: '40' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.volumeRelative", - "params": { - "relativeSteps": 1 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyVolume', '41'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - 'currentVolume': 41, - 'online': true - }, - "status": "SUCCESS" - }] - }); - }); - - test('MediaPause for TV', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV" - } - }, - "members": [{ - type: 'Player', - name: 'MyTransport', - metadata: { - ga: { - value: 'tvTransport' - } - }, - state: 'PLAY' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.mediaPause" - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyTransport', 'PAUSE'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - }, - "status": "SUCCESS" - }] - }); - }); -}); diff --git a/tests/openhab.test.js b/tests/openhab.test.js index 3cb88dde..111fc428 100644 --- a/tests/openhab.test.js +++ b/tests/openhab.test.js @@ -1,5 +1,2399 @@ const OpenHAB = require('../functions/openhab.js'); +describe('Test SYNC', () => { + test('Light Devices', async () => { + const items = [ + { + "state": "OFF", + "type": "Switch", + "name": "MySwitch", + "label": "SwitchLight", + "metadata": { + "ga": { + "value": "Light", + "config": { + "name": "Light Switch" + } + }, + "synonyms": { + "value": "Testlight,Cool Light" + } + } + }, + { + "state": "0", + "type": "Dimmer", + "name": "MyDimmer", + "label": "DimmLight", + "metadata": { + "ga": { + "value": "Light" + } + } + }, + { + "state": "0,0,0", + "type": "Color", + "name": "MyLight", + "label": "ColorLight", + "metadata": { + "ga": { + "value": "Light" + } + } + }, + { + "members": [], + "state": "NULL", + "type": "Group", + "groupType": "Switch", + "name": "MyLightGroup", + "label": "GroupLight", + "metadata": { + "ga": { + "value": "Light" + } + } + }, + { + "members": [], + "state": "NULL", + "type": "Group", + "groupType": "Dimmer", + "name": "MyDimmerGroup", + "label": "GroupDimmer", + "metadata": { + "ga": { + "value": "Light" + } + } + }, + { + "members": [], + "state": "NULL", + "type": "Group", + "groupType": "Color", + "name": "MyColorGroup", + "label": "GroupColor", + "metadata": { + "ga": { + "value": "Light" + } + } + }, + { + "state": "50", + "type": "Dimmer", + "name": "MyFan", + "label": "Fan", + "metadata": { + "ga": { + "value": "Fan" + } + } + } + ]; + + const getItemsMock = jest.fn(); + getItemsMock.mockReturnValue(Promise.resolve(items)); + + const apiHandler = { + getItems: getItemsMock + }; + + const payload = await new OpenHAB(apiHandler).handleSync(); + + expect(getItemsMock).toHaveBeenCalledTimes(1); + expect(payload).toMatchSnapshot(); + }); + + + + test('Scene Device', async () => { + const items = [ + { + "state": "OFF", + "metadata": { + "ga": { + "value": "Scene" + } + }, + "type": "Switch", + "name": "MyScene", + "label": "My Scene", + "tags": [] + } + ]; + const getItemsMock = jest.fn(); + getItemsMock.mockReturnValue(Promise.resolve(items)); + + const apiHandler = { + getItems: getItemsMock + }; + + const payload = await new OpenHAB(apiHandler).handleSync(); + + expect(getItemsMock).toHaveBeenCalledTimes(1); + expect(payload).toMatchSnapshot(); + }); + + test('Sensor Device', async () => { + const items = [ + { + "state": "14", + "metadata": { + "ga": { + "value": "Sensor", + "config": { + "sensorName": "AirQuality", + "states": "healthy=0,moderate=10,unhealthy=50,very unhealthy=100" + } + } + }, + "type": "Number", + "name": "MySensor", + "label": "My Sensor", + "tags": [] + } + ]; + const getItemsMock = jest.fn(); + getItemsMock.mockReturnValue(Promise.resolve(items)); + + const apiHandler = { + getItems: getItemsMock + }; + + const payload = await new OpenHAB(apiHandler).handleSync(); + + expect(getItemsMock).toHaveBeenCalledTimes(1); + expect(payload).toMatchSnapshot(); + }); + + test('Temperature Sensor Device', async () => { + const items = [ + { + "state": "14", + "metadata": { + "ga": { + "value": "TemperatureSensor", + "config": { + "useFahrenheit": true + } + } + }, + "type": "Number", + "name": "MySensor", + "label": "My Sensor", + "tags": [] + } + ]; + const getItemsMock = jest.fn(); + getItemsMock.mockReturnValue(Promise.resolve(items)); + + const apiHandler = { + getItems: getItemsMock + }; + + const payload = await new OpenHAB(apiHandler).handleSync(); + + expect(getItemsMock).toHaveBeenCalledTimes(1); + expect(payload).toMatchSnapshot(); + }); + + test('Thermostat Device', async () => { + const items = [ + { + "state": "NULL", + "metadata": { + "ga": { + "value": "Thermostat", + "config": { + "modes": "off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST:ON,eco=ECO,auto=AUTOMATIC" + } + } + }, + "type": "Group", + "name": "MyThermostat", + "label": "My Thermostat", + "tags": [], + "groupNames": [], + }, + { + "state": "AUTOMATIC", + "type": "String", + "name": "MyThermostat_Mode", + "label": "My Thermostat Mode", + "tags": [], + "groupNames": [ + "MyThermostat" + ], + }, + { + "state": "OFF", + "metadata": { + "ga": { + "value": "thermostatMode" + } + }, + "type": "String", + "name": "MyThermostat_RadiatorMode", + "label": "My Thermostat Mode", + "tags": [], + "groupNames": [ + "MyThermostat" + ], + }, + { + "state": "6.0 °C", + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpoint" + } + }, + "type": "Number:Temperature", + "name": "MyThermostat_SetpointTemperature", + "label": "My Thermostat Setpoint Temperature", + "tags": [], + "groupNames": [ + "MyThermostat" + ], + }, + { + "state": "22.5 °C", + "metadata": { + "ga": { + "value": "thermostatTemperatureAmbient" + } + }, + "type": "Number:Temperature", + "name": "MyThermostat_CurrentTemperature", + "label": "My Thermostat Current Temperature", + "tags": [], + "groupNames": [ + "MyThermostat" + ], + } + ]; + const getItemsMock = jest.fn(); + getItemsMock.mockReturnValue(Promise.resolve(items)); + + const apiHandler = { + getItems: getItemsMock + }; + + const payload = await new OpenHAB(apiHandler).handleSync(); + + expect(getItemsMock).toHaveBeenCalledTimes(1); + expect(payload).toMatchSnapshot(); + }); +}); + +/* ================================================= */ + +describe('Test QUERY', () => { + test('Lock Device as Contact', async () => { + const item = + { + "state": "CLOSED", + "type": "Contact", + "name": "MyLock", + "metadata": { + "ga": { + "value": "Lock" + } + } + }; + + const getItemMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + + const apiHandler = { + getItem: getItemMock + }; + + const payload = await new OpenHAB(apiHandler).handleQuery([{ + "id": "MyLock" + }]); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(payload).toStrictEqual({ + "devices": { + "MyLock": { + "isLocked": true, + "online": true, + }, + }, + }); + }); + + test('Multiple Light Devices', async () => { + const item1 = + { + "state": "OFF", + "type": "Switch", + "name": "MySwitch", + "metadata": { + "ga": { + "value": "Light" + } + } + }; + + const item2 = + { + "state": "20", + "type": "Dimmer", + "name": "MyDimmer", + "metadata": { + "ga": { + "value": "Light" + } + } + }; + + const getItemMock = jest.fn(); + getItemMock.mockReturnValueOnce(Promise.resolve(item1)) + .mockReturnValueOnce(Promise.resolve(item2)); + + const apiHandler = { + getItem: getItemMock + }; + + const payload = await new OpenHAB(apiHandler).handleQuery([{ + "id": "MySwitch" + }, { + "id": "MyDimmer" + }]); + + expect(getItemMock).toHaveBeenCalledTimes(2); + expect(payload).toStrictEqual({ + "devices": { + "MySwitch": { + "on": false, + "online": true, + }, + "MyDimmer": { + "on": true, + "brightness": 20, + "online": true, + }, + }, + }); + }); + + test('Temperature Sensor', async () => { + const item = + { + "state": "20", + "type": "Number", + "name": "MySensor", + "metadata": { + "ga": { + "value": "TemperatureSensor" + } + } + }; + + const getItemMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + + const apiHandler = { + getItem: getItemMock + }; + + const payload = await new OpenHAB(apiHandler).handleQuery([{ + "id": "MySensor" + }]); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(payload).toStrictEqual({ + "devices": { + "MySensor": { + "temperatureAmbientCelsius": 20, + "temperatureSetpointCelsius": 20, + "online": true + }, + }, + }); + }); + + test('Window as Contact', async () => { + const item = + { + "state": "CLOSED", + "type": "Contact", + "name": "MyWindow", + "metadata": { + "ga": { + "value": "Window" + } + } + }; + + const getItemMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + + const apiHandler = { + getItem: getItemMock + }; + + const payload = await new OpenHAB(apiHandler).handleQuery([{ + "id": "MyWindow" + }]); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(payload).toStrictEqual({ + "devices": { + "MyWindow": { + "openPercent": 0, + "online": true + }, + }, + }); + }); + + test('Thermostat Device', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyThermostat", + "label": "Thermostat", + "metadata": { + "ga": { + "value": "Thermostat", + "config": { + "useFahrenheit": true, + "modes": "off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto=AUTOMATIC" + } + } + }, + "members": [{ + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' + } + }, + state: '10' + }, { + name: 'MyTargetTemperature', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpoint' + } + }, + state: '10' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatMode' + } + }, + state: 'COMFORT' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatHumidityAmbient' + } + }, + state: '50' + }] + }; + const getItemMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + + const apiHandler = { + getItem: getItemMock + }; + + const payload = await new OpenHAB(apiHandler).handleQuery([{ + "id": "MyThermostat" + }]); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(payload).toStrictEqual({ + "devices": { + "MyThermostat": { + "thermostatHumidityAmbient": 50, + "thermostatMode": "heat", + "thermostatTemperatureAmbient": -12.2, + "thermostatTemperatureSetpoint": -12.2, + "online": true + }, + }, + }); + }); +}); + +describe('Test EXECUTE with Metadata', () => { + test('OnOff with Switch Device', async () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MySwitch" + }], + "execution": [{ + "command": "action.devices.commands.OnOff", + "params": { + "on": true + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toBeCalledWith('MySwitch', 'ON'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MySwitch" + ], + "states": { + "online": true, + "on": true + }, + "status": "SUCCESS" + }] + }); + }); + + test('OnOff with Inverted Switch Device', async () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MySwitch", + "customData": { + "inverted": true + } + }], + "execution": [{ + "command": "action.devices.commands.OnOff", + "params": { + "on": true + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toBeCalledWith('MySwitch', 'OFF'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MySwitch" + ], + "states": { + "online": true, + "on": true + }, + "status": "SUCCESS" + }] + }); + }); + + test('ThermostatTemperatureSetpoint', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyThermostat", + "label": "Thermostat", + "metadata": { + "ga": { + "value": "Thermostat", + "config": { + "useFahrenheit": true + } + } + }, + "members": [{ + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' + } + }, + state: '10' + }, { + name: 'MyTargetTemperature', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpoint' + } + }, + state: '10' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatMode' + } + }, + state: 'heat' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatHumidityAmbient' + } + }, + state: '50' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyThermostat" + }], + "execution": [{ + "command": "action.devices.commands.ThermostatTemperatureSetpoint", + "params": { + "thermostatTemperatureSetpoint": 25 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyTargetTemperature', '77'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyThermostat" + ], + "states": { + "online": true, + "thermostatHumidityAmbient": 50, + "thermostatMode": "heat", + "thermostatTemperatureAmbient": -12.2, + "thermostatTemperatureSetpoint": 25 + }, + "status": "SUCCESS" + }] + }); + }); + + test('ThermostatTemperatureSetpointHigh', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyThermostat", + "label": "Thermostat", + "metadata": { + "ga": { + "value": "Thermostat", + "config": { + "useFahrenheit": true + } + } + }, + "members": [{ + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' + } + }, + state: '10' + }, { + name: 'MyTargetTemperatureHigh', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointHigh' + } + }, + state: '20' + }, { + name: 'MyTargetTemperatureLow', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointLow' + } + }, + state: '10' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatMode' + } + }, + state: 'heat' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatHumidityAmbient' + } + }, + state: '50' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyThermostat" + }], + "execution": [{ + "command": "action.devices.commands.ThermostatTemperatureSetpointHigh", + "params": { + "thermostatTemperatureSetpointHigh": 25 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyTargetTemperatureHigh', '77'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyThermostat" + ], + "states": { + "online": true, + "thermostatHumidityAmbient": 50, + "thermostatMode": "heat", + "thermostatTemperatureAmbient": -12.2, + "thermostatTemperatureSetpointHigh": 25, + "thermostatTemperatureSetpointLow": -12.2, + }, + "status": "SUCCESS" + }] + }); + }); + + test('ThermostatTemperatureSetpointLow', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyThermostat", + "label": "Thermostat", + "metadata": { + "ga": { + "value": "Thermostat", + "config": { + "useFahrenheit": true + } + } + }, + "members": [{ + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' + } + }, + state: '10' + }, { + name: 'MyTargetTemperatureHigh', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointHigh' + } + }, + state: '20' + }, { + name: 'MyTargetTemperatureLow', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointLow' + } + }, + state: '10' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatMode' + } + }, + state: 'heat' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatHumidityAmbient' + } + }, + state: '50' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyThermostat" + }], + "execution": [{ + "command": "action.devices.commands.ThermostatTemperatureSetpointLow", + "params": { + "thermostatTemperatureSetpointLow": 5 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyTargetTemperatureLow', '41'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyThermostat" + ], + "states": { + "online": true, + "thermostatHumidityAmbient": 50, + "thermostatMode": "heat", + "thermostatTemperatureAmbient": -12.2, + "thermostatTemperatureSetpointHigh": -6.7, + "thermostatTemperatureSetpointLow": 5 + }, + "status": "SUCCESS" + }] + }); + }); + + test('ThermostatTemperatureSetRange', async () => { + const item1 = + { + "state": "NULL", + "type": "Group", + "name": "MyThermostat", + "label": "Thermostat", + "metadata": { + "ga": { + "value": "Thermostat" + } + }, + "members": [{ + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' + } + }, + state: '10' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpoint' + } + }, + state: '10' + }, { + name: 'MyTargetTemperatureHigh', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointHigh' + } + }, + state: '13' + }, { + name: 'MyTargetTemperatureLow', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointLow' + } + }, + state: '7' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatMode' + } + }, + state: 'heatcool' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatHumidityAmbient' + } + }, + state: '50' + }] + }; + + const item2 = + { + "state": "NULL", + "type": "Group", + "name": "MyThermostat", + "label": "Thermostat", + "metadata": { + "ga": { + "value": "Thermostat" + } + }, + "members": [{ + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' + } + }, + state: '10' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpoint' + } + }, + state: '10' + }, { + name: 'MyTargetTemperatureHigh', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointHigh' + } + }, + state: '25' + }, { + name: 'MyTargetTemperatureLow', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointLow' + } + }, + state: '7' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatMode' + } + }, + state: 'heatcool' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatHumidityAmbient' + } + }, + state: '50' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValueOnce(Promise.resolve(item1)) + .mockReturnValueOnce(Promise.resolve(item2)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyThermostat" + }], + "execution": [{ + "command": "action.devices.commands.ThermostatTemperatureSetRange", + "params": { + "thermostatTemperatureSetpointHigh": 25, + "thermostatTemperatureSetpointLow": 15 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(2); + expect(sendCommandMock).toBeCalledWith('MyTargetTemperatureHigh', '25'); + expect(sendCommandMock).toBeCalledWith('MyTargetTemperatureLow', '15'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyThermostat" + ], + "states": { + "online": true, + "thermostatHumidityAmbient": 50, + "thermostatMode": "heatcool", + "thermostatTemperatureAmbient": 10, + "thermostatTemperatureSetpoint": 10, + "thermostatTemperatureSetpointHigh": 25, + "thermostatTemperatureSetpointLow": 15 + }, + "status": "SUCCESS" + }] + }); + }); + + test('ThermostatSetMode invalid', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyThermostat", + "label": "Thermostat", + "metadata": { + "ga": { + "value": "Thermostat", + "config": { + "modes": "on=3,heat=5" + } + } + }, + "members": [{ + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' + } + }, + state: '20' + }, { + name: 'MyTargetTemperature', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpoint' + } + }, + state: '10' + }, { + name: 'MyMode', + type: 'Number', + metadata: { + ga: { + value: 'thermostatMode' + } + }, + state: '3' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatHumidityAmbient' + } + }, + state: '50' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyThermostat" + }], + "execution": [{ + "command": "action.devices.commands.ThermostatSetMode", + "params": { + "thermostatMode": "off" + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toHaveBeenCalledTimes(0); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyThermostat" + ], + "errorCode": "notSupported", + "status": "ERROR" + }] + }); + }); + + test('ThermostatSetMode', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyThermostat", + "label": "Thermostat", + "metadata": { + "ga": { + "value": "Thermostat", + "config": { + "modes": "on=1,off=5" + } + } + }, + "members": [{ + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' + } + }, + state: '20' + }, { + name: 'MyTargetTemperature', + type: 'Number', + metadata: { + ga: { + value: 'thermostatTemperatureSetpoint' + } + }, + state: '10' + }, { + name: 'MyMode', + type: 'Number', + metadata: { + ga: { + value: 'thermostatMode' + } + }, + state: '1' + }, { + type: 'Number', + metadata: { + ga: { + value: 'thermostatHumidityAmbient' + } + }, + state: '50' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyThermostat" + }], + "execution": [{ + "command": "action.devices.commands.ThermostatSetMode", + "params": { + "thermostatMode": "off" + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyMode', '5'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyThermostat" + ], + "states": { + "online": true, + "thermostatHumidityAmbient": 50, + "thermostatMode": "off", + "thermostatTemperatureAmbient": 20, + "thermostatTemperatureSetpoint": 10 + }, + "status": "SUCCESS" + }] + }); + }); + + test('LockUnlock with Lock and required acknowledge', async () => { + const item = + { + "state": "OFF", + "type": "Switch", + "name": "MyLock", + "label": "My Lock", + "metadata": { + "ga": { + "value": "Lock", + "config": { + "ackNeeded": true + } + } + }, + "tags": [] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "customData": { + "ackNeeded": true + }, + "id": "MyLock" + }], + "execution": [{ + "command": "action.devices.commands.LockUnlock", + "params": { + "lock": true + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toHaveBeenCalledTimes(0); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyLock" + ], + "states": { + "online": true, + "isLocked": true + }, + "status": "ERROR", + "errorCode": "challengeNeeded", + "challengeNeeded": { + "type": "ackNeeded", + } + }] + }); + }); + + test('LockUnlock with Lock and acknowledged challenge', async () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "customData": { + "ackNeeded": true + }, + "id": "MyLock" + }], + "execution": [{ + "command": "action.devices.commands.LockUnlock", + "params": { + "lock": true + }, + "challenge": { + "ack": true + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toBeCalledWith('MyLock', 'ON'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyLock" + ], + "states": { + "online": true, + "isLocked": true + }, + "status": "SUCCESS" + }] + }); + }); + + test('LockUnlock with Lock inverted', async () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "customData": { + "inverted": true + }, + "id": "MyLock" + }], + "execution": [{ + "command": "action.devices.commands.LockUnlock", + "params": { + "lock": true + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toBeCalledWith('MyLock', 'OFF'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyLock" + ], + "states": { + "online": true, + "isLocked": true + }, + "status": "SUCCESS" + }] + }); + }); + + test('LockUnlock with Lock as Contact', async () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "customData": { + "itemType": "Contact" + }, + "id": "MyLock" + }], + "execution": [{ + "command": "action.devices.commands.LockUnlock", + "params": { + "lock": true + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(0); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyLock" + ], + "status": "ERROR", + "errorCode": "notSupported" + }] + }); + }); + + test('OpenClose with OpenCloseDevice as Contact', async () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "customData": { + "itemType": "Contact" + }, + "id": "MyLock" + }], + "execution": [{ + "command": "action.devices.commands.OpenClose", + "params": { + "openPercent": 0 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(0); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyLock" + ], + "status": "ERROR", + "errorCode": "notSupported" + }] + }); + }); + + test('BrightnessAbsolute with required acknowledge', async () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "customData": { + "ackNeeded": true + }, + "id": "MyLight" + }], + "execution": [{ + "command": "action.devices.commands.BrightnessAbsolute", + "params": { + "brightness": 30 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyLight" + ], + "states": { + "online": true, + "brightness": 30 + }, + "status": "ERROR", + "errorCode": "challengeNeeded", + "challengeNeeded": { + "type": "ackNeeded", + } + }] + }); + }); + + test('Arm with required pin', async () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "customData": { + "pinNeeded": "1234" + }, + "id": "MyAlarm" + }], + "execution": [{ + "command": "action.devices.commands.ArmDisarm", + "params": { + arm: true + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyAlarm" + ], + "status": "ERROR", + "errorCode": "challengeNeeded", + "challengeNeeded": { + "type": "pinNeeded", + } + }] + }); + }); + + test('Arm with wrong pin', async () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "customData": { + "pinNeeded": "1234" + }, + "id": "MyAlarm" + }], + "execution": [{ + "command": "action.devices.commands.ArmDisarm", + "params": { + "arm": true + }, + "challenge": { + "pin": "3456" + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyAlarm" + ], + "status": "ERROR", + "errorCode": "challengeNeeded", + "challengeNeeded": { + "type": "challengeFailedPinNeeded" + } + }] + }); + }); + + test('Arm with correct pin', async () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "customData": { + "pinNeeded": "1234" + }, + "id": "MyAlarm" + }], + "execution": [{ + "command": "action.devices.commands.ArmDisarm", + "params": { + "arm": true + }, + "challenge": { + "pin": "1234" + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyAlarm" + ], + "states": { + "online": true, + "isArmed": true + }, + "status": "SUCCESS" + }] + }); + }); + + test('OpenClose Blinds Group as Rollershutter', async () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyBlinds", + "customData": { + "itemType": "Rollershutter" + } + }], + "execution": [{ + "command": "action.devices.commands.OpenClose", + "params": { + "openPercent": 0 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toBeCalledWith('MyBlinds', 'DOWN'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyBlinds" + ], + "states": { + "online": true, + "openPercent": 0 + }, + "status": "SUCCESS" + }] + }); + }); + + test('OpenClose Blinds Group as Switch', async () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve()); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyBlinds", + "customData": { + "itemType": "Switch" + } + }], + "execution": [{ + "command": "action.devices.commands.OpenClose", + "params": { + "openPercent": 0 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toBeCalledWith('MyBlinds', 'OFF'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyBlinds" + ], + "states": { + "online": true, + "openPercent": 0 + }, + "status": "SUCCESS" + }] + }); + }); + + test('OnOff for TV', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV" + } + }, + "members": [{ + type: 'Switch', + name: 'MyPower', + metadata: { + ga: { + value: 'tvPower' + } + }, + state: 'ON' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV", + "customData": { + "deviceType": "TV" + } + }], + "execution": [{ + "command": "action.devices.commands.OnOff", + "params": { + "on": false + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyPower', 'OFF'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'on': false, + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); + + test('Mute for TV with Switch', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV" + } + }, + "members": [{ + type: 'Switch', + name: 'MyMute', + metadata: { + ga: { + value: 'tvMute' + } + }, + state: 'OFF' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV", + "customData": { + "deviceType": "TV" + } + }], + "execution": [{ + "command": "action.devices.commands.mute", + "params": { + "mute": true + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyMute', 'ON'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'isMuted': true, + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); + + test('Mute for TV without Switch', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV" + } + }, + "members": [{ + type: 'Dimmer', + name: 'MyVolume', + metadata: { + ga: { + value: 'tvVolume' + } + }, + state: '12' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV", + "customData": { + "deviceType": "TV" + } + }], + "execution": [{ + "command": "action.devices.commands.mute", + "params": { + "mute": true + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyVolume', '0'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'isMuted': true, + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); + + test('SelectChannel with Number for TV', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV", + "config": { + "availableChannels": "20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2" + } + } + }, + "members": [{ + type: 'Number', + name: 'MyChannel', + metadata: { + ga: { + value: 'tvChannel' + } + }, + state: '10' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV", + "customData": { + "deviceType": "TV" + } + }], + "execution": [{ + "command": "action.devices.commands.selectChannel", + "params": { + "channelNumber": "20" + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyChannel', '20'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'channelNumber': '20', + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); + + test('SelectChannel with Name for TV', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV", + "config": { + "availableChannels": "20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2" + } + } + }, + "members": [{ + type: 'Number', + name: 'MyChannel', + metadata: { + ga: { + value: 'tvChannel' + } + }, + state: '10' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV", + "customData": { + "deviceType": "TV" + } + }], + "execution": [{ + "command": "action.devices.commands.selectChannel", + "params": { + "channelName": "Channel 1" + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyChannel', '20'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'channelNumber': '20', + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); + + test('SelectInput for TV', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV", + "config": { + "availableInputs": "tv=TV,hdmi1=HDMI1,hdmi2=HDMI2" + } + } + }, + "members": [{ + type: 'String', + name: 'MyInput', + metadata: { + ga: { + value: 'tvInput' + } + }, + state: 'tv' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV", + "customData": { + "deviceType": "TV" + } + }], + "execution": [{ + "command": "action.devices.commands.SetInput", + "params": { + "newInput": "hdmi1" + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyInput', 'hdmi1'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'currentInput': 'hdmi1', + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); + + test('SetVolume for TV', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV" + } + }, + "members": [{ + type: 'Dimmer', + name: 'MyVolume', + metadata: { + ga: { + value: 'tvVolume' + } + }, + state: '40' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV", + "customData": { + "deviceType": "TV" + } + }], + "execution": [{ + "command": "action.devices.commands.setVolume", + "params": { + "volumeLevel": 10 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyVolume', '10'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + "currentVolume": 10, + "online": true + }, + "status": "SUCCESS" + }] + }); + }); + + test('volumeRelative for TV', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV" + } + }, + "members": [{ + type: 'Dimmer', + name: 'MyVolume', + metadata: { + ga: { + value: 'tvVolume' + } + }, + state: '40' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV", + "customData": { + "deviceType": "TV" + } + }], + "execution": [{ + "command": "action.devices.commands.volumeRelative", + "params": { + "relativeSteps": 1 + } + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyVolume', '41'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + 'currentVolume': 41, + 'online': true + }, + "status": "SUCCESS" + }] + }); + }); + + test('MediaPause for TV', async () => { + const item = + { + "state": "NULL", + "type": "Group", + "name": "MyTV", + "label": "TV", + "metadata": { + "ga": { + "value": "TV" + } + }, + "members": [{ + type: 'Player', + name: 'MyTransport', + metadata: { + ga: { + value: 'tvTransport' + } + }, + state: 'PLAY' + }] + }; + + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + getItemMock.mockReturnValue(Promise.resolve(item)); + sendCommandMock.mockReturnValue(Promise.resolve()); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const commands = [{ + "devices": [{ + "id": "MyTV", + "customData": { + "deviceType": "TV" + } + }], + "execution": [{ + "command": "action.devices.commands.mediaPause" + }] + }]; + + const payload = await new OpenHAB(apiHandler).handleExecute(commands); + + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toBeCalledWith('MyTransport', 'PAUSE'); + expect(payload).toStrictEqual({ + "commands": [{ + "ids": [ + "MyTV" + ], + "states": { + }, + "status": "SUCCESS" + }] + }); + }); +}); + describe('Test EXECUTE', () => { test('OnOff Switch', async () => { const getItemMock = jest.fn(); From 7059c997f179490f23a64e6c18d59dd63cf56c18 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 15:33:09 +0200 Subject: [PATCH 39/94] Make traits a function Signed-off-by: Michael Krug --- functions/devices/camera.js | 2 +- functions/devices/colorlight.js | 2 +- functions/devices/default.js | 73 +++++++++++++++++--------- functions/devices/dimmablelight.js | 2 +- functions/devices/fan.js | 2 +- functions/devices/lock.js | 2 +- functions/devices/openclosedevice.js | 2 +- functions/devices/scene.js | 2 +- functions/devices/securitysystem.js | 2 +- functions/devices/sensor.js | 2 +- functions/devices/speaker.js | 2 +- functions/devices/specialcolorlight.js | 2 +- functions/devices/startstopswitch.js | 2 +- functions/devices/switch.js | 2 +- functions/devices/temperaturesensor.js | 2 +- functions/devices/thermostat.js | 2 +- functions/devices/valve.js | 2 +- 17 files changed, 64 insertions(+), 41 deletions(-) diff --git a/functions/devices/camera.js b/functions/devices/camera.js index b3a36f45..aeb5063b 100644 --- a/functions/devices/camera.js +++ b/functions/devices/camera.js @@ -5,7 +5,7 @@ class Camera extends DefaultDevice { return 'action.devices.types.CAMERA'; } - static get traits() { + static getTraits() { return [ 'action.devices.traits.CameraStream' ]; diff --git a/functions/devices/colorlight.js b/functions/devices/colorlight.js index cfd2f8b1..d015ec54 100644 --- a/functions/devices/colorlight.js +++ b/functions/devices/colorlight.js @@ -5,7 +5,7 @@ class ColorLight extends DefaultDevice { return 'action.devices.types.LIGHT'; } - static get traits() { + static getTraits() { return [ 'action.devices.traits.OnOff', 'action.devices.traits.Brightness', diff --git a/functions/devices/default.js b/functions/devices/default.js index 603527ef..d7e03d9c 100644 --- a/functions/devices/default.js +++ b/functions/devices/default.js @@ -3,25 +3,59 @@ class DefaultDevice { return ''; } - static get traits() { + /** + * @param {object} item + */ + static getTraits(item) { return []; } - static getAttributes(item = {}) { + static get requiredItemTypes() { + return []; + } + + /** + * @param {object} item + */ static isCompatible(item) { + return item.metadata && item.metadata.ga && + this.type.toLowerCase() === `action.devices.types.${item.metadata.ga.value}`.toLowerCase() || + this.hasTag(item, this.type.substr(21).replace('SWITCH', 'SWITCHABLE').replace('LIGHT', 'LIGHTING')) + } + + /** + * @param {object} item + */ + static matchesItemType(item) { + return ( + !this.requiredItemTypes.length || + this.requiredItemTypes.includes(item.type) || + (item.type === 'Group' && item.groupType && this.requiredItemTypes.includes(item.groupType)) + ); + } + + /** + * @param {object} item + */ static getAttributes(item) { return {}; } - static getConfig(item = {}) { + /** + * @param {object} item + */ + static getConfig(item) { return item && item.metadata && item.metadata.ga && item.metadata.ga.config || {}; } - static getMetadata(item = {}) { + /** + * @param {object} item + */ + static getMetadata(item) { const config = this.getConfig(item); const itemType = item.type === 'Group' && item.groupType ? item.groupType : item.type; const metadata = { id: item.name, type: this.type, - traits: this.traits, + traits: this.getTraits(item), name: { name: config.name || item.label, defaultNames: [config.name || item.label], @@ -54,29 +88,18 @@ class DefaultDevice { return metadata; } - static get requiredItemTypes() { - return []; - } - - static isCompatible(item = {}) { - return item.metadata && item.metadata.ga && - this.type.toLowerCase() === `action.devices.types.${item.metadata.ga.value}`.toLowerCase() || - this.hasTag(item, this.type.substr(21).replace('SWITCH', 'SWITCHABLE').replace('LIGHT', 'LIGHTING')) - } - - static matchesItemType(item = {}) { - return ( - !this.requiredItemTypes.length || - this.requiredItemTypes.includes(item.type) || - (item.type === 'Group' && item.groupType && this.requiredItemTypes.includes(item.groupType)) - ); - } - - static getState(item = {}) { + /** + * @param {object} item + */ + static getState(item) { return {}; } - static hasTag(item = {}, tag = '') { + /** + * @param {object} item + * @param {string} tag + */ + static hasTag(item, tag) { return item.tags && item.tags.map(t => t.toLowerCase()).includes(tag.toLowerCase()) || false; } } diff --git a/functions/devices/dimmablelight.js b/functions/devices/dimmablelight.js index 6b84ea10..87d26bf0 100644 --- a/functions/devices/dimmablelight.js +++ b/functions/devices/dimmablelight.js @@ -5,7 +5,7 @@ class DimmableLight extends DefaultDevice { return 'action.devices.types.LIGHT'; } - static get traits() { + static getTraits() { return [ 'action.devices.traits.OnOff', 'action.devices.traits.Brightness' diff --git a/functions/devices/fan.js b/functions/devices/fan.js index c250e296..ca0de02c 100644 --- a/functions/devices/fan.js +++ b/functions/devices/fan.js @@ -5,7 +5,7 @@ class Fan extends DefaultDevice { return 'action.devices.types.FAN'; } - static get traits() { + static getTraits() { return [ 'action.devices.traits.OnOff', 'action.devices.traits.FanSpeed' diff --git a/functions/devices/lock.js b/functions/devices/lock.js index 00b47ce7..3f96ca6a 100644 --- a/functions/devices/lock.js +++ b/functions/devices/lock.js @@ -5,7 +5,7 @@ class Lock extends DefaultDevice { return 'action.devices.types.LOCK'; } - static get traits() { + static getTraits() { return [ 'action.devices.traits.LockUnlock' ]; diff --git a/functions/devices/openclosedevice.js b/functions/devices/openclosedevice.js index 3356d4c2..2d22600b 100644 --- a/functions/devices/openclosedevice.js +++ b/functions/devices/openclosedevice.js @@ -1,7 +1,7 @@ const DefaultDevice = require('./default.js'); class OpenCloseDevice extends DefaultDevice { - static get traits() { + static getTraits() { return [ 'action.devices.traits.OpenClose', 'action.devices.traits.StartStop' diff --git a/functions/devices/scene.js b/functions/devices/scene.js index 4c884895..89ec7f8e 100644 --- a/functions/devices/scene.js +++ b/functions/devices/scene.js @@ -5,7 +5,7 @@ class Scene extends DefaultDevice { return 'action.devices.types.SCENE'; } - static get traits() { + static getTraits() { return [ 'action.devices.traits.Scene' ]; diff --git a/functions/devices/securitysystem.js b/functions/devices/securitysystem.js index 00097cda..0adfea7b 100644 --- a/functions/devices/securitysystem.js +++ b/functions/devices/securitysystem.js @@ -5,7 +5,7 @@ class SecuritySystem extends DefaultDevice { return 'action.devices.types.SECURITYSYSTEM'; } - static get traits() { + static getTraits() { return [ 'action.devices.traits.ArmDisarm' ]; diff --git a/functions/devices/sensor.js b/functions/devices/sensor.js index 7640afeb..010da0a9 100644 --- a/functions/devices/sensor.js +++ b/functions/devices/sensor.js @@ -5,7 +5,7 @@ class Sensor extends DefaultDevice { return 'action.devices.types.SENSOR'; } - static get traits() { + static getTraits() { return [ 'action.devices.traits.SensorState' ]; diff --git a/functions/devices/speaker.js b/functions/devices/speaker.js index 828ddf55..34abd556 100644 --- a/functions/devices/speaker.js +++ b/functions/devices/speaker.js @@ -5,7 +5,7 @@ class Speaker extends DefaultDevice { return 'action.devices.types.SPEAKER'; } - static get traits() { + static getTraits() { return [ 'action.devices.traits.Volume' ]; diff --git a/functions/devices/specialcolorlight.js b/functions/devices/specialcolorlight.js index 8f5870b4..af82dd54 100644 --- a/functions/devices/specialcolorlight.js +++ b/functions/devices/specialcolorlight.js @@ -5,7 +5,7 @@ class SpecialColorLight extends DefaultDevice { return 'action.devices.types.LIGHT'; } - static get traits() { + static getTraits() { return [ 'action.devices.traits.OnOff', 'action.devices.traits.Brightness', diff --git a/functions/devices/startstopswitch.js b/functions/devices/startstopswitch.js index 2b9cff56..1bbc73a7 100644 --- a/functions/devices/startstopswitch.js +++ b/functions/devices/startstopswitch.js @@ -1,7 +1,7 @@ const DefaultDevice = require('./default.js'); class StartStopSwitch extends DefaultDevice { - static get traits() { + static getTraits() { return [ 'action.devices.traits.StartStop' ]; diff --git a/functions/devices/switch.js b/functions/devices/switch.js index c3456204..18ad7e80 100644 --- a/functions/devices/switch.js +++ b/functions/devices/switch.js @@ -5,7 +5,7 @@ class Switch extends DefaultDevice { return 'action.devices.types.SWITCH'; } - static get traits() { + static getTraits() { return [ 'action.devices.traits.OnOff' ]; diff --git a/functions/devices/temperaturesensor.js b/functions/devices/temperaturesensor.js index f4aa88c4..357c9de9 100644 --- a/functions/devices/temperaturesensor.js +++ b/functions/devices/temperaturesensor.js @@ -6,7 +6,7 @@ class TemperatureSensor extends DefaultDevice { return 'action.devices.types.SENSOR'; } - static get traits() { + static getTraits() { return [ 'action.devices.traits.TemperatureControl' ]; diff --git a/functions/devices/thermostat.js b/functions/devices/thermostat.js index b058b153..0e07130b 100644 --- a/functions/devices/thermostat.js +++ b/functions/devices/thermostat.js @@ -6,7 +6,7 @@ class Thermostat extends DefaultDevice { return 'action.devices.types.THERMOSTAT'; } - static get traits() { + static getTraits() { return [ 'action.devices.traits.TemperatureSetting' ]; diff --git a/functions/devices/valve.js b/functions/devices/valve.js index eec0c1c4..dc3314d0 100644 --- a/functions/devices/valve.js +++ b/functions/devices/valve.js @@ -5,7 +5,7 @@ class Valve extends DefaultDevice { return 'action.devices.types.VALVE'; } - static get traits() { + static getTraits() { return [ 'action.devices.traits.OpenClose' ]; From abef64c43162957dbd9572344063914e3fa6eae1 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 15:33:30 +0200 Subject: [PATCH 40/94] Update dependencies Signed-off-by: Michael Krug --- functions/package-lock.json | 92 +- functions/package.json | 2 +- package-lock.json | 2819 ++++++++++++++++++++++++----------- package.json | 8 +- 4 files changed, 2007 insertions(+), 914 deletions(-) diff --git a/functions/package-lock.json b/functions/package-lock.json index 564d0daa..e603fc19 100644 --- a/functions/package-lock.json +++ b/functions/package-lock.json @@ -1,6 +1,6 @@ { "name": "openhab.google-assistant-smarthome.cloud-function", - "version": "2.0.22", + "version": "2.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -32,9 +32,9 @@ "integrity": "sha512-orGL5LXERPYsLov6CWs3Fh6203+dXzJkR7OnddIr2514Hsecwc8xRpzCapshBbKFImCsvS/mk6+FWiN5LyZJAQ==" }, "@types/express": { - "version": "4.17.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz", - "integrity": "sha512-n/mr9tZI83kd4azlPG5y997C/M4DNABK9yErhFM6hKdym4kkmd9j0vtsJyjFIwfRBxtrxZtAfGZCNRIBMFLK5w==", + "version": "4.17.8", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.8.tgz", + "integrity": "sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ==", "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "*", @@ -43,28 +43,29 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.5.tgz", - "integrity": "sha512-578YH5Lt88AKoADy0b2jQGwJtrBxezXtVe/MBqWXKZpqx91SnC0pVkVCcxcytz3lWW+cHBYDi3Ysh0WXc+rAYw==", + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.13.tgz", + "integrity": "sha512-RgDi5a4nuzam073lRGKTUIaL3eF2+H7LJvJ8eUnCI0wA6SNjXc44DCmWNiTLs/AZ7QlsFWZiw/gTG3nSQGL0fA==", "requires": { "@types/node": "*", + "@types/qs": "*", "@types/range-parser": "*" } }, "@types/mime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", - "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", + "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==" }, "@types/node": { - "version": "9.6.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.55.tgz", - "integrity": "sha512-e/5tg8Ok0gSrN6pvHphnwTK0/CD9VPZrtZqpvvpEFAtfs+ZntusgGaWkf2lSEq1OFe2EDPeUMiMVpy4nZpJ4AQ==" + "version": "9.6.59", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.59.tgz", + "integrity": "sha512-TX/dHK9lFrXoMFtHdF3oyEw6EpfYfu+8AZ1zP6Oj3rOiQGbit2rgQlJzvBRx712b9ReaCfkSNPRXYzZDYI4YSw==" }, "@types/qs": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.1.tgz", - "integrity": "sha512-lhbQXx9HKZAPgBkISrBcmAcMpZsmpe/Cd/hY7LGZS5OfkySUBItnPZHgQPssWYUET8elF+yCFBbP1Q0RZPTdaw==" + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", + "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==" }, "@types/range-parser": { "version": "1.2.3", @@ -72,9 +73,9 @@ "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" }, "@types/serve-static": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz", - "integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.5.tgz", + "integrity": "sha512-6M64P58N+OXjU432WoLLBQxbA0LRGBCRm7aAGQJ+SMC1IMl0dgRVi9EFfoDcS2a7Xogygk/eGN94CfwU9UF7UQ==", "requires": { "@types/express-serve-static-core": "*", "@types/mime": "*" @@ -89,13 +90,14 @@ } }, "actions-on-google": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/actions-on-google/-/actions-on-google-2.12.0.tgz", - "integrity": "sha512-D1F3hv3bEztZtUQEghJSqTkwEE2/mWptxOjUdDPqNZ4cj4TnPVZmVcZtmtKE5PtiPjx/oPdcV0yrX2NXTNKgdQ==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/actions-on-google/-/actions-on-google-2.13.0.tgz", + "integrity": "sha512-Nt6nkaAurayTWDbtvP4k++esQvGVmPOy5y2DsBmM2TXrjU/PW8ZBuDw4MDI2RyJE5gMLejbgGHToxx7SY/Gc5A==", "requires": { "@types/aws-lambda": "^0.0.33", "@types/debug": "^0.0.30", - "@types/express": "^4.11.1", + "@types/express": "~4.17.7", + "@types/express-serve-static-core": "~4.17.7", "@types/node": "^9.4.6", "debug": "^3.1.0", "google-auth-library": "^1.6.1", @@ -130,9 +132,9 @@ "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, "bignumber.js": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", - "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" }, "brace-expansion": { "version": "1.1.11", @@ -193,9 +195,9 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "fast-text-encoding": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.1.tgz", - "integrity": "sha512-x4FEgaz3zNRtJfLFqJmHWxkMDDvXVtaznj2V9jiP8ACUJrUgist4bP9FmDL2Vew2Y9mEQI/tG4GqabaitYp9CQ==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz", + "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==" }, "follow-redirects": { "version": "1.5.10", @@ -425,11 +427,11 @@ "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" }, "json-bigint": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", - "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.1.tgz", + "integrity": "sha512-DGWnSzmusIreWlEupsUelHrhwmPPE+FiQvg+drKfk2p+bdEYa5mp4PJ8JsCWqae0M2jQNb0HPvnwvf1qOTThzQ==", "requires": { - "bignumber.js": "^7.0.0" + "bignumber.js": "^9.0.0" } }, "jwa": { @@ -466,9 +468,9 @@ } }, "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" }, "minimatch": { "version": "3.0.4", @@ -484,9 +486,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "node-forge": { "version": "0.8.5", @@ -517,9 +519,9 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "qs": { - "version": "6.9.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz", - "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==" + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" }, "retry-axios": { "version": "0.3.2", @@ -527,9 +529,9 @@ "integrity": "sha512-jp4YlI0qyDFfXiXGhkCOliBN1G7fRH03Nqy8YdShzGqbY5/9S2x/IR6C88ls2DFkbWuL3ASkP7QD3pVrNpPgwQ==" }, "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "semver": { "version": "5.7.1", diff --git a/functions/package.json b/functions/package.json index d84dd5b0..81a9889c 100644 --- a/functions/package.json +++ b/functions/package.json @@ -12,7 +12,7 @@ ], "license": "EPL-2.0", "dependencies": { - "actions-on-google": "^2.12.0", + "actions-on-google": "^2.13.0", "glob": "^7.1.6" } } diff --git a/package-lock.json b/package-lock.json index 5eb2a0e6..6b0b2c0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,19 +14,19 @@ } }, "@babel/core": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.0.tgz", - "integrity": "sha512-mkLq8nwaXmDtFmRkQ8ED/eA2CnVw4zr7dCztKalZXBvdK5EeNUAesrrwUqjQEzFgomJssayzB0aqlOsP1vGLqg==", + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.6.tgz", + "integrity": "sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.0", + "@babel/generator": "^7.11.6", "@babel/helper-module-transforms": "^7.11.0", "@babel/helpers": "^7.10.4", - "@babel/parser": "^7.11.0", + "@babel/parser": "^7.11.5", "@babel/template": "^7.10.4", - "@babel/traverse": "^7.11.0", - "@babel/types": "^7.11.0", + "@babel/traverse": "^7.11.5", + "@babel/types": "^7.11.5", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", @@ -38,12 +38,21 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", "dev": true, "requires": { - "ms": "^2.1.1" + "minimist": "^1.2.5" } }, "ms": { @@ -61,12 +70,12 @@ } }, "@babel/generator": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz", - "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==", + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", + "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", "dev": true, "requires": { - "@babel/types": "^7.11.0", + "@babel/types": "^7.11.5", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -259,9 +268,9 @@ } }, "@babel/parser": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz", - "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", + "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -375,29 +384,29 @@ } }, "@babel/traverse": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", - "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", + "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.0", + "@babel/generator": "^7.11.5", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.0", - "@babel/types": "^7.11.0", + "@babel/parser": "^7.11.5", + "@babel/types": "^7.11.5", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -409,9 +418,9 @@ } }, "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -446,6 +455,57 @@ "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } } }, "@istanbuljs/schema": { @@ -455,48 +515,82 @@ "dev": true }, "@jest/console": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.2.0.tgz", - "integrity": "sha512-mXQfx3nSLwiHm1i7jbu+uvi+vvpVjNGzIQYLCfsat9rapC+MJkS4zBseNrgJE0vU921b3P67bQzhduphjY3Tig==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.3.0.tgz", + "integrity": "sha512-/5Pn6sJev0nPUcAdpJHMVIsA8sKizL2ZkcKPE5+dJrCccks7tcM7c9wbgHudBJbxXLoTbqsHkG1Dofoem4F09w==", "dev": true, "requires": { - "@jest/types": "^26.2.0", + "@jest/types": "^26.3.0", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^26.2.0", - "jest-util": "^26.2.0", + "jest-message-util": "^26.3.0", + "jest-util": "^26.3.0", "slash": "^3.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "@jest/core": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.2.2.tgz", - "integrity": "sha512-UwA8gNI8aeV4FHGfGAUfO/DHjrFVvlBravF1Tm9Kt6qFE+6YHR47kFhgdepOFpADEKstyO+MVdPvkV6/dyt9sA==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.4.2.tgz", + "integrity": "sha512-sDva7YkeNprxJfepOctzS8cAk9TOekldh+5FhVuXS40+94SHbiicRO1VV2tSoRtgIo+POs/Cdyf8p76vPTd6dg==", "dev": true, "requires": { - "@jest/console": "^26.2.0", - "@jest/reporters": "^26.2.2", - "@jest/test-result": "^26.2.0", - "@jest/transform": "^26.2.2", - "@jest/types": "^26.2.0", + "@jest/console": "^26.3.0", + "@jest/reporters": "^26.4.1", + "@jest/test-result": "^26.3.0", + "@jest/transform": "^26.3.0", + "@jest/types": "^26.3.0", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.4", - "jest-changed-files": "^26.2.0", - "jest-config": "^26.2.2", - "jest-haste-map": "^26.2.2", - "jest-message-util": "^26.2.0", + "jest-changed-files": "^26.3.0", + "jest-config": "^26.4.2", + "jest-haste-map": "^26.3.0", + "jest-message-util": "^26.3.0", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.2.2", - "jest-resolve-dependencies": "^26.2.2", - "jest-runner": "^26.2.2", - "jest-runtime": "^26.2.2", - "jest-snapshot": "^26.2.2", - "jest-util": "^26.2.0", - "jest-validate": "^26.2.0", - "jest-watcher": "^26.2.0", + "jest-resolve": "^26.4.0", + "jest-resolve-dependencies": "^26.4.2", + "jest-runner": "^26.4.2", + "jest-runtime": "^26.4.2", + "jest-snapshot": "^26.4.2", + "jest-util": "^26.3.0", + "jest-validate": "^26.4.2", + "jest-watcher": "^26.3.0", "micromatch": "^4.0.2", "p-each-series": "^2.1.0", "rimraf": "^3.0.0", @@ -504,62 +598,190 @@ "strip-ansi": "^6.0.0" }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "@jest/environment": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.2.0.tgz", - "integrity": "sha512-oCgp9NmEiJ5rbq9VI/v/yYLDpladAAVvFxZgNsnJxOETuzPZ0ZcKKHYjKYwCtPOP1WCrM5nmyuOhMStXFGHn+g==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.3.0.tgz", + "integrity": "sha512-EW+MFEo0DGHahf83RAaiqQx688qpXgl99wdb8Fy67ybyzHwR1a58LHcO376xQJHfmoXTu89M09dH3J509cx2AA==", "dev": true, "requires": { - "@jest/fake-timers": "^26.2.0", - "@jest/types": "^26.2.0", + "@jest/fake-timers": "^26.3.0", + "@jest/types": "^26.3.0", "@types/node": "*", - "jest-mock": "^26.2.0" + "jest-mock": "^26.3.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "@jest/fake-timers": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.2.0.tgz", - "integrity": "sha512-45Gfe7YzYTKqTayBrEdAF0qYyAsNRBzfkV0IyVUm3cx7AsCWlnjilBM4T40w7IXT5VspOgMPikQlV0M6gHwy/g==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.3.0.tgz", + "integrity": "sha512-ZL9ytUiRwVP8ujfRepffokBvD2KbxbqMhrXSBhSdAhISCw3gOkuntisiSFv+A6HN0n0fF4cxzICEKZENLmW+1A==", "dev": true, "requires": { - "@jest/types": "^26.2.0", + "@jest/types": "^26.3.0", "@sinonjs/fake-timers": "^6.0.1", "@types/node": "*", - "jest-message-util": "^26.2.0", - "jest-mock": "^26.2.0", - "jest-util": "^26.2.0" + "jest-message-util": "^26.3.0", + "jest-mock": "^26.3.0", + "jest-util": "^26.3.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "@jest/globals": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.2.0.tgz", - "integrity": "sha512-Hoc6ScEIPaym7RNytIL2ILSUWIGKlwEv+JNFof9dGYOdvPjb2evEURSslvCMkNuNg1ECEClTE8PH7ULlMJntYA==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.4.2.tgz", + "integrity": "sha512-Ot5ouAlehhHLRhc+sDz2/9bmNv9p5ZWZ9LE1pXGGTCXBasmi5jnYjlgYcYt03FBwLmZXCZ7GrL29c33/XRQiow==", "dev": true, "requires": { - "@jest/environment": "^26.2.0", - "@jest/types": "^26.2.0", - "expect": "^26.2.0" + "@jest/environment": "^26.3.0", + "@jest/types": "^26.3.0", + "expect": "^26.4.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "@jest/reporters": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.2.2.tgz", - "integrity": "sha512-7854GPbdFTAorWVh+RNHyPO9waRIN6TcvCezKVxI1khvFq9YjINTW7J3WU+tbR038Ynn6WjYred6vtT0YmIWVQ==", + "version": "26.4.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.4.1.tgz", + "integrity": "sha512-aROTkCLU8++yiRGVxLsuDmZsQEKO6LprlrxtAuzvtpbIFl3eIjgIf3EUxDKgomkS25R9ZzwGEdB5weCcBZlrpQ==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.2.0", - "@jest/test-result": "^26.2.0", - "@jest/transform": "^26.2.2", - "@jest/types": "^26.2.0", + "@jest/console": "^26.3.0", + "@jest/test-result": "^26.3.0", + "@jest/transform": "^26.3.0", + "@jest/types": "^26.3.0", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", @@ -570,94 +792,138 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.2.2", - "jest-resolve": "^26.2.2", - "jest-util": "^26.2.0", - "jest-worker": "^26.2.1", - "node-notifier": "^7.0.0", + "jest-haste-map": "^26.3.0", + "jest-resolve": "^26.4.0", + "jest-util": "^26.3.0", + "jest-worker": "^26.3.0", + "node-notifier": "^8.0.0", "slash": "^3.0.0", "source-map": "^0.6.0", "string-length": "^4.0.1", "terminal-link": "^2.0.0", - "v8-to-istanbul": "^4.1.3" + "v8-to-istanbul": "^5.0.1" }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "@jest/source-map": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.1.0.tgz", - "integrity": "sha512-XYRPYx4eEVX15cMT9mstnO7hkHP3krNtKfxUYd8L7gbtia8JvZZ6bMzSwa6IQJENbudTwKMw5R1BePRD+bkEmA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.3.0.tgz", + "integrity": "sha512-hWX5IHmMDWe1kyrKl7IhFwqOuAreIwHhbe44+XH2ZRHjrKIh0LO5eLQ/vxHFeAfRwJapmxuqlGAEYLadDq6ZGQ==", "dev": true, "requires": { "callsites": "^3.0.0", "graceful-fs": "^4.2.4", "source-map": "^0.6.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - } } }, "@jest/test-result": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.2.0.tgz", - "integrity": "sha512-kgPlmcVafpmfyQEu36HClK+CWI6wIaAWDHNxfQtGuKsgoa2uQAYdlxjMDBEa3CvI40+2U3v36gQF6oZBkoKatw==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.3.0.tgz", + "integrity": "sha512-a8rbLqzW/q7HWheFVMtghXV79Xk+GWwOK1FrtimpI5n1la2SY0qHri3/b0/1F0Ve0/yJmV8pEhxDfVwiUBGtgg==", "dev": true, "requires": { - "@jest/console": "^26.2.0", - "@jest/types": "^26.2.0", + "@jest/console": "^26.3.0", + "@jest/types": "^26.3.0", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "@jest/test-sequencer": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.2.2.tgz", - "integrity": "sha512-SliZWon5LNqV/lVXkeowSU6L8++FGOu3f43T01L1Gv6wnFDP00ER0utV9jyK9dVNdXqfMNCN66sfcyar/o7BNw==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.4.2.tgz", + "integrity": "sha512-83DRD8N3M0tOhz9h0bn6Kl6dSp+US6DazuVF8J9m21WAp5x7CqSMaNycMP0aemC/SH/pDQQddbsfHRTBXVUgog==", "dev": true, "requires": { - "@jest/test-result": "^26.2.0", + "@jest/test-result": "^26.3.0", "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.2.2", - "jest-runner": "^26.2.2", - "jest-runtime": "^26.2.2" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - } + "jest-haste-map": "^26.3.0", + "jest-runner": "^26.4.2", + "jest-runtime": "^26.4.2" } }, "@jest/transform": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.2.2.tgz", - "integrity": "sha512-c1snhvi5wRVre1XyoO3Eef5SEWpuBCH/cEbntBUd9tI5sNYiBDmO0My/lc5IuuGYKp/HFIHV1eZpSx5yjdkhKw==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.3.0.tgz", + "integrity": "sha512-Isj6NB68QorGoFWvcOjlUhpkT56PqNIsXKR7XfvoDlCANn/IANlh8DrKAA2l2JKC3yWSMH5wS0GwuQM20w3b2A==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^26.2.0", + "@jest/types": "^26.3.0", "babel-plugin-istanbul": "^6.0.0", "chalk": "^4.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.2.2", + "jest-haste-map": "^26.3.0", "jest-regex-util": "^26.0.0", - "jest-util": "^26.2.0", + "jest-util": "^26.3.0", "micromatch": "^4.0.2", "pirates": "^4.0.1", "slash": "^3.0.0", @@ -665,25 +931,50 @@ "write-file-atomic": "^3.0.0" }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "@jest/types": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz", - "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==", + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", - "@types/node": "*", "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" + "chalk": "^3.0.0" } }, "@sinonjs/commons": { @@ -705,9 +996,9 @@ } }, "@types/babel__core": { - "version": "7.1.9", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz", - "integrity": "sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw==", + "version": "7.1.10", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.10.tgz", + "integrity": "sha512-x8OM8XzITIMyiwl5Vmo2B1cR1S1Ipkyv4mdlbJjMa1lmuKvKY9FrBbEANIaMlnWn5Rf7uO+rC/VgYabNkE17Hw==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -718,18 +1009,18 @@ } }, "@types/babel__generator": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", - "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", "dev": true, "requires": { "@babel/types": "^7.0.0" } }, "@types/babel__template": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", - "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.3.tgz", + "integrity": "sha512-uCoznIPDmnickEi6D0v11SBpW0OuVqHJCa7syXqQHy5uktSCreIlt0iglsCnmvz8yCb38hGcWeseA8cWJSwv5Q==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -737,9 +1028,9 @@ } }, "@types/babel__traverse": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.13.tgz", - "integrity": "sha512-i+zS7t6/s9cdQvbqKDARrcbrPvtJGlbYsMkazo03nTAK3RX9FNrLllXys22uiTGJapPOTZTQ35nHh4ISph4SLQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.15.tgz", + "integrity": "sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==", "dev": true, "requires": { "@babel/types": "^7.3.0" @@ -786,73 +1077,13 @@ } }, "@types/jest": { - "version": "26.0.8", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.8.tgz", - "integrity": "sha512-eo3VX9jGASSuv680D4VQ89UmuLZneNxv2MCZjfwlInav05zXVJTzfc//lavdV0GPwSxsXJTy2jALscB7Acqg0g==", + "version": "26.0.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.14.tgz", + "integrity": "sha512-Hz5q8Vu0D288x3iWXePSn53W7hAjP0H7EQ6QvDO9c7t46mR0lNOLlfuwQ+JkVxuhygHzlzPX+0jKdA3ZgSh+Vg==", "dev": true, "requires": { "jest-diff": "^25.2.1", "pretty-format": "^25.2.1" - }, - "dependencies": { - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "diff-sequences": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz", - "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", - "dev": true - }, - "jest-diff": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", - "integrity": "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==", - "dev": true, - "requires": { - "chalk": "^3.0.0", - "diff-sequences": "^25.2.6", - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" - } - }, - "jest-get-type": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", - "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", - "dev": true - }, - "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - } } }, "@types/json5": { @@ -862,9 +1093,9 @@ "dev": true }, "@types/node": { - "version": "14.0.27", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz", - "integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g==", + "version": "14.11.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz", + "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==", "dev": true }, "@types/normalize-package-data": { @@ -874,9 +1105,9 @@ "dev": true }, "@types/prettier": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.0.2.tgz", - "integrity": "sha512-IkVfat549ggtkZUthUzEX49562eGikhSYeVGX97SkMFn+sTZrgRewXjQ4tPKFPCykZHkX1Zfd9OoELGqKU2jJA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.1.tgz", + "integrity": "sha512-2zs+O+UkDsJ1Vcp667pd3f8xearMdopz/z54i99wtRDI5KLmngk7vlrYZD0ZjKHaROR03EznlBbVY9PfAEyJIQ==", "dev": true }, "@types/stack-utils": { @@ -886,9 +1117,9 @@ "dev": true }, "@types/yargs": { - "version": "15.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz", - "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==", + "version": "15.0.7", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.7.tgz", + "integrity": "sha512-Gf4u3EjaPNcC9cTu4/j2oN14nSVhr8PQ+BvBcBQHAhDZfl0bVIiLgvnRXv/dn58XhTm9UXvBpvJpDlwV65QxOA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -901,9 +1132,9 @@ "dev": true }, "abab": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", - "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", "dev": true }, "accepts": { @@ -916,9 +1147,9 @@ } }, "acorn": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", - "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", + "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", "dev": true }, "acorn-globals": { @@ -938,9 +1169,9 @@ "dev": true }, "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "version": "6.12.5", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", + "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -1091,32 +1322,58 @@ "dev": true }, "aws4": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", - "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", + "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", "dev": true }, "babel-jest": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.2.2.tgz", - "integrity": "sha512-JmLuePHgA+DSOdOL8lPxCgD2LhPPm+rdw1vnxR73PpIrnmKCS2/aBhtkAcxQWuUcW2hBrH8MJ3LKXE7aWpNZyA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.3.0.tgz", + "integrity": "sha512-sxPnQGEyHAOPF8NcUsD0g7hDCnvLL2XyblRBcgrzTWBB/mAIpWow3n1bEL+VghnnZfreLhFSBsFluRoK2tRK4g==", "dev": true, "requires": { - "@jest/transform": "^26.2.2", - "@jest/types": "^26.2.0", + "@jest/transform": "^26.3.0", + "@jest/types": "^26.3.0", "@types/babel__core": "^7.1.7", "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.2.0", + "babel-preset-jest": "^26.3.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "slash": "^3.0.0" }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, @@ -1165,13 +1422,13 @@ } }, "babel-preset-jest": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.2.0.tgz", - "integrity": "sha512-R1k8kdP3R9phYQugXeNnK/nvCGlBzG4m3EoIIukC80GXb6wCv2XiwPhK6K9MAkQcMszWBYvl2Wm+yigyXFQqXg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.3.0.tgz", + "integrity": "sha512-5WPdf7nyYi2/eRxCbVrE1kKCWxgWY4RsPEbdJWFm7QsesFGqjdkyLeu1zRkwM1cxK6EPIlNd6d2AxLk7J+t4pw==", "dev": true, "requires": { "babel-plugin-jest-hoist": "^26.2.0", - "babel-preset-current-node-syntax": "^0.1.2" + "babel-preset-current-node-syntax": "^0.1.3" } }, "balanced-match": { @@ -1351,9 +1608,9 @@ "dev": true }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -1585,9 +1842,9 @@ "dev": true }, "decimal.js": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", - "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", "dev": true }, "decode-uri-component": { @@ -1681,9 +1938,9 @@ "dev": true }, "diff-sequences": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.0.0.tgz", - "integrity": "sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg==", + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz", + "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", "dev": true }, "doctrine": { @@ -1764,22 +2021,22 @@ } }, "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, "es-to-primitive": { @@ -1841,60 +2098,6 @@ "requires": { "debug": "^2.6.9", "pkg-dir": "^2.0.0" - }, - "dependencies": { - "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" - } - }, - "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" - } - }, - "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 - }, - "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" - } - } } }, "eslint-plugin-es": { @@ -1908,9 +2111,9 @@ } }, "eslint-plugin-import": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz", - "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==", + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", + "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", "dev": true, "requires": { "array-includes": "^3.1.1", @@ -1918,7 +2121,7 @@ "contains-path": "^0.1.0", "debug": "^2.6.9", "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.3", + "eslint-import-resolver-node": "^0.3.4", "eslint-module-utils": "^2.6.0", "has": "^1.0.3", "minimatch": "^3.0.4", @@ -1928,106 +2131,6 @@ "tsconfig-paths": "^3.9.0" }, "dependencies": { - "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" - } - }, - "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" - } - }, - "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 - }, - "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" - } - }, - "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" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "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" - } - }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", @@ -2174,17 +2277,57 @@ } }, "expect": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.2.0.tgz", - "integrity": "sha512-8AMBQ9UVcoUXt0B7v+5/U5H6yiUR87L6eKCfjE3spx7Ya5lF+ebUo37MCFBML2OiLfkX1sxmQOZhIDonyVTkcw==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.4.2.tgz", + "integrity": "sha512-IlJ3X52Z0lDHm7gjEp+m76uX46ldH5VpqmU0006vqDju/285twh7zaWMRhs67VpQhBwjjMchk+p5aA0VkERCAA==", "dev": true, "requires": { - "@jest/types": "^26.2.0", + "@jest/types": "^26.3.0", "ansi-styles": "^4.0.0", - "jest-get-type": "^26.0.0", - "jest-matcher-utils": "^26.2.0", - "jest-message-util": "^26.2.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.4.2", + "jest-message-util": "^26.3.0", "jest-regex-util": "^26.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true + } } }, "express": { @@ -2373,21 +2516,12 @@ } }, "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "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": "^5.0.0", - "path-exists": "^4.0.0" - }, - "dependencies": { - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - } + "locate-path": "^2.0.0" } }, "for-in": { @@ -2514,9 +2648,9 @@ "dev": true }, "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, "growly": { @@ -2687,6 +2821,66 @@ "requires": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + } } }, "imurmurhash": { @@ -2754,9 +2948,9 @@ "dev": true }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", "dev": true }, "is-ci": { @@ -2814,9 +3008,9 @@ } }, "is-docker": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", - "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", "dev": true, "optional": true }, @@ -2838,6 +3032,12 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -2860,12 +3060,12 @@ "dev": true }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "dev": true, "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "is-stream": { @@ -2984,12 +3184,12 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -3011,39 +3211,65 @@ } }, "jest": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/jest/-/jest-26.2.2.tgz", - "integrity": "sha512-EkJNyHiAG1+A8pqSz7cXttoVa34hOEzN/MrnJhYnfp5VHxflVcf2pu3oJSrhiy6LfIutLdWo+n6q63tjcoIeig==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.4.2.tgz", + "integrity": "sha512-LLCjPrUh98Ik8CzW8LLVnSCfLaiY+wbK53U7VxnFSX7Q+kWC4noVeDvGWIFw0Amfq1lq2VfGm7YHWSLBV62MJw==", "dev": true, "requires": { - "@jest/core": "^26.2.2", + "@jest/core": "^26.4.2", "import-local": "^3.0.2", - "jest-cli": "^26.2.2" + "jest-cli": "^26.4.2" }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } }, "jest-cli": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.2.2.tgz", - "integrity": "sha512-vVcly0n/ijZvdy6gPQiQt0YANwX2hLTPQZHtW7Vi3gcFdKTtif7YpI85F8R8JYy5DFSWz4x1OW0arnxlziu5Lw==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.4.2.tgz", + "integrity": "sha512-zb+lGd/SfrPvoRSC/0LWdaWCnscXc1mGYW//NP4/tmBvRPT3VntZ2jtKUONsRi59zc5JqmsSajA9ewJKFYp8Cw==", "dev": true, "requires": { - "@jest/core": "^26.2.2", - "@jest/test-result": "^26.2.0", - "@jest/types": "^26.2.0", + "@jest/core": "^26.4.2", + "@jest/test-result": "^26.3.0", + "@jest/types": "^26.3.0", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.4", "import-local": "^3.0.2", "is-ci": "^2.0.0", - "jest-config": "^26.2.2", - "jest-util": "^26.2.0", - "jest-validate": "^26.2.0", + "jest-config": "^26.4.2", + "jest-util": "^26.3.0", + "jest-validate": "^26.4.2", "prompts": "^2.0.1", "yargs": "^15.3.1" } @@ -3051,16 +3277,48 @@ } }, "jest-changed-files": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.2.0.tgz", - "integrity": "sha512-+RyJb+F1K/XBLIYiL449vo5D+CvlHv29QveJUWNPXuUicyZcq+tf1wNxmmFeRvAU1+TzhwqczSjxnCCFt7+8iA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.3.0.tgz", + "integrity": "sha512-1C4R4nijgPltX6fugKxM4oQ18zimS7LqQ+zTTY8lMCMFPrxqBFb7KJH0Z2fRQJvw2Slbaipsqq7s1mgX5Iot+g==", "dev": true, "requires": { - "@jest/types": "^26.2.0", + "@jest/types": "^26.3.0", "execa": "^4.0.0", "throat": "^5.0.0" }, "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3090,9 +3348,9 @@ } }, "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "requires": { "pump": "^3.0.0" @@ -3146,49 +3404,93 @@ } }, "jest-config": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.2.2.tgz", - "integrity": "sha512-2lhxH0y4YFOijMJ65usuf78m7+9/8+hAb1PZQtdRdgnQpAb4zP6KcVDDktpHEkspBKnc2lmFu+RQdHukUUbiTg==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.4.2.tgz", + "integrity": "sha512-QBf7YGLuToiM8PmTnJEdRxyYy3mHWLh24LJZKVdXZ2PNdizSe1B/E8bVm+HYcjbEzGuVXDv/di+EzdO/6Gq80A==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.2.2", - "@jest/types": "^26.2.0", - "babel-jest": "^26.2.2", + "@jest/test-sequencer": "^26.4.2", + "@jest/types": "^26.3.0", + "babel-jest": "^26.3.0", "chalk": "^4.0.0", "deepmerge": "^4.2.2", "glob": "^7.1.1", "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.2.0", - "jest-environment-node": "^26.2.0", - "jest-get-type": "^26.0.0", - "jest-jasmine2": "^26.2.2", + "jest-environment-jsdom": "^26.3.0", + "jest-environment-node": "^26.3.0", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.4.2", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.2.2", - "jest-util": "^26.2.0", - "jest-validate": "^26.2.0", + "jest-resolve": "^26.4.0", + "jest-util": "^26.3.0", + "jest-validate": "^26.4.2", "micromatch": "^4.0.2", - "pretty-format": "^26.2.0" + "pretty-format": "^26.4.2" }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - } - } - }, - "jest-diff": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.2.0.tgz", - "integrity": "sha512-Wu4Aopi2nzCsHWLBlD48TgRy3Z7OsxlwvHNd1YSnHc7q1NJfrmyCPoUXrTIrydQOG5ApaYpsAsdfnMbJqV1/wQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.0.0", - "jest-get-type": "^26.0.0", - "pretty-format": "^26.2.0" + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true + }, + "pretty-format": { + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", + "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", + "dev": true, + "requires": { + "@jest/types": "^26.3.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + } + } + }, + "jest-diff": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", + "integrity": "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "diff-sequences": "^25.2.6", + "jest-get-type": "^25.2.6", + "pretty-format": "^25.5.0" } }, "jest-docblock": { @@ -3201,60 +3503,180 @@ } }, "jest-each": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.2.0.tgz", - "integrity": "sha512-gHPCaho1twWHB5bpcfnozlc6mrMi+VAewVPNgmwf81x2Gzr6XO4dl+eOrwPWxbkYlgjgrYjWK2xgKnixbzH3Ew==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.4.2.tgz", + "integrity": "sha512-p15rt8r8cUcRY0Mvo1fpkOGYm7iI8S6ySxgIdfh3oOIv+gHwrHTy5VWCGOecWUhDsit4Nz8avJWdT07WLpbwDA==", "dev": true, "requires": { - "@jest/types": "^26.2.0", + "@jest/types": "^26.3.0", "chalk": "^4.0.0", - "jest-get-type": "^26.0.0", - "jest-util": "^26.2.0", - "pretty-format": "^26.2.0" + "jest-get-type": "^26.3.0", + "jest-util": "^26.3.0", + "pretty-format": "^26.4.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true + }, + "pretty-format": { + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", + "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", + "dev": true, + "requires": { + "@jest/types": "^26.3.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + } } }, "jest-environment-jsdom": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.2.0.tgz", - "integrity": "sha512-sDG24+5M4NuIGzkI3rJW8XUlrpkvIdE9Zz4jhD8OBnVxAw+Y1jUk9X+lAOD48nlfUTlnt3lbAI3k2Ox+WF3S0g==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.3.0.tgz", + "integrity": "sha512-zra8He2btIMJkAzvLaiZ9QwEPGEetbxqmjEBQwhH3CA+Hhhu0jSiEJxnJMbX28TGUvPLxBt/zyaTLrOPF4yMJA==", "dev": true, "requires": { - "@jest/environment": "^26.2.0", - "@jest/fake-timers": "^26.2.0", - "@jest/types": "^26.2.0", + "@jest/environment": "^26.3.0", + "@jest/fake-timers": "^26.3.0", + "@jest/types": "^26.3.0", "@types/node": "*", - "jest-mock": "^26.2.0", - "jest-util": "^26.2.0", + "jest-mock": "^26.3.0", + "jest-util": "^26.3.0", "jsdom": "^16.2.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-environment-node": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.2.0.tgz", - "integrity": "sha512-4M5ExTYkJ19efBzkiXtBi74JqKLDciEk4CEsp5tTjWGYMrlKFQFtwIVG3tW1OGE0AlXhZjuHPwubuRYY4j4uOw==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.3.0.tgz", + "integrity": "sha512-c9BvYoo+FGcMj5FunbBgtBnbR5qk3uky8PKyRVpSfe2/8+LrNQMiXX53z6q2kY+j15SkjQCOSL/6LHnCPLVHNw==", "dev": true, "requires": { - "@jest/environment": "^26.2.0", - "@jest/fake-timers": "^26.2.0", - "@jest/types": "^26.2.0", + "@jest/environment": "^26.3.0", + "@jest/fake-timers": "^26.3.0", + "@jest/types": "^26.3.0", "@types/node": "*", - "jest-mock": "^26.2.0", - "jest-util": "^26.2.0" + "jest-mock": "^26.3.0", + "jest-util": "^26.3.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-get-type": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", - "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==", + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", + "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", "dev": true }, "jest-haste-map": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.2.2.tgz", - "integrity": "sha512-3sJlMSt+NHnzCB+0KhJ1Ut4zKJBiJOlbrqEYNdRQGlXTv8kqzZWjUKQRY3pkjmlf+7rYjAV++MQ4D6g4DhAyOg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.3.0.tgz", + "integrity": "sha512-DHWBpTJgJhLLGwE5Z1ZaqLTYqeODQIZpby0zMBsCU9iRFHYyhklYqP4EiG73j5dkbaAdSZhgB938mL51Q5LeZA==", "dev": true, "requires": { - "@jest/types": "^26.2.0", + "@jest/types": "^26.3.0", "@types/graceful-fs": "^4.1.2", "@types/node": "*", "anymatch": "^3.0.3", @@ -3262,78 +3684,272 @@ "fsevents": "^2.1.2", "graceful-fs": "^4.2.4", "jest-regex-util": "^26.0.0", - "jest-serializer": "^26.2.0", - "jest-util": "^26.2.0", - "jest-worker": "^26.2.1", + "jest-serializer": "^26.3.0", + "jest-util": "^26.3.0", + "jest-worker": "^26.3.0", "micromatch": "^4.0.2", "sane": "^4.0.3", "walker": "^1.0.7" }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "jest-jasmine2": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.2.2.tgz", - "integrity": "sha512-Q8AAHpbiZMVMy4Hz9j1j1bg2yUmPa1W9StBvcHqRaKa9PHaDUMwds8LwaDyzP/2fkybcTQE4+pTMDOG9826tEw==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.4.2.tgz", + "integrity": "sha512-z7H4EpCldHN1J8fNgsja58QftxBSL+JcwZmaXIvV9WKIM+x49F4GLHu/+BQh2kzRKHAgaN/E82od+8rTOBPyPA==", "dev": true, "requires": { "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.2.0", - "@jest/source-map": "^26.1.0", - "@jest/test-result": "^26.2.0", - "@jest/types": "^26.2.0", + "@jest/environment": "^26.3.0", + "@jest/source-map": "^26.3.0", + "@jest/test-result": "^26.3.0", + "@jest/types": "^26.3.0", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", - "expect": "^26.2.0", + "expect": "^26.4.2", "is-generator-fn": "^2.0.0", - "jest-each": "^26.2.0", - "jest-matcher-utils": "^26.2.0", - "jest-message-util": "^26.2.0", - "jest-runtime": "^26.2.2", - "jest-snapshot": "^26.2.2", - "jest-util": "^26.2.0", - "pretty-format": "^26.2.0", + "jest-each": "^26.4.2", + "jest-matcher-utils": "^26.4.2", + "jest-message-util": "^26.3.0", + "jest-runtime": "^26.4.2", + "jest-snapshot": "^26.4.2", + "jest-util": "^26.3.0", + "pretty-format": "^26.4.2", "throat": "^5.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "pretty-format": { + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", + "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", + "dev": true, + "requires": { + "@jest/types": "^26.3.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + } } }, "jest-leak-detector": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.2.0.tgz", - "integrity": "sha512-aQdzTX1YiufkXA1teXZu5xXOJgy7wZQw6OJ0iH5CtQlOETe6gTSocaYKUNui1SzQ91xmqEUZ/WRavg9FD82rtQ==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.4.2.tgz", + "integrity": "sha512-akzGcxwxtE+9ZJZRW+M2o+nTNnmQZxrHJxX/HjgDaU5+PLmY1qnQPnMjgADPGCRPhB+Yawe1iij0REe+k/aHoA==", "dev": true, "requires": { - "jest-get-type": "^26.0.0", - "pretty-format": "^26.2.0" + "jest-get-type": "^26.3.0", + "pretty-format": "^26.4.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true + }, + "pretty-format": { + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", + "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", + "dev": true, + "requires": { + "@jest/types": "^26.3.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + } } }, "jest-matcher-utils": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.2.0.tgz", - "integrity": "sha512-2cf/LW2VFb3ayPHrH36ZDjp9+CAeAe/pWBAwsV8t3dKcrINzXPVxq8qMWOxwt5BaeBCx4ZupVGH7VIgB8v66vQ==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.4.2.tgz", + "integrity": "sha512-KcbNqWfWUG24R7tu9WcAOKKdiXiXCbMvQYT6iodZ9k1f7065k0keUOW6XpJMMvah+hTfqkhJhRXmA3r3zMAg0Q==", "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^26.2.0", - "jest-get-type": "^26.0.0", - "pretty-format": "^26.2.0" + "jest-diff": "^26.4.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.4.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "diff-sequences": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.3.0.tgz", + "integrity": "sha512-5j5vdRcw3CNctePNYN0Wy2e/JbWT6cAYnXv5OuqPhDpyCGc0uLu2TK0zOCJWNB9kOIfYMSpIulRaDgIi4HJ6Ig==", + "dev": true + }, + "jest-diff": { + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.4.2.tgz", + "integrity": "sha512-6T1XQY8U28WH0Z5rGpQ+VqZSZz8EN8rZcBtfvXaOkbwxIEeRre6qnuZQlbY1AJ4MKDxQF8EkrCvK+hL/VkyYLQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^26.3.0", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.4.2" + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true + }, + "pretty-format": { + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", + "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", + "dev": true, + "requires": { + "@jest/types": "^26.3.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + } } }, "jest-message-util": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.2.0.tgz", - "integrity": "sha512-g362RhZaJuqeqG108n1sthz5vNpzTNy926eNDszo4ncRbmmcMRIUAZibnd6s5v2XSBCChAxQtCoN25gnzp7JbQ==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.3.0.tgz", + "integrity": "sha512-xIavRYqr4/otGOiLxLZGj3ieMmjcNE73Ui+LdSW/Y790j5acqCsAdDiLIbzHCZMpN07JOENRWX5DcU+OQ+TjTA==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.2.0", + "@jest/types": "^26.3.0", "@types/stack-utils": "^1.0.1", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", @@ -3342,22 +3958,82 @@ "stack-utils": "^2.0.2" }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "jest-mock": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.2.0.tgz", - "integrity": "sha512-XeC7yWtWmWByoyVOHSsE7NYsbXJLtJNgmhD7z4MKumKm6ET0si81bsSLbQ64L5saK3TgsHo2B/UqG5KNZ1Sp/Q==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.3.0.tgz", + "integrity": "sha512-PeaRrg8Dc6mnS35gOo/CbZovoDPKAeB1FICZiuagAgGvbWdNNyjQjkOaGUa/3N3JtpQ/Mh9P4A2D4Fv51NnP8Q==", "dev": true, "requires": { - "@jest/types": "^26.2.0", + "@jest/types": "^26.3.0", "@types/node": "*" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-pnp-resolver": { @@ -3373,27 +4049,145 @@ "dev": true }, "jest-resolve": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.2.2.tgz", - "integrity": "sha512-ye9Tj/ILn/0OgFPE/3dGpQPUqt4dHwIocxt5qSBkyzxQD8PbL0bVxBogX2FHxsd3zJA7V2H/cHXnBnNyyT9YoQ==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.4.0.tgz", + "integrity": "sha512-bn/JoZTEXRSlEx3+SfgZcJAVuTMOksYq9xe9O6s4Ekg84aKBObEaVXKOEilULRqviSLAYJldnoWV9c07kwtiCg==", "dev": true, "requires": { - "@jest/types": "^26.2.0", + "@jest/types": "^26.3.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.2.0", + "jest-util": "^26.3.0", "read-pkg-up": "^7.0.1", "resolve": "^1.17.0", "slash": "^3.0.0" }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", @@ -3406,91 +4200,177 @@ } }, "jest-resolve-dependencies": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.2.2.tgz", - "integrity": "sha512-S5vufDmVbQXnpP7435gr710xeBGUFcKNpNswke7RmFvDQtmqPjPVU/rCeMlEU0p6vfpnjhwMYeaVjKZAy5QYJA==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.4.2.tgz", + "integrity": "sha512-ADHaOwqEcVc71uTfySzSowA/RdxUpCxhxa2FNLiin9vWLB1uLPad3we+JSSROq5+SrL9iYPdZZF8bdKM7XABTQ==", "dev": true, "requires": { - "@jest/types": "^26.2.0", + "@jest/types": "^26.3.0", "jest-regex-util": "^26.0.0", - "jest-snapshot": "^26.2.2" + "jest-snapshot": "^26.4.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-runner": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.2.2.tgz", - "integrity": "sha512-/qb6ptgX+KQ+aNMohJf1We695kaAfuu3u3ouh66TWfhTpLd9WbqcF6163d/tMoEY8GqPztXPLuyG0rHRVDLxCA==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.4.2.tgz", + "integrity": "sha512-FgjDHeVknDjw1gRAYaoUoShe1K3XUuFMkIaXbdhEys+1O4bEJS8Avmn4lBwoMfL8O5oFTdWYKcf3tEJyyYyk8g==", "dev": true, "requires": { - "@jest/console": "^26.2.0", - "@jest/environment": "^26.2.0", - "@jest/test-result": "^26.2.0", - "@jest/types": "^26.2.0", + "@jest/console": "^26.3.0", + "@jest/environment": "^26.3.0", + "@jest/test-result": "^26.3.0", + "@jest/types": "^26.3.0", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.7.1", "exit": "^0.1.2", "graceful-fs": "^4.2.4", - "jest-config": "^26.2.2", + "jest-config": "^26.4.2", "jest-docblock": "^26.0.0", - "jest-haste-map": "^26.2.2", - "jest-leak-detector": "^26.2.0", - "jest-message-util": "^26.2.0", - "jest-resolve": "^26.2.2", - "jest-runtime": "^26.2.2", - "jest-util": "^26.2.0", - "jest-worker": "^26.2.1", + "jest-haste-map": "^26.3.0", + "jest-leak-detector": "^26.4.2", + "jest-message-util": "^26.3.0", + "jest-resolve": "^26.4.0", + "jest-runtime": "^26.4.2", + "jest-util": "^26.3.0", + "jest-worker": "^26.3.0", "source-map-support": "^0.5.6", "throat": "^5.0.0" }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "jest-runtime": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.2.2.tgz", - "integrity": "sha512-a8VXM3DxCDnCIdl9+QucWFfQ28KdqmyVFqeKLigHdErtsx56O2ZIdQkhFSuP1XtVrG9nTNHbKxjh5XL1UaFDVQ==", - "dev": true, - "requires": { - "@jest/console": "^26.2.0", - "@jest/environment": "^26.2.0", - "@jest/fake-timers": "^26.2.0", - "@jest/globals": "^26.2.0", - "@jest/source-map": "^26.1.0", - "@jest/test-result": "^26.2.0", - "@jest/transform": "^26.2.2", - "@jest/types": "^26.2.0", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.4.2.tgz", + "integrity": "sha512-4Pe7Uk5a80FnbHwSOk7ojNCJvz3Ks2CNQWT5Z7MJo4tX0jb3V/LThKvD9tKPNVNyeMH98J/nzGlcwc00R2dSHQ==", + "dev": true, + "requires": { + "@jest/console": "^26.3.0", + "@jest/environment": "^26.3.0", + "@jest/fake-timers": "^26.3.0", + "@jest/globals": "^26.4.2", + "@jest/source-map": "^26.3.0", + "@jest/test-result": "^26.3.0", + "@jest/transform": "^26.3.0", + "@jest/types": "^26.3.0", "@types/yargs": "^15.0.0", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.4", - "jest-config": "^26.2.2", - "jest-haste-map": "^26.2.2", - "jest-message-util": "^26.2.0", - "jest-mock": "^26.2.0", + "jest-config": "^26.4.2", + "jest-haste-map": "^26.3.0", + "jest-message-util": "^26.3.0", + "jest-mock": "^26.3.0", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.2.2", - "jest-snapshot": "^26.2.2", - "jest-util": "^26.2.0", - "jest-validate": "^26.2.0", + "jest-resolve": "^26.4.0", + "jest-snapshot": "^26.4.2", + "jest-util": "^26.3.0", + "jest-validate": "^26.4.2", "slash": "^3.0.0", "strip-bom": "^4.0.0", "yargs": "^15.3.1" }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } }, "strip-bom": { "version": "4.0.0", @@ -3501,52 +4381,106 @@ } }, "jest-serializer": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.2.0.tgz", - "integrity": "sha512-V7snZI9IVmyJEu0Qy0inmuXgnMWDtrsbV2p9CRAcmlmPVwpC2ZM8wXyYpiugDQnwLHx0V4+Pnog9Exb3UO8M6Q==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.3.0.tgz", + "integrity": "sha512-IDRBQBLPlKa4flg77fqg0n/pH87tcRKwe8zxOVTWISxGpPHYkRZ1dXKyh04JOja7gppc60+soKVZ791mruVdow==", "dev": true, "requires": { "@types/node": "*", "graceful-fs": "^4.2.4" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - } } }, "jest-snapshot": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.2.2.tgz", - "integrity": "sha512-NdjD8aJS7ePu268Wy/n/aR1TUisG0BOY+QOW4f6h46UHEKOgYmmkvJhh2BqdVZQ0BHSxTMt04WpCf9njzx8KtA==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.4.2.tgz", + "integrity": "sha512-N6Uub8FccKlf5SBFnL2Ri/xofbaA68Cc3MGjP/NuwgnsvWh+9hLIR/DhrxbSiKXMY9vUW5dI6EW1eHaDHqe9sg==", "dev": true, "requires": { "@babel/types": "^7.0.0", - "@jest/types": "^26.2.0", + "@jest/types": "^26.3.0", "@types/prettier": "^2.0.0", "chalk": "^4.0.0", - "expect": "^26.2.0", + "expect": "^26.4.2", "graceful-fs": "^4.2.4", - "jest-diff": "^26.2.0", - "jest-get-type": "^26.0.0", - "jest-haste-map": "^26.2.2", - "jest-matcher-utils": "^26.2.0", - "jest-message-util": "^26.2.0", - "jest-resolve": "^26.2.2", + "jest-diff": "^26.4.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.3.0", + "jest-matcher-utils": "^26.4.2", + "jest-message-util": "^26.3.0", + "jest-resolve": "^26.4.0", "natural-compare": "^1.4.0", - "pretty-format": "^26.2.0", + "pretty-format": "^26.4.2", "semver": "^7.3.2" }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "diff-sequences": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.3.0.tgz", + "integrity": "sha512-5j5vdRcw3CNctePNYN0Wy2e/JbWT6cAYnXv5OuqPhDpyCGc0uLu2TK0zOCJWNB9kOIfYMSpIulRaDgIi4HJ6Ig==", + "dev": true + }, + "jest-diff": { + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.4.2.tgz", + "integrity": "sha512-6T1XQY8U28WH0Z5rGpQ+VqZSZz8EN8rZcBtfvXaOkbwxIEeRre6qnuZQlbY1AJ4MKDxQF8EkrCvK+hL/VkyYLQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^26.3.0", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.4.2" + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", "dev": true }, + "pretty-format": { + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", + "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", + "dev": true, + "requires": { + "@jest/types": "^26.3.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + }, "semver": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", @@ -3556,12 +4490,12 @@ } }, "jest-util": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.2.0.tgz", - "integrity": "sha512-YmDwJxLZ1kFxpxPfhSJ0rIkiZOM0PQbRcfH0TzJOhqCisCAsI1WcmoQqO83My9xeVA2k4n+rzg2UuexVKzPpig==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.3.0.tgz", + "integrity": "sha512-4zpn6bwV0+AMFN0IYhH/wnzIQzRaYVrz1A8sYnRnj4UXDXbOVtWmlaZkO9mipFqZ13okIfN87aDoJWB7VH6hcw==", "dev": true, "requires": { - "@jest/types": "^26.2.0", + "@jest/types": "^26.3.0", "@types/node": "*", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", @@ -3569,55 +4503,165 @@ "micromatch": "^4.0.2" }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } } } }, "jest-validate": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.2.0.tgz", - "integrity": "sha512-8XKn3hM6VIVmLNuyzYLCPsRCT83o8jMZYhbieh4dAyKLc4Ypr36rVKC+c8WMpWkfHHpGnEkvWUjjIAyobEIY/Q==", + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.4.2.tgz", + "integrity": "sha512-blft+xDX7XXghfhY0mrsBCYhX365n8K5wNDC4XAcNKqqjEzsRUSXP44m6PL0QJEW2crxQFLLztVnJ4j7oPlQrQ==", "dev": true, "requires": { - "@jest/types": "^26.2.0", + "@jest/types": "^26.3.0", "camelcase": "^6.0.0", "chalk": "^4.0.0", - "jest-get-type": "^26.0.0", + "jest-get-type": "^26.3.0", "leven": "^3.1.0", - "pretty-format": "^26.2.0" + "pretty-format": "^26.4.2" }, "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, "camelcase": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz", "integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==", "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true + }, + "pretty-format": { + "version": "26.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", + "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", + "dev": true, + "requires": { + "@jest/types": "^26.3.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } } } }, "jest-watcher": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.2.0.tgz", - "integrity": "sha512-674Boco4Joe0CzgKPL6K4Z9LgyLx+ZvW2GilbpYb8rFEUkmDGgsZdv1Hv5rxsRpb1HLgKUOL/JfbttRCuFdZXQ==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.3.0.tgz", + "integrity": "sha512-XnLdKmyCGJ3VoF6G/p5ohbJ04q/vv5aH9ENI+i6BL0uu9WWB6Z7Z2lhQQk0d2AVZcRGp1yW+/TsoToMhBFPRdQ==", "dev": true, "requires": { - "@jest/test-result": "^26.2.0", - "@jest/types": "^26.2.0", + "@jest/test-result": "^26.3.0", + "@jest/types": "^26.3.0", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "jest-util": "^26.2.0", + "jest-util": "^26.3.0", "string-length": "^4.0.1" + }, + "dependencies": { + "@jest/types": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", + "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "jest-worker": { - "version": "26.2.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.2.1.tgz", - "integrity": "sha512-+XcGMMJDTeEGncRb5M5Zq9P7K4sQ1sirhjdOxsN1462h6lFo9w59bl2LVQmdGEEeU3m+maZCkS2Tcc9SfCHO4A==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.3.0.tgz", + "integrity": "sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw==", "dev": true, "requires": { "@types/node": "*", @@ -3648,9 +4692,9 @@ "dev": true }, "jsdom": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.3.0.tgz", - "integrity": "sha512-zggeX5UuEknpdZzv15+MS1dPYG0J/TftiiNunOeNxSl3qr8Z6cIlQpN0IdJa44z9aFxZRIVqRncvEhQ7X5DtZg==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", + "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", "dev": true, "requires": { "abab": "^2.0.3", @@ -3687,10 +4731,10 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, "json-schema": { @@ -3712,12 +4756,12 @@ "dev": true }, "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "^1.2.0" } }, "jsprim": { @@ -3766,19 +4810,32 @@ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", "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": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "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": "^4.1.0" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" } }, "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "dev": true }, "lodash.sortby": { @@ -3973,9 +5030,9 @@ "dev": true }, "node-notifier": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-7.0.2.tgz", - "integrity": "sha512-ux+n4hPVETuTL8+daJXTOC6uKLgMsl1RYfFv7DKRzyvzBapqco0rZZ9g72ZN8VS6V+gvNYHYa/ofcCY8fkJWsA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.0.tgz", + "integrity": "sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA==", "dev": true, "optional": true, "requires": { @@ -3983,7 +5040,7 @@ "is-wsl": "^2.2.0", "semver": "^7.3.2", "shellwords": "^0.1.1", - "uuid": "^8.2.0", + "uuid": "^8.3.0", "which": "^2.0.2" }, "dependencies": { @@ -4077,9 +5134,9 @@ } }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", "dev": true }, "object-keys": { @@ -4098,15 +5155,37 @@ } }, "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } } }, "object.pick": { @@ -4148,9 +5227,9 @@ } }, "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { "mimic-fn": "^2.1.0" @@ -4183,39 +5262,36 @@ "dev": true }, "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "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": "^2.0.0" + "p-try": "^1.0.0" } }, "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "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": "^2.2.0" + "p-limit": "^1.1.0" } }, "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, "parse-json": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.1.tgz", - "integrity": "sha512-ztoZ4/DYeXQq4E21v169sC8qWINGpcosGv9XhTDvg9/hWvx/zrFkc9BiWxR58OJLHGk28j5BL0SDLeV2WmFZlQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", - "lines-and-columns": "^1.1.6" + "error-ex": "^1.2.0" } }, "parse5": { @@ -4264,6 +5340,15 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "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" + } + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -4276,6 +5361,12 @@ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, "pirates": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", @@ -4286,12 +5377,12 @@ } }, "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "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": "^4.0.0" + "find-up": "^2.1.0" } }, "posix-character-classes": { @@ -4307,12 +5398,12 @@ "dev": true }, "pretty-format": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.2.0.tgz", - "integrity": "sha512-qi/8IuBu2clY9G7qCXgCdD1Bf9w+sXakdHTRToknzMtVy0g7c4MBWaZy7MfB7ndKZovRO6XRwJiAYqq+MC7SDA==", + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", "dev": true, "requires": { - "@jest/types": "^26.2.0", + "@jest/types": "^25.5.0", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -4387,34 +5478,24 @@ "dev": true }, "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", "dev": true, "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" } }, "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "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": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" } }, "regex-not": { @@ -5034,9 +6115,9 @@ "dev": true }, "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -5044,15 +6125,15 @@ } }, "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", "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==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -5060,9 +6141,9 @@ } }, "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", "dev": true }, "split-string": { @@ -5177,28 +6258,6 @@ "es-abstract": "^1.17.5" } }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } - }, "string.prototype.trimstart": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", @@ -5237,9 +6296,9 @@ "dev": true }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -5376,17 +6435,6 @@ "json5": "^1.0.1", "minimist": "^1.2.0", "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } } }, "tunnel-agent": { @@ -5501,9 +6549,9 @@ } }, "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -5534,9 +6582,9 @@ "optional": true }, "v8-to-istanbul": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz", - "integrity": "sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-5.0.1.tgz", + "integrity": "sha512-mbDNjuDajqYe3TXFk5qxcQy8L1msXNE37WTlLoqqpBfRsimbNcrlhQlDPntmECEcUvdC+AQ8CyMMf6EUx1r74Q==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.1", @@ -5627,22 +6675,14 @@ "dev": true }, "whatwg-url": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.1.0.tgz", - "integrity": "sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.3.0.tgz", + "integrity": "sha512-BQRf/ej5Rp3+n7k0grQXZj9a1cHtsp4lqj01p59xBWFKdezR8sO37XnpafwNqiFac/v2Il12EIMjX/Y4VZtT8Q==", "dev": true, "requires": { "lodash.sortby": "^4.7.0", "tr46": "^2.0.2", - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } + "webidl-conversions": "^6.1.0" } }, "which": { @@ -5736,6 +6776,57 @@ "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } } }, "yargs-parser": { diff --git a/package.json b/package.json index cadecd54..54233717 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ ], "scripts": { "start": "node testServer.js", - "test": "jest" + "test": "jest --silent --coverage" }, "license": "EPL-2.0", "bugs": { @@ -25,12 +25,12 @@ "express": "^4.17.1" }, "devDependencies": { - "@types/jest": "^26.0.8", + "@types/jest": "^26.0.14", "eslint-config-standard": "^14.1.1", - "eslint-plugin-import": "^2.22.0", + "eslint-plugin-import": "^2.22.1", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1", - "jest": "^26.2.2" + "jest": "^26.4.2" } } From 41455e51265a011dbe49b7ef453c569edfaf22e7 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 15:35:50 +0200 Subject: [PATCH 41/94] Add new attributes to speaker Signed-off-by: Michael Krug --- functions/devices/speaker.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/functions/devices/speaker.js b/functions/devices/speaker.js index 34abd556..f3eaed64 100644 --- a/functions/devices/speaker.js +++ b/functions/devices/speaker.js @@ -11,15 +11,28 @@ class Speaker extends DefaultDevice { ]; } + static getAttributes(item) { + const config = this.getConfig(item); + const attributes = { + volumeMaxLevel: 100, + volumeCanMuteAndUnmute: false + }; + if ('volumeDefaultPercentage' in config) { + attributes.volumeDefaultPercentage = Number(config.volumeDefaultPercentage); + } + if ('levelStepSize' in config) { + attributes.levelStepSize = Number(config.levelStepSize); + } + return attributes; + } + static get requiredItemTypes() { return ['Dimmer']; } static getState(item) { - const volume = Number(item.state) || 0; return { - currentVolume: volume, - isMuted: volume === 0 + currentVolume: Number(item.state) || 0 }; } } From 2b1bed9000b3802fd4872655b384e278850c2e97 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 15:36:05 +0200 Subject: [PATCH 42/94] Fix specialcolorlight check Signed-off-by: Michael Krug --- functions/devices/specialcolorlight.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/devices/specialcolorlight.js b/functions/devices/specialcolorlight.js index af82dd54..8e09c332 100644 --- a/functions/devices/specialcolorlight.js +++ b/functions/devices/specialcolorlight.js @@ -14,7 +14,7 @@ class SpecialColorLight extends DefaultDevice { } static matchesItemType(item) { - return item.type === 'Group' && Object.keys(this.getMembers(item)).length > 1 && this.getAttributes(item).colorTemperatureRange; + return item.type === 'Group' && Object.keys(this.getMembers(item)).length > 1 && !!this.getAttributes(item).colorTemperatureRange; } static getAttributes(item) { From 024d1c7e19a2ce17789554ade329fdaff4440246 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 15:36:24 +0200 Subject: [PATCH 43/94] Remove old code Signed-off-by: Michael Krug --- functions/devices/temperaturesensor.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/functions/devices/temperaturesensor.js b/functions/devices/temperaturesensor.js index 357c9de9..90bac38f 100644 --- a/functions/devices/temperaturesensor.js +++ b/functions/devices/temperaturesensor.js @@ -37,10 +37,6 @@ class TemperatureSensor extends DefaultDevice { temperatureAmbientCelsius: state }; } - - static convertToCelsius(value = 0) { - return Number(((value - 32) * 5 / 9).toFixed(1)); - } } module.exports = TemperatureSensor; From bd0e9fe134e24527217e90a649d65801c7b4a57a Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 15:37:10 +0200 Subject: [PATCH 44/94] Make check for sensor more strict Signed-off-by: Michael Krug --- functions/devices/sensor.js | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/functions/devices/sensor.js b/functions/devices/sensor.js index 010da0a9..cd9c9fd8 100644 --- a/functions/devices/sensor.js +++ b/functions/devices/sensor.js @@ -10,24 +10,28 @@ class Sensor extends DefaultDevice { 'action.devices.traits.SensorState' ]; } + static matchesItemType(item) { + const config = this.getConfig(item); + return 'sensorName' in config && ('valueUnit' in config || 'states' in config); + } static getAttributes(item) { const config = this.getConfig(item); - if (!config || !config.sensorName) { - return {}; - } - const attributes = { - sensorStatesSupported: { + const attributes = { sensorStatesSupported: {} }; + if ('sensorName' in config) { + attributes.sensorStatesSupported = { name: config.sensorName - } - }; - if (config.valueUnit) { - attributes.sensorStatesSupported.numericCapabilities = {} - attributes.sensorStatesSupported.numericCapabilities.rawValueUnit = config.valueUnit + }; + } + if ('valueUnit' in config) { + attributes.sensorStatesSupported.numericCapabilities = { + rawValueUnit: config.valueUnit + }; } - if (config.states) { - attributes.sensorStatesSupported.descriptiveCapabilities = {} - attributes.sensorStatesSupported.descriptiveCapabilities.availableStates = config.states.split(',').map(s => s.trim().split('=')[0].trim()); + if ('states' in config) { + attributes.sensorStatesSupported.descriptiveCapabilities = { + availableStates: config.states.split(',').map(s => s.trim().split('=')[0].trim()) + }; } return attributes; } From 81a49b9393a2402512e7e9cd89237b6b50ad0bf1 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 15:38:00 +0200 Subject: [PATCH 45/94] Make check for thermostat more strict Signed-off-by: Michael Krug --- functions/devices/thermostat.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/functions/devices/thermostat.js b/functions/devices/thermostat.js index 0e07130b..45ed7f0b 100644 --- a/functions/devices/thermostat.js +++ b/functions/devices/thermostat.js @@ -12,6 +12,10 @@ class Thermostat extends DefaultDevice { ]; } + static matchesItemType(item) { + return item.type === 'Group' && Object.keys(this.getMembers(item)).length > 0; + } + static getAttributes(item) { const config = this.getConfig(item); const attributes = { @@ -37,10 +41,6 @@ class Thermostat extends DefaultDevice { return attributes; } - static matchesItemType(item) { - return item.type === 'Group'; - } - static getState(item) { const state = {}; const members = this.getMembers(item); From 54d40a349ff779a941c36336ade05494b914acd7 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 15:38:15 +0200 Subject: [PATCH 46/94] Add alternative temperature unit attribute Signed-off-by: Michael Krug --- functions/devices/thermostat.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/functions/devices/thermostat.js b/functions/devices/thermostat.js index 45ed7f0b..c4182e94 100644 --- a/functions/devices/thermostat.js +++ b/functions/devices/thermostat.js @@ -94,7 +94,8 @@ class Thermostat extends DefaultDevice { } static usesFahrenheit(item) { - return this.getConfig(item).useFahrenheit === true || this.hasTag(item, 'Fahrenheit'); + const config = this.getConfig(item); + return config.thermostatTemperatureUnit === 'F' || config.useFahrenheit === true || this.hasTag(item, 'Fahrenheit'); } static getModeMap(item) { From 7c635833033f2263a720f5545dc0eb927f343699 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 15:38:48 +0200 Subject: [PATCH 47/94] Remove or disable old tests Signed-off-by: Michael Krug --- tests/__snapshots__/openhab.tags.test.js.snap | 198 ---- tests/__snapshots__/openhab.test.js.snap | 399 -------- tests/devices/devices.metadata.test.js | 871 ------------------ tests/devices/devices.tags.test.js | 297 ------ tests/openhab.tags.test.js | 6 +- tests/openhab.test.js | 10 +- 6 files changed, 8 insertions(+), 1773 deletions(-) delete mode 100644 tests/__snapshots__/openhab.tags.test.js.snap delete mode 100644 tests/__snapshots__/openhab.test.js.snap delete mode 100644 tests/devices/devices.metadata.test.js delete mode 100644 tests/devices/devices.tags.test.js diff --git a/tests/__snapshots__/openhab.tags.test.js.snap b/tests/__snapshots__/openhab.tags.test.js.snap deleted file mode 100644 index 8316f50d..00000000 --- a/tests/__snapshots__/openhab.tags.test.js.snap +++ /dev/null @@ -1,198 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Test SYNC with Tags Light Devices 1`] = ` -Object { - "devices": Array [ - Object { - "attributes": Object {}, - "customData": Object { - "deviceType": "SimpleLight", - "itemType": "Switch", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "SwitchLight", - "swVersion": "2.5.0", - }, - "id": "MySwitch", - "name": Object { - "defaultNames": Array [ - "SwitchLight", - ], - "name": "SwitchLight", - "nicknames": Array [ - "SwitchLight", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.OnOff", - ], - "type": "action.devices.types.LIGHT", - "willReportState": false, - }, - Object { - "attributes": Object {}, - "customData": Object { - "deviceType": "DimmableLight", - "itemType": "Dimmer", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "DimmLight", - "swVersion": "2.5.0", - }, - "id": "MyDimmer", - "name": Object { - "defaultNames": Array [ - "DimmLight", - ], - "name": "DimmLight", - "nicknames": Array [ - "DimmLight", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.OnOff", - "action.devices.traits.Brightness", - ], - "type": "action.devices.types.LIGHT", - "willReportState": false, - }, - Object { - "attributes": Object { - "colorModel": "hsv", - }, - "customData": Object { - "deviceType": "ColorLight", - "itemType": "Color", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "ColorLight", - "swVersion": "2.5.0", - }, - "id": "MyLight", - "name": Object { - "defaultNames": Array [ - "ColorLight", - ], - "name": "ColorLight", - "nicknames": Array [ - "ColorLight", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.OnOff", - "action.devices.traits.Brightness", - "action.devices.traits.ColorSetting", - ], - "type": "action.devices.types.LIGHT", - "willReportState": false, - }, - Object { - "attributes": Object {}, - "customData": Object { - "deviceType": "SimpleLight", - "itemType": "Switch", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "GroupLight", - "swVersion": "2.5.0", - }, - "id": "MyLightGroup", - "name": Object { - "defaultNames": Array [ - "GroupLight", - ], - "name": "GroupLight", - "nicknames": Array [ - "GroupLight", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.OnOff", - ], - "type": "action.devices.types.LIGHT", - "willReportState": false, - }, - Object { - "attributes": Object {}, - "customData": Object { - "deviceType": "DimmableLight", - "itemType": "Dimmer", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "GroupDimmer", - "swVersion": "2.5.0", - }, - "id": "MyDimmerGroup", - "name": Object { - "defaultNames": Array [ - "GroupDimmer", - ], - "name": "GroupDimmer", - "nicknames": Array [ - "GroupDimmer", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.OnOff", - "action.devices.traits.Brightness", - ], - "type": "action.devices.types.LIGHT", - "willReportState": false, - }, - Object { - "attributes": Object { - "colorModel": "hsv", - }, - "customData": Object { - "deviceType": "ColorLight", - "itemType": "Color", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "GroupColor", - "swVersion": "2.5.0", - }, - "id": "MyColorGroup", - "name": Object { - "defaultNames": Array [ - "GroupColor", - ], - "name": "GroupColor", - "nicknames": Array [ - "GroupColor", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.OnOff", - "action.devices.traits.Brightness", - "action.devices.traits.ColorSetting", - ], - "type": "action.devices.types.LIGHT", - "willReportState": false, - }, - ], -} -`; diff --git a/tests/__snapshots__/openhab.test.js.snap b/tests/__snapshots__/openhab.test.js.snap deleted file mode 100644 index 756d86e7..00000000 --- a/tests/__snapshots__/openhab.test.js.snap +++ /dev/null @@ -1,399 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Test SYNC Light Devices 1`] = ` -Object { - "devices": Array [ - Object { - "attributes": Object {}, - "customData": Object { - "deviceType": "SimpleLight", - "itemType": "Switch", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Switch:MySwitch", - "swVersion": "2.5.0", - }, - "id": "MySwitch", - "name": Object { - "defaultNames": Array [ - "Light Switch", - ], - "name": "Light Switch", - "nicknames": Array [ - "Light Switch", - "Testlight", - "Cool Light", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.OnOff", - ], - "type": "action.devices.types.LIGHT", - "willReportState": false, - }, - Object { - "attributes": Object {}, - "customData": Object { - "deviceType": "DimmableLight", - "itemType": "Dimmer", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Dimmer:MyDimmer", - "swVersion": "2.5.0", - }, - "id": "MyDimmer", - "name": Object { - "defaultNames": Array [ - "DimmLight", - ], - "name": "DimmLight", - "nicknames": Array [ - "DimmLight", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.OnOff", - "action.devices.traits.Brightness", - ], - "type": "action.devices.types.LIGHT", - "willReportState": false, - }, - Object { - "attributes": Object { - "colorModel": "hsv", - }, - "customData": Object { - "deviceType": "ColorLight", - "itemType": "Color", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Color:MyLight", - "swVersion": "2.5.0", - }, - "id": "MyLight", - "name": Object { - "defaultNames": Array [ - "ColorLight", - ], - "name": "ColorLight", - "nicknames": Array [ - "ColorLight", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.OnOff", - "action.devices.traits.Brightness", - "action.devices.traits.ColorSetting", - ], - "type": "action.devices.types.LIGHT", - "willReportState": false, - }, - Object { - "attributes": Object {}, - "customData": Object { - "deviceType": "SimpleLight", - "itemType": "Switch", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Switch:MyLightGroup", - "swVersion": "2.5.0", - }, - "id": "MyLightGroup", - "name": Object { - "defaultNames": Array [ - "GroupLight", - ], - "name": "GroupLight", - "nicknames": Array [ - "GroupLight", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.OnOff", - ], - "type": "action.devices.types.LIGHT", - "willReportState": false, - }, - Object { - "attributes": Object {}, - "customData": Object { - "deviceType": "DimmableLight", - "itemType": "Dimmer", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Dimmer:MyDimmerGroup", - "swVersion": "2.5.0", - }, - "id": "MyDimmerGroup", - "name": Object { - "defaultNames": Array [ - "GroupDimmer", - ], - "name": "GroupDimmer", - "nicknames": Array [ - "GroupDimmer", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.OnOff", - "action.devices.traits.Brightness", - ], - "type": "action.devices.types.LIGHT", - "willReportState": false, - }, - Object { - "attributes": Object { - "colorModel": "hsv", - }, - "customData": Object { - "deviceType": "ColorLight", - "itemType": "Color", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Color:MyColorGroup", - "swVersion": "2.5.0", - }, - "id": "MyColorGroup", - "name": Object { - "defaultNames": Array [ - "GroupColor", - ], - "name": "GroupColor", - "nicknames": Array [ - "GroupColor", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.OnOff", - "action.devices.traits.Brightness", - "action.devices.traits.ColorSetting", - ], - "type": "action.devices.types.LIGHT", - "willReportState": false, - }, - Object { - "attributes": Object {}, - "customData": Object { - "deviceType": "Fan", - "itemType": "Dimmer", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Dimmer:MyFan", - "swVersion": "2.5.0", - }, - "id": "MyFan", - "name": Object { - "defaultNames": Array [ - "Fan", - ], - "name": "Fan", - "nicknames": Array [ - "Fan", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.OnOff", - "action.devices.traits.FanSpeed", - ], - "type": "action.devices.types.FAN", - "willReportState": false, - }, - ], -} -`; - -exports[`Test SYNC Scene Device 1`] = ` -Object { - "devices": Array [ - Object { - "attributes": Object { - "sceneReversible": true, - }, - "customData": Object { - "deviceType": "Scene", - "itemType": "Switch", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Switch:MyScene", - "swVersion": "2.5.0", - }, - "id": "MyScene", - "name": Object { - "defaultNames": Array [ - "My Scene", - ], - "name": "My Scene", - "nicknames": Array [ - "My Scene", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.Scene", - ], - "type": "action.devices.types.SCENE", - "willReportState": false, - }, - ], -} -`; - -exports[`Test SYNC Sensor Device 1`] = ` -Object { - "devices": Array [ - Object { - "attributes": Object { - "sensorStatesSupported": Object { - "descriptiveCapabilities": Object { - "availableStates": Array [ - "healthy", - "moderate", - "unhealthy", - "very unhealthy", - ], - }, - "name": "AirQuality", - }, - }, - "customData": Object { - "deviceType": "Sensor", - "itemType": "Number", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Number:MySensor", - "swVersion": "2.5.0", - }, - "id": "MySensor", - "name": Object { - "defaultNames": Array [ - "My Sensor", - ], - "name": "My Sensor", - "nicknames": Array [ - "My Sensor", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.SensorState", - ], - "type": "action.devices.types.SENSOR", - "willReportState": false, - }, - ], -} -`; - -exports[`Test SYNC Temperature Sensor Device 1`] = ` -Object { - "devices": Array [ - Object { - "attributes": Object { - "queryOnlyTemperatureControl": true, - "temperatureUnitForUX": "F", - }, - "customData": Object { - "deviceType": "TemperatureSensor", - "itemType": "Number", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Number:MySensor", - "swVersion": "2.5.0", - }, - "id": "MySensor", - "name": Object { - "defaultNames": Array [ - "My Sensor", - ], - "name": "My Sensor", - "nicknames": Array [ - "My Sensor", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.TemperatureControl", - ], - "type": "action.devices.types.SENSOR", - "willReportState": false, - }, - ], -} -`; - -exports[`Test SYNC Thermostat Device 1`] = ` -Object { - "devices": Array [ - Object { - "attributes": Object { - "availableThermostatModes": "off,heat,eco,auto", - "thermostatTemperatureUnit": "C", - }, - "customData": Object { - "deviceType": "Thermostat", - "itemType": "Group", - }, - "deviceInfo": Object { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Group:MyThermostat", - "swVersion": "2.5.0", - }, - "id": "MyThermostat", - "name": Object { - "defaultNames": Array [ - "My Thermostat", - ], - "name": "My Thermostat", - "nicknames": Array [ - "My Thermostat", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": Array [ - "action.devices.traits.TemperatureSetting", - ], - "type": "action.devices.types.THERMOSTAT", - "willReportState": false, - }, - ], -} -`; diff --git a/tests/devices/devices.metadata.test.js b/tests/devices/devices.metadata.test.js deleted file mode 100644 index e9b6e359..00000000 --- a/tests/devices/devices.metadata.test.js +++ /dev/null @@ -1,871 +0,0 @@ -const OpenHAB = require('../../functions/openhab.js'); -const Thermostat = require('../../functions/devices/thermostat.js'); -const TV = require('../../functions/devices/tv.js'); - -describe('Test Switch Devices with Metadata', () => { - test('Valve Switch Type', () => { - const item = { - type: 'Switch', - state: 'ON', - metadata: { - ga: { - value: 'Valve' - } - } - }; - const device = OpenHAB.getDeviceForItem(item); - expect(device.name).toBe('Valve'); - expect(device.getState(item)).toStrictEqual({ - openPercent: 100 - }); - }); - - test('Sprinkler Switch Type', () => { - const item = { - type: 'Switch', - state: 'ON', - metadata: { - ga: { - value: 'Sprinkler' - } - } - }; - const device = OpenHAB.getDeviceForItem(item); - expect(device.name).toBe('Sprinkler'); - expect(device.getState(item)).toStrictEqual({ - isRunning: true, - isPaused: false - }); - }); - - test('Lock Switch Type', () => { - const item = { - type: 'Switch', - state: 'ON', - metadata: { - ga: { - value: 'Lock', - config: { - ackNeeded: true - } - } - } - }; - const device = OpenHAB.getDeviceForItem(item); - expect(device.name).toBe('Lock'); - expect(device.getMetadata(item).customData.ackNeeded).toBe(true); - expect(device.getMetadata(item).customData.pinNeeded).toBeUndefined(); - expect(device.getState(item)).toStrictEqual({ - isLocked: true - }); - }); - - test('SecuritySystem Switch Type', () => { - const item = { - type: 'Switch', - state: 'ON', - metadata: { - ga: { - value: 'SecuritySystem', - config: { - pinNeeded: '1234' - } - } - } - }; - const device = OpenHAB.getDeviceForItem(item); - expect(device.name).toBe('SecuritySystem'); - expect(device.getMetadata(item).customData.ackNeeded).toBeUndefined(); - expect(device.getMetadata(item).customData.pinNeeded).toBe('1234'); - expect(device.getState(item)).toStrictEqual({ - isArmed: true - }); - }); -}); - -describe('Test Light Devices with Metadata', () => { - test('Special Color Light Type', () => { - const item = { - type: 'Group', - metadata: { - ga: { - value: 'LIGHT', - config: { - colorTemperatureRange: '1000,4000' - } - } - }, - members: [{ - name: 'Brightness', - type: 'Dimmer', - metadata: { - ga: { - value: 'lightBrightness' - } - }, - state: '10' - }, { - name: 'ColorTemp', - type: 'Dimmer', - metadata: { - ga: { - value: 'lightColorTemperature' - } - }, - state: '50' - }] - }; - const device = OpenHAB.getDeviceForItem(item); - expect(device.name).toBe('SpecialColorLight'); - expect(device.getAttributes(item)).toStrictEqual({ - colorTemperatureRange: { - temperatureMinK: 1000, - temperatureMaxK: 4000 - } - }); - expect(device.getState(item)).toStrictEqual({ - brightness: 10, - color: { - temperatureK: 2500 - }, - on: true - }); - }); -}); - -describe('Test OpenClose Devices', () => { - test('Invalid Blinds Type', () => { - expect(OpenHAB.getDeviceForItem({ - type: 'Dimmer', - metadata: { - ga: { - value: 'BLINDS' - } - } - })).toBe(undefined); - }); - - test('Window as Contact', () => { - const item = { - type: 'Contact', - state: 'OPEN', - metadata: { - ga: { - value: 'WINDOW' - } - } - }; - const device = OpenHAB.getDeviceForItem(item); - expect(device.name).toBe('Window'); - expect(device.getAttributes(item)).toStrictEqual({ - discreteOnlyOpenClose: true, - queryOnlyOpenClose: true - }); - expect(device.getState(item)).toStrictEqual({ - openPercent: 100 - }); - }); -}); - -describe('Test Sensor Device with Metadata', () => { - const item = { - type: 'Number', - metadata: { - ga: { - value: 'Sensor', - config: { - sensorName: 'MySensor', - valueUnit: 'Percent', - states: 'off=0,low=50,high=100' - } - } - }, - state: '100' - }; - const device = OpenHAB.getDeviceForItem(item); - - test('getAttributes', () => { - expect(device.getAttributes({ - metadata: { - ga: { - value: 'Sensor', - config: { - sensorName: 'AirQuality' - } - } - } - })).toStrictEqual({ - 'sensorStatesSupported': { - 'name': 'AirQuality' - } - }); - - expect(device.getAttributes({ - metadata: { - ga: { - value: 'Sensor', - config: { - sensorName: 'MySensor', - valueUnit: 'Percent' - } - } - } - })).toStrictEqual({ - 'sensorStatesSupported': { - 'name': 'MySensor', - 'numericCapabilities': { - 'rawValueUnit': 'Percent' - } - } - }); - - expect(device.getAttributes({ - metadata: { - ga: { - value: 'Sensor', - config: { - sensorName: 'MySensor', - states: 'off=0,low=50,high=100' - } - } - } - })).toStrictEqual({ - 'sensorStatesSupported': { - 'name': 'MySensor', - 'descriptiveCapabilities': { - 'availableStates': ['off', 'low', 'high'] - } - } - }); - }); - - test('translateModeToGoogle', () => { - expect(device.name).toBe('Sensor'); - expect(device.translateStateToGoogle(item)).toBe('high'); - }); - - test('getState', () => { - expect(device.getState(item)).toStrictEqual({ - "currentSensorStateData": { - "currentSensorState": "high", - "name": "MySensor", - "rawValue": 100, - } - }); - }); -}); - -describe('Test Thermostat Device with Metadata', () => { - test('getDeviceForItem', () => { - expect(OpenHAB.getDeviceForItem({ - type: 'Group', - metadata: { - ga: { - value: 'Thermostat' - } - } - }).name).toBe('Thermostat'); - - expect(OpenHAB.getDeviceForItem({ - type: 'Switch', - metadata: { - ga: { - value: 'Thermostat' - } - } - })).toBe(undefined); - - expect(OpenHAB.getDeviceForItem({ - type: 'Group', - metadata: { - ga: { - value: 'Something' - } - } - })).toBe(undefined); - }); - - test('usesFahrenheit', () => { - expect(Thermostat.usesFahrenheit({ - metadata: { - ga: { - value: 'Thermostat', - config: { - useFahrenheit: true - } - } - } - })).toBe(true); - }); - - test('getAttributes', () => { - expect(Thermostat.getAttributes({ - metadata: { - ga: { - value: 'Thermostat', - config: { - useFahrenheit: true - } - } - } - })).toStrictEqual({ - availableThermostatModes: 'off,heat,cool,on,heatcool,auto,eco', - thermostatTemperatureUnit: 'F', - }); - - expect(Thermostat.getAttributes({ - metadata: { - ga: { - value: 'Thermostat', - config: { - modes: 'on,off' - } - } - } - })).toStrictEqual({ - availableThermostatModes: 'on,off', - thermostatTemperatureUnit: 'C', - }); - - expect(Thermostat.getAttributes({ - metadata: { - ga: { - value: 'Thermostat', - config: { - modes: 'off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto' - } - } - } - })).toStrictEqual({ - availableThermostatModes: 'off,heat,eco,on,auto', - thermostatTemperatureUnit: 'C', - }); - - // test thermostatTemperatureRange attribute - expect(Thermostat.getAttributes({ - metadata: { - ga: { - value: 'Thermostat', - config: { - thermostatTemperatureRange: '10,30' - } - } - } - })).toStrictEqual({ - availableThermostatModes: 'off,heat,cool,on,heatcool,auto,eco', - thermostatTemperatureUnit: 'C', - thermostatTemperatureRange: { - minThresholdCelsius: 10.0, - maxThresholdCelsius: 30.0 - } - }); - - // wrong value for thermostatTemperatureRange should not throw error - expect(Thermostat.getAttributes({ - metadata: { - ga: { - value: 'Thermostat', - config: { - thermostatTemperatureRange: '100' - } - } - } - })).toStrictEqual({ - availableThermostatModes: 'off,heat,cool,on,heatcool,auto,eco', - thermostatTemperatureUnit: 'C' - }); - - // wrong value for thermostatTemperatureRange should not throw error - expect(Thermostat.getAttributes({ - metadata: { - ga: { - value: 'Thermostat', - config: { - thermostatTemperatureRange: 'a,b' - } - } - } - })).toStrictEqual({ - availableThermostatModes: 'off,heat,cool,on,heatcool,auto,eco', - thermostatTemperatureUnit: 'C' - }); - }); - - test('getModeMap', () => { - expect(Thermostat.getModeMap({ - metadata: { - ga: { - value: 'Thermostat' - } - } - })).toStrictEqual({ - 'off': ['off'], - 'heat': ['heat'], - 'cool': ['cool'], - 'on': ['on'], - 'heatcool': ['heatcool'], - 'auto': ['auto'], - 'eco': ['eco'] - }); - - expect(Thermostat.getModeMap({ - metadata: { - ga: { - value: 'Thermostat', - config: { - modes: 'on,off,heat,cool' - } - } - } - })).toStrictEqual({ - 'on': ['on'], - 'off': ['off'], - 'heat': ['heat'], - 'cool': ['cool'] - }); - - expect(Thermostat.getModeMap({ - metadata: { - ga: { - value: 'Thermostat', - config: { - modes: 'off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto' - } - } - } - })).toStrictEqual({ - 'off': ['OFF', 'WINDOW_OPEN'], - 'heat': ['COMFORT', 'BOOST'], - 'eco': ['ECO'], - 'on': ['ON'], - 'auto': ['auto'] - }); - }); - - test('translateModeToGoogle', () => { - expect(Thermostat.translateModeToGoogle({ - metadata: { - ga: { - value: 'Thermostat', - config: { - modes: 'off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto' - } - } - } - }, 'COMFORT')).toBe('heat'); - - expect(Thermostat.translateModeToGoogle({ - metadata: { - ga: { - value: 'Thermostat', - config: { - modes: 'off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto' - } - } - } - }, 'WINDOW_OPEN')).toBe('off'); - }); - - test('translateModeToOpenhab', () => { - expect(Thermostat.translateModeToOpenhab({ - metadata: { - ga: { - value: 'Thermostat', - config: { - modes: 'off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto' - } - } - } - }, 'heat')).toBe('COMFORT'); - - expect(Thermostat.translateModeToOpenhab({ - metadata: { - ga: { - value: 'Thermostat', - config: { - modes: 'off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto' - } - } - } - }, 'auto')).toBe('auto'); - }); - - test('getMetadata', () => { - expect(Thermostat.getMetadata( - { - name: 'MyItem', - label: 'MyThermostat', - type: 'Group', - metadata: { - ga: { - value: 'Thermostat', - config: { - name: 'My Thermostat', - modes: 'off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto=AUTOMATIC' - } - } - }, - members: [{ - name: 'Ambient', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - name: 'SetPoint', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '20' - }, { - name: 'Mode', - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: 'off' - }] - })).toStrictEqual({ - "attributes": { - "availableThermostatModes": "off,heat,eco,on,auto", - "thermostatTemperatureUnit": "C" - }, - "customData": { - "deviceType": "Thermostat", - "itemType": "Group" - }, - "roomHint": undefined, - "structureHint": undefined, - "deviceInfo": { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Group:MyItem", - "swVersion": "2.5.0", - }, - "id": "MyItem", - "name": { - "defaultNames": [ - "My Thermostat", - ], - "name": "My Thermostat", - "nicknames": [ - "My Thermostat", - ] - }, - "traits": [ - "action.devices.traits.TemperatureSetting", - ], - "type": "action.devices.types.THERMOSTAT", - "willReportState": false - }); - }); - - test('getState', () => { - expect(Thermostat.getState({ - type: 'Group', - metadata: { - ga: { - value: 'Thermostat' - } - } - })).toStrictEqual({}); - - expect(Thermostat.getState({ - type: 'Group', - metadata: { - ga: { - value: 'Thermostat', - config: { - useFahrenheit: true - } - } - }, - members: [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '20' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: 'off' - }] - })).toStrictEqual({ - 'thermostatTemperatureAmbient': -12.2, - 'thermostatTemperatureSetpoint': -6.7, - 'thermostatMode': 'off' - }); - - expect(Thermostat.getState({ - type: 'Group', - metadata: { - ga: { - value: 'Thermostat', - config: { - modes: 'heat=1,off=2' - } - } - }, - members: [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '20' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointHigh' - } - }, - state: '22' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointLow' - } - }, - state: '18' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: '1' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - })).toStrictEqual({ - 'thermostatTemperatureAmbient': 10, - 'thermostatTemperatureSetpoint': 20, - 'thermostatTemperatureSetpointHigh': 22, - 'thermostatTemperatureSetpointLow': 18, - 'thermostatMode': 'heat', - 'thermostatHumidityAmbient': 50, - }); - }); - - describe('Test TV Device with Metadata', () => { - test('getAttributes', () => { - expect(TV.getAttributes({ - metadata: { - ga: { - value: 'TV', - config: { - availableInputs: 'tv=TV,hdmi1=HDMI1,hdmi2=HDMI2', - availableChannels: '20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2' - } - } - } - })).toStrictEqual({ - "availableInputs": [ - { - "key": "tv", - "names": [ - { - "lang": "en", - "name_synonym": [ - "TV", - ], - }, - ], - }, - { - "key": "hdmi1", - "names": [ - { - "lang": "en", - "name_synonym": [ - "HDMI1", - ], - }, - ], - }, - { - "key": "hdmi2", - "names": [ - { - "lang": "en", - "name_synonym": [ - "HDMI2", - ], - }, - ], - }, - ], - "orderedInputs": false, - "availableChannels": [ - { - "key": "channel1", - "names": [ - "Channel 1", - "Kanal 1", - ], - "number": "20", - }, - { - "key": "channel2", - "names": [ - "Channel 2", - "Kanal 2", - ], - "number": "10", - }, - ], - "transportControlSupportedCommands": [ - "NEXT", - "PREVIOUS", - "PAUSE", - "RESUME", - ], - "volumeCanMuteAndUnmute": false - }); - }); - - test('getChannelMap', () => { - expect(TV.getChannelMap({ - metadata: { - ga: { - value: 'TV', - config: { - availableChannels: '20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2' - } - } - } - })).toStrictEqual({ - "10": [ - "Channel 2", - "Kanal 2", - "channel2", - ], - "20": [ - "Channel 1", - "Kanal 1", - "channel1", - ], - }); - }); - - test('getState', () => { - expect(TV.getState({ - type: 'Group', - metadata: { - ga: { - value: 'TV' - } - } - })).toStrictEqual({}); - - expect(TV.getState({ - type: 'Group', - metadata: { - ga: { - value: 'TV', - config: { - availableChannels: '20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2' - } - } - }, - members: [{ - type: 'Switch', - metadata: { - ga: { - value: 'tvPower' - } - }, - state: 'ON' - }, { - type: 'Switch', - metadata: { - ga: { - value: 'tvMute' - } - }, - state: 'OFF' - }, { - type: 'String', - metadata: { - ga: { - value: 'tvInput' - } - }, - state: 'tv' - }, { - type: 'Number', - metadata: { - ga: { - value: 'tvChannel' - } - }, - state: '20' - }, { - type: 'Number', - metadata: { - ga: { - value: 'tvVolume' - } - }, - state: '50' - }, { - type: 'String', - metadata: { - ga: { - value: 'tvTransport' - } - }, - state: 'PLAY' - }] - })).toStrictEqual({ - 'channelName': 'Channel 1', - 'channelNumber': '20', - 'currentInput': 'tv', - 'currentVolume': 50, - 'isMuted': false, - 'on': true - }); - }); - }); -}); diff --git a/tests/devices/devices.tags.test.js b/tests/devices/devices.tags.test.js deleted file mode 100644 index e879599f..00000000 --- a/tests/devices/devices.tags.test.js +++ /dev/null @@ -1,297 +0,0 @@ -const OpenHAB = require('../../functions/openhab.js'); -const Thermostat = require('../../functions/devices/thermostat.js'); - -describe('Test Switch Devices with Tags', () => { - test('Switch Type', () => { - const item = { - type: 'Switch', - state: 'ON', - tags: [ - 'Switchable' - ] - }; - const device = OpenHAB.getDeviceForItem(item); - expect(device.name).toBe('Switch'); - expect(device.getState(item)).toStrictEqual({ - on: true - }); - }); - - test('Valve Switch Type', () => { - const item = { - type: 'Switch', - state: 'ON', - tags: [ - 'Valve' - ] - }; - const device = OpenHAB.getDeviceForItem(item); - expect(device.name).toBe('Valve'); - expect(device.getState(item)).toStrictEqual({ - openPercent: 100 - }); - }); - - test('Sprinkler Switch Type', () => { - const item = { - type: 'Switch', - state: 'ON', - tags: [ - 'Sprinkler' - ] - }; - const device = OpenHAB.getDeviceForItem(item); - expect(device.name).toBe('Sprinkler'); - expect(device.getState(item)).toStrictEqual({ - isRunning: true, - isPaused: false - }); - }); - - test('Lock Switch Type', () => { - const item = { - type: 'Switch', - state: 'ON', - tags: [ - 'Lock' - ] - }; - const device = OpenHAB.getDeviceForItem(item); - expect(device.name).toBe('Lock'); - expect(device.getState(item)).toStrictEqual({ - isLocked: true - }); - }); - - test('SecuritySystem Switch Type', () => { - const item = { - type: 'Switch', - state: 'ON', - tags: [ - 'SecuritySystem' - ] - }; - const device = OpenHAB.getDeviceForItem(item); - expect(device.name).toBe('SecuritySystem'); - expect(device.getState(item)).toStrictEqual({ - isArmed: true - }); - }); -}); - -describe('Test Light Devices with Tags', () => { - test('Switch Light Type', () => { - expect(OpenHAB.getDeviceForItem({ - type: 'Switch', - tags: [ - 'Lighting' - ] - }).name).toBe('SimpleLight'); - }); - - test('Switch Group Light Type', () => { - expect(OpenHAB.getDeviceForItem({ - type: 'Group', - groupType: 'Switch', - tags: [ - 'Lighting' - ] - }).name).toBe('SimpleLight'); - }); - - test('Dimmer Light Type', () => { - expect(OpenHAB.getDeviceForItem({ - type: 'Dimmer', - tags: [ - 'Lighting' - ] - }).name).toBe('DimmableLight'); - }); - - test('Dimmer Group Light Type', () => { - expect(OpenHAB.getDeviceForItem({ - type: 'Group', - groupType: 'Dimmer', - tags: [ - 'Lighting' - ] - }).name).toBe('DimmableLight'); - }); - - test('Color Light Type', () => { - expect(OpenHAB.getDeviceForItem({ - type: 'Color', - tags: [ - 'Lighting' - ] - }).name).toBe('ColorLight'); - }); - - test('Color Group Light Type', () => { - expect(OpenHAB.getDeviceForItem({ - type: 'Group', - groupType: 'Color', - tags: [ - 'Lighting' - ] - }).name).toBe('ColorLight'); - }); -}); - -describe('Test Rollershutter Devices with Tags', () => { - test('Invalid Blinds Type', () => { - expect(OpenHAB.getDeviceForItem({ - type: 'Dimmer', - tags: [ - 'Blinds' - ] - })).toBe(undefined); - }); - - test('Blinds Rollershutter Type', () => { - const item = { - type: 'Rollershutter', - state: '0', - tags: [ - 'Blinds' - ] - }; - const device = OpenHAB.getDeviceForItem(item); - expect(device.name).toBe('Blinds'); - expect(device.getState(item)).toStrictEqual({ - openPercent: 100 - }); - }); -}); - -describe('Test Thermostat Device with Tags', () => { - test('getDeviceForItem', () => { - expect(OpenHAB.getDeviceForItem({ - type: 'Group', - tags: [ - 'Thermostat' - ] - }).name).toBe('Thermostat'); - - expect(OpenHAB.getDeviceForItem({ - type: 'Switch', - tags: [ - 'Thermostat' - ] - })).toBe(undefined); - - expect(OpenHAB.getDeviceForItem({ - type: 'Group', - tags: [ - 'Something' - ] - })).toBe(undefined); - }); - - test('usesFahrenheit', () => { - expect(Thermostat.usesFahrenheit({ - tags: [ - 'Thermostat', - 'Fahrenheit' - ] - })).toBe(true); - }); - - test('getAttributes', () => { - expect(Thermostat.getAttributes({ - tags: [ - 'Thermostat', - 'Fahrenheit' - ] - })).toStrictEqual({ - 'availableThermostatModes': 'off,heat,cool,on,heatcool,auto,eco', - 'thermostatTemperatureUnit': 'F', - }); - - expect(Thermostat.getAttributes({ - tags: [ - 'Thermostat' - ] - })).toStrictEqual({ - 'availableThermostatModes': 'off,heat,cool,on,heatcool,auto,eco', - 'thermostatTemperatureUnit': 'C', - }); - }); - - test('getState', () => { - expect(Thermostat.getState({ - type: 'Group', - tags: [ - 'Thermostat' - ] - })).toStrictEqual({}); - - expect(Thermostat.getState({ - type: 'Group', - tags: [ - 'Thermostat', - 'Fahrenheit' - ], - members: [{ - type: 'Number', - tags: [ - 'CurrentTemperature' - ], - state: '10' - }, { - type: 'Number', - tags: [ - 'TargetTemperature' - ], - state: '20' - }, { - type: 'Number', - tags: [ - 'HeatingCoolingMode' - ], - state: 'off' - }] - })).toStrictEqual({ - 'thermostatTemperatureAmbient': -12.2, - 'thermostatTemperatureSetpoint': -6.7, - 'thermostatMode': 'off' - }); - - expect(Thermostat.getState({ - type: 'Group', - tags: [ - 'Thermostat' - ], - members: [{ - type: 'Number', - tags: [ - 'CurrentTemperature' - ], - state: '10' - }, { - type: 'Number', - tags: [ - 'TargetTemperature' - ], - state: '20' - }, { - type: 'Number', - tags: [ - 'HeatingCoolingMode' - ], - state: 'heat' - }, { - type: 'Number', - tags: [ - 'CurrentHumidity' - ], - state: '50' - }] - })).toStrictEqual({ - 'thermostatTemperatureAmbient': 10, - 'thermostatTemperatureSetpoint': 20, - 'thermostatMode': 'heat', - 'thermostatHumidityAmbient': 50, - }); - }); -}); diff --git a/tests/openhab.tags.test.js b/tests/openhab.tags.test.js index b1ed1ebc..335b6b9a 100644 --- a/tests/openhab.tags.test.js +++ b/tests/openhab.tags.test.js @@ -1,6 +1,6 @@ const OpenHAB = require('../functions/openhab.js'); -describe('Test SYNC with Tags', () => { +xdescribe('SYNC with Tags', () => { test('Light Devices', async () => { const items = [ { @@ -79,7 +79,7 @@ describe('Test SYNC with Tags', () => { }); }); -describe('Test QUERY with Tags', () => { +xdescribe('QUERY with Tags', () => { test('Single Light Device ', async () => { const item = { @@ -165,7 +165,7 @@ describe('Test QUERY with Tags', () => { }); }); -describe('Test EXECUTE with Tags', () => { +xdescribe('EXECUTE with Tags', () => { test('ThermostatTemperatureSetpoint', async () => { const item = { diff --git a/tests/openhab.test.js b/tests/openhab.test.js index 111fc428..1d7d97c6 100644 --- a/tests/openhab.test.js +++ b/tests/openhab.test.js @@ -1,6 +1,6 @@ const OpenHAB = require('../functions/openhab.js'); -describe('Test SYNC', () => { +xdescribe('SYNC', () => { test('Light Devices', async () => { const items = [ { @@ -290,7 +290,7 @@ describe('Test SYNC', () => { /* ================================================= */ -describe('Test QUERY', () => { +xdescribe('QUERY', () => { test('Lock Device as Contact', async () => { const item = { @@ -529,7 +529,7 @@ describe('Test QUERY', () => { }); }); -describe('Test EXECUTE with Metadata', () => { +xdescribe('EXECUTE with Metadata', () => { test('OnOff with Switch Device', async () => { const getItemMock = jest.fn(); const sendCommandMock = jest.fn(); @@ -2394,7 +2394,7 @@ describe('Test EXECUTE with Metadata', () => { }); }); -describe('Test EXECUTE', () => { +xdescribe('EXECUTE', () => { test('OnOff Switch', async () => { const getItemMock = jest.fn(); const sendCommandMock = jest.fn(); @@ -2920,7 +2920,7 @@ describe('Test EXECUTE', () => { const payload = await new OpenHAB(apiHandler).handleExecute(commands); expect(getItemMock).toHaveBeenCalled(); - expect(sendCommandMock).toBeCalledWith('MyColor', '26.97,34.9,50'); + expect(sendCommandMock).toBeCalledWith('MyColor', '26.97,35,50'); expect(payload).toStrictEqual({ "commands": [{ "ids": [ From bd0a8a1eb8ae75b2868a8a0512e2308194b6e209 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 15:39:00 +0200 Subject: [PATCH 48/94] Extend utilities tests Signed-off-by: Michael Krug --- functions/utilities.js | 6 +++--- tests/utilities.test.js | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/functions/utilities.js b/functions/utilities.js index dc3034b9..9626a584 100644 --- a/functions/utilities.js +++ b/functions/utilities.js @@ -47,9 +47,9 @@ module.exports = { let v = Math.max(r, g, b), n = v - Math.min(r, g, b); let h = n && ((v == r) ? (g - b) / n : ((v == g) ? 2 + (b - r) / n : 4 + (r - g) / n)); return { - hue: 60 * (h < 0 ? h + 6 : h), - saturation: v && n / v, - value: v + hue: Math.round(60 * (h < 0 ? h + 6 : h) * 100) / 100, + saturation: Math.round(v && n / v * 100) / 100, + value: Math.round(v * 100) / 100 }; } diff --git a/tests/utilities.test.js b/tests/utilities.test.js index 84b5be29..7c9c0894 100644 --- a/tests/utilities.test.js +++ b/tests/utilities.test.js @@ -1,6 +1,6 @@ const Utilities = require('../functions/utilities.js'); -describe('Test Utilities', () => { +describe('Utilities', () => { test('convertToCelsius', () => { expect(Utilities.convertToCelsius(10.0)).toEqual(-12.2); expect(Utilities.convertToCelsius(0.0)).toEqual(-17.8); @@ -10,4 +10,18 @@ describe('Test Utilities', () => { expect(Utilities.convertToFahrenheit(10.0)).toEqual(50); expect(Utilities.convertToFahrenheit(0.0)).toEqual(32); }); + + test('kelvin2rgb', () => { + expect(Utilities.kelvin2rgb(2000)).toStrictEqual({ "b": 14, "g": 137, "r": 255 }); + expect(Utilities.kelvin2rgb(5900)).toEqual({ "b": 234, "g": 244, "r": 255 }); + expect(Utilities.kelvin2rgb(9000)).toEqual({ "b": 255, "g": 223, "r": 210 }); + }); + + test('rgb2hsv', () => { + expect(Utilities.rgb2hsv({ r: 50, g: 10, b: 100 })).toStrictEqual({ "hue": 266.67, "saturation": 0.9, "value": 0.39 }); + expect(Utilities.rgb2hsv({ r: 0, g: 0, b: 0 })).toStrictEqual({ "hue": 0, "saturation": 0, "value": 0 }); + expect(Utilities.rgb2hsv({ r: 255, g: 0, b: 0 })).toStrictEqual({ "hue": 0, "saturation": 1, "value": 1 }); + expect(Utilities.rgb2hsv({ r: 0, g: 255, b: 0 })).toStrictEqual({ "hue": 120, "saturation": 1, "value": 1 }); + expect(Utilities.rgb2hsv({ r: 0, g: 0, b: 255 })).toStrictEqual({ "hue": 240, "saturation": 1, "value": 1 }); + }); }); From bb94a7e67695185f1a0421c425256ac6446c6a13 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 15:41:50 +0200 Subject: [PATCH 49/94] Refactor TV device Signed-off-by: Michael Krug --- functions/devices/tv.js | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/functions/devices/tv.js b/functions/devices/tv.js index 7aedc3e0..db1345f4 100644 --- a/functions/devices/tv.js +++ b/functions/devices/tv.js @@ -5,27 +5,34 @@ class TV extends DefaultDevice { return 'action.devices.types.TV'; } - static get traits() { - return [ - 'action.devices.traits.OnOff', - 'action.devices.traits.Channel', - 'action.devices.traits.Volume', - 'action.devices.traits.InputSelector', - 'action.devices.traits.TransportControl' - ]; + static getTraits(item) { + const traits = []; + const members = this.getMembers(item); + if ('tvPower' in members) traits.push('action.devices.traits.OnOff'); + if ('tvVolume' in members) traits.push('action.devices.traits.Volume'); + if ('tvChannel' in members) traits.push('action.devices.traits.Channel'); + if ('tvInput' in members) traits.push('action.devices.traits.InputSelector'); + if ('tvTransport' in members) traits.push('action.devices.traits.TransportControl'); + return traits; + } + + static matchesItemType(item) { + return item.type === 'Group' && Object.keys(this.getMembers(item)).length > 0; } static getAttributes(item) { const config = this.getConfig(item); const members = this.getMembers(item); const attributes = { - transportControlSupportedCommands: ['NEXT', 'PREVIOUS', 'PAUSE', 'RESUME'], volumeCanMuteAndUnmute: 'tvMute' in members }; - if ('transportControlSupportedCommands' in config) { - attributes.transportControlSupportedCommands = config.transportControlSupportedCommands.split(',').map(s => s.toUpperCase()); + if ('tvTransport' in members) { + attributes.transportControlSupportedCommands = ['NEXT', 'PREVIOUS', 'PAUSE', 'RESUME']; + if ('transportControlSupportedCommands' in config) { + attributes.transportControlSupportedCommands = config.transportControlSupportedCommands.split(',').map(s => s.toUpperCase()); + } } - if ('availableInputs' in config) { + if ('tvInput' in members && 'availableInputs' in config) { attributes.availableInputs = []; config.availableInputs.split(',').forEach(input => { const [key, synonyms] = input.split('='); @@ -39,7 +46,7 @@ class TV extends DefaultDevice { }); attributes.orderedInputs = config.orderedInputs === true; } - if ('availableChannels' in config) { + if ('tvChannel' in members && 'availableChannels' in config) { attributes.availableChannels = []; config.availableChannels.split(',').forEach(channel => { const [number, key, names] = channel.split('='); @@ -53,10 +60,6 @@ class TV extends DefaultDevice { return attributes; } - static matchesItemType(item) { - return item.type === 'Group'; - } - static getState(item) { const state = {}; const members = this.getMembers(item); From 2ea1ee20c340f3a9d0208f3d1d6600e56bedc580 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 15:42:28 +0200 Subject: [PATCH 50/94] Add or extend tests for devices Signed-off-by: Michael Krug --- tests/devices/camera.test.js | 59 +++ tests/devices/colorlight.test.js | 65 ++-- tests/devices/default.test.js | 14 +- tests/devices/dimmablelight.test.js | 15 +- tests/devices/fan.test.js | 126 +++---- tests/devices/lock.test.js | 74 ++++ tests/devices/openclosedevice.test.js | 145 +++++-- tests/devices/scene.test.js | 30 ++ tests/devices/securitysystem.test.js | 48 +++ tests/devices/sensor.test.js | 132 +++++++ tests/devices/simplelight.test.js | 11 +- tests/devices/speaker.test.js | 64 ++++ tests/devices/specialcolorlight.test.js | 119 ++++++ tests/devices/startstopswitch.test.js | 45 +++ tests/devices/switch.test.js | 21 +- tests/devices/temperaturesensor.test.js | 73 ++++ tests/devices/thermostat.test.js | 450 ++++++++++++++++++++++ tests/devices/tv.test.js | 480 ++++++++++++++++++++++++ tests/devices/valve.test.js | 51 +++ 19 files changed, 1846 insertions(+), 176 deletions(-) create mode 100644 tests/devices/camera.test.js create mode 100644 tests/devices/lock.test.js create mode 100644 tests/devices/scene.test.js create mode 100644 tests/devices/securitysystem.test.js create mode 100644 tests/devices/sensor.test.js create mode 100644 tests/devices/speaker.test.js create mode 100644 tests/devices/specialcolorlight.test.js create mode 100644 tests/devices/startstopswitch.test.js create mode 100644 tests/devices/temperaturesensor.test.js create mode 100644 tests/devices/thermostat.test.js create mode 100644 tests/devices/tv.test.js create mode 100644 tests/devices/valve.test.js diff --git a/tests/devices/camera.test.js b/tests/devices/camera.test.js new file mode 100644 index 00000000..941ad1f8 --- /dev/null +++ b/tests/devices/camera.test.js @@ -0,0 +1,59 @@ +const Device = require('../../functions/devices/camera.js'); + +describe('Camera Device', () => { + test('isCompatible', () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "CAMERA" + } + } + })).toBe(true); + }); + + test('matchesItemType', () => { + expect(Device.matchesItemType({ "type": "String" })).toBe(true); + expect(Device.matchesItemType({ "type": "Number" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(true); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Number" })).toBe(false); + }); + + describe('getAttributes', () => { + test('getAttributes no config', () => { + const item = { + "metadata": { + "ga": { + "config": {} + } + } + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "cameraStreamSupportedProtocols": ["hls", "dash"], + "cameraStreamNeedAuthToken": false, + "cameraStreamNeedDrmEncryption": false + }); + }); + + test('getAttributes protocols, token', () => { + const item = { + "metadata": { + "ga": { + "config": { + "protocols": "hls,test", + "token": true + } + } + } + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "cameraStreamSupportedProtocols": ["hls", "test"], + "cameraStreamNeedAuthToken": true, + "cameraStreamNeedDrmEncryption": false + }); + }); + }); + + test('getState', () => { + expect(Device.getState({})).toStrictEqual({}); + }); +}); diff --git a/tests/devices/colorlight.test.js b/tests/devices/colorlight.test.js index fcffe0c1..3dc4e7b8 100644 --- a/tests/devices/colorlight.test.js +++ b/tests/devices/colorlight.test.js @@ -1,7 +1,7 @@ const Device = require('../../functions/devices/colorlight.js'); -describe('Test ColorLight Device', () => { - test('Test isCompatible', async () => { +describe('ColorLight Device', () => { + test('isCompatible', () => { expect(Device.isCompatible({ "metadata": { "ga": { @@ -9,55 +9,52 @@ describe('Test ColorLight Device', () => { } } })).toBe(true); - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "SOMETHING" - } - } - })).toBe(false); }); - test('Test matchesItemType', async () => { + test('matchesItemType', () => { expect(Device.matchesItemType({ "type": "Color" })).toBe(true); expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(false); expect(Device.matchesItemType({ "type": "Group", "groupType": "Color" })).toBe(true); expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(false); }); - test('Test getAttributes', async () => { - const item1 = { - "metadata": { - "ga": { - "config": { - "colorTemperatureRange": "a,b" + describe('getAttributes', () => { + test('getAttributes colorTemperatureRange', () => { + const item = { + "metadata": { + "ga": { + "config": { + "colorTemperatureRange": "1000,2000" + } } } - } - }; - expect(Device.getAttributes(item1)).toStrictEqual({ - "colorModel": "hsv" + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "colorModel": "hsv", + "colorTemperatureRange": { + "temperatureMinK": 1000, + "temperatureMaxK": 2000 + } + }); }); - const item2 = { - "metadata": { - "ga": { - "config": { - "colorTemperatureRange": "1000,2000" + test('getAttributes invalid colorTemperatureRange', () => { + const item = { + "metadata": { + "ga": { + "config": { + "colorTemperatureRange": "a,b" + } } } - } - }; - expect(Device.getAttributes(item2)).toStrictEqual({ - "colorModel": "hsv", - "colorTemperatureRange": { - "temperatureMinK": 1000, - "temperatureMaxK": 2000 - } + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "colorModel": "hsv" + }); }); }); - test('Test getState', async () => { + test('getState', () => { expect(Device.getState({ "state": "100,50,10" })).toStrictEqual({ "on": true, "brightness": 10, diff --git a/tests/devices/default.test.js b/tests/devices/default.test.js index 49910594..638882e1 100644 --- a/tests/devices/default.test.js +++ b/tests/devices/default.test.js @@ -1,6 +1,6 @@ const Device = require('../../functions/devices/default.js'); -describe('Test Fan Device', () => { +describe('Fan Device', () => { const item = { "type": "Number", "state": "50", @@ -14,11 +14,14 @@ describe('Test Fan Device', () => { "ackNeeded": true, "pinNeeded": "1234" } + }, + "synonyms": { + "value": "Standard Device" } } }; - test('Test getConfig', async () => { + test('getConfig', () => { expect(Device.getConfig(item)).toStrictEqual({ "ackNeeded": true, "inverted": true, @@ -26,11 +29,11 @@ describe('Test Fan Device', () => { }); }); - test('Test getState', async () => { + test('getState', () => { expect(Device.getState(item)).toStrictEqual({}); }); - test('Test getMetadata', async () => { + test('getMetadata', () => { expect(Device.getMetadata(item)).toStrictEqual({ "attributes": {}, "customData": { @@ -54,6 +57,7 @@ describe('Test Fan Device', () => { "name": "Default Device", "nicknames": [ "Default Device", + "Standard Device" ], }, "roomHint": undefined, @@ -64,7 +68,7 @@ describe('Test Fan Device', () => { }); }); - test('Test hasTag', async () => { + test('hasTag', () => { expect(Device.hasTag({}, "testtag")).toBe(false); expect(Device.hasTag({ "tags": ["test"] }, "testtag")).toBe(false); expect(Device.hasTag({ "tags": ["testtag"] }, "testtag")).toBe(true); diff --git a/tests/devices/dimmablelight.test.js b/tests/devices/dimmablelight.test.js index a3842656..b48d167e 100644 --- a/tests/devices/dimmablelight.test.js +++ b/tests/devices/dimmablelight.test.js @@ -1,7 +1,7 @@ const Device = require('../../functions/devices/dimmablelight.js'); -describe('Test DimmableLight Device', () => { - test('Test isCompatible', async () => { +describe('DimmableLight Device', () => { + test('isCompatible', () => { expect(Device.isCompatible({ "metadata": { "ga": { @@ -9,23 +9,16 @@ describe('Test DimmableLight Device', () => { } } })).toBe(true); - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "SOMETHING" - } - } - })).toBe(false); }); - test('Test matchesItemType', async () => { + test('matchesItemType', () => { expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(true); expect(Device.matchesItemType({ "type": "String" })).toBe(false); expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(true); expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); }); - test('Test getState', async () => { + test('getState', () => { expect(Device.getState({ "state": "50" })).toStrictEqual({ "on": true, "brightness": 50 diff --git a/tests/devices/fan.test.js b/tests/devices/fan.test.js index 2d683f3d..af3576d9 100644 --- a/tests/devices/fan.test.js +++ b/tests/devices/fan.test.js @@ -1,7 +1,7 @@ const Device = require('../../functions/devices/fan.js'); -describe('Test Fan Device', () => { - test('Test isCompatible', async () => { +describe('Fan Device', () => { + test('isCompatible', () => { expect(Device.isCompatible({ "metadata": { "ga": { @@ -9,85 +9,79 @@ describe('Test Fan Device', () => { } } })).toBe(true); - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "SOMETHING" - } - } - })).toBe(false); }); - test('Test matchesItemType', async () => { + test('matchesItemType', () => { expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(true); expect(Device.matchesItemType({ "type": "String" })).toBe(false); expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(true); expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); }); - test('Test getAttributes', async () => { - const item1 = { - "metadata": { - "ga": { - "config": {} + describe('getAttributes', () => { + test('getAttributes no config', () => { + const item = { + "metadata": { + "ga": { + "config": {} + } } - } - }; - expect(Device.getAttributes(item1)).toStrictEqual({}); + }; + expect(Device.getAttributes(item)).toStrictEqual({}); + }); - const item2 = { - "metadata": { - "ga": { - "config": { - "ordered": true, - "speeds": "0=null:off,50=slow,100=full:fast", - "lang": "en" + test('getAttributes speeds', () => { + const item = { + "metadata": { + "ga": { + "config": { + "ordered": true, + "speeds": "0=null:off,50=slow,100=full:fast", + "lang": "en" + } } } - } - }; - expect(Device.getAttributes(item2)).toStrictEqual({ - "availableFanSpeeds": { - "speeds": [ - { - "speed_name": "0", - "speed_values": [ - { - "speed_synonym": ["null", "off"], - "lang": "en" - } - ] - }, - { - "speed_name": "50", - "speed_values": [ - { - "speed_synonym": ["slow"], - "lang": "en" - } - ] - }, - { - "speed_name": "100", - "speed_values": [ - { - "speed_synonym": ["full", "fast"], - "lang": "en" - } - ] - } - ], - "ordered": true - }, - "reversible": false + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "availableFanSpeeds": { + "speeds": [ + { + "speed_name": "0", + "speed_values": [ + { + "speed_synonym": ["null", "off"], + "lang": "en" + } + ] + }, + { + "speed_name": "50", + "speed_values": [ + { + "speed_synonym": ["slow"], + "lang": "en" + } + ] + }, + { + "speed_name": "100", + "speed_values": [ + { + "speed_synonym": ["full", "fast"], + "lang": "en" + } + ] + } + ], + "ordered": true + }, + "reversible": false + }); }); }); - test('Test getState', async () => { - const item = { - "state": "50" - }; - expect(Device.getState(item)).toStrictEqual({ + test('getState', () => { + expect(Device.getState({ "state": "50" })).toStrictEqual({ "currentFanSpeedSetting": "50", "on": true }); diff --git a/tests/devices/lock.test.js b/tests/devices/lock.test.js new file mode 100644 index 00000000..162c5ee9 --- /dev/null +++ b/tests/devices/lock.test.js @@ -0,0 +1,74 @@ +const Device = require('../../functions/devices/lock.js'); + +describe('Lock Device', () => { + test('isCompatible', () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "LOCK" + } + } + })).toBe(true); + }); + + test('matchesItemType', () => { + expect(Device.matchesItemType({ "type": "Switch" })).toBe(true); + expect(Device.matchesItemType({ "type": "Contact" })).toBe(true); + expect(Device.matchesItemType({ "type": "String" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Switch" })).toBe(true); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Contact" })).toBe(true); + expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); + }); + + describe('getState', () => { + test('getState Switch', () => { + expect(Device.getState({ "state": "ON" })).toStrictEqual({ + "isLocked": true + }); + expect(Device.getState({ "state": "OFF" })).toStrictEqual({ + "isLocked": false + }); + }); + + test('getState Contact', () => { + expect(Device.getState({ "state": "CLOSED" })).toStrictEqual({ + "isLocked": true + }); + expect(Device.getState({ "state": "OPEN" })).toStrictEqual({ + "isLocked": false + }); + }); + + test('getState inverted Swtich', () => { + const item1 = { + "state": "ON", + "metadata": { + "ga": { + "config": { + "inverted": true + } + } + } + }; + expect(Device.getState(item1)).toStrictEqual({ + "isLocked": false + }); + }); + + test('getState inverted Contact', () => { + const item2 = { + "state": "OPEN", + "metadata": { + "ga": { + "config": { + "inverted": true + } + } + } + }; + expect(Device.getState(item2)).toStrictEqual({ + "isLocked": true + }); + }); + }); +}); diff --git a/tests/devices/openclosedevice.test.js b/tests/devices/openclosedevice.test.js index 312e804b..48f25d16 100644 --- a/tests/devices/openclosedevice.test.js +++ b/tests/devices/openclosedevice.test.js @@ -1,7 +1,7 @@ const Device = require('../../functions/devices/openclosedevice.js'); -describe('Test OpenCloseDevice Device', () => { - test('Test getAttributes', async () => { +describe('OpenCloseDevice Device', () => { + test('getAttributes', () => { expect(Device.getAttributes({ "type": "Rollershutter" })).toStrictEqual({ "discreteOnlyOpenClose": false, "queryOnlyOpenClose": false @@ -14,53 +14,120 @@ describe('Test OpenCloseDevice Device', () => { "discreteOnlyOpenClose": true, "queryOnlyOpenClose": true }); - }); - test('Test getState', async () => { - const item1 = { - "type": "Switch", - "state": "ON" - }; - expect(Device.getState(item1)).toStrictEqual({ - "openPercent": 100 + expect(Device.getAttributes({ "type": "Group" })).toStrictEqual({ + "discreteOnlyOpenClose": false, + "queryOnlyOpenClose": false }); - const item2 = { - "type": "Rollershutter", - "state": "25" - }; - expect(Device.getState(item2)).toStrictEqual({ - "openPercent": 75 + expect(Device.getAttributes({ "type": "Group", "groupType": "Switch" })).toStrictEqual({ + "discreteOnlyOpenClose": true, + "queryOnlyOpenClose": false + }); + expect(Device.getAttributes({ "type": "Group", "groupType": "Contact" })).toStrictEqual({ + "discreteOnlyOpenClose": true, + "queryOnlyOpenClose": true }); }); - test('Test getState inverted', async () => { - const item = { - "type": "Switch", - "state": "ON", - "metadata": { - "ga": { - "config": { - "inverted": true + describe('getState', () => { + test('getState Contact', () => { + const item = { + "type": "Contact", + "state": "OPEN" + }; + expect(Device.getState(item)).toStrictEqual({ + "openPercent": 100 + }); + item.state = "CLOSED"; + expect(Device.getState(item)).toStrictEqual({ + "openPercent": 0 + }); + }); + + test('getState Switch', () => { + const item = { + "type": "Switch", + "state": "ON" + }; + expect(Device.getState(item)).toStrictEqual({ + "openPercent": 100 + }); + item.state = "OFF"; + expect(Device.getState(item)).toStrictEqual({ + "openPercent": 0 + }); + }); + + test('getState Rollershutter', () => { + const item = { + "type": "Rollershutter", + "state": "25" + }; + expect(Device.getState(item)).toStrictEqual({ + "openPercent": 75 + }); + }); + + test('getState Group Rollershutter', () => { + const item = { + "type": "Group", + "groupType": "Rollershutter", + "state": "25" + }; + expect(Device.getState(item)).toStrictEqual({ + "openPercent": 75 + }); + }); + + test('getState inverted Contact', () => { + const item = { + "type": "Contact", + "state": "CLOSED", + "metadata": { + "ga": { + "config": { + "inverted": true + } } } - } - }; - expect(Device.getState(item)).toStrictEqual({ - "openPercent": 0 + }; + expect(Device.getState(item)).toStrictEqual({ + "openPercent": 100 + }); }); - const item2 = { - "type": "Rollershutter", - "state": "25", - "metadata": { - "ga": { - "config": { - "inverted": true + + test('getState inverted Switch', () => { + const item = { + "type": "Switch", + "state": "ON", + "metadata": { + "ga": { + "config": { + "inverted": true + } + } + } + }; + expect(Device.getState(item)).toStrictEqual({ + "openPercent": 0 + }); + }); + + test('getState inverted Rollershutter', () => { + const item = { + "type": "Rollershutter", + "state": "25", + "metadata": { + "ga": { + "config": { + "inverted": true + } } } - } - }; - expect(Device.getState(item2)).toStrictEqual({ - "openPercent": 25 + }; + expect(Device.getState(item)).toStrictEqual({ + "openPercent": 25 + }); }); }); }); diff --git a/tests/devices/scene.test.js b/tests/devices/scene.test.js new file mode 100644 index 00000000..98f889d6 --- /dev/null +++ b/tests/devices/scene.test.js @@ -0,0 +1,30 @@ +const Device = require('../../functions/devices/scene.js'); + +describe('Scene Device', () => { + test('isCompatible', () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "SCENE" + } + } + })).toBe(true); + }); + + test('matchesItemType', () => { + expect(Device.matchesItemType({ "type": "Switch" })).toBe(true); + expect(Device.matchesItemType({ "type": "String" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Switch" })).toBe(true); + expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); + }); + + test('getAttributes', () => { + expect(Device.getAttributes()).toStrictEqual({ + "sceneReversible": true + }); + }); + + test('getState', () => { + expect(Device.getState({})).toStrictEqual({}); + }); +}); diff --git a/tests/devices/securitysystem.test.js b/tests/devices/securitysystem.test.js new file mode 100644 index 00000000..135c3dbc --- /dev/null +++ b/tests/devices/securitysystem.test.js @@ -0,0 +1,48 @@ +const Device = require('../../functions/devices/securitysystem.js'); + +describe('SecuritySystem Device', () => { + test('isCompatible', () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "SECURITYSYSTEM" + } + } + })).toBe(true); + }); + + test('matchesItemType', () => { + expect(Device.matchesItemType({ "type": "Switch" })).toBe(true); + expect(Device.matchesItemType({ "type": "String" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Switch" })).toBe(true); + expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); + }); + + + describe('getState', () => { + test('getState', () => { + expect(Device.getState({ "state": "ON" })).toStrictEqual({ + "isArmed": true + }); + expect(Device.getState({ "state": "OFF" })).toStrictEqual({ + "isArmed": false + }); + }); + + test('getState inverted', () => { + const item = { + "state": "ON", + "metadata": { + "ga": { + "config": { + "inverted": true + } + } + } + }; + expect(Device.getState(item)).toStrictEqual({ + "isArmed": false + }); + }); + }); +}); diff --git a/tests/devices/sensor.test.js b/tests/devices/sensor.test.js new file mode 100644 index 00000000..835421a6 --- /dev/null +++ b/tests/devices/sensor.test.js @@ -0,0 +1,132 @@ +const Device = require('../../functions/devices/sensor.js'); + +describe('Sensor Device', () => { + test('isCompatible', () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "SENSOR" + } + } + })).toBe(true); + }); + + describe('matchesItemType', () => { + describe('matchesItemType numeric', () => { + const item = { + "metadata": { + "ga": { + "config": { + "sensorName": "Sensor", + "valueUnit": "PERCENT" + } + } + } + }; + expect(Device.matchesItemType(item)).toBe(true); + expect(Device.matchesItemType({ "type": "Number" })).toBe(false); + }); + + describe('matchesItemType descriptive', () => { + const item = { + "metadata": { + "ga": { + "config": { + "sensorName": "Sensor", + "states": "50=good,100=bad" + } + } + } + }; + expect(Device.matchesItemType(item)).toBe(true); + }); + }); + + describe('getAttributes', () => { + test('getAttributes no config', () => { + const item = { + "metadata": { + "ga": { + "config": {} + } + } + }; + expect(Device.getAttributes(item)).toStrictEqual({ sensorStatesSupported: {} }); + }); + + test('getAttributes states', () => { + const item = { + "metadata": { + "ga": { + "config": { + "sensorName": "Sensor", + "valueUnit": "AQI", + "states": "good=10,moderate=50,poor=90" + } + } + } + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "sensorStatesSupported": { + "descriptiveCapabilities": { + "availableStates": [ + "good", + "moderate", + "poor" + ], + }, + "name": "Sensor", + "numericCapabilities": { + "rawValueUnit": "AQI" + } + } + }); + }); + }); + + describe('getState', () => { + test('getState', () => { + const item = { + "metadata": { + "ga": { + "config": { + "sensorName": "Sensor", + "valueUnit": "AQI", + "states": "good=10,moderate=50,poor=90" + } + } + }, + "state": "10" + }; + expect(Device.getState(item)).toStrictEqual({ + "currentSensorStateData": { + "currentSensorState": "good", + "name": "Sensor", + "rawValue": 10 + } + }); + }); + + test('getState no matching state', () => { + const item = { + "metadata": { + "ga": { + "config": { + "sensorName": "Sensor", + "valueUnit": "AQI", + "states": "good=10,moderate=50,poor=90" + } + } + }, + "state": "20" + }; + expect(Device.getState(item)).toStrictEqual({ + "currentSensorStateData": { + "currentSensorState": "", + "name": "Sensor", + "rawValue": 20 + } + }); + }); + }); +}); diff --git a/tests/devices/simplelight.test.js b/tests/devices/simplelight.test.js index e4b620ff..77b11a3d 100644 --- a/tests/devices/simplelight.test.js +++ b/tests/devices/simplelight.test.js @@ -1,7 +1,7 @@ const Device = require('../../functions/devices/simplelight.js'); -describe('Test SimpleLight Device', () => { - test('Test isCompatible', async () => { +describe('SimpleLight Device', () => { + test('isCompatible', () => { expect(Device.isCompatible({ "metadata": { "ga": { @@ -9,12 +9,5 @@ describe('Test SimpleLight Device', () => { } } })).toBe(true); - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "SOMETHING" - } - } - })).toBe(false); }); }); diff --git a/tests/devices/speaker.test.js b/tests/devices/speaker.test.js new file mode 100644 index 00000000..a8a462ce --- /dev/null +++ b/tests/devices/speaker.test.js @@ -0,0 +1,64 @@ +const Device = require('../../functions/devices/speaker.js'); + +describe('Speaker Device', () => { + test('isCompatible', () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "SPEAKER" + } + } + })).toBe(true); + }); + + test('matchesItemType', () => { + expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(true); + expect(Device.matchesItemType({ "type": "Number" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(true); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Number" })).toBe(false); + }); + + describe('getAttributes', () => { + test('getAttributes no config', () => { + const item = { + "metadata": { + "ga": { + "config": {} + } + } + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "volumeCanMuteAndUnmute": false, + "volumeMaxLevel": 100 + }); + }); + + test('getAttributes volumeDefaultPercentage, levelStepSize', () => { + const item = { + "metadata": { + "ga": { + "config": { + "volumeDefaultPercentage": "20", + "levelStepSize": "10" + } + } + } + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "volumeCanMuteAndUnmute": false, + "volumeMaxLevel": 100, + "volumeDefaultPercentage": 20, + "levelStepSize": 10 + }); + }); + }); + + test('getState', () => { + expect(Device.getState({ "state": "10" })).toStrictEqual({ + "currentVolume": 10 + }); + expect(Device.getState({ "state": "90" })).toStrictEqual({ + "currentVolume": 90 + }); + }); +}); diff --git a/tests/devices/specialcolorlight.test.js b/tests/devices/specialcolorlight.test.js new file mode 100644 index 00000000..80bf2146 --- /dev/null +++ b/tests/devices/specialcolorlight.test.js @@ -0,0 +1,119 @@ +const Device = require('../../functions/devices/specialcolorlight.js'); + +describe('SpecialColorLight Device', () => { + test('isCompatible', () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "LIGHT" + } + } + })).toBe(true); + }); + + test('matchesItemType', () => { + const item = { + "type": "Group", + "metadata": { + "ga": { + "value": "LIGHT", + "config": { + "colorTemperatureRange": "1000,4000" + } + } + }, + "members": [ + { + "metadata": { + "ga": { + "value": "lightBrightness" + } + } + }, + { + "metadata": { + "ga": { + "value": "lightColorTemperature" + } + } + } + ] + }; + expect(Device.matchesItemType(item)).toBe(true); + expect(Device.matchesItemType({ "type": "Color" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Color" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(false); + }); + + describe('getAttributes', () => { + test('getAttributes colorTemperatureRange', () => { + const item = { + "metadata": { + "ga": { + "config": { + "colorTemperatureRange": "1000,2000" + } + } + } + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "colorTemperatureRange": { + "temperatureMinK": 1000, + "temperatureMaxK": 2000 + } + }); + }); + + test('getAttributes invalid colorTemperatureRange', () => { + const item1 = { + "metadata": { + "ga": { + "config": { + "colorTemperatureRange": "a,b" + } + } + } + }; + expect(Device.getAttributes(item1)).toStrictEqual({}); + }); + }); + + test('getState', () => { + const item = { + "type": "Group", + "metadata": { + "ga": { + "value": "LIGHT", + "config": { + "colorTemperatureRange": "1000,4000" + } + } + }, + "members": [ + { + "state": "50", + "metadata": { + "ga": { + "value": "lightBrightness" + } + } + }, + { + "state": "20", + "metadata": { + "ga": { + "value": "lightColorTemperature" + } + } + } + ] + }; + expect(Device.getState(item)).toStrictEqual({ + "on": true, + "brightness": 50, + "color": { + "temperatureK": 3400 + } + }); + }); +}); diff --git a/tests/devices/startstopswitch.test.js b/tests/devices/startstopswitch.test.js new file mode 100644 index 00000000..3d415a4a --- /dev/null +++ b/tests/devices/startstopswitch.test.js @@ -0,0 +1,45 @@ +const Device = require('../../functions/devices/startstopswitch.js'); + +describe('StartStopSwitch Device', () => { + test('matchesItemType', () => { + expect(Device.matchesItemType({ "type": "Switch" })).toBe(true); + expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Switch" })).toBe(true); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(false); + }); + + describe('getState', () => { + test('getState', () => { + expect(Device.getState({ "state": "ON" })).toStrictEqual({ + "isRunning": true, + "isPaused": false + }); + expect(Device.getState({ "state": "OFF" })).toStrictEqual({ + "isRunning": false, + "isPaused": true + }); + }); + + test('getState inverted', () => { + const item = { + "state": "ON", + "metadata": { + "ga": { + "config": { + "inverted": true + } + } + } + }; + expect(Device.getState(item)).toStrictEqual({ + "isRunning": false, + "isPaused": true + }); + item.state = "OFF"; + expect(Device.getState(item)).toStrictEqual({ + "isRunning": true, + "isPaused": false + }); + }); + }); +}); diff --git a/tests/devices/switch.test.js b/tests/devices/switch.test.js index 547a78fe..4e51663b 100644 --- a/tests/devices/switch.test.js +++ b/tests/devices/switch.test.js @@ -1,7 +1,7 @@ const Device = require('../../functions/devices/switch.js'); -describe('Test Switch Device', () => { - test('Test isCompatible', async () => { +describe('Switch Device', () => { + test('isCompatible', () => { expect(Device.isCompatible({ "metadata": { "ga": { @@ -9,23 +9,16 @@ describe('Test Switch Device', () => { } } })).toBe(true); - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "SOMETHING" - } - } - })).toBe(false); }); - test('Test matchesItemType', async () => { + test('matchesItemType', () => { expect(Device.matchesItemType({ "type": "Switch" })).toBe(true); expect(Device.matchesItemType({ "type": "String" })).toBe(false); expect(Device.matchesItemType({ "type": "Group", "groupType": "Switch" })).toBe(true); expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); }); - test('Test getState', async () => { + test('getState', () => { expect(Device.getState({ "state": "ON" })).toStrictEqual({ "on": true }); @@ -34,7 +27,7 @@ describe('Test Switch Device', () => { }); }); - test('Test getState inverted', async () => { + test('getState inverted', () => { const item = { "state": "ON", "metadata": { @@ -48,5 +41,9 @@ describe('Test Switch Device', () => { expect(Device.getState(item)).toStrictEqual({ "on": false }); + item.state = "OFF"; + expect(Device.getState(item)).toStrictEqual({ + "on": true + }); }); }); diff --git a/tests/devices/temperaturesensor.test.js b/tests/devices/temperaturesensor.test.js new file mode 100644 index 00000000..39917adb --- /dev/null +++ b/tests/devices/temperaturesensor.test.js @@ -0,0 +1,73 @@ +const Device = require('../../functions/devices/temperaturesensor.js'); + +describe('TemperatureSensor Device', () => { + test('isCompatible', () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "temperaturesensor" + } + } + })).toBe(true); + }); + + test('matchesItemType', () => { + expect(Device.matchesItemType({ "type": "Number" })).toBe(true); + expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Number" })).toBe(true); + }); + + describe('getAttributes', () => { + test('getAttributes no config', () => { + const item1 = { + "metadata": { + "ga": { + "config": {} + } + } + }; + expect(Device.getAttributes(item1)).toStrictEqual({ + "queryOnlyTemperatureControl": true, + "temperatureUnitForUX": "C" + }); + }); + + test('getAttributes useFahrenheit', () => { + const item2 = { + "metadata": { + "ga": { + "config": { + "useFahrenheit": true + } + } + } + }; + expect(Device.getAttributes(item2)).toStrictEqual({ + "queryOnlyTemperatureControl": true, + "temperatureUnitForUX": "F" + }); + }); + }); + + test('getState', () => { + expect(Device.getState({ "state": "10" })).toStrictEqual({ + "temperatureSetpointCelsius": 10, + "temperatureAmbientCelsius": 10 + }); + const item = { + "state": "10", + "metadata": { + "ga": { + "config": { + "useFahrenheit": true + } + } + } + }; + expect(Device.getState(item)).toStrictEqual({ + "temperatureSetpointCelsius": -12.2, + "temperatureAmbientCelsius": -12.2 + }); + }); +}); diff --git a/tests/devices/thermostat.test.js b/tests/devices/thermostat.test.js new file mode 100644 index 00000000..02136de7 --- /dev/null +++ b/tests/devices/thermostat.test.js @@ -0,0 +1,450 @@ +const Device = require('../../functions/devices/thermostat.js'); + +describe('Thermostat Device', () => { + test('isCompatible', () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "THERMOSTAT" + } + } + })).toBe(true); + }); + + test('matchesItemType', () => { + const item = { + "type": "Group", + "members": [ + { + "metadata": { + "ga": { + "value": "thermostatTemperatureAmbient" + } + } + } + ] + }; + expect(Device.matchesItemType(item)).toBe(true); + expect(Device.matchesItemType({ "type": "Group" })).toBe(false); + }); + + describe('usesFahrenheit', () => { + test('usesFahrenheit unit', () => { + const item = { + "metadata": { + "ga": { + "config": { + "thermostatTemperatureUnit": "F" + } + } + } + }; + expect(Device.usesFahrenheit(item)).toBe(true); + }); + test('usesFahrenheit unit', () => { + const item = { + "metadata": { + "ga": { + "config": { + "useFahrenheit": true + } + } + } + }; + expect(Device.usesFahrenheit(item)).toBe(true); + }); + test('usesFahrenheit unit', () => { + const item = { + "tags": ["Fahrenheit"] + }; + expect(Device.usesFahrenheit(item)).toBe(true); + }); + }); + + describe('getAttributes', () => { + test('getAttributes no config', () => { + const item = { + "metadata": { + "ga": { + "config": {} + } + } + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "availableThermostatModes": "off,heat,cool,on,heatcool,auto,eco", + "thermostatTemperatureUnit": "C" + }); + }); + + test('getAttributes modes, fahrenheit', () => { + const item = { + "metadata": { + "ga": { + "config": { + "modes": "on=1,off=2", + "useFahrenheit": true + } + } + } + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "availableThermostatModes": "on,off", + "thermostatTemperatureUnit": "F" + }); + }); + + test('getAttributes temperaturerange', () => { + const item = { + "metadata": { + "ga": { + "config": { + "thermostatTemperatureRange": "10,30" + } + } + } + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "availableThermostatModes": "off,heat,cool,on,heatcool,auto,eco", + "thermostatTemperatureUnit": "C", + "thermostatTemperatureRange": { + "maxThresholdCelsius": 30, + "minThresholdCelsius": 10 + } + }); + }); + + test('getAttributes invalid temperaturerange', () => { + const item = { + "metadata": { + "ga": { + "config": { + "thermostatTemperatureRange": "a,b" + } + } + } + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "availableThermostatModes": "off,heat,cool,on,heatcool,auto,eco", + "thermostatTemperatureUnit": "C" + }); + }); + + test('getAttributes queryOnly', () => { + const item = { + "metadata": { + "ga": { + "config": {} + } + }, + "members": [ + { + "metadata": { + "ga": { + "value": "thermostatTemperatureAmbient" + } + } + } + ] + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "thermostatTemperatureUnit": "C", + "queryOnlyTemperatureSetting": true + }); + }); + }); + + describe('getMembers', () => { + test('getMembers', () => { + const item = { + "members": [ + { + "name": "Mode", + "state": "on", + "metadata": { + "ga": { + "value": "thermostatMode" + } + } + }, + { + "name": "Setpoint", + "state": "20", + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpoint" + } + } + }, + { + "name": "High", + "state": "25", + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpointHigh" + } + } + }, + { + "name": "Low", + "state": "5", + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpointLow" + } + } + }, + { + "name": "Temperature", + "state": "20", + "metadata": { + "ga": { + "value": "thermostatTemperatureAmbient" + } + } + }, + { + "name": "Humidity", + "state": "50", + "metadata": { + "ga": { + "value": "thermostatHumidityAmbient" + } + } + } + ] + }; + expect(Device.getMembers(item)).toStrictEqual({ + "thermostatMode": { + "name": "Mode", + "state": "on" + }, + "thermostatTemperatureSetpoint": { + "name": "Setpoint", + "state": "20" + }, + "thermostatTemperatureSetpointHigh": { + "name": "High", + "state": "25" + }, + "thermostatTemperatureSetpointLow": { + "name": "Low", + "state": "5" + }, + "thermostatTemperatureAmbient": { + "name": "Temperature", + "state": "20" + }, + "thermostatHumidityAmbient": { + "name": "Humidity", + "state": "50" + } + }); + }); + + test('getMembers with tags', () => { + const item = { + "members": [ + { + "name": "Mode", + "state": "on", + "tags": ["HeatingCoolingMode"] + }, + { + "name": "Setpoint", + "state": "20", + "tags": ["TargetTemperature"] + }, + { + "name": "Temperature", + "state": "20", + "tags": ["CurrentTemperature"] + }, + { + "name": "Humidity", + "state": "50", + "tags": ["CurrentHumidity"] + } + ] + }; + expect(Device.getMembers(item)).toStrictEqual({ + "thermostatMode": { + "name": "Mode", + "state": "on" + }, + "thermostatTemperatureSetpoint": { + "name": "Setpoint", + "state": "20" + }, + "thermostatTemperatureAmbient": { + "name": "Temperature", + "state": "20" + }, + "thermostatHumidityAmbient": { + "name": "Humidity", + "state": "50" + } + }); + }); + }); + + test('getModeMap', () => { + const item = { + "metadata": { + "ga": { + "config": { + "modes": "on=ON:1,off=OFF:2,auto=3" + } + } + } + }; + expect(Device.getModeMap(item)).toStrictEqual({ + "on": [ + "ON", + "1" + ], + "off": [ + "OFF", + "2" + ], + "auto": [ + "3" + ] + }); + expect(Device.getModeMap({})).toStrictEqual({ + "off": ["off"], + "heat": ["heat"], + "cool": ["cool"], + "on": ["on"], + "heatcool": ["heatcool"], + "auto": ["auto"], + "eco": ["eco"] + }); + }); + + test('translateModeToOpenhab', () => { + const item = { + "metadata": { + "ga": { + "config": { + "modes": "on=ON:1,off=OFF:2,auto=3" + } + } + } + }; + expect(Device.translateModeToOpenhab(item, "off")).toBe("OFF"); + expect(Device.translateModeToOpenhab(item, "auto")).toBe("3"); + expect(() => { Device.translateModeToOpenhab(item, "invalid") }).toThrow(); + }); + + test('translateModeToGoogle', () => { + const item = { + "metadata": { + "ga": { + "config": { + "modes": "on=ON:1,off=OFF:2,auto=3" + } + } + } + }; + expect(Device.translateModeToGoogle(item, "OFF")).toBe("off"); + expect(Device.translateModeToGoogle(item, "3")).toBe("auto"); + expect(Device.translateModeToGoogle(item, "invalid")).toBe("on"); + }); + + describe('getState', () => { + test('getState', () => { + const item = { + "members": [ + { + "name": "Mode", + "state": "on", + "metadata": { + "ga": { + "value": "thermostatMode" + } + } + }, + { + "name": "Setpoint", + "state": "20", + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpoint" + } + } + }, + { + "name": "High", + "state": "25", + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpointHigh" + } + } + }, + { + "name": "Low", + "state": "5", + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpointLow" + } + } + }, + { + "name": "Temperature", + "state": "20", + "metadata": { + "ga": { + "value": "thermostatTemperatureAmbient" + } + } + }, + { + "name": "Humidity", + "state": "50", + "metadata": { + "ga": { + "value": "thermostatHumidityAmbient" + } + } + } + ] + }; + expect(Device.getState(item)).toStrictEqual({ + "thermostatHumidityAmbient": 50, + "thermostatMode": "on", + "thermostatTemperatureAmbient": 20, + "thermostatTemperatureSetpoint": 20, + "thermostatTemperatureSetpointHigh": 25, + "thermostatTemperatureSetpointLow": 5 + }); + }); + + test('getState only temperature', () => { + const item = { + "metadata": { + "ga": { + "config": { + "useFahrenheit": true + } + } + }, + "members": [ + { + "name": "Temperature", + "state": "20", + "metadata": { + "ga": { + "value": "thermostatTemperatureAmbient" + } + } + } + ] + }; + expect(Device.getState(item)).toStrictEqual({ + "thermostatTemperatureAmbient": -6.7 + }); + }); + }); +}); diff --git a/tests/devices/tv.test.js b/tests/devices/tv.test.js new file mode 100644 index 00000000..06b8fbee --- /dev/null +++ b/tests/devices/tv.test.js @@ -0,0 +1,480 @@ +const Device = require('../../functions/devices/tv.js'); + +describe('TV Device', () => { + test('isCompatible', () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "TV" + } + } + })).toBe(true); + }); + + test('matchesItemType', () => { + const item = { + "type": "Group", + "members": [ + { + "metadata": { + "ga": { + "value": "tvPower" + } + } + } + ] + }; + expect(Device.matchesItemType(item)).toBe(true); + expect(Device.matchesItemType({ "type": "Group" })).toBe(false); + }); + + describe('getTraits', () => { + test('getTraits only power', () => { + const item = { + "members": [ + { + "state": "ON", + "metadata": { + "ga": { + "value": "tvPower" + } + } + } + ] + }; + expect(Device.getTraits(item)).toStrictEqual([ + "action.devices.traits.OnOff" + ]); + }); + + test('getTraits all members', () => { + const item = { + "members": [ + { + "state": "1", + "metadata": { + "ga": { + "value": "tvChannel" + } + } + }, + { + "state": "50", + "metadata": { + "ga": { + "value": "tvVolume" + } + } + }, + { + "state": "input1", + "metadata": { + "ga": { + "value": "tvInput" + } + } + }, + { + "state": "PLAY", + "metadata": { + "ga": { + "value": "tvTransport" + } + } + }, + { + "state": "ON", + "metadata": { + "ga": { + "value": "tvPower" + } + } + }, + { + "state": "OFF", + "metadata": { + "ga": { + "value": "tvMute" + } + } + } + ] + }; + expect(Device.getTraits(item)).toStrictEqual([ + "action.devices.traits.OnOff", + "action.devices.traits.Volume", + "action.devices.traits.Channel", + "action.devices.traits.InputSelector", + "action.devices.traits.TransportControl" + ]); + }); + }); + + describe('getAttributes', () => { + test('getAttributes no config', () => { + const item = { + "metadata": { + "ga": { + "config": {} + } + } + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "volumeCanMuteAndUnmute": false + }); + }); + + test('getAttributes transport, mute', () => { + const item = { + "metadata": { + "ga": { + "config": { + "transportControlSupportedCommands": "PAUSE,RESUME" + } + } + }, + "members": [ + { + "metadata": { + "ga": { + "value": "tvTransport" + } + } + }, + { + "metadata": { + "ga": { + "value": "tvMute" + } + } + } + ] + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "transportControlSupportedCommands": ["PAUSE", "RESUME"], + "volumeCanMuteAndUnmute": true + }); + }); + + test('getAttributes inputs', () => { + const item = { + "metadata": { + "ga": { + "config": { + "availableInputs": "input1=hdmi1,input2=hdmi2" + } + } + }, + "members": [ + { + "metadata": { + "ga": { + "value": "tvInput" + } + } + } + ] + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "availableInputs": [ + { + "key": "input1", + "names": [ + { + "lang": "en", + "name_synonym": ["hdmi1"], + }, + ], + }, + { + "key": "input2", + "names": [ + { + "lang": "en", + "name_synonym": ["hdmi2"] + } + ] + } + ], + "orderedInputs": false, + "volumeCanMuteAndUnmute": false + }); + }); + + test('getAttributes channels', () => { + const item = { + "metadata": { + "ga": { + "config": { + "availableChannels": "1=channel1=ARD,2=channel2=ZDF" + } + } + }, + "members": [ + { + "metadata": { + "ga": { + "value": "tvChannel" + } + } + } + ] + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "availableChannels": [ + { + "key": "channel1", + "names": ["ARD"], + "number": "1" + }, + { + "key": "channel2", + "names": ["ZDF"], + "number": "2" + } + ], + "volumeCanMuteAndUnmute": false + }); + }); + }); + + test('getMembers', () => { + const item = { + "members": [ + { + "name": "Channel", + "state": "1", + "metadata": { + "ga": { + "value": "tvChannel" + } + } + }, + { + "name": "Volume", + "state": "50", + "metadata": { + "ga": { + "value": "tvVolume" + } + } + }, + { + "name": "Input", + "state": "input1", + "metadata": { + "ga": { + "value": "tvInput" + } + } + }, + { + "name": "Transport", + "state": "PLAY", + "metadata": { + "ga": { + "value": "tvTransport" + } + } + }, + { + "name": "Power", + "state": "ON", + "metadata": { + "ga": { + "value": "tvPower" + } + } + }, + { + "name": "Mute", + "state": "OFF", + "metadata": { + "ga": { + "value": "tvMute" + } + } + } + ] + }; + expect(Device.getMembers(item)).toStrictEqual({ + "tvChannel": { + "name": "Channel", + "state": "1" + }, + "tvInput": { + "name": "Input", + "state": "input1" + }, + "tvMute": { + "name": "Mute", + "state": "OFF" + }, + "tvPower": { + "name": "Power", + "state": "ON" + }, + "tvTransport": { + "name": "Transport", + "state": "PLAY" + }, + "tvVolume": { + "name": "Volume", + "state": "50" + } + }); + }); + + test('getChannelMap', () => { + const item = { + "metadata": { + "ga": { + "value": "TV", + "config": { + "availableChannels": "20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2" + } + } + } + }; + expect(Device.getChannelMap(item)).toStrictEqual({ + "10": [ + "Channel 2", + "Kanal 2", + "channel2" + ], + "20": [ + "Channel 1", + "Kanal 1", + "channel1" + ], + }); + }); + + describe('getState', () => { + test('getState', () => { + const item = { + "type": "Group", + "metadata": { + "ga": { + "value": "TV", + "config": { + "transportControlSupportedCommands": "PAUSE,RESUME", + "availableInputs": "input1=hdmi1,input2=hdmi2", + "availableChannels": "1=channel1=ARD,2=channel2=ZDF" + } + } + }, + "members": [ + { + "state": "1", + "metadata": { + "ga": { + "value": "tvChannel" + } + } + }, + { + "state": "50", + "metadata": { + "ga": { + "value": "tvVolume" + } + } + }, + { + "state": "input1", + "metadata": { + "ga": { + "value": "tvInput" + } + } + }, + { + "state": "PLAY", + "metadata": { + "ga": { + "value": "tvTransport" + } + } + }, + { + "state": "ON", + "metadata": { + "ga": { + "value": "tvPower" + } + } + }, + { + "state": "OFF", + "metadata": { + "ga": { + "value": "tvMute" + } + } + } + ] + }; + expect(Device.getState(item)).toStrictEqual({ + "channelName": "ARD", + "channelNumber": "1", + "currentInput": "input1", + "currentVolume": 50, + "isMuted": false, + "on": true, + }); + }); + + test('getState only channel without map', () => { + const item = { + "type": "Group", + "members": [ + { + "state": "1", + "metadata": { + "ga": { + "value": "tvChannel" + } + } + } + ] + }; + expect(Device.getState(item)).toStrictEqual({ + "channelNumber": "1" + }); + }); + + test('getState only power, mute', () => { + const item = { + "type": "Group", + "metadata": { + "ga": { + "value": "TV" + } + }, + "members": [ + { + "state": "50", + "metadata": { + "ga": { + "value": "tvVolume" + } + } + }, + { + "state": "ON", + "metadata": { + "ga": { + "value": "tvPower" + } + } + } + ] + }; + expect(Device.getState(item)).toStrictEqual({ + "currentVolume": 50, + "on": true, + }); + }); + }); +}); diff --git a/tests/devices/valve.test.js b/tests/devices/valve.test.js new file mode 100644 index 00000000..63579e6d --- /dev/null +++ b/tests/devices/valve.test.js @@ -0,0 +1,51 @@ +const Device = require('../../functions/devices/valve.js'); + +describe('Valve Device', () => { + test('isCompatible', () => { + expect(Device.isCompatible({ + "metadata": { + "ga": { + "value": "VALVE" + } + } + })).toBe(true); + }); + + test('matchesItemType', () => { + expect(Device.matchesItemType({ "type": "Switch" })).toBe(true); + expect(Device.matchesItemType({ "type": "String" })).toBe(false); + expect(Device.matchesItemType({ "type": "Group", "groupType": "Switch" })).toBe(true); + expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); + }); + + describe('getState', () => { + test('getState', () => { + expect(Device.getState({ "state": "ON" })).toStrictEqual({ + "openPercent": 100 + }); + expect(Device.getState({ "state": "OFF" })).toStrictEqual({ + "openPercent": 0 + }); + }); + + test('getState inverted', () => { + const item = { + "state": "ON", + "metadata": { + "ga": { + "config": { + "inverted": true + } + } + } + }; + expect(Device.getState(item)).toStrictEqual({ + "openPercent": 0 + }); + item.state = "OFF"; + expect(Device.getState(item)).toStrictEqual({ + "openPercent": 100 + }); + }); + }); +}); From 6e71674a01713d2fe98cdbf517f3168b42ea26ca Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 16:35:24 +0200 Subject: [PATCH 51/94] Minor fixes Signed-off-by: Michael Krug --- .../commands/{armdisaarm.js => armdisarm.js} | 0 functions/commands/brightnessabsolute.js | 2 +- functions/commands/mute.js | 2 +- functions/commands/onoff.js | 2 +- functions/commands/setvolume.js | 2 +- functions/devices/default.js | 6 ++++-- functions/utilities.js | 21 ++++++++++++------- 7 files changed, 22 insertions(+), 13 deletions(-) rename functions/commands/{armdisaarm.js => armdisarm.js} (100%) diff --git a/functions/commands/armdisaarm.js b/functions/commands/armdisarm.js similarity index 100% rename from functions/commands/armdisaarm.js rename to functions/commands/armdisarm.js diff --git a/functions/commands/brightnessabsolute.js b/functions/commands/brightnessabsolute.js index a7fe5b52..7307ac6c 100644 --- a/functions/commands/brightnessabsolute.js +++ b/functions/commands/brightnessabsolute.js @@ -11,7 +11,7 @@ class BrightnessAbsolute extends DefaultCommand { } static requiresItem(device) { - return device.customData && device.customData.deviceType === 'SpecialColorLight'; + return !!device.customData && device.customData.deviceType === 'SpecialColorLight'; } static getItemName(item, device) { diff --git a/functions/commands/mute.js b/functions/commands/mute.js index 7d43bc32..2db7c783 100644 --- a/functions/commands/mute.js +++ b/functions/commands/mute.js @@ -11,7 +11,7 @@ class Mute extends DefaultCommand { } static requiresItem(device) { - return device.customData && device.customData.deviceType === 'TV'; + return !!device.customData && device.customData.deviceType === 'TV'; } static getItemName(item, device) { diff --git a/functions/commands/onoff.js b/functions/commands/onoff.js index 57383a6b..8a49a014 100644 --- a/functions/commands/onoff.js +++ b/functions/commands/onoff.js @@ -12,7 +12,7 @@ class OnOff extends DefaultCommand { } static requiresItem(device) { - return device.customData && ( + return !!device.customData && ( device.customData.deviceType === 'SpecialColorLight' || device.customData.deviceType === 'TV' ); } diff --git a/functions/commands/setvolume.js b/functions/commands/setvolume.js index 9b9261d3..d2e0df40 100644 --- a/functions/commands/setvolume.js +++ b/functions/commands/setvolume.js @@ -11,7 +11,7 @@ class SetVolume extends DefaultCommand { } static requiresItem(device) { - return device.customData && device.customData.deviceType === 'TV'; + return !!device.customData && device.customData.deviceType === 'TV'; } static getItemName(item, device) { diff --git a/functions/devices/default.js b/functions/devices/default.js index d7e03d9c..46c2b91d 100644 --- a/functions/devices/default.js +++ b/functions/devices/default.js @@ -16,7 +16,8 @@ class DefaultDevice { /** * @param {object} item - */ static isCompatible(item) { + */ + static isCompatible(item) { return item.metadata && item.metadata.ga && this.type.toLowerCase() === `action.devices.types.${item.metadata.ga.value}`.toLowerCase() || this.hasTag(item, this.type.substr(21).replace('SWITCH', 'SWITCHABLE').replace('LIGHT', 'LIGHTING')) @@ -35,7 +36,8 @@ class DefaultDevice { /** * @param {object} item - */ static getAttributes(item) { + */ + static getAttributes(item) { return {}; } diff --git a/functions/utilities.js b/functions/utilities.js index 9626a584..a21cd1ee 100644 --- a/functions/utilities.js +++ b/functions/utilities.js @@ -19,15 +19,21 @@ */ module.exports = { - - convertToCelsius: (value = 0) => { + /** + * @param {number} value + */ + convertToCelsius: (value) => { return Number(((value - 32) * 5 / 9).toFixed(1)); }, - - convertToFahrenheit: (value = 0) => { + /** + * @param {number} value + */ + convertToFahrenheit: (value) => { return Math.round(value * 9 / 5 + 32); }, - + /** + * @param {number} kelvin + */ kelvin2rgb: (kelvin) => { const temp = kelvin / 100; const r = temp <= 66 ? 255 : 329.698727446 * Math.pow(temp - 60, -0.1332047592); @@ -39,7 +45,9 @@ module.exports = { b: b < 0 ? 0 : b > 255 ? 255 : Math.round(b), }; }, - + /** + * @param {object} rgb + */ rgb2hsv: ({ r, g, b }) => { r = r / 255; g = g / 255; @@ -52,5 +60,4 @@ module.exports = { value: Math.round(v * 100) / 100 }; } - } From cbbc7017d6b1c49da1a6e2b83b074bbba3120463 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 16:51:44 +0200 Subject: [PATCH 52/94] Add tests for commands (WIP) Signed-off-by: Michael Krug --- tests/commands/activatescene.test.js | 20 +++++ tests/commands/armdisarm.test.js | 26 ++++++ tests/commands/brightnessabsolute.test.js | 45 ++++++++++ tests/commands/colorabsolute.test.js | 34 +++++++ .../commands/colorabsolutetemperature.test.js | 90 +++++++++++++++++++ 5 files changed, 215 insertions(+) create mode 100644 tests/commands/activatescene.test.js create mode 100644 tests/commands/armdisarm.test.js create mode 100644 tests/commands/brightnessabsolute.test.js create mode 100644 tests/commands/colorabsolute.test.js create mode 100644 tests/commands/colorabsolutetemperature.test.js diff --git a/tests/commands/activatescene.test.js b/tests/commands/activatescene.test.js new file mode 100644 index 00000000..94c359b2 --- /dev/null +++ b/tests/commands/activatescene.test.js @@ -0,0 +1,20 @@ +const Command = require('../../functions/commands/activatescene.js'); + +describe('ActivateScene Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(true); + expect(Command.validateParams({ "deactivate": true })).toBe(true); + }); + + describe('convertParamsToValue', () => { + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue({ "deactivate": true }, {}, {})).toBe("OFF"); + expect(Command.convertParamsToValue({ "deactivate": false }, {}, {})).toBe("ON"); + }); + + test('convertParamsToValue inverted', () => { + expect(Command.convertParamsToValue({ "deactivate": true }, {}, { "customData": { "inverted": true } })).toBe("ON"); + expect(Command.convertParamsToValue({ "deactivate": false }, {}, { "customData": { "inverted": true } })).toBe("OFF"); + }); + }); +}); diff --git a/tests/commands/armdisarm.test.js b/tests/commands/armdisarm.test.js new file mode 100644 index 00000000..5c55f1ed --- /dev/null +++ b/tests/commands/armdisarm.test.js @@ -0,0 +1,26 @@ +const Command = require('../../functions/commands/armdisarm.js'); + +describe('ArmDisarm Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams({ "arm": true })).toBe(true); + expect(Command.validateParams({ "arm": "true" })).toBe(false); + }); + + describe('convertParamsToValue', () => { + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue({ "arm": true }, {}, {})).toBe("ON"); + expect(Command.convertParamsToValue({ "arm": false }, {}, {})).toBe("OFF"); + }); + + test('convertParamsToValue inverted', () => { + expect(Command.convertParamsToValue({ "arm": true }, {}, { "customData": { "inverted": true } })).toBe("OFF"); + expect(Command.convertParamsToValue({ "arm": false }, {}, { "customData": { "inverted": true } })).toBe("ON"); + }); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates({ "arm": true })).toStrictEqual({ "isArmed": true }); + expect(Command.getResponseStates({ "arm": false })).toStrictEqual({ "isArmed": false }); + }); +}); diff --git a/tests/commands/brightnessabsolute.test.js b/tests/commands/brightnessabsolute.test.js new file mode 100644 index 00000000..9ed00881 --- /dev/null +++ b/tests/commands/brightnessabsolute.test.js @@ -0,0 +1,45 @@ +const Command = require('../../functions/commands/brightnessabsolute.js'); + +describe('BrightnessAbsolute Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams({ "brightness": 100 })).toBe(true); + expect(Command.validateParams({ "brightness": "100" })).toBe(false); + }); + + test('requiresItem', () => { + expect(Command.requiresItem({})).toBe(false); + expect(Command.requiresItem({ "customData": {} })).toBe(false); + expect(Command.requiresItem({ "customData": { "deviceType": "SpecialColorLight" } })).toBe(true); + }); + + test('getItemName', () => { + expect(Command.getItemName({ "name": "Item" }, {})).toBe("Item"); + expect(Command.getItemName({ "name": "Item" }, { "customData": {} })).toBe("Item"); + expect(() => { Command.getItemName({ "name": "Item" }, { "customData": { "deviceType": "SpecialColorLight" } }) }).toThrow(); + const item = { + "name": "Item", + "members": [ + { + "name": "BrightnessItem", + "metadata": { + "ga": { + "value": "lightBrightness" + } + } + } + ] + }; + expect(Command.getItemName(item, { "customData": { "deviceType": "SpecialColorLight" } })).toBe("BrightnessItem"); + }); + + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue({ "brightness": 0 })).toBe("0"); + expect(Command.convertParamsToValue({ "brightness": 100 })).toBe("100"); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates({ "brightness": 0 })).toStrictEqual({ "brightness": 0 }); + expect(Command.getResponseStates({ "brightness": 100 })).toStrictEqual({ "brightness": 100 }); + }); +}); diff --git a/tests/commands/colorabsolute.test.js b/tests/commands/colorabsolute.test.js new file mode 100644 index 00000000..80e6bdb4 --- /dev/null +++ b/tests/commands/colorabsolute.test.js @@ -0,0 +1,34 @@ +const Command = require('../../functions/commands/colorabsolute.js'); + +describe('ColorAbsolute Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams({ "color": {} })).toBe(false); + expect(Command.validateParams({ "color": { "spectrumHSV": {} } })).toBe(true); + }); + + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue({ + "color": { + "spectrumHSV": { "hue": 10, "saturation": 0.2, "value": 0.3 } + } + }, {}, {})).toBe("10,20,30"); + expect(() => (Command.convertParamsToValue({ + "color": { + "spectrumHSV": { "hue": 10, "saturation": 0.2, "value": 0.3 } + } + }, {}, { "customData": { "deviceType": "Light" } }))).toThrow(); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates({ + "color": { + "spectrumHSV": { "hue": 10, "saturation": 0.2, "value": 0.3 } + } + })).toStrictEqual({ + "color": { + "spectrumHsv": { "hue": 10, "saturation": 0.2, "value": 0.3 } + } + }); + }); +}); diff --git a/tests/commands/colorabsolutetemperature.test.js b/tests/commands/colorabsolutetemperature.test.js new file mode 100644 index 00000000..d6557146 --- /dev/null +++ b/tests/commands/colorabsolutetemperature.test.js @@ -0,0 +1,90 @@ +const Command = require('../../functions/commands/colorabsolutetemperature.js'); + +describe('ColorAbsoluteTemperature Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams({ "color": {} })).toBe(false); + expect(Command.validateParams({ "color": { "temperature": 1000 } })).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem()).toBe(true); + }); + + test('getItemName', () => { + expect(Command.getItemName({ "name": "Item" }, {})).toBe("Item"); + expect(Command.getItemName({ "name": "Item" }, { "customData": {} })).toBe("Item"); + expect(() => { Command.getItemName({ "name": "Item" }, { "customData": { "deviceType": "SpecialColorLight" } }) }).toThrow(); + const item = { + "name": "Item", + "members": [ + { + "name": "ColorItem", + "metadata": { + "ga": { + "value": "lightColorTemperature" + } + } + } + ] + }; + expect(Command.getItemName(item, { "customData": { "deviceType": "SpecialColorLight" } })).toBe("ColorItem"); + }); + + describe('convertParamsToValue', () => { + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue( + { + "color": { + "temperature": 2000 + } + }, + { "state": "100,100,50" }, + {} + )).toBe("30.62,95,50"); + }); + + test('convertParamsToValue SpecialColorLight', () => { + const item = { + "metadata": { + "ga": { + "config": { + "colorTemperatureRange": "1000,5000" + } + } + } + }; + expect(Command.convertParamsToValue( + { + "color": { + "temperature": 2000 + } + }, + item, + { "customData": { "deviceType": "SpecialColorLight" } } + )).toBe("75"); + + expect(Command.convertParamsToValue( + { + "color": { + "temperature": 2000 + } + }, + { "state": "100,100,50" }, + { "customData": { "deviceType": "SpecialColorLight" } } + )).toBe("0"); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates({ + "color": { + "temperature": 2000 + } + })).toStrictEqual({ + "color": { + "temperatureK": 2000 + } + }); + }); + }); +}); From 351a565898c0ad22eac1b754f1cc57a067ab6c8b Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 23:36:53 +0200 Subject: [PATCH 53/94] Add JSDoc, Remove unused defaults Signed-off-by: Michael Krug --- functions/commands/default.js | 51 +++++++++++++++++++++++++++++------ functions/devices/default.js | 34 +++++++++++------------ 2 files changed, 60 insertions(+), 25 deletions(-) diff --git a/functions/commands/default.js b/functions/commands/default.js index a2bde00a..53d04c0e 100644 --- a/functions/commands/default.js +++ b/functions/commands/default.js @@ -16,27 +16,51 @@ class DefaultCommand { return ''; } - static validateParams(params = {}) { + /** + * @param {object} params + */ + static validateParams(params) { return true; } - static convertParamsToValue(params = {}, item = {}, device = {}) { + /** + * @param {object} params + * @param {object} item + * @param {object} device + */ + static convertParamsToValue(params, item, device) { return null; } - static getResponseStates(params = {}, item = {}, device = {}) { + /** + * @param {object} params + * @param {object} item + * @param {object} device + */ + static getResponseStates(params, item, device) { return {}; } - static getItemName(item = {}, device = {}) { + /** + * @param {object} item + * @param {object} device + */ + static getItemName(item, device) { return item.name; } - static requiresItem(device = {}) { + /** + * @param {object} device + */ + static requiresItem(device) { return false; } - static handleAuthPin(device = {}, challenge = {}) { + /** + * @param {object} device + * @param {object} challenge + */ + static handleAuthPin(device, challenge) { if (!device.customData || !device.customData.pinNeeded || challenge.pin === device.customData.pinNeeded) { return; } @@ -50,7 +74,12 @@ class DefaultCommand { }; } - static handleAuthAck(device = {}, challenge = {}, responseStates = {}) { + /** + * @param {object} device + * @param {object} challenge + * @param {object} responseStates + */ + static handleAuthAck(device, challenge, responseStates) { if (!device.customData || !device.customData.ackNeeded || challenge.ack === true) { return; } @@ -65,7 +94,13 @@ class DefaultCommand { }; } - static execute(apiHandler = {}, devices = [], params = {}, challenge = {}) { + /** + * @param {object} apiHandler + * @param {array} devices + * @param {object} params + * @param {object} challenge + */ + static execute(apiHandler, devices, params, challenge) { console.log(`openhabGoogleAssistant - ${this.type}: ${JSON.stringify({ devices: devices, params: params })}`); const commandsResponse = []; const promises = devices.map((device) => { diff --git a/functions/devices/default.js b/functions/devices/default.js index 46c2b91d..b4f96098 100644 --- a/functions/devices/default.js +++ b/functions/devices/default.js @@ -4,8 +4,8 @@ class DefaultDevice { } /** - * @param {object} item - */ + * @param {object} item + */ static getTraits(item) { return []; } @@ -15,8 +15,8 @@ class DefaultDevice { } /** - * @param {object} item - */ + * @param {object} item + */ static isCompatible(item) { return item.metadata && item.metadata.ga && this.type.toLowerCase() === `action.devices.types.${item.metadata.ga.value}`.toLowerCase() || @@ -24,8 +24,8 @@ class DefaultDevice { } /** - * @param {object} item - */ + * @param {object} item + */ static matchesItemType(item) { return ( !this.requiredItemTypes.length || @@ -35,22 +35,22 @@ class DefaultDevice { } /** - * @param {object} item - */ + * @param {object} item + */ static getAttributes(item) { return {}; } /** - * @param {object} item - */ + * @param {object} item + */ static getConfig(item) { return item && item.metadata && item.metadata.ga && item.metadata.ga.config || {}; } /** - * @param {object} item - */ + * @param {object} item + */ static getMetadata(item) { const config = this.getConfig(item); const itemType = item.type === 'Group' && item.groupType ? item.groupType : item.type; @@ -91,16 +91,16 @@ class DefaultDevice { } /** - * @param {object} item - */ + * @param {object} item + */ static getState(item) { return {}; } /** - * @param {object} item - * @param {string} tag - */ + * @param {object} item + * @param {string} tag + */ static hasTag(item, tag) { return item.tags && item.tags.map(t => t.toLowerCase()).includes(tag.toLowerCase()) || false; } From e4560461fc8ef53d0904236a10225ab8e90dea1a Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 23:37:16 +0200 Subject: [PATCH 54/94] Make mute invertable Signed-off-by: Michael Krug --- functions/commands/mute.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/functions/commands/mute.js b/functions/commands/mute.js index 2db7c783..865e48d6 100644 --- a/functions/commands/mute.js +++ b/functions/commands/mute.js @@ -38,10 +38,14 @@ class Mute extends DefaultCommand { itemType = 'Dimmer'; } } + let mute = params.mute; if (itemType !== 'Switch') { - return params.mute ? '0' : undefined; + return mute ? '0' : undefined; } - return params.mute ? 'ON' : 'OFF'; + if (device.customData && device.customData.inverted === true) { + mute = !mute; + } + return mute ? 'ON' : 'OFF'; } static getResponseStates(params) { From c9f562cfad87b6f764d1990be10b91326584fe0e Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 23:38:16 +0200 Subject: [PATCH 55/94] Rework OpenClose and StartStop default conversion Signed-off-by: Michael Krug --- functions/commands/openclose.js | 11 ++++++----- functions/commands/startstop.js | 8 +++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/functions/commands/openclose.js b/functions/commands/openclose.js index 3ba3487f..27e2ad50 100644 --- a/functions/commands/openclose.js +++ b/functions/commands/openclose.js @@ -17,12 +17,13 @@ class OpenClose extends DefaultCommand { if (device.customData && device.customData.inverted === true) { openPercent = 100 - openPercent; } - let value = openPercent === 0 ? 'DOWN' : openPercent === 100 ? 'UP' : (100 - openPercent).toString(); - // item can not handle OpenClose --> we will send "ON" / "OFF" - if (device.customData && device.customData.itemType !== 'Rollershutter') { - value = openPercent === 0 ? 'OFF' : 'ON'; + if (device.customData && device.customData.itemType === 'Rollershutter') { + return openPercent === 0 ? 'DOWN' : openPercent === 100 ? 'UP' : (100 - openPercent).toString(); } - return value; + if (device.customData && device.customData.itemType === 'Switch') { + return openPercent === 0 ? 'OFF' : 'ON'; + } + return openPercent.toString(); } static getResponseStates(params) { diff --git a/functions/commands/startstop.js b/functions/commands/startstop.js index da4a1971..92b334f0 100644 --- a/functions/commands/startstop.js +++ b/functions/commands/startstop.js @@ -13,12 +13,10 @@ class StartStop extends DefaultCommand { if (device.customData && device.customData.itemType === 'Contact') { throw { statusCode: 400 }; } - let value = params.start ? 'MOVE' : 'STOP'; - // item can not handle StartStop --> we will send "ON" / "OFF" - if (device.customData && device.customData.itemType !== 'Rollershutter') { - value = params.start ? 'ON' : 'OFF'; + if (device.customData && device.customData.itemType === 'Rollershutter') { + return params.start ? 'MOVE' : 'STOP'; } - return value; + return params.start ? 'ON' : 'OFF'; } static getResponseStates(params) { From b531aab7781328913ab6b0eb7ac7e189f135022f Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 23:38:38 +0200 Subject: [PATCH 56/94] Add pausable attribute Signed-off-by: Michael Krug --- functions/devices/openclosedevice.js | 8 +++++--- functions/devices/startstopswitch.js | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/functions/devices/openclosedevice.js b/functions/devices/openclosedevice.js index 2d22600b..023b9c6b 100644 --- a/functions/devices/openclosedevice.js +++ b/functions/devices/openclosedevice.js @@ -9,9 +9,11 @@ class OpenCloseDevice extends DefaultDevice { } static getAttributes(item) { - const attributes = {}; - attributes.discreteOnlyOpenClose = this.getConfig(item).discreteOnlyOpenClose === true; - attributes.queryOnlyOpenClose = this.getConfig(item).queryOnlyOpenClose === true; + const attributes = { + pausable: false, + discreteOnlyOpenClose: this.getConfig(item).discreteOnlyOpenClose === true, + queryOnlyOpenClose: this.getConfig(item).queryOnlyOpenClose === true + }; const itemType = item.type === 'Group' && item.groupType ? item.groupType : item.type; if (itemType === 'Switch') { attributes.discreteOnlyOpenClose = true; diff --git a/functions/devices/startstopswitch.js b/functions/devices/startstopswitch.js index 1bbc73a7..ada0e559 100644 --- a/functions/devices/startstopswitch.js +++ b/functions/devices/startstopswitch.js @@ -11,6 +11,10 @@ class StartStopSwitch extends DefaultDevice { return ['Switch']; } + static getAttributes() { + return { pausable: false }; + } + static getState(item) { let state = item.state === 'ON'; if (this.getConfig(item).inverted === true) { From 060c21c0728847caa4a6d42dca93fa9f5ab2daaa Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 23:38:49 +0200 Subject: [PATCH 57/94] Add volume attributes to TV Signed-off-by: Michael Krug --- functions/devices/tv.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/functions/devices/tv.js b/functions/devices/tv.js index db1345f4..0e5cf048 100644 --- a/functions/devices/tv.js +++ b/functions/devices/tv.js @@ -26,6 +26,15 @@ class TV extends DefaultDevice { const attributes = { volumeCanMuteAndUnmute: 'tvMute' in members }; + if ('tvVolume' in members) { + attributes.volumeMaxLevel = 100; + if ('volumeDefaultPercentage' in config) { + attributes.volumeDefaultPercentage = Number(config.volumeDefaultPercentage); + } + if ('levelStepSize' in config) { + attributes.levelStepSize = Number(config.levelStepSize); + } + } if ('tvTransport' in members) { attributes.transportControlSupportedCommands = ['NEXT', 'PREVIOUS', 'PAUSE', 'RESUME']; if ('transportControlSupportedCommands' in config) { From 90bd0faee185865207ac23ea770363e6bd41087a Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 23:39:11 +0200 Subject: [PATCH 58/94] Update device tests Signed-off-by: Michael Krug --- tests/devices/openclosedevice.test.js | 6 +++ tests/devices/startstopswitch.test.js | 4 ++ tests/devices/thermostat.test.js | 2 + tests/devices/tv.test.js | 57 ++++++++++++++++++++++++++- 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/tests/devices/openclosedevice.test.js b/tests/devices/openclosedevice.test.js index 48f25d16..5a523046 100644 --- a/tests/devices/openclosedevice.test.js +++ b/tests/devices/openclosedevice.test.js @@ -3,27 +3,33 @@ const Device = require('../../functions/devices/openclosedevice.js'); describe('OpenCloseDevice Device', () => { test('getAttributes', () => { expect(Device.getAttributes({ "type": "Rollershutter" })).toStrictEqual({ + "pausable": false, "discreteOnlyOpenClose": false, "queryOnlyOpenClose": false }); expect(Device.getAttributes({ "type": "Switch", })).toStrictEqual({ + "pausable": false, "discreteOnlyOpenClose": true, "queryOnlyOpenClose": false }); expect(Device.getAttributes({ "type": "Contact" })).toStrictEqual({ + "pausable": false, "discreteOnlyOpenClose": true, "queryOnlyOpenClose": true }); expect(Device.getAttributes({ "type": "Group" })).toStrictEqual({ + "pausable": false, "discreteOnlyOpenClose": false, "queryOnlyOpenClose": false }); expect(Device.getAttributes({ "type": "Group", "groupType": "Switch" })).toStrictEqual({ + "pausable": false, "discreteOnlyOpenClose": true, "queryOnlyOpenClose": false }); expect(Device.getAttributes({ "type": "Group", "groupType": "Contact" })).toStrictEqual({ + "pausable": false, "discreteOnlyOpenClose": true, "queryOnlyOpenClose": true }); diff --git a/tests/devices/startstopswitch.test.js b/tests/devices/startstopswitch.test.js index 3d415a4a..7a1e99d8 100644 --- a/tests/devices/startstopswitch.test.js +++ b/tests/devices/startstopswitch.test.js @@ -8,6 +8,10 @@ describe('StartStopSwitch Device', () => { expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(false); }); + test('getAttributes', () => { + expect(Device.getAttributes()).toStrictEqual({ "pausable": false }); + }); + describe('getState', () => { test('getState', () => { expect(Device.getState({ "state": "ON" })).toStrictEqual({ diff --git a/tests/devices/thermostat.test.js b/tests/devices/thermostat.test.js index 02136de7..7bb0c6e1 100644 --- a/tests/devices/thermostat.test.js +++ b/tests/devices/thermostat.test.js @@ -154,6 +154,8 @@ describe('Thermostat Device', () => { }); describe('getMembers', () => { + expect(Device.getMembers({ "members": [{}] })).toStrictEqual({}); + expect(Device.getMembers({ "members": [{ "metadata": { "ga": { "value": "invalid" } } }] })).toStrictEqual({}); test('getMembers', () => { const item = { "members": [ diff --git a/tests/devices/tv.test.js b/tests/devices/tv.test.js index 06b8fbee..9c6ce153 100644 --- a/tests/devices/tv.test.js +++ b/tests/devices/tv.test.js @@ -117,10 +117,61 @@ describe('TV Device', () => { "ga": { "config": {} } - } + }, + "members": [ + { + "metadata": { + "ga": { + "value": "tvVolume" + } + } + }, + { + "metadata": { + "ga": { + "value": "tvTransport" + } + } + } + ] }; expect(Device.getAttributes(item)).toStrictEqual({ - "volumeCanMuteAndUnmute": false + "transportControlSupportedCommands": [ + "NEXT", + "PREVIOUS", + "PAUSE", + "RESUME", + ], + "volumeCanMuteAndUnmute": false, + "volumeMaxLevel": 100, + }); + }); + + test('getAttributes volume', () => { + const item = { + "metadata": { + "ga": { + "config": { + "volumeDefaultPercentage": "20", + "levelStepSize": "10" + } + } + }, + "members": [ + { + "metadata": { + "ga": { + "value": "tvVolume" + } + } + } + ] + }; + expect(Device.getAttributes(item)).toStrictEqual({ + "levelStepSize": 10, + "volumeCanMuteAndUnmute": false, + "volumeDefaultPercentage": 20, + "volumeMaxLevel": 100 }); }); @@ -239,6 +290,8 @@ describe('TV Device', () => { }); test('getMembers', () => { + expect(Device.getMembers({ "members": [{}] })).toStrictEqual({}); + expect(Device.getMembers({ "members": [{ "metadata": { "ga": { "value": "invalid" } } }] })).toStrictEqual({}); const item = { "members": [ { From 5e6ec440e61ff5ace1b1af0224dc39dbb3460aca Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 23:39:37 +0200 Subject: [PATCH 59/94] Refactor color command tests Signed-off-by: Michael Krug --- tests/commands/colorabsolute.test.js | 26 ++++----- .../commands/colorabsolutetemperature.test.js | 56 ++++++------------- 2 files changed, 27 insertions(+), 55 deletions(-) diff --git a/tests/commands/colorabsolute.test.js b/tests/commands/colorabsolute.test.js index 80e6bdb4..453da3d9 100644 --- a/tests/commands/colorabsolute.test.js +++ b/tests/commands/colorabsolute.test.js @@ -1,31 +1,25 @@ const Command = require('../../functions/commands/colorabsolute.js'); describe('ColorAbsolute Command', () => { + const params = { + "color": { + "spectrumHSV": { "hue": 10, "saturation": 0.2, "value": 0.3 } + } + }; + test('validateParams', () => { expect(Command.validateParams({})).toBe(false); expect(Command.validateParams({ "color": {} })).toBe(false); - expect(Command.validateParams({ "color": { "spectrumHSV": {} } })).toBe(true); + expect(Command.validateParams(params)).toBe(true); }); test('convertParamsToValue', () => { - expect(Command.convertParamsToValue({ - "color": { - "spectrumHSV": { "hue": 10, "saturation": 0.2, "value": 0.3 } - } - }, {}, {})).toBe("10,20,30"); - expect(() => (Command.convertParamsToValue({ - "color": { - "spectrumHSV": { "hue": 10, "saturation": 0.2, "value": 0.3 } - } - }, {}, { "customData": { "deviceType": "Light" } }))).toThrow(); + expect(Command.convertParamsToValue(params, {}, {})).toBe("10,20,30"); + expect(() => (Command.convertParamsToValue(params, {}, { "customData": { "deviceType": "Light" } }))).toThrow(); }); test('getResponseStates', () => { - expect(Command.getResponseStates({ - "color": { - "spectrumHSV": { "hue": 10, "saturation": 0.2, "value": 0.3 } - } - })).toStrictEqual({ + expect(Command.getResponseStates(params)).toStrictEqual({ "color": { "spectrumHsv": { "hue": 10, "saturation": 0.2, "value": 0.3 } } diff --git a/tests/commands/colorabsolutetemperature.test.js b/tests/commands/colorabsolutetemperature.test.js index d6557146..fd871d44 100644 --- a/tests/commands/colorabsolutetemperature.test.js +++ b/tests/commands/colorabsolutetemperature.test.js @@ -1,10 +1,16 @@ const Command = require('../../functions/commands/colorabsolutetemperature.js'); describe('ColorAbsoluteTemperature Command', () => { + const params = { + "color": { + "temperature": 2000 + } + }; + test('validateParams', () => { expect(Command.validateParams({})).toBe(false); expect(Command.validateParams({ "color": {} })).toBe(false); - expect(Command.validateParams({ "color": { "temperature": 1000 } })).toBe(true); + expect(Command.validateParams(params)).toBe(true); }); test('requiresItem', () => { @@ -33,15 +39,7 @@ describe('ColorAbsoluteTemperature Command', () => { describe('convertParamsToValue', () => { test('convertParamsToValue', () => { - expect(Command.convertParamsToValue( - { - "color": { - "temperature": 2000 - } - }, - { "state": "100,100,50" }, - {} - )).toBe("30.62,95,50"); + expect(Command.convertParamsToValue(params, { "state": "100,100,50" }, {})).toBe("30.62,95,50"); }); test('convertParamsToValue SpecialColorLight', () => { @@ -54,37 +52,17 @@ describe('ColorAbsoluteTemperature Command', () => { } } }; - expect(Command.convertParamsToValue( - { - "color": { - "temperature": 2000 - } - }, - item, - { "customData": { "deviceType": "SpecialColorLight" } } - )).toBe("75"); - - expect(Command.convertParamsToValue( - { - "color": { - "temperature": 2000 - } - }, - { "state": "100,100,50" }, - { "customData": { "deviceType": "SpecialColorLight" } } - )).toBe("0"); + const device = { "customData": { "deviceType": "SpecialColorLight" } }; + expect(Command.convertParamsToValue(params, item, device)).toBe("75"); + expect(Command.convertParamsToValue(params, { "state": "100,100,50" }, device)).toBe("0"); }); + }); - test('getResponseStates', () => { - expect(Command.getResponseStates({ - "color": { - "temperature": 2000 - } - })).toStrictEqual({ - "color": { - "temperatureK": 2000 - } - }); + test('getResponseStates', () => { + expect(Command.getResponseStates(params)).toStrictEqual({ + "color": { + "temperatureK": 2000 + } }); }); }); From ef44d09da6c66164bf43f835c14797f4dbe80bf4 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 23:39:51 +0200 Subject: [PATCH 60/94] Add a lot of command tests Signed-off-by: Michael Krug --- tests/commands/default.test.js | 58 +++++++++ tests/commands/getcamerastream.test.js | 23 ++++ tests/commands/lockunlock.test.js | 27 +++++ tests/commands/medianext.test.js | 40 +++++++ tests/commands/mediapause.test.js | 40 +++++++ tests/commands/mediaprevious.test.js | 40 +++++++ tests/commands/mediaresume.test.js | 40 +++++++ tests/commands/mute.test.js | 111 ++++++++++++++++++ tests/commands/onoff.test.js | 70 +++++++++++ tests/commands/openclose.test.js | 51 ++++++++ tests/commands/selectchannel.test.js | 59 ++++++++++ tests/commands/setfanspeed.test.js | 18 +++ tests/commands/setinput.test.js | 39 ++++++ tests/commands/setvolume.test.js | 47 ++++++++ tests/commands/startstop.test.js | 32 +++++ tests/commands/thermostatsetmode.test.js | 59 ++++++++++ .../thermostattemperaturesetpoint.test.js | 60 ++++++++++ .../thermostattemperaturesetpointhigh.test.js | 60 ++++++++++ .../thermostattemperaturesetpointlow.test.js | 60 ++++++++++ tests/commands/volumerelative.test.js | 65 ++++++++++ 20 files changed, 999 insertions(+) create mode 100644 tests/commands/default.test.js create mode 100644 tests/commands/getcamerastream.test.js create mode 100644 tests/commands/lockunlock.test.js create mode 100644 tests/commands/medianext.test.js create mode 100644 tests/commands/mediapause.test.js create mode 100644 tests/commands/mediaprevious.test.js create mode 100644 tests/commands/mediaresume.test.js create mode 100644 tests/commands/mute.test.js create mode 100644 tests/commands/onoff.test.js create mode 100644 tests/commands/openclose.test.js create mode 100644 tests/commands/selectchannel.test.js create mode 100644 tests/commands/setfanspeed.test.js create mode 100644 tests/commands/setinput.test.js create mode 100644 tests/commands/setvolume.test.js create mode 100644 tests/commands/startstop.test.js create mode 100644 tests/commands/thermostatsetmode.test.js create mode 100644 tests/commands/thermostattemperaturesetpoint.test.js create mode 100644 tests/commands/thermostattemperaturesetpointhigh.test.js create mode 100644 tests/commands/thermostattemperaturesetpointlow.test.js create mode 100644 tests/commands/volumerelative.test.js diff --git a/tests/commands/default.test.js b/tests/commands/default.test.js new file mode 100644 index 00000000..ff4be7c0 --- /dev/null +++ b/tests/commands/default.test.js @@ -0,0 +1,58 @@ +const Command = require('../../functions/commands/default.js'); + +describe('Default Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(true); + }); + + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue({}, {}, {})).toBe(null); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates({}, {}, {})).toStrictEqual({}); + }); + + test('getItemName', () => { + expect(Command.getItemName({ "name": "Item" }, {})).toBe("Item"); + }); + + test('requiresItem', () => { + expect(Command.requiresItem({})).toBe(false); + }); + + test('handleAuthPin', () => { + expect(Command.handleAuthPin({ "id": "Item", "customData": {} }, {})).toBeUndefined(); + expect(Command.handleAuthPin({ "id": "Item", "customData": { "pinNeeded": "1234" } }, { "pin": "1234" })).toBeUndefined(); + expect(Command.handleAuthPin({ "id": "Item", "customData": { "pinNeeded": "1234" } }, {})).toStrictEqual({ + ids: ["Item"], + status: 'ERROR', + errorCode: 'challengeNeeded', + challengeNeeded: { + type: 'pinNeeded' + } + }); + expect(Command.handleAuthPin({ "id": "Item", "customData": { "pinNeeded": "1234" } }, { "pin": "5678" })).toStrictEqual({ + ids: ["Item"], + status: 'ERROR', + errorCode: 'challengeNeeded', + challengeNeeded: { + type: 'challengeFailedPinNeeded' + } + }); + }); + + test('handleAuthAck', () => { + expect(Command.handleAuthAck({ "id": "Item", "customData": {} }, {}, {})).toBeUndefined(); + expect(Command.handleAuthAck({ "id": "Item", "customData": { "ackNeeded": true } }, { "ack": true }, {})).toBeUndefined(); + expect(Command.handleAuthAck({ "id": "Item", "customData": { "ackNeeded": true } }, {}, { "key": "value" })).toStrictEqual({ + ids: ["Item"], + status: 'ERROR', + states: { "key": "value" }, + errorCode: 'challengeNeeded', + challengeNeeded: { + type: 'ackNeeded' + } + }); + }); +}); diff --git a/tests/commands/getcamerastream.test.js b/tests/commands/getcamerastream.test.js new file mode 100644 index 00000000..34e412d3 --- /dev/null +++ b/tests/commands/getcamerastream.test.js @@ -0,0 +1,23 @@ +const Command = require('../../functions/commands/getcamerastream.js'); + +describe('GetCameraStream Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams({ "StreamToChromecast": true })).toBe(false); + expect(Command.validateParams({ "StreamToChromecast": true, "SupportedStreamProtocols": {} })).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem()).toBe(true); + }); + + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue()).toBe(null); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates({}, { "state": "https://example.org" })).toStrictEqual({ + "cameraStreamAccessUrl": "https://example.org" + }); + }); +}); diff --git a/tests/commands/lockunlock.test.js b/tests/commands/lockunlock.test.js new file mode 100644 index 00000000..37637ee5 --- /dev/null +++ b/tests/commands/lockunlock.test.js @@ -0,0 +1,27 @@ +const Command = require('../../functions/commands/lockunlock.js'); + +describe('LockUnlock Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams({ "lock": true })).toBe(true); + }); + + describe('convertParamsToValue', () => { + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue({ "lock": true }, {}, {})).toBe("ON"); + expect(Command.convertParamsToValue({ "lock": false }, {}, {})).toBe("OFF"); + }); + test('convertParamsToValue inverted', () => { + expect(Command.convertParamsToValue({ "lock": true }, {}, { "customData": { "inverted": true } })).toBe("OFF"); + expect(Command.convertParamsToValue({ "lock": false }, {}, { "customData": { "inverted": true } })).toBe("ON"); + }); + test('convertParamsToValue Contact', () => { + expect(() => { Command.convertParamsToValue({ "lock": true }, {}, { "customData": { "itemType": "Contact" } }) }).toThrow(); + }); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates({ "lock": true })).toStrictEqual({ "isLocked": true }); + expect(Command.getResponseStates({ "lock": false })).toStrictEqual({ "isLocked": false }); + }); +}); diff --git a/tests/commands/medianext.test.js b/tests/commands/medianext.test.js new file mode 100644 index 00000000..cb04f902 --- /dev/null +++ b/tests/commands/medianext.test.js @@ -0,0 +1,40 @@ +const Command = require('../../functions/commands/medianext.js'); + +describe('mediaNext Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem()).toBe(true); + }); + + describe('getItemName', () => { + test('getItemName', () => { + const item = { + "members": [ + { + "name": "TransportItem", + "metadata": { + "ga": { + "value": "tvTransport" + } + } + } + ] + }; + expect(Command.getItemName(item)).toBe("TransportItem"); + }); + + test('getItemName no transport', () => { + const item = { + "members": [] + }; + expect(() => { Command.getItemName(item) }).toThrow(); + }); + }); + + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue()).toBe("NEXT"); + }); +}); diff --git a/tests/commands/mediapause.test.js b/tests/commands/mediapause.test.js new file mode 100644 index 00000000..b221c0cf --- /dev/null +++ b/tests/commands/mediapause.test.js @@ -0,0 +1,40 @@ +const Command = require('../../functions/commands/mediapause.js'); + +describe('mediaPause Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem()).toBe(true); + }); + + describe('getItemName', () => { + test('getItemName', () => { + const item = { + "members": [ + { + "name": "TransportItem", + "metadata": { + "ga": { + "value": "tvTransport" + } + } + } + ] + }; + expect(Command.getItemName(item)).toBe("TransportItem"); + }); + + test('getItemName no transport', () => { + const item = { + "members": [] + }; + expect(() => { Command.getItemName(item) }).toThrow(); + }); + }); + + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue()).toBe("PAUSE"); + }); +}); diff --git a/tests/commands/mediaprevious.test.js b/tests/commands/mediaprevious.test.js new file mode 100644 index 00000000..c160b4d0 --- /dev/null +++ b/tests/commands/mediaprevious.test.js @@ -0,0 +1,40 @@ +const Command = require('../../functions/commands/mediaprevious.js'); + +describe('mediaPrevious Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem()).toBe(true); + }); + + describe('getItemName', () => { + test('getItemName', () => { + const item = { + "members": [ + { + "name": "TransportItem", + "metadata": { + "ga": { + "value": "tvTransport" + } + } + } + ] + }; + expect(Command.getItemName(item)).toBe("TransportItem"); + }); + + test('getItemName no transport', () => { + const item = { + "members": [] + }; + expect(() => { Command.getItemName(item) }).toThrow(); + }); + }); + + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue()).toBe("PREVIOUS"); + }); +}); diff --git a/tests/commands/mediaresume.test.js b/tests/commands/mediaresume.test.js new file mode 100644 index 00000000..a76b67c9 --- /dev/null +++ b/tests/commands/mediaresume.test.js @@ -0,0 +1,40 @@ +const Command = require('../../functions/commands/mediaresume.js'); + +describe('mediaResume Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem()).toBe(true); + }); + + describe('getItemName', () => { + test('getItemName', () => { + const item = { + "members": [ + { + "name": "TransportItem", + "metadata": { + "ga": { + "value": "tvTransport" + } + } + } + ] + }; + expect(Command.getItemName(item)).toBe("TransportItem"); + }); + + test('getItemName no transport', () => { + const item = { + "members": [] + }; + expect(() => { Command.getItemName(item) }).toThrow(); + }); + }); + + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue()).toBe("PLAY"); + }); +}); diff --git a/tests/commands/mute.test.js b/tests/commands/mute.test.js new file mode 100644 index 00000000..0c036908 --- /dev/null +++ b/tests/commands/mute.test.js @@ -0,0 +1,111 @@ +const Command = require('../../functions/commands/mute.js'); + +describe('Mute Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams({ "mute": true })).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem({})).toBe(false); + expect(Command.requiresItem({ "customData": {} })).toBe(false); + expect(Command.requiresItem({ "customData": { "deviceType": "TV" } })).toBe(true); + }); + + describe('getItemName', () => { + test('getItemName', () => { + expect(Command.getItemName({ "name": "Item" }, {})).toBe("Item"); + expect(Command.getItemName({ "name": "Item" }, { "customData": {} })).toBe("Item"); + }); + + test('getItemName TV no members', () => { + expect(() => { Command.getItemName({ "name": "Item" }, { "customData": { "deviceType": "TV" } }) }).toThrow(); + }); + + test('getItemName TV mute', () => { + const item = { + "name": "Item", + "members": [ + { + "name": "MuteItem", + "metadata": { + "ga": { + "value": "tvMute" + } + } + } + ] + }; + expect(Command.getItemName(item, { "customData": { "deviceType": "TV" } })).toBe("MuteItem"); + }); + + test('getItemName TV volume', () => { + const item = { + "name": "Item", + "members": [ + { + "name": "VolumeItem", + "metadata": { + "ga": { + "value": "tvVolume" + } + } + } + ] + }; + expect(Command.getItemName(item, { "customData": { "deviceType": "TV" } })).toBe("VolumeItem"); + }); + }); + + describe('convertParamsToValue', () => { + test('convertParamsToValue no Switch', () => { + expect(Command.convertParamsToValue({ "mute": true }, {}, {})).toBe("0"); + expect(Command.convertParamsToValue({ "mute": false }, {}, {})).toBeUndefined(); + }); + + test('convertParamsToValue Switch', () => { + expect(Command.convertParamsToValue({ "mute": true }, {}, { "customData": { "itemType": "Switch" } })).toBe("ON"); + expect(Command.convertParamsToValue({ "mute": false }, {}, { "customData": { "itemType": "Switch" } })).toBe("OFF"); + }); + + test('convertParamsToValue inverted', () => { + expect(Command.convertParamsToValue({ "mute": true }, {}, { "customData": { "itemType": "Switch", "inverted": true } })).toBe("OFF"); + expect(Command.convertParamsToValue({ "mute": false }, {}, { "customData": { "itemType": "Switch", "inverted": true } })).toBe("ON"); + }); + + test('convertParamsToValue TV mute', () => { + const item = { + "members": [ + { + "metadata": { + "ga": { + "value": "tvMute" + } + } + } + ] + }; + expect(Command.convertParamsToValue({ "mute": true }, item, { "customData": { "deviceType": "TV" } })).toBe("ON"); + }); + + test('convertParamsToValue TV volume', () => { + const item = { + "members": [ + { + "metadata": { + "ga": { + "value": "tvVolume" + } + } + } + ] + }; + expect(Command.convertParamsToValue({ "mute": true }, item, { "customData": { "deviceType": "TV" } })).toBe("0"); + }); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates({ "mute": true })).toStrictEqual({ "isMuted": true }); + expect(Command.getResponseStates({ "mute": false })).toStrictEqual({ "isMuted": false }); + }); +}); diff --git a/tests/commands/onoff.test.js b/tests/commands/onoff.test.js new file mode 100644 index 00000000..b91fe4cc --- /dev/null +++ b/tests/commands/onoff.test.js @@ -0,0 +1,70 @@ +const Command = require('../../functions/commands/onoff.js'); + +describe('OnOff Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams({ "on": true })).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem({})).toBe(false); + expect(Command.requiresItem({ "customData": { "deviceType": "SpecialColorLight" } })).toBe(true); + expect(Command.requiresItem({ "customData": { "deviceType": "TV" } })).toBe(true); + }); + + describe('getItemName', () => { + test('getItemName', () => { + expect(Command.getItemName({ "name": "Item" }, {})).toBe("Item"); + expect(Command.getItemName({ "name": "Item" }, { "customData": {} })).toBe("Item"); + }); + + test('getItemName SpecialColorLight', () => { + expect(() => { Command.getItemName({ "name": "Item" }, { "customData": { "deviceType": "SpecialColorLight" } }) }).toThrow(); + const item = { + "members": [ + { + "name": "BrightnessItem", + "metadata": { + "ga": { + "value": "lightBrightness" + } + } + } + ] + }; + expect(Command.getItemName(item, { "customData": { "deviceType": "SpecialColorLight" } })).toBe("BrightnessItem"); + }); + + test('getItemName TV', () => { + expect(() => { Command.getItemName({ "name": "Item" }, { "customData": { "deviceType": "TV" } }) }).toThrow(); + const item = { + "members": [ + { + "name": "PowerItem", + "metadata": { + "ga": { + "value": "tvPower" + } + } + } + ] + }; + expect(Command.getItemName(item, { "customData": { "deviceType": "TV" } })).toBe("PowerItem"); + }); + }); + + describe('convertParamsToValue', () => { + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue({ "on": true }, {}, {})).toBe("ON"); + }); + + test('convertParamsToValue inverted', () => { + expect(Command.convertParamsToValue({ "on": true }, {}, { "customData": { "inverted": true } })).toBe("OFF"); + }); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates({ "on": true })).toStrictEqual({ "on": true }); + expect(Command.getResponseStates({ "on": false })).toStrictEqual({ "on": false }); + }); +}); diff --git a/tests/commands/openclose.test.js b/tests/commands/openclose.test.js new file mode 100644 index 00000000..2b57be6d --- /dev/null +++ b/tests/commands/openclose.test.js @@ -0,0 +1,51 @@ +const Command = require('../../functions/commands/openclose.js'); + +describe('OpenClose Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams({ "openPercent": 100 })).toBe(true); + expect(Command.validateParams({ "openPercent": "5" })).toBe(false); + }); + + describe('convertParamsToValue', () => { + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue({ "openPercent": 0 }, {}, {})).toBe("0"); + expect(Command.convertParamsToValue({ "openPercent": 20 }, {}, {})).toBe("20"); + expect(Command.convertParamsToValue({ "openPercent": 50 }, {}, {})).toBe("50"); + expect(Command.convertParamsToValue({ "openPercent": 70 }, {}, {})).toBe("70"); + expect(Command.convertParamsToValue({ "openPercent": 100 }, {}, {})).toBe("100"); + }); + + test('convertParamsToValue inverted', () => { + const device = { "customData": { "inverted": true } }; + expect(Command.convertParamsToValue({ "openPercent": 0 }, {}, device)).toBe("100"); + expect(Command.convertParamsToValue({ "openPercent": 20 }, {}, device)).toBe("80"); + expect(Command.convertParamsToValue({ "openPercent": 50 }, {}, device)).toBe("50"); + expect(Command.convertParamsToValue({ "openPercent": 70 }, {}, device)).toBe("30"); + expect(Command.convertParamsToValue({ "openPercent": 100 }, {}, device)).toBe("0"); + }); + + test('convertParamsToValue Rollershutter', () => { + const device = { "customData": { "itemType": "Rollershutter" } }; + expect(Command.convertParamsToValue({ "openPercent": 0 }, {}, device)).toBe("DOWN"); + expect(Command.convertParamsToValue({ "openPercent": 20 }, {}, device)).toBe("80"); + expect(Command.convertParamsToValue({ "openPercent": 100 }, {}, device)).toBe("UP"); + }); + + test('convertParamsToValue Switch', () => { + const device = { "customData": { "itemType": "Switch" } }; + expect(Command.convertParamsToValue({ "openPercent": 0 }, {}, device)).toBe("OFF"); + expect(Command.convertParamsToValue({ "openPercent": 20 }, {}, device)).toBe("ON"); + expect(Command.convertParamsToValue({ "openPercent": 100 }, {}, device)).toBe("ON"); + }); + + test('convertParamsToValue Contact', () => { + const device = { "customData": { "itemType": "Contact" } }; + expect(() => { Command.convertParamsToValue({}, {}, device) }).toThrow(); + }); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates({ "openPercent": 10 })).toStrictEqual({ "openPercent": 10 }); + }); +}); diff --git a/tests/commands/selectchannel.test.js b/tests/commands/selectchannel.test.js new file mode 100644 index 00000000..3c11ec81 --- /dev/null +++ b/tests/commands/selectchannel.test.js @@ -0,0 +1,59 @@ +const Command = require('../../functions/commands/selectchannel.js'); + +describe('selectChannel Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams({ "channelCode": "channel1" })).toBe(true); + expect(Command.validateParams({ "channelName": "Channel 1" })).toBe(true); + expect(Command.validateParams({ "channelNumber": "1" })).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem()).toBe(true); + }); + + test('getItemName', () => { + expect(() => { Command.getItemName({ "name": "Item" }) }).toThrow(); + const item = { + "members": [ + { + "name": "ChannelItem", + "metadata": { + "ga": { + "value": "tvChannel" + } + } + } + ] + }; + expect(Command.getItemName(item)).toBe("ChannelItem"); + }); + + test('convertParamsToValue', () => { + const item = { + "metadata": { + "ga": { + "config": { + "availableChannels": "1=channel1=ARD,2=channel2=ZDF" + } + } + } + }; + expect(Command.convertParamsToValue({ "channelCode": "channel1" }, item)).toBe("1"); + expect(Command.convertParamsToValue({ "channelName": "ARD" }, item)).toBe("1"); + expect(Command.convertParamsToValue({ "channelNumber": "1" }, item)).toBe("1"); + }); + + test('getResponseStates', () => { + const item = { + "metadata": { + "ga": { + "config": { + "availableChannels": "1=channel1=ARD,2=channel2=ZDF" + } + } + } + }; + expect(Command.getResponseStates({ "channelName": "ZDF" }, item)).toStrictEqual({ "channelNumber": "2" }); + }); +}); diff --git a/tests/commands/setfanspeed.test.js b/tests/commands/setfanspeed.test.js new file mode 100644 index 00000000..8e26fd4e --- /dev/null +++ b/tests/commands/setfanspeed.test.js @@ -0,0 +1,18 @@ +const Command = require('../../functions/commands/setfanspeed.js'); + +describe('SetFanSpeed Command', () => { + const params = { "fanSpeed": "50" }; + + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams(params)).toBe(true); + }); + + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue(params)).toBe("50"); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates(params)).toStrictEqual({ "currentFanSpeedSetting": "50" }); + }); +}); diff --git a/tests/commands/setinput.test.js b/tests/commands/setinput.test.js new file mode 100644 index 00000000..de5903de --- /dev/null +++ b/tests/commands/setinput.test.js @@ -0,0 +1,39 @@ +const Command = require('../../functions/commands/setinput.js'); + +describe('SetInput Command', () => { + const params = { "newInput": "hdmi1" }; + + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams(params)).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem()).toBe(true); + }); + + test('getItemName', () => { + expect(() => { Command.getItemName({ "name": "Item" }) }).toThrow(); + const item = { + "members": [ + { + "name": "InputItem", + "metadata": { + "ga": { + "value": "tvInput" + } + } + } + ] + }; + expect(Command.getItemName(item)).toBe("InputItem"); + }); + + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue(params)).toBe("hdmi1"); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates(params)).toStrictEqual({ "currentInput": "hdmi1" }); + }); +}); diff --git a/tests/commands/setvolume.test.js b/tests/commands/setvolume.test.js new file mode 100644 index 00000000..e75e3b2f --- /dev/null +++ b/tests/commands/setvolume.test.js @@ -0,0 +1,47 @@ +const Command = require('../../functions/commands/setvolume.js'); + +describe('setVolume Command', () => { + const params = { "volumeLevel": 20 }; + + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams(params)).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem({})).toBe(false); + expect(Command.requiresItem({ "customData": { "deviceType": "TV" } })).toBe(true); + }); + + describe('getItemName', () => { + test('getItemName', () => { + expect(Command.getItemName({ "name": "Item" }, {})).toBe("Item"); + expect(Command.getItemName({ "name": "Item" }, { "customData": {} })).toBe("Item"); + }); + + test('getItemName TV', () => { + expect(() => { Command.getItemName({ "name": "Item" }, { "customData": { "deviceType": "TV" } }) }).toThrow(); + const item = { + "members": [ + { + "name": "VolumeItem", + "metadata": { + "ga": { + "value": "tvVolume" + } + } + } + ] + }; + expect(Command.getItemName(item, { "customData": { "deviceType": "TV" } })).toBe("VolumeItem"); + }); + }); + + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue(params)).toBe("20"); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates(params)).toStrictEqual({ "currentVolume": 20 }); + }); +}); diff --git a/tests/commands/startstop.test.js b/tests/commands/startstop.test.js new file mode 100644 index 00000000..3dcf1073 --- /dev/null +++ b/tests/commands/startstop.test.js @@ -0,0 +1,32 @@ +const Command = require('../../functions/commands/startstop.js'); + +describe('StartStop Command', () => { + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams({ "start": true })).toBe(true); + expect(Command.validateParams({ "start": "1" })).toBe(false); + }); + + describe('convertParamsToValue', () => { + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue({ "start": true }, {}, {})).toBe("ON"); + expect(Command.convertParamsToValue({ "start": false }, {}, {})).toBe("OFF"); + }); + + test('convertParamsToValue Rollershutter', () => { + const device = { "customData": { "itemType": "Rollershutter" } }; + expect(Command.convertParamsToValue({ "start": true }, {}, device)).toBe("MOVE"); + expect(Command.convertParamsToValue({ "start": false }, {}, device)).toBe("STOP"); + }); + + test('convertParamsToValue Contact', () => { + const device = { "customData": { "itemType": "Contact" } }; + expect(() => { Command.convertParamsToValue({}, {}, device) }).toThrow(); + }); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates({ "start": true })).toStrictEqual({ "isRunning": true, "isPaused": false }); + expect(Command.getResponseStates({ "start": false })).toStrictEqual({ "isRunning": false, "isPaused": true }); + }); +}); diff --git a/tests/commands/thermostatsetmode.test.js b/tests/commands/thermostatsetmode.test.js new file mode 100644 index 00000000..6c1b3f31 --- /dev/null +++ b/tests/commands/thermostatsetmode.test.js @@ -0,0 +1,59 @@ +const Command = require('../../functions/commands/thermostatsetmode.js'); + +describe('ThermostatSetMode Command', () => { + const params = { "thermostatMode": "eco" }; + + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams(params)).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem()).toBe(true); + }); + + test('getItemName', () => { + expect(() => { Command.getItemName({ "name": "Item" }) }).toThrow(); + const item = { + "members": [ + { + "name": "ModeItem", + "metadata": { + "ga": { + "value": "thermostatMode" + } + } + } + ] + }; + expect(Command.getItemName(item)).toBe("ModeItem"); + }); + + test('convertParamsToValue', () => { + const item = { + "metadata": { + "ga": { + "config": { + "modes": "eco=ECO" + } + } + } + }; + expect(Command.convertParamsToValue(params, item)).toBe("ECO"); + }); + + test('getResponseStates', () => { + const item = { + "members": [ + { + "metadata": { + "ga": { + "value": "thermostatMode" + } + } + } + ] + }; + expect(Command.getResponseStates(params, item)).toStrictEqual({ "thermostatMode": "eco" }); + }); +}); diff --git a/tests/commands/thermostattemperaturesetpoint.test.js b/tests/commands/thermostattemperaturesetpoint.test.js new file mode 100644 index 00000000..17114097 --- /dev/null +++ b/tests/commands/thermostattemperaturesetpoint.test.js @@ -0,0 +1,60 @@ +const Command = require('../../functions/commands/thermostattemperaturesetpoint.js'); + +describe('ThermostatTemperatureSetpoint Command', () => { + const params = { "thermostatTemperatureSetpoint": 20 }; + + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams(params)).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem()).toBe(true); + }); + + test('getItemName', () => { + expect(() => { Command.getItemName({ "name": "Item" }) }).toThrow(); + const item = { + "members": [ + { + "name": "SetpointItem", + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpoint" + } + } + } + ] + }; + expect(Command.getItemName(item)).toBe("SetpointItem"); + }); + + test('convertParamsToValue', () => { + const item = { + "metadata": { + "ga": { + "config": { + "useFahrenheit": true + } + } + } + }; + expect(Command.convertParamsToValue(params, item)).toBe("68"); + expect(Command.convertParamsToValue(params, {})).toBe("20"); + }); + + test('getResponseStates', () => { + const item = { + "members": [ + { + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpoint" + } + } + } + ] + }; + expect(Command.getResponseStates(params, item)).toStrictEqual({ "thermostatTemperatureSetpoint": 20 }); + }); +}); diff --git a/tests/commands/thermostattemperaturesetpointhigh.test.js b/tests/commands/thermostattemperaturesetpointhigh.test.js new file mode 100644 index 00000000..6c386a94 --- /dev/null +++ b/tests/commands/thermostattemperaturesetpointhigh.test.js @@ -0,0 +1,60 @@ +const Command = require('../../functions/commands/thermostattemperaturesetpointhigh.js'); + +describe('ThermostatTemperatureSetpointHigh Command', () => { + const params = { "thermostatTemperatureSetpointHigh": 20 }; + + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams(params)).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem()).toBe(true); + }); + + test('getItemName', () => { + expect(() => { Command.getItemName({ "name": "Item" }) }).toThrow(); + const item = { + "members": [ + { + "name": "SetpointItem", + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpointHigh" + } + } + } + ] + }; + expect(Command.getItemName(item)).toBe("SetpointItem"); + }); + + test('convertParamsToValue', () => { + const item = { + "metadata": { + "ga": { + "config": { + "useFahrenheit": true + } + } + } + }; + expect(Command.convertParamsToValue(params, item)).toBe("68"); + expect(Command.convertParamsToValue(params, {})).toBe("20"); + }); + + test('getResponseStates', () => { + const item = { + "members": [ + { + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpointHigh" + } + } + } + ] + }; + expect(Command.getResponseStates(params, item)).toStrictEqual({ "thermostatTemperatureSetpointHigh": 20 }); + }); +}); diff --git a/tests/commands/thermostattemperaturesetpointlow.test.js b/tests/commands/thermostattemperaturesetpointlow.test.js new file mode 100644 index 00000000..de05b11a --- /dev/null +++ b/tests/commands/thermostattemperaturesetpointlow.test.js @@ -0,0 +1,60 @@ +const Command = require('../../functions/commands/thermostattemperaturesetpointlow.js'); + +describe('ThermostatTemperatureSetpointLow Command', () => { + const params = { "thermostatTemperatureSetpointLow": 20 }; + + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams(params)).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem()).toBe(true); + }); + + test('getItemName', () => { + expect(() => { Command.getItemName({ "name": "Item" }) }).toThrow(); + const item = { + "members": [ + { + "name": "SetpointItem", + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpointLow" + } + } + } + ] + }; + expect(Command.getItemName(item)).toBe("SetpointItem"); + }); + + test('convertParamsToValue', () => { + const item = { + "metadata": { + "ga": { + "config": { + "useFahrenheit": true + } + } + } + }; + expect(Command.convertParamsToValue(params, item)).toBe("68"); + expect(Command.convertParamsToValue(params, {})).toBe("20"); + }); + + test('getResponseStates', () => { + const item = { + "members": [ + { + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpointLow" + } + } + } + ] + }; + expect(Command.getResponseStates(params, item)).toStrictEqual({ "thermostatTemperatureSetpointLow": 20 }); + }); +}); diff --git a/tests/commands/volumerelative.test.js b/tests/commands/volumerelative.test.js new file mode 100644 index 00000000..01823b32 --- /dev/null +++ b/tests/commands/volumerelative.test.js @@ -0,0 +1,65 @@ +const Command = require('../../functions/commands/volumerelative.js'); + +describe('volumeRelative Command', () => { + const params = { "relativeSteps": 10 }; + + test('validateParams', () => { + expect(Command.validateParams({})).toBe(false); + expect(Command.validateParams(params)).toBe(true); + }); + + test('requiresItem', () => { + expect(Command.requiresItem()).toBe(true); + }); + + describe('getItemName', () => { + test('getItemName', () => { + expect(Command.getItemName({ "name": "Item" }, {})).toBe("Item"); + expect(Command.getItemName({ "name": "Item" }, { "customData": {} })).toBe("Item"); + }); + + test('getItemName TV', () => { + expect(() => { Command.getItemName({ "name": "Item" }, { "customData": { "deviceType": "TV" } }) }).toThrow(); + const item = { + "members": [ + { + "name": "VolumeItem", + "metadata": { + "ga": { + "value": "tvVolume" + } + } + } + ] + }; + expect(Command.getItemName(item, { "customData": { "deviceType": "TV" } })).toBe("VolumeItem"); + }); + }); + + describe('convertParamsToValue', () => { + test('convertParamsToValue', () => { + expect(Command.convertParamsToValue(params, { "state": 20 }, {})).toBe("30"); + }); + + test('convertParamsToValue TV', () => { + const item = { + "members": [ + { + "state": "20", + "metadata": { + "ga": { + "value": "tvVolume" + } + } + } + ] + }; + expect(Command.convertParamsToValue(params, item, { "customData": { "deviceType": "TV" } })).toBe("30"); + expect(() => { Command.convertParamsToValue(params, {}, { "customData": { "deviceType": "TV" } }) }).toThrow(); + }); + }); + + test('getResponseStates', () => { + expect(Command.getResponseStates(params, { "state": 20 }, {})).toStrictEqual({ "currentVolume": 30 }); + }); +}); From a145eb69a4e705217495b6163ce5b0c212aa8473 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 3 Oct 2020 20:59:21 +0200 Subject: [PATCH 61/94] Set Volume trait when tvMute is provided Signed-off-by: Michael Krug --- functions/devices/tv.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/devices/tv.js b/functions/devices/tv.js index 0e5cf048..f16e989f 100644 --- a/functions/devices/tv.js +++ b/functions/devices/tv.js @@ -9,7 +9,7 @@ class TV extends DefaultDevice { const traits = []; const members = this.getMembers(item); if ('tvPower' in members) traits.push('action.devices.traits.OnOff'); - if ('tvVolume' in members) traits.push('action.devices.traits.Volume'); + if ('tvMute' in members || 'tvVolume' in members) traits.push('action.devices.traits.Volume'); if ('tvChannel' in members) traits.push('action.devices.traits.Channel'); if ('tvInput' in members) traits.push('action.devices.traits.InputSelector'); if ('tvTransport' in members) traits.push('action.devices.traits.TransportControl'); From 1c3b8ec4f4df39e4d8c80a58c1de82a4043a6e59 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 3 Oct 2020 21:00:10 +0200 Subject: [PATCH 62/94] Add tests for default command Signed-off-by: Michael Krug --- tests/commands/default.test.js | 229 +++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) diff --git a/tests/commands/default.test.js b/tests/commands/default.test.js index ff4be7c0..ad0393c6 100644 --- a/tests/commands/default.test.js +++ b/tests/commands/default.test.js @@ -1,5 +1,41 @@ const Command = require('../../functions/commands/default.js'); +class TestCommand1 extends Command { + static requiresItem() { + return false; + } + static convertParamsToValue() { + return 'TEST'; + } + static getResponseStates(params) { + return params; + } +} + +class TestCommand2 extends TestCommand1 { + static get type() { + return 'action.devices.commands.OnOff'; + } + static requiresItem() { + return true; + } +} + +class TestCommand3 extends Command { + static convertParamsToValue() { + return; + } + static getResponseStates(params) { + return params; + } +} + +class TestCommand4 extends Command { + static convertParamsToValue() { + throw { statusCode: 400 }; + } +} + describe('Default Command', () => { test('validateParams', () => { expect(Command.validateParams({})).toBe(true); @@ -55,4 +91,197 @@ describe('Default Command', () => { } }); }); + + describe('execute', () => { + const getItemMock = jest.fn(); + const sendCommandMock = jest.fn(); + sendCommandMock.mockReturnValue(Promise.resolve()); + getItemMock.mockReturnValue(Promise.resolve({ "name": "TestItem" })); + + const apiHandler = { + getItem: getItemMock, + sendCommand: sendCommandMock + }; + + const successResponse = { + "ids": ["Item1"], + "states": { + "on": true, + "online": true, + }, + "status": "SUCCESS" + }; + + beforeEach(() => { + getItemMock.mockClear(); + sendCommandMock.mockClear(); + }); + + test('execute without responseStates', async () => { + const devices = [{ "id": "Item1" }]; + const result = await TestCommand1.execute(apiHandler, devices, {}, {}); + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual([ + { + "ids": ["Item1"], + "states": {}, + "status": "SUCCESS" + } + ]); + }); + + test('execute without sent command', async () => { + const devices = [{ "id": "Item1" }]; + const result = await TestCommand3.execute(apiHandler, devices, { "on": true }, {}); + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(0); + expect(result).toStrictEqual([successResponse]); + }); + + test('execute without getItem', async () => { + const devices = [{ "id": "Item1" }]; + const result = await TestCommand1.execute(apiHandler, devices, { "on": true }, {}); + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual([successResponse]); + }); + + test('execute with getItem', async () => { + const devices = [{ "id": "Item1" }]; + const result = await TestCommand2.execute(apiHandler, devices, { "on": true }, {}); + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual([successResponse]); + }); + + test('execute with multiple getItem', async () => { + const successResponse2 = Object.assign({}, successResponse); + successResponse2.ids = ["Item2"]; + const devices = [{ "id": "Item1" }, { "id": "Item2" }]; + const result = await TestCommand2.execute(apiHandler, devices, { "on": true }, {}); + expect(getItemMock).toHaveBeenCalledTimes(2); + expect(sendCommandMock).toHaveBeenCalledTimes(2); + expect(result).toStrictEqual([successResponse, successResponse2]); + }); + + test('execute with pinNeeded', async () => { + const devices = [{ "id": "Item1", "customData": { "pinNeeded": "1234" } }]; + const result = await TestCommand1.execute(apiHandler, devices, { "on": true }, {}); + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(0); + expect(result).toStrictEqual([ + { + "ids": ["Item1"], + "challengeNeeded": { + "type": "pinNeeded", + }, + "errorCode": "challengeNeeded", + "status": "ERROR" + } + ]); + }); + + test('execute with corrrect pin', async () => { + const devices = [{ "id": "Item1", "customData": { "pinNeeded": "1234" } }]; + const result = await TestCommand1.execute(apiHandler, devices, { "on": true }, { "pin": "1234" }); + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual([successResponse]); + }); + + test('execute with ackNeeded', async () => { + const devices = [{ "id": "Item1", "customData": { "ackNeeded": true } }]; + const result = await TestCommand1.execute(apiHandler, devices, { "on": true }, {}); + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(0); + expect(result).toStrictEqual([ + { + "ids": ["Item1"], + "challengeNeeded": { + "type": "ackNeeded", + }, + "errorCode": "challengeNeeded", + "states": { + "on": true, + "online": true, + }, + "status": "ERROR" + } + ]); + }); + + test('execute with ackNeeded and state', async () => { + const devices = [{ "id": "Item1", "customData": { "ackNeeded": true } }]; + const result = await TestCommand2.execute(apiHandler, devices, { "on": true }, {}); + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toHaveBeenCalledTimes(0); + expect(result).toStrictEqual([ + { + "ids": ["Item1"], + "challengeNeeded": { + "type": "ackNeeded", + }, + "errorCode": "challengeNeeded", + "states": { + "on": true, + "online": true, + }, + "status": "ERROR" + } + ]); + }); + + test('execute with ack', async () => { + const devices = [{ "id": "Item1", "customData": { "ackNeeded": true } }]; + const result = await TestCommand1.execute(apiHandler, devices, { "on": true }, { "ack": true }); + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual([successResponse]); + }); + + test('execute with device not found', async () => { + getItemMock.mockReturnValue(Promise.reject({ "statusCode": "404" })); + const devices = [{ "id": "Item1" }]; + const result = await TestCommand2.execute(apiHandler, devices, { "on": true }, {}); + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(sendCommandMock).toHaveBeenCalledTimes(0); + expect(result).toStrictEqual([{ + "errorCode": "deviceNotFound", + "ids": [ + "Item1", + ], + "status": "ERROR" + }]); + }); + + test('execute with not supported', async () => { + const devices = [{ "id": "Item1" }]; + const result = await TestCommand4.execute(apiHandler, devices, { "on": true }, {}); + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(0); + expect(result).toStrictEqual([{ + "errorCode": "notSupported", + "ids": [ + "Item1", + ], + "status": "ERROR" + }]); + }); + + test('execute with device offline', async () => { + sendCommandMock.mockReturnValue(Promise.reject({ "statusCode": 500 })); + const devices = [{ "id": "Item1" }]; + const result = await TestCommand1.execute(apiHandler, devices, { "on": true }, {}); + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual([{ + "errorCode": "deviceOffline", + "ids": [ + "Item1", + ], + "status": "ERROR" + }]); + }); + }); }); From 41c06dc2932e9fe6ef7197dbc363d7a280a782fa Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 3 Oct 2020 21:01:15 +0200 Subject: [PATCH 63/94] Add new tests for OpenHAB class Signed-off-by: Michael Krug --- functions/openhab.js | 15 +- tests/openhab.tags.test.js | 331 ---- tests/openhab.test.js | 3450 +++++------------------------------- 3 files changed, 429 insertions(+), 3367 deletions(-) delete mode 100644 tests/openhab.tags.test.js diff --git a/functions/openhab.js b/functions/openhab.js index 7bd11d9d..ed967b61 100644 --- a/functions/openhab.js +++ b/functions/openhab.js @@ -41,15 +41,22 @@ class OpenHAB { /** * @param {object} apiHandler */ - constructor(apiHandler = {}) { + constructor(apiHandler) { this._apiHandler = apiHandler; } - static getCommandType(command = '', params = {}) { + /** + * @param {string} command + * @param {object} params + */ + static getCommandType(command, params) { return Commands.find((commandType) => command === commandType.type && commandType.validateParams(params)); } - static getDeviceForItem(item = {}) { + /** + * @param {object} item + */ + static getDeviceForItem(item) { return Devices.find((device) => device.matchesItemType(item) && device.isCompatible(item)); } @@ -86,7 +93,7 @@ class OpenHAB { if (item.state === 'NULL' && !('getMembers' in DeviceType)) { throw { statusCode: 406 }; } - payload.devices[device.id] = Object.assign({ online: true }, DeviceType.getState(item)); + payload.devices[device.id] = Object.assign({ status: 'SUCCESS', online: true }, DeviceType.getState(item)); }).catch((error) => { payload.devices[device.id] = { status: 'ERROR', diff --git a/tests/openhab.tags.test.js b/tests/openhab.tags.test.js deleted file mode 100644 index 335b6b9a..00000000 --- a/tests/openhab.tags.test.js +++ /dev/null @@ -1,331 +0,0 @@ -const OpenHAB = require('../functions/openhab.js'); - -xdescribe('SYNC with Tags', () => { - test('Light Devices', async () => { - const items = [ - { - "state": "OFF", - "type": "Switch", - "name": "MySwitch", - "label": "SwitchLight", - "tags": [ - "Lighting" - ] - }, - { - "state": "0", - "type": "Dimmer", - "name": "MyDimmer", - "label": "DimmLight", - "tags": [ - "Lighting" - ] - }, - { - "state": "0,0,0", - "type": "Color", - "name": "MyLight", - "label": "ColorLight", - "tags": [ - "Lighting" - ] - }, - { - "members": [], - "state": "NULL", - "type": "Group", - "groupType": "Switch", - "name": "MyLightGroup", - "label": "GroupLight", - "tags": [ - "Lighting" - ] - }, - { - "members": [], - "state": "NULL", - "type": "Group", - "groupType": "Dimmer", - "name": "MyDimmerGroup", - "label": "GroupDimmer", - "tags": [ - "Lighting" - ] - }, - { - "members": [], - "state": "NULL", - "type": "Group", - "groupType": "Color", - "name": "MyColorGroup", - "label": "GroupColor", - "tags": [ - "Lighting" - ] - } - ]; - - const getItemsMock = jest.fn(); - getItemsMock.mockReturnValue(Promise.resolve(items)); - - const apiHandler = { - getItems: getItemsMock - }; - - const payload = await new OpenHAB(apiHandler).handleSync(); - - expect(getItemsMock).toHaveBeenCalledTimes(1); - expect(payload).toMatchSnapshot(); - }); -}); - -xdescribe('QUERY with Tags', () => { - test('Single Light Device ', async () => { - const item = - { - "state": "OFF", - "type": "Switch", - "name": "MySwitch", - "tags": [ - "Lighting" - ] - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MySwitch" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MySwitch": { - "on": false, - "online": true, - }, - }, - }); - }); - - test('Multiple Light Devices', async () => { - const item1 = - { - "state": "OFF", - "type": "Switch", - "name": "MySwitch", - "tags": [ - "Lighting" - ] - }; - - const item2 = - { - "state": "20", - "type": "Dimmer", - "name": "MyDimmer", - "tags": [ - "Lighting" - ] - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValueOnce(Promise.resolve(item1)) - .mockReturnValueOnce(Promise.resolve(item2)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MySwitch" - }, { - "id": "MyDimmer" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(2); - expect(payload).toStrictEqual({ - "devices": { - "MySwitch": { - "on": false, - "online": true, - }, - "MyDimmer": { - "on": true, - "brightness": 20, - "online": true, - }, - }, - }); - }); -}); - -xdescribe('EXECUTE with Tags', () => { - test('ThermostatTemperatureSetpoint', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "tags": [ - "Thermostat", - "Fahrenheit" - ], - "members": [{ - type: 'Number', - tags: [ - 'CurrentTemperature' - ], - state: '10' - }, { - name: 'MyTargetTemperature', - type: 'Number', - tags: [ - 'TargetTemperature' - ], - state: '10' - }, { - type: 'String', - tags: [ - 'HeatingCoolingMode' - ], - state: 'heat' - }, { - type: 'Number', - tags: [ - 'CurrentHumidity' - ], - state: '50' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyThermostat" - }], - "execution": [{ - "command": "action.devices.commands.ThermostatTemperatureSetpoint", - "params": { - "thermostatTemperatureSetpoint": 25 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyTargetTemperature', '77'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyThermostat" - ], - "states": { - "online": true, - "thermostatHumidityAmbient": 50, - "thermostatMode": "heat", - "thermostatTemperatureAmbient": -12.2, - "thermostatTemperatureSetpoint": 25 - }, - "status": "SUCCESS" - }] - }); - }); - - test('ThermostatSetModeCommand', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "tags": [ - "Thermostat" - ], - "members": [{ - type: 'Number', - tags: [ - 'CurrentTemperature' - ], - state: '10' - }, { - type: 'Number', - tags: [ - 'TargetTemperature' - ], - state: '10' - }, { - name: 'MyHeatingCoolingMode', - type: 'String', - tags: [ - 'HeatingCoolingMode' - ], - state: 'cool' - }, { - type: 'Number', - tags: [ - 'CurrentHumidity' - ], - state: '50' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyThermostat" - }], - "execution": [{ - "command": "action.devices.commands.ThermostatSetMode", - "params": { - "thermostatMode": "heat" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyHeatingCoolingMode', 'heat'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyThermostat" - ], - "states": { - "online": true, - "thermostatHumidityAmbient": 50, - "thermostatMode": "heat", - "thermostatTemperatureAmbient": 10, - "thermostatTemperatureSetpoint": 10 - }, - "status": "SUCCESS" - }] - }); - }); -}); diff --git a/tests/openhab.test.js b/tests/openhab.test.js index 1d7d97c6..69e5336d 100644 --- a/tests/openhab.test.js +++ b/tests/openhab.test.js @@ -1,3097 +1,483 @@ const OpenHAB = require('../functions/openhab.js'); -xdescribe('SYNC', () => { - test('Light Devices', async () => { - const items = [ - { - "state": "OFF", - "type": "Switch", - "name": "MySwitch", - "label": "SwitchLight", - "metadata": { - "ga": { - "value": "Light", - "config": { - "name": "Light Switch" - } - }, - "synonyms": { - "value": "Testlight,Cool Light" - } - } - }, - { - "state": "0", - "type": "Dimmer", - "name": "MyDimmer", - "label": "DimmLight", - "metadata": { - "ga": { - "value": "Light" - } - } - }, - { - "state": "0,0,0", - "type": "Color", - "name": "MyLight", - "label": "ColorLight", - "metadata": { - "ga": { - "value": "Light" - } - } - }, - { - "members": [], - "state": "NULL", - "type": "Group", - "groupType": "Switch", - "name": "MyLightGroup", - "label": "GroupLight", - "metadata": { - "ga": { - "value": "Light" - } - } - }, - { - "members": [], - "state": "NULL", - "type": "Group", - "groupType": "Dimmer", - "name": "MyDimmerGroup", - "label": "GroupDimmer", - "metadata": { - "ga": { - "value": "Light" - } - } - }, - { - "members": [], - "state": "NULL", - "type": "Group", - "groupType": "Color", - "name": "MyColorGroup", - "label": "GroupColor", - "metadata": { - "ga": { - "value": "Light" - } - } - }, - { - "state": "50", - "type": "Dimmer", - "name": "MyFan", - "label": "Fan", - "metadata": { - "ga": { - "value": "Fan" - } - } - } - ]; - - const getItemsMock = jest.fn(); - getItemsMock.mockReturnValue(Promise.resolve(items)); - - const apiHandler = { - getItems: getItemsMock - }; - - const payload = await new OpenHAB(apiHandler).handleSync(); - - expect(getItemsMock).toHaveBeenCalledTimes(1); - expect(payload).toMatchSnapshot(); - }); - - - - test('Scene Device', async () => { - const items = [ - { - "state": "OFF", - "metadata": { - "ga": { - "value": "Scene" - } - }, - "type": "Switch", - "name": "MyScene", - "label": "My Scene", - "tags": [] - } - ]; - const getItemsMock = jest.fn(); - getItemsMock.mockReturnValue(Promise.resolve(items)); - - const apiHandler = { - getItems: getItemsMock - }; - - const payload = await new OpenHAB(apiHandler).handleSync(); - - expect(getItemsMock).toHaveBeenCalledTimes(1); - expect(payload).toMatchSnapshot(); - }); - - test('Sensor Device', async () => { - const items = [ - { - "state": "14", - "metadata": { - "ga": { - "value": "Sensor", - "config": { - "sensorName": "AirQuality", - "states": "healthy=0,moderate=10,unhealthy=50,very unhealthy=100" - } - } - }, - "type": "Number", - "name": "MySensor", - "label": "My Sensor", - "tags": [] - } - ]; - const getItemsMock = jest.fn(); - getItemsMock.mockReturnValue(Promise.resolve(items)); - - const apiHandler = { - getItems: getItemsMock - }; - - const payload = await new OpenHAB(apiHandler).handleSync(); - - expect(getItemsMock).toHaveBeenCalledTimes(1); - expect(payload).toMatchSnapshot(); - }); - - test('Temperature Sensor Device', async () => { - const items = [ - { - "state": "14", - "metadata": { - "ga": { - "value": "TemperatureSensor", - "config": { - "useFahrenheit": true - } - } - }, - "type": "Number", - "name": "MySensor", - "label": "My Sensor", - "tags": [] - } - ]; - const getItemsMock = jest.fn(); - getItemsMock.mockReturnValue(Promise.resolve(items)); - - const apiHandler = { - getItems: getItemsMock - }; - - const payload = await new OpenHAB(apiHandler).handleSync(); - - expect(getItemsMock).toHaveBeenCalledTimes(1); - expect(payload).toMatchSnapshot(); - }); - - test('Thermostat Device', async () => { - const items = [ - { - "state": "NULL", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "modes": "off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST:ON,eco=ECO,auto=AUTOMATIC" - } - } - }, - "type": "Group", - "name": "MyThermostat", - "label": "My Thermostat", - "tags": [], - "groupNames": [], - }, - { - "state": "AUTOMATIC", - "type": "String", - "name": "MyThermostat_Mode", - "label": "My Thermostat Mode", - "tags": [], - "groupNames": [ - "MyThermostat" - ], - }, - { - "state": "OFF", - "metadata": { - "ga": { - "value": "thermostatMode" - } - }, - "type": "String", - "name": "MyThermostat_RadiatorMode", - "label": "My Thermostat Mode", - "tags": [], - "groupNames": [ - "MyThermostat" - ], - }, - { - "state": "6.0 °C", - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpoint" - } - }, - "type": "Number:Temperature", - "name": "MyThermostat_SetpointTemperature", - "label": "My Thermostat Setpoint Temperature", - "tags": [], - "groupNames": [ - "MyThermostat" - ], - }, - { - "state": "22.5 °C", - "metadata": { - "ga": { - "value": "thermostatTemperatureAmbient" - } - }, - "type": "Number:Temperature", - "name": "MyThermostat_CurrentTemperature", - "label": "My Thermostat Current Temperature", - "tags": [], - "groupNames": [ - "MyThermostat" - ], - } - ]; - const getItemsMock = jest.fn(); - getItemsMock.mockReturnValue(Promise.resolve(items)); - - const apiHandler = { - getItems: getItemsMock - }; - - const payload = await new OpenHAB(apiHandler).handleSync(); - - expect(getItemsMock).toHaveBeenCalledTimes(1); - expect(payload).toMatchSnapshot(); - }); -}); - -/* ================================================= */ - -xdescribe('QUERY', () => { - test('Lock Device as Contact', async () => { - const item = - { - "state": "CLOSED", - "type": "Contact", - "name": "MyLock", - "metadata": { - "ga": { - "value": "Lock" - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MyLock" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MyLock": { - "isLocked": true, - "online": true, - }, - }, - }); - }); - - test('Multiple Light Devices', async () => { - const item1 = - { - "state": "OFF", - "type": "Switch", - "name": "MySwitch", - "metadata": { - "ga": { - "value": "Light" - } - } - }; - - const item2 = - { - "state": "20", - "type": "Dimmer", - "name": "MyDimmer", - "metadata": { - "ga": { - "value": "Light" - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValueOnce(Promise.resolve(item1)) - .mockReturnValueOnce(Promise.resolve(item2)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MySwitch" - }, { - "id": "MyDimmer" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(2); - expect(payload).toStrictEqual({ - "devices": { - "MySwitch": { - "on": false, - "online": true, - }, - "MyDimmer": { - "on": true, - "brightness": 20, - "online": true, - }, - }, - }); - }); - - test('Temperature Sensor', async () => { - const item = - { - "state": "20", - "type": "Number", - "name": "MySensor", - "metadata": { - "ga": { - "value": "TemperatureSensor" - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MySensor" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MySensor": { - "temperatureAmbientCelsius": 20, - "temperatureSetpointCelsius": 20, - "online": true - }, - }, - }); - }); - - test('Window as Contact', async () => { - const item = - { - "state": "CLOSED", - "type": "Contact", - "name": "MyWindow", - "metadata": { - "ga": { - "value": "Window" - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MyWindow" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MyWindow": { - "openPercent": 0, - "online": true - }, - }, - }); - }); - - test('Thermostat Device', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "useFahrenheit": true, - "modes": "off=OFF:WINDOW_OPEN,heat=COMFORT:BOOST,eco=ECO,on=ON,auto=AUTOMATIC" - } - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - name: 'MyTargetTemperature', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '10' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: 'COMFORT' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const apiHandler = { - getItem: getItemMock - }; - - const payload = await new OpenHAB(apiHandler).handleQuery([{ - "id": "MyThermostat" - }]); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(payload).toStrictEqual({ - "devices": { - "MyThermostat": { - "thermostatHumidityAmbient": 50, - "thermostatMode": "heat", - "thermostatTemperatureAmbient": -12.2, - "thermostatTemperatureSetpoint": -12.2, - "online": true - }, - }, - }); - }); -}); - -xdescribe('EXECUTE with Metadata', () => { - test('OnOff with Switch Device', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MySwitch" - }], - "execution": [{ - "command": "action.devices.commands.OnOff", - "params": { - "on": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toBeCalledWith('MySwitch', 'ON'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MySwitch" - ], - "states": { - "online": true, - "on": true - }, - "status": "SUCCESS" - }] - }); - }); - - test('OnOff with Inverted Switch Device', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MySwitch", - "customData": { - "inverted": true - } - }], - "execution": [{ - "command": "action.devices.commands.OnOff", - "params": { - "on": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toBeCalledWith('MySwitch', 'OFF'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MySwitch" - ], - "states": { - "online": true, - "on": true - }, - "status": "SUCCESS" - }] - }); - }); - - test('ThermostatTemperatureSetpoint', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "useFahrenheit": true - } - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - name: 'MyTargetTemperature', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '10' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: 'heat' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyThermostat" - }], - "execution": [{ - "command": "action.devices.commands.ThermostatTemperatureSetpoint", - "params": { - "thermostatTemperatureSetpoint": 25 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyTargetTemperature', '77'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyThermostat" - ], - "states": { - "online": true, - "thermostatHumidityAmbient": 50, - "thermostatMode": "heat", - "thermostatTemperatureAmbient": -12.2, - "thermostatTemperatureSetpoint": 25 - }, - "status": "SUCCESS" - }] - }); - }); - - test('ThermostatTemperatureSetpointHigh', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "useFahrenheit": true - } - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - name: 'MyTargetTemperatureHigh', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointHigh' - } - }, - state: '20' - }, { - name: 'MyTargetTemperatureLow', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointLow' - } - }, - state: '10' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: 'heat' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyThermostat" - }], - "execution": [{ - "command": "action.devices.commands.ThermostatTemperatureSetpointHigh", - "params": { - "thermostatTemperatureSetpointHigh": 25 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyTargetTemperatureHigh', '77'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyThermostat" - ], - "states": { - "online": true, - "thermostatHumidityAmbient": 50, - "thermostatMode": "heat", - "thermostatTemperatureAmbient": -12.2, - "thermostatTemperatureSetpointHigh": 25, - "thermostatTemperatureSetpointLow": -12.2, - }, - "status": "SUCCESS" - }] - }); - }); - - test('ThermostatTemperatureSetpointLow', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "useFahrenheit": true - } - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - name: 'MyTargetTemperatureHigh', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointHigh' - } - }, - state: '20' - }, { - name: 'MyTargetTemperatureLow', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointLow' - } - }, - state: '10' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: 'heat' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyThermostat" - }], - "execution": [{ - "command": "action.devices.commands.ThermostatTemperatureSetpointLow", - "params": { - "thermostatTemperatureSetpointLow": 5 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyTargetTemperatureLow', '41'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyThermostat" - ], - "states": { - "online": true, - "thermostatHumidityAmbient": 50, - "thermostatMode": "heat", - "thermostatTemperatureAmbient": -12.2, - "thermostatTemperatureSetpointHigh": -6.7, - "thermostatTemperatureSetpointLow": 5 - }, - "status": "SUCCESS" - }] - }); - }); - - test('ThermostatTemperatureSetRange', async () => { - const item1 = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat" - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '10' - }, { - name: 'MyTargetTemperatureHigh', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointHigh' - } - }, - state: '13' - }, { - name: 'MyTargetTemperatureLow', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointLow' - } - }, - state: '7' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: 'heatcool' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - - const item2 = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat" - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '10' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '10' - }, { - name: 'MyTargetTemperatureHigh', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointHigh' - } - }, - state: '25' - }, { - name: 'MyTargetTemperatureLow', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpointLow' - } - }, - state: '7' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: 'heatcool' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValueOnce(Promise.resolve(item1)) - .mockReturnValueOnce(Promise.resolve(item2)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyThermostat" - }], - "execution": [{ - "command": "action.devices.commands.ThermostatTemperatureSetRange", - "params": { - "thermostatTemperatureSetpointHigh": 25, - "thermostatTemperatureSetpointLow": 15 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(2); - expect(sendCommandMock).toBeCalledWith('MyTargetTemperatureHigh', '25'); - expect(sendCommandMock).toBeCalledWith('MyTargetTemperatureLow', '15'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyThermostat" - ], - "states": { - "online": true, - "thermostatHumidityAmbient": 50, - "thermostatMode": "heatcool", - "thermostatTemperatureAmbient": 10, - "thermostatTemperatureSetpoint": 10, - "thermostatTemperatureSetpointHigh": 25, - "thermostatTemperatureSetpointLow": 15 - }, - "status": "SUCCESS" - }] - }); - }); - - test('ThermostatSetMode invalid', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "modes": "on=3,heat=5" - } - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '20' - }, { - name: 'MyTargetTemperature', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '10' - }, { - name: 'MyMode', - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: '3' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyThermostat" - }], - "execution": [{ - "command": "action.devices.commands.ThermostatSetMode", - "params": { - "thermostatMode": "off" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyThermostat" - ], - "errorCode": "notSupported", - "status": "ERROR" - }] - }); - }); - - test('ThermostatSetMode', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyThermostat", - "label": "Thermostat", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "modes": "on=1,off=5" - } - } - }, - "members": [{ - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureAmbient' - } - }, - state: '20' - }, { - name: 'MyTargetTemperature', - type: 'Number', - metadata: { - ga: { - value: 'thermostatTemperatureSetpoint' - } - }, - state: '10' - }, { - name: 'MyMode', - type: 'Number', - metadata: { - ga: { - value: 'thermostatMode' - } - }, - state: '1' - }, { - type: 'Number', - metadata: { - ga: { - value: 'thermostatHumidityAmbient' - } - }, - state: '50' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyThermostat" - }], - "execution": [{ - "command": "action.devices.commands.ThermostatSetMode", - "params": { - "thermostatMode": "off" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyMode', '5'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyThermostat" - ], - "states": { - "online": true, - "thermostatHumidityAmbient": 50, - "thermostatMode": "off", - "thermostatTemperatureAmbient": 20, - "thermostatTemperatureSetpoint": 10 - }, - "status": "SUCCESS" - }] - }); - }); - - test('LockUnlock with Lock and required acknowledge', async () => { - const item = - { - "state": "OFF", - "type": "Switch", - "name": "MyLock", - "label": "My Lock", - "metadata": { - "ga": { - "value": "Lock", - "config": { - "ackNeeded": true - } - } - }, - "tags": [] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "ackNeeded": true - }, - "id": "MyLock" - }], - "execution": [{ - "command": "action.devices.commands.LockUnlock", - "params": { - "lock": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyLock" - ], - "states": { - "online": true, - "isLocked": true - }, - "status": "ERROR", - "errorCode": "challengeNeeded", - "challengeNeeded": { - "type": "ackNeeded", - } - }] - }); - }); - - test('LockUnlock with Lock and acknowledged challenge', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "ackNeeded": true - }, - "id": "MyLock" - }], - "execution": [{ - "command": "action.devices.commands.LockUnlock", - "params": { - "lock": true - }, - "challenge": { - "ack": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toBeCalledWith('MyLock', 'ON'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyLock" - ], - "states": { - "online": true, - "isLocked": true - }, - "status": "SUCCESS" - }] - }); - }); - - test('LockUnlock with Lock inverted', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "inverted": true - }, - "id": "MyLock" - }], - "execution": [{ - "command": "action.devices.commands.LockUnlock", - "params": { - "lock": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toBeCalledWith('MyLock', 'OFF'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyLock" - ], - "states": { - "online": true, - "isLocked": true - }, - "status": "SUCCESS" - }] - }); - }); - - test('LockUnlock with Lock as Contact', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "itemType": "Contact" - }, - "id": "MyLock" - }], - "execution": [{ - "command": "action.devices.commands.LockUnlock", - "params": { - "lock": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyLock" - ], - "status": "ERROR", - "errorCode": "notSupported" - }] - }); - }); - - test('OpenClose with OpenCloseDevice as Contact', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "itemType": "Contact" - }, - "id": "MyLock" - }], - "execution": [{ - "command": "action.devices.commands.OpenClose", - "params": { - "openPercent": 0 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyLock" - ], - "status": "ERROR", - "errorCode": "notSupported" - }] - }); - }); - - test('BrightnessAbsolute with required acknowledge', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "ackNeeded": true - }, - "id": "MyLight" - }], - "execution": [{ - "command": "action.devices.commands.BrightnessAbsolute", - "params": { - "brightness": 30 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyLight" - ], - "states": { - "online": true, - "brightness": 30 - }, - "status": "ERROR", - "errorCode": "challengeNeeded", - "challengeNeeded": { - "type": "ackNeeded", - } - }] - }); - }); - - test('Arm with required pin', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "pinNeeded": "1234" - }, - "id": "MyAlarm" - }], - "execution": [{ - "command": "action.devices.commands.ArmDisarm", - "params": { - arm: true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyAlarm" - ], - "status": "ERROR", - "errorCode": "challengeNeeded", - "challengeNeeded": { - "type": "pinNeeded", - } - }] - }); - }); - - test('Arm with wrong pin', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "pinNeeded": "1234" - }, - "id": "MyAlarm" - }], - "execution": [{ - "command": "action.devices.commands.ArmDisarm", - "params": { - "arm": true - }, - "challenge": { - "pin": "3456" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyAlarm" - ], - "status": "ERROR", - "errorCode": "challengeNeeded", - "challengeNeeded": { - "type": "challengeFailedPinNeeded" - } - }] - }); - }); - - test('Arm with correct pin', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "pinNeeded": "1234" - }, - "id": "MyAlarm" - }], - "execution": [{ - "command": "action.devices.commands.ArmDisarm", - "params": { - "arm": true - }, - "challenge": { - "pin": "1234" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyAlarm" - ], - "states": { - "online": true, - "isArmed": true - }, - "status": "SUCCESS" - }] - }); - }); - - test('OpenClose Blinds Group as Rollershutter', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyBlinds", - "customData": { - "itemType": "Rollershutter" - } - }], - "execution": [{ - "command": "action.devices.commands.OpenClose", - "params": { - "openPercent": 0 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toBeCalledWith('MyBlinds', 'DOWN'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyBlinds" - ], - "states": { - "online": true, - "openPercent": 0 - }, - "status": "SUCCESS" - }] - }); - }); - - test('OpenClose Blinds Group as Switch', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyBlinds", - "customData": { - "itemType": "Switch" - } - }], - "execution": [{ - "command": "action.devices.commands.OpenClose", - "params": { - "openPercent": 0 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toBeCalledWith('MyBlinds', 'OFF'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyBlinds" - ], - "states": { - "online": true, - "openPercent": 0 - }, - "status": "SUCCESS" - }] - }); - }); - - test('OnOff for TV', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV" - } - }, - "members": [{ - type: 'Switch', - name: 'MyPower', - metadata: { - ga: { - value: 'tvPower' - } - }, - state: 'ON' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.OnOff", - "params": { - "on": false - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyPower', 'OFF'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - 'on': false, - 'online': true - }, - "status": "SUCCESS" - }] - }); - }); - - test('Mute for TV with Switch', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV" - } - }, - "members": [{ - type: 'Switch', - name: 'MyMute', - metadata: { - ga: { - value: 'tvMute' - } - }, - state: 'OFF' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.mute", - "params": { - "mute": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyMute', 'ON'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - 'isMuted': true, - 'online': true - }, - "status": "SUCCESS" - }] - }); - }); - - test('Mute for TV without Switch', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV" - } - }, - "members": [{ - type: 'Dimmer', - name: 'MyVolume', - metadata: { - ga: { - value: 'tvVolume' - } - }, - state: '12' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.mute", - "params": { - "mute": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyVolume', '0'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - 'isMuted': true, - 'online': true - }, - "status": "SUCCESS" - }] - }); - }); - - test('SelectChannel with Number for TV', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV", - "config": { - "availableChannels": "20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2" - } - } - }, - "members": [{ - type: 'Number', - name: 'MyChannel', - metadata: { - ga: { - value: 'tvChannel' - } - }, - state: '10' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.selectChannel", - "params": { - "channelNumber": "20" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyChannel', '20'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - 'channelNumber': '20', - 'online': true - }, - "status": "SUCCESS" - }] - }); - }); - - test('SelectChannel with Name for TV', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV", - "config": { - "availableChannels": "20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2" - } - } - }, - "members": [{ - type: 'Number', - name: 'MyChannel', - metadata: { - ga: { - value: 'tvChannel' - } - }, - state: '10' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.selectChannel", - "params": { - "channelName": "Channel 1" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyChannel', '20'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - 'channelNumber': '20', - 'online': true - }, - "status": "SUCCESS" - }] - }); - }); - - test('SelectInput for TV', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV", - "config": { - "availableInputs": "tv=TV,hdmi1=HDMI1,hdmi2=HDMI2" - } - } - }, - "members": [{ - type: 'String', - name: 'MyInput', - metadata: { - ga: { - value: 'tvInput' - } - }, - state: 'tv' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.SetInput", - "params": { - "newInput": "hdmi1" - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyInput', 'hdmi1'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - 'currentInput': 'hdmi1', - 'online': true - }, - "status": "SUCCESS" - }] - }); - }); - - test('SetVolume for TV', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV" - } - }, - "members": [{ - type: 'Dimmer', - name: 'MyVolume', - metadata: { - ga: { - value: 'tvVolume' - } - }, - state: '40' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.setVolume", - "params": { - "volumeLevel": 10 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyVolume', '10'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - "currentVolume": 10, - "online": true - }, - "status": "SUCCESS" - }] - }); - }); - - test('volumeRelative for TV', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV" - } - }, - "members": [{ - type: 'Dimmer', - name: 'MyVolume', - metadata: { - ga: { - value: 'tvVolume' - } - }, - state: '40' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.volumeRelative", - "params": { - "relativeSteps": 1 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyVolume', '41'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - 'currentVolume': 41, - 'online': true - }, - "status": "SUCCESS" - }] - }); - }); - - test('MediaPause for TV', async () => { - const item = - { - "state": "NULL", - "type": "Group", - "name": "MyTV", - "label": "TV", - "metadata": { - "ga": { - "value": "TV" - } - }, - "members": [{ - type: 'Player', - name: 'MyTransport', - metadata: { - ga: { - value: 'tvTransport' - } - }, - state: 'PLAY' - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyTV", - "customData": { - "deviceType": "TV" - } - }], - "execution": [{ - "command": "action.devices.commands.mediaPause" - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyTransport', 'PAUSE'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyTV" - ], - "states": { - }, - "status": "SUCCESS" - }] - }); - }); -}); - -xdescribe('EXECUTE', () => { - test('OnOff Switch', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "itemType": "Switch" - }, - "id": "MySwitch" - }], - "execution": [{ - "command": "action.devices.commands.OnOff", - "params": { - "on": false - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).not.toHaveBeenCalled(); - expect(sendCommandMock).toBeCalledWith('MySwitch', 'OFF'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MySwitch" - ], - "states": { - "online": true, - "on": false - }, - "status": "SUCCESS" - }] - }); +describe('OpenHAB', () => { + test('getCommandType', () => { + const command = OpenHAB.getCommandType('action.devices.commands.OnOff', { "on": true }); + expect(command).not.toBeUndefined(); + expect(command.name).toBe('OnOff'); }); - test('mute Dimmer', async () => { - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - const sendCommandMock = jest.fn(); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "itemType": "Dimmer" - }, - "id": "MySpeaker" - }], - "execution": [{ - "command": "action.devices.commands.mute", - "params": { - "mute": true - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toBeCalledWith('MySpeaker', '0'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MySpeaker" - ], - "states": { - "isMuted": true, - "online": true - }, - "status": "SUCCESS" - }] + describe('getDeviceForItem', () => { + test('getDeviceForItem switch tag', () => { + const device = OpenHAB.getDeviceForItem({ "type": "Switch", "metadata": { "ga": { "value": "Switch" } } }); + expect(device).not.toBeUndefined(); + expect(device.name).toBe('Switch'); }); - }); - - test('setVolume Dimmer', async () => { - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve()); - const sendCommandMock = jest.fn(); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "itemType": "Dimmer" - }, - "id": "MySpeaker" - }], - "execution": [{ - "command": "action.devices.commands.setVolume", - "params": { - "volumeLevel": 40 - } - }] - }]; - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(0); - expect(sendCommandMock).toBeCalledWith('MySpeaker', '40'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MySpeaker" - ], - "states": { - "currentVolume": 40, - "online": true - }, - "status": "SUCCESS" - }] + test('getDeviceForItem switch tag', () => { + const device = OpenHAB.getDeviceForItem({ "type": "Switch", "tags": ["Switchable"] }); + expect(device).not.toBeUndefined(); + expect(device.name).toBe('Switch'); }); }); - test('volumeRelative Dimmer', async () => { - const item = - { - "state": "40", - "type": "Dimmer", - "name": "MySpeaker", - "metadata": { - "ga": { - "value": "Speaker" - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const sendCommandMock = jest.fn(); - sendCommandMock.mockReturnValue(Promise.resolve()); + describe('handleSync', () => { + const getItemsMock = jest.fn(); const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock + getItems: getItemsMock }; - const commands = [{ - "devices": [{ - "customData": { - "itemType": "Dimmer" - }, - "id": "MySpeaker" - }], - "execution": [{ - "command": "action.devices.commands.volumeRelative", - "params": { - "relativeSteps": 20 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MySpeaker', '60'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MySpeaker" - ], - "states": { - "currentVolume": 60, - "online": true - }, - "status": "SUCCESS" - }] + beforeEach(() => { + getItemsMock.mockClear(); }); - }); - - - test('volumeRelative Dimmer max overflow', async () => { - const item = - { - "state": "100", - "type": "Dimmer", - "name": "MySpeaker", - "metadata": { - "ga": { - "value": "Speaker" - } - } - }; - - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const sendCommandMock = jest.fn(); - sendCommandMock.mockReturnValue(Promise.resolve()); - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "itemType": "Dimmer" - }, - "id": "MySpeaker" - }], - "execution": [{ - "command": "action.devices.commands.volumeRelative", - "params": { - "relativeSteps": 20 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MySpeaker', '100'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MySpeaker" - ], - "states": { - "currentVolume": 100, - "online": true - }, - "status": "SUCCESS" - }] + test('handleSync no matching items', async () => { + getItemsMock.mockReturnValue(Promise.resolve([{ "name": "TestItem" }])); + const openHAB = new OpenHAB(apiHandler); + const result = await openHAB.handleSync(); + expect(getItemsMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual({ "devices": [] }); }); - }); - - test('volumeRelative Dimmer min overflow', async () => { - const item = - { - "state": "10", - "type": "Dimmer", - "name": "MySpeaker", - "metadata": { - "ga": { - "value": "Speaker" - } - } - }; - const getItemMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - - const sendCommandMock = jest.fn(); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "customData": { - "itemType": "Dimmer" - }, - "id": "MySpeaker" - }], - "execution": [{ - "command": "action.devices.commands.volumeRelative", - "params": { - "relativeSteps": -20 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MySpeaker', '0'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MySpeaker" + test('handleSync single switch', async () => { + getItemsMock.mockReturnValue(Promise.resolve([{ + "type": "Switch", + "name": "SwitchItem", + "label": "Switch Item", + "metadata": { "ga": { "value": "Switch" } } + }])); + const openHAB = new OpenHAB(apiHandler); + const result = await openHAB.handleSync(); + expect(getItemsMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual({ + "devices": [ + { + "attributes": {}, + "customData": { + "deviceType": "Switch", + "itemType": "Switch", + }, + "deviceInfo": { + "hwVersion": "2.5.0", + "manufacturer": "openHAB", + "model": "Switch:SwitchItem", + "swVersion": "2.5.0", + }, + "id": "SwitchItem", + "name": { + "defaultNames": [ + "Switch Item", + ], + "name": "Switch Item", + "nicknames": [ + "Switch Item", + ], + }, + "roomHint": undefined, + "structureHint": undefined, + "traits": [ + "action.devices.traits.OnOff", + ], + "type": "action.devices.types.SWITCH", + "willReportState": false, + }, ], - "states": { - "currentVolume": 0, - "online": true - }, - "status": "SUCCESS" - }] + }); }); - }); - test('OpenClose Rollershutter', async () => { + test('handleSync switch and light group', async () => { + getItemsMock.mockReturnValue(Promise.resolve([{ + "type": "Switch", + "name": "SwitchItem", + "label": "Switch Item", + "metadata": { "ga": { "value": "Switch" } } + }, + { + "type": "Group", + "name": "TVItem", + "label": "TV Item", + "metadata": { "ga": { "value": "TV" } } + }, + { + "type": "Switch", + "name": "TVMute", + "label": "TV Mute", + "groupNames": ["TVItem"], + "metadata": { "ga": { "value": "tvMute" } } + }, + { + "type": "Switch", + "name": "TVPower", + "label": "TV Power", + "groupNames": ["TVItem"], + "metadata": { "ga": { "value": "tvPower" } } + } + ])); + const openHAB = new OpenHAB(apiHandler); + const result = await openHAB.handleSync(); + expect(getItemsMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual({ + "devices": [ + { + "attributes": {}, + "customData": { + "deviceType": "Switch", + "itemType": "Switch" + }, + "deviceInfo": { + "hwVersion": "2.5.0", + "manufacturer": "openHAB", + "model": "Switch:SwitchItem", + "swVersion": "2.5.0" + }, + "id": "SwitchItem", + "name": { + "defaultNames": [ + "Switch Item" + ], + "name": "Switch Item", + "nicknames": [ + "Switch Item" + ], + }, + "roomHint": undefined, + "structureHint": undefined, + "traits": [ + "action.devices.traits.OnOff" + ], + "type": "action.devices.types.SWITCH", + "willReportState": false + }, + { + "attributes": { + "volumeCanMuteAndUnmute": true + }, + "customData": { + "deviceType": "TV", + "itemType": "Group" + }, + "deviceInfo": { + "hwVersion": "2.5.0", + "manufacturer": "openHAB", + "model": "Group:TVItem", + "swVersion": "2.5.0" + }, + "id": "TVItem", + "name": { + "defaultNames": [ + "TV Item" + ], + "name": "TV Item", + "nicknames": [ + "TV Item" + ], + }, + "roomHint": undefined, + "structureHint": undefined, + "traits": [ + "action.devices.traits.OnOff", + "action.devices.traits.Volume" + ], + "type": "action.devices.types.TV", + "willReportState": false + } + ] + }); + }); + }); + + describe('handleQuery', () => { const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - sendCommandMock.mockReturnValue(Promise.resolve()); const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock + getItem: getItemMock }; - const commands = [{ - "devices": [{ - "customData": { - "itemType": "Rollershutter" - }, - "id": "MyRollershutter" - }], - "execution": [{ - "command": "action.devices.commands.OpenClose", - "params": { - "openPercent": 0 - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).not.toHaveBeenCalled(); - expect(sendCommandMock).toBeCalledWith('MyRollershutter', 'DOWN'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyRollershutter" - ], - "states": { - "online": true, - "openPercent": 0 - }, - "status": "SUCCESS" - }] + beforeEach(() => { + getItemMock.mockReset(); }); - }); - - test('OpenClose Rollershutter inverted', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - const commands = [{ - "devices": [{ - "customData": { - "itemType": "Rollershutter", - "inverted": true - }, - "id": "MyRollershutter" - }], - "execution": [{ - "command": "action.devices.commands.OpenClose", - "params": { - "openPercent": 0 + test('handleQuery device offline', async () => { + getItemMock.mockReturnValue(Promise.reject({ "statusCode": 500 })); + const openHAB = new OpenHAB(apiHandler); + const result = await openHAB.handleQuery([{ "id": "TestItem" }]); + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual({ + "devices": { + "TestItem": { + "errorCode": "deviceOffline", + "status": "ERROR" + } } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).not.toHaveBeenCalled(); - expect(sendCommandMock).toBeCalledWith('MyRollershutter', 'UP'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyRollershutter" - ], - "states": { - "online": true, - "openPercent": 0 - }, - "status": "SUCCESS" - }] + }); }); - }); - - test('OpenClose Switch', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - const commands = [{ - "devices": [{ - "customData": { - "itemType": "Switch" - }, - "id": "MyRollershutter" - }], - "execution": [{ - "command": "action.devices.commands.OpenClose", - "params": { - "openPercent": 0 + test('handleQuery device not found', async () => { + getItemMock.mockReturnValue(Promise.resolve({ "name": "TestItem" })); + const openHAB = new OpenHAB(apiHandler); + const result = await openHAB.handleQuery([{ "id": "TestItem" }]); + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual({ + "devices": { + "TestItem": { + "errorCode": "deviceNotFound", + "status": "ERROR" + } } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).not.toHaveBeenCalled(); - expect(sendCommandMock).toBeCalledWith('MyRollershutter', 'OFF'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyRollershutter" - ], - "states": { - "online": true, - "openPercent": 0 - }, - "status": "SUCCESS" - }] + }); }); - }); - - test('ColorAbsolute HSV', async () => { - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - const commands = [{ - "devices": [{ - "id": "MyColor" - }], - "execution": [{ - "command": "action.devices.commands.ColorAbsolute", - "params": { - "color": { - "spectrumHSV": { - "hue": 240.0, - "saturation": 1.0, - "value": 1.0 - } + test('handleQuery device not ready', async () => { + getItemMock.mockReturnValue(Promise.resolve({ + "name": "TestItem", + "type": "Group", + "groupType": "Switch", + "state": "NULL", + "metadata": { "ga": { "value": "Switch" } } + })); + const openHAB = new OpenHAB(apiHandler); + const result = await openHAB.handleQuery([{ "id": "TestItem" }]); + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual({ + "devices": { + "TestItem": { + "errorCode": "deviceNotReady", + "status": "ERROR" } } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); + }); + }); - expect(getItemMock).not.toHaveBeenCalled(); - expect(sendCommandMock).toBeCalledWith('MyColor', '240,100,100'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyColor" - ], - "states": { - "online": true, - "color": { - "spectrumHsv": { - "hue": 240.0, - "saturation": 1.0, - "value": 1.0 + // there is currently no case + xtest('handleQuery notSupported', async () => { + getItemMock.mockReturnValue(Promise.resolve({ + "name": "TestItem", + "type": "Group", + "state": "NULL", + "metadata": { + "ga": { + "value": "Thermostat", + "config": { + "modes": "on=1,off=2" } } }, - "status": "SUCCESS" - }] - }); - }); - - test('ColorAbsolute Temperature ColorLight', async () => { - const item = - { - "state": "50,50,50", - "type": "Color", - "name": "MyColor", - "metadata": { - "ga": { - "value": "LIGHT" - } - } - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyColor" - }], - "execution": [{ - "command": "action.devices.commands.ColorAbsolute", - "params": { - "color": { - "temperature": 4000 + "members": [ + { + "state": "3", + "metadata": { "ga": { "value": "thermostatMode" } } } - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalled(); - expect(sendCommandMock).toBeCalledWith('MyColor', '26.97,35,50'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyColor" - ], - "states": { - "online": true, - "color": { - "temperatureK": 4000 + ] + })); + const openHAB = new OpenHAB(apiHandler); + const result = await openHAB.handleQuery([{ "id": "TestItem" }]); + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual({ + "devices": { + "TestItem": { + "errorCode": "notSupported", + "status": "ERROR" } - }, - "status": "SUCCESS" - }] + } + }); }); - }); - test('ColorAbsolute Temperature SpecialColorLight', async () => { - const item = - { - "type": "Group", - "name": "MyColor", - "metadata": { - "ga": { - "value": "LIGHT", - "config": { - "colorTemperatureRange": "1000,4000" - } - } - }, - "members": [{ - "type": "Dimmer", - "name": "MyBrightness", - "metadata": { - "ga": { - "value": "lightBrightness" - } - }, - "state": "10" - }, { + test('handleQuery Switch', async () => { + getItemMock.mockReturnValue(Promise.resolve({ + "name": "TestItem", + "type": "Switch", + "state": "ON", + "metadata": { "ga": { "value": "Switch" } } + })); + const openHAB = new OpenHAB(apiHandler); + const result = await openHAB.handleQuery([{ "id": "TestItem" }]); + expect(getItemMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual({ + "devices": { + "TestItem": { + "status": "SUCCESS", + "on": true, + "online": true + } + } + }); + }); + + test('handleQuery mutliple devices', async () => { + getItemMock.mockReturnValueOnce(Promise.resolve({ + "name": "TestItem", + "type": "Switch", + "state": "ON", + "metadata": { "ga": { "value": "Switch" } } + })); + getItemMock.mockReturnValueOnce(Promise.resolve({ + "name": "TestItem2", "type": "Dimmer", - "name": "MyTemperature", - "metadata": { - "ga": { - "value": "lightColorTemperature" + "state": "50", + "metadata": { "ga": { "value": "Light" } } + })); + const openHAB = new OpenHAB(apiHandler); + const result = await openHAB.handleQuery([{ "id": "TestItem" }, { "id": "TestItem2" }]); + expect(getItemMock).toHaveBeenCalledTimes(2); + expect(result).toStrictEqual({ + "devices": { + "TestItem": { + "status": "SUCCESS", + "on": true, + "online": true + }, + "TestItem2": { + "status": "SUCCESS", + "brightness": 50, + "on": true, + "online": true } - }, - "state": "50" - }] - }; + } + }); + }); + }); + describe('handleExecute', () => { const getItemMock = jest.fn(); const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); const apiHandler = { getItem: getItemMock, sendCommand: sendCommandMock }; - const commands = [{ - "devices": [{ - "id": "MyColor", - "customData": { - "deviceType": "SpecialColorLight" - } - }], - "execution": [{ - "command": "action.devices.commands.ColorAbsolute", - "params": { - "color": { - "temperature": 4000 - } - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalled(); - expect(sendCommandMock).toBeCalledWith('MyTemperature', '0'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyColor" - ], - "states": { - "online": true, - "color": { - "temperatureK": 4000 - } - }, - "status": "SUCCESS" - }] + beforeEach(() => { + getItemMock.mockReset(); + sendCommandMock.mockReset(); }); - }); - test('OnOff SpecialColorLight', async () => { - const item = - { - "type": "Group", - "name": "MyColor", - "metadata": { - "ga": { - "value": "LIGHT", - "config": { - "colorTemperatureRange": "1000,4000" - } - } - }, - "members": [{ - "type": "Dimmer", - "name": "MyBrightness", + test('handleExecute OnOff', async () => { + sendCommandMock.mockReturnValue(Promise.resolve()); + const openHAB = new OpenHAB(apiHandler); + const result = await openHAB.handleExecute([{ + "devices": [ + { + "id": "TestItem", + "customData": {} + }, + ], + "execution": [ + { + "command": "action.devices.commands.OnOff", + "params": { "on": true } + } + ] + }]); + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(1); + expect(result).toStrictEqual({ + "commands": [ + { + "ids": ["TestItem"], + "states": { + "on": true, + "online": true + }, + "status": "SUCCESS" + } + ] + }); + }); + + test('handleExecute function not supported', async () => { + const openHAB = new OpenHAB(apiHandler); + const result = await openHAB.handleExecute([{ + "devices": [ + { + "id": "TestItem", + "customData": {} + }, + ], + "execution": [ + { + "command": "action.devices.commands.Invalid", + "params": {} + } + ] + }]); + expect(getItemMock).toHaveBeenCalledTimes(0); + expect(sendCommandMock).toHaveBeenCalledTimes(0); + expect(result).toStrictEqual({ + "commands": [ + { + "ids": ["TestItem"], + "errorCode": "functionNotSupported", + "status": "ERROR" + } + ] + }); + }); + + test('handleExecute ThermostatTemperatureSetRange', async () => { + getItemMock.mockReturnValue(Promise.resolve({ + "name": "TestItem", + "type": "Group", "metadata": { "ga": { - "value": "lightBrightness" + "value": "Thermostat", } }, - "state": "10" - }, { - "type": "Dimmer", - "name": "MyTemperature", - "metadata": { - "ga": { - "value": "lightColorTemperature" + "members": [ + { + "name": "High", + "state": "25", + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpointHigh" + } + } + }, + { + "name": "Low", + "state": "5", + "metadata": { + "ga": { + "value": "thermostatTemperatureSetpointLow" + } + } } - }, - "state": "50" - }] - }; - - const getItemMock = jest.fn(); - const sendCommandMock = jest.fn(); - getItemMock.mockReturnValue(Promise.resolve(item)); - sendCommandMock.mockReturnValue(Promise.resolve()); - - const apiHandler = { - getItem: getItemMock, - sendCommand: sendCommandMock - }; - - const commands = [{ - "devices": [{ - "id": "MyColor", - "customData": { - "deviceType": "SpecialColorLight" - } - }], - "execution": [{ - "command": "action.devices.commands.OnOff", - "params": { - "on": false - } - }] - }]; - - const payload = await new OpenHAB(apiHandler).handleExecute(commands); - - expect(getItemMock).toHaveBeenCalledTimes(1); - expect(sendCommandMock).toBeCalledWith('MyBrightness', 'OFF'); - expect(payload).toStrictEqual({ - "commands": [{ - "ids": [ - "MyColor" + ] + })); + sendCommandMock.mockReturnValue(Promise.resolve()); + const openHAB = new OpenHAB(apiHandler); + const result = await openHAB.handleExecute([{ + "devices": [ + { + "id": "TestItem", + "customData": {} + }, ], - "states": { - "online": true, - "on": false - }, - "status": "SUCCESS" - }] + "execution": [ + { + "command": "action.devices.commands.ThermostatTemperatureSetRange", + "params": { + "thermostatTemperatureSetpointLow": 10, + "thermostatTemperatureSetpointHigh": 20 + } + } + ] + }]); + expect(getItemMock).toHaveBeenCalledTimes(2); + expect(sendCommandMock).toHaveBeenCalledTimes(2); + expect(result).toStrictEqual({ + "commands": [ + { + "ids": ["TestItem"], + "states": { + "thermostatTemperatureSetpointHigh": 25, + "thermostatTemperatureSetpointLow": 10, + "online": true + }, + "status": "SUCCESS" + } + ] + }); }); }); }); From abfe9f2055e699514f23121039ab5538fc341344 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 3 Oct 2020 21:01:46 +0200 Subject: [PATCH 64/94] Add tests for config Signed-off-by: Michael Krug --- tests/config.test.js | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/config.test.js diff --git a/tests/config.test.js b/tests/config.test.js new file mode 100644 index 00000000..2aa84f6e --- /dev/null +++ b/tests/config.test.js @@ -0,0 +1,7 @@ +const Config = require('../functions/config.js'); + +describe('Config', () => { + test('Config all properties', () => { + expect(Object.keys(Config)).toStrictEqual(["host", "port", "path"]); + }); +}); From 0e37102f8aa696f03a494d3389e6c554f73a02f9 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 3 Oct 2020 21:44:28 +0200 Subject: [PATCH 65/94] Replace tab with two spaces Signed-off-by: Michael Krug --- functions/config.js | 8 +- functions/index.js | 66 ++++++------- functions/openhab.js | 216 ++++++++++++++++++++--------------------- functions/utilities.js | 10 +- 4 files changed, 150 insertions(+), 150 deletions(-) diff --git a/functions/config.js b/functions/config.js index 29f70eb0..1ea757b7 100644 --- a/functions/config.js +++ b/functions/config.js @@ -33,8 +33,8 @@ * **/ module.exports = { - //userpass: 'user@foo.com:Password1', - host: '', - port: 443, - path: '/YOUR/REST/ENDPOINT', + //userpass: 'user@foo.com:Password1', + host: '', + port: 443, + path: '/YOUR/REST/ENDPOINT', }; diff --git a/functions/index.js b/functions/index.js index f99c86a8..a54c8bde 100644 --- a/functions/index.js +++ b/functions/index.js @@ -24,51 +24,51 @@ const config = require('./config.js'); const app = require('actions-on-google').smarthome(); app.onDisconnect(() => { - return {}; + return {}; }); app.onExecute(async (body, headers) => { - const authToken = headers.authorization ? headers.authorization.split(' ')[1] : null; - const apiHandler = new ApiHandler(config, authToken); - const payload = await new OpenHAB(apiHandler).handleExecute(body.inputs[0].payload.commands).catch(() => ({ - errorCode: 'actionNotAvailable', - status: 'ERROR', - commands: [] - })); + const authToken = headers.authorization ? headers.authorization.split(' ')[1] : null; + const apiHandler = new ApiHandler(config, authToken); + const payload = await new OpenHAB(apiHandler).handleExecute(body.inputs[0].payload.commands).catch(() => ({ + errorCode: 'actionNotAvailable', + status: 'ERROR', + commands: [] + })); - return { - requestId: body.requestId, - payload: payload - }; + return { + requestId: body.requestId, + payload: payload + }; }); app.onQuery(async (body, headers) => { - const authToken = headers.authorization ? headers.authorization.split(' ')[1] : null; - const apiHandler = new ApiHandler(config, authToken); - const payload = await new OpenHAB(apiHandler).handleQuery(body.inputs[0].payload.devices).catch(() => ({ - errorCode: 'actionNotAvailable', - status: 'ERROR', - devices: {} - })); + const authToken = headers.authorization ? headers.authorization.split(' ')[1] : null; + const apiHandler = new ApiHandler(config, authToken); + const payload = await new OpenHAB(apiHandler).handleQuery(body.inputs[0].payload.devices).catch(() => ({ + errorCode: 'actionNotAvailable', + status: 'ERROR', + devices: {} + })); - return { - requestId: body.requestId, - payload: payload - }; + return { + requestId: body.requestId, + payload: payload + }; }); app.onSync(async (body, headers) => { - const authToken = headers.authorization ? headers.authorization.split(' ')[1] : null; - const apiHandler = new ApiHandler(config, authToken); - const payload = await new OpenHAB(apiHandler).handleSync().catch(() => ({ - errorCode: 'actionNotAvailable', - status : 'ERROR' - })); + const authToken = headers.authorization ? headers.authorization.split(' ')[1] : null; + const apiHandler = new ApiHandler(config, authToken); + const payload = await new OpenHAB(apiHandler).handleSync().catch(() => ({ + errorCode: 'actionNotAvailable', + status : 'ERROR' + })); - return { - requestId: body.requestId, - payload: payload - }; + return { + requestId: body.requestId, + payload: payload + }; }); exports.openhabGoogleAssistant = app; diff --git a/functions/openhab.js b/functions/openhab.js index ed967b61..0185ca21 100644 --- a/functions/openhab.js +++ b/functions/openhab.js @@ -24,126 +24,126 @@ const Commands = []; const Devices = []; glob.sync('./commands/*.js', { cwd: __dirname }).forEach(file => { - const command = require(file); - if (command.type) { - Commands.push(command); - } + const command = require(file); + if (command.type) { + Commands.push(command); + } }); glob.sync('./devices/*.js', { cwd: __dirname }).forEach(file => { - const device = require(file); - if (device.type) { - Devices.push(device); - } + const device = require(file); + if (device.type) { + Devices.push(device); + } }); class OpenHAB { - /** - * @param {object} apiHandler - */ - constructor(apiHandler) { - this._apiHandler = apiHandler; - } + /** + * @param {object} apiHandler + */ + constructor(apiHandler) { + this._apiHandler = apiHandler; + } - /** - * @param {string} command - * @param {object} params - */ - static getCommandType(command, params) { - return Commands.find((commandType) => command === commandType.type && commandType.validateParams(params)); - } + /** + * @param {string} command + * @param {object} params + */ + static getCommandType(command, params) { + return Commands.find((commandType) => command === commandType.type && commandType.validateParams(params)); + } - /** - * @param {object} item - */ - static getDeviceForItem(item) { - return Devices.find((device) => device.matchesItemType(item) && device.isCompatible(item)); - } + /** + * @param {object} item + */ + static getDeviceForItem(item) { + return Devices.find((device) => device.matchesItemType(item) && device.isCompatible(item)); + } - handleSync() { - console.log('openhabGoogleAssistant - handleSync'); - return this._apiHandler.getItems().then((items) => { - let discoveredDevicesList = []; - items.forEach((item) => { - item.members = items.filter((member) => member.groupNames && member.groupNames.includes(item.name)); - const DeviceType = OpenHAB.getDeviceForItem(item); - if (DeviceType) { - console.log(`openhabGoogleAssistant - handleSync - SYNC is adding: ${item.type}:${item.name} with type: ${DeviceType.type}`); - discoveredDevicesList.push(DeviceType.getMetadata(item)); - } - }); - return { devices: discoveredDevicesList }; - }); - } + handleSync() { + console.log('openhabGoogleAssistant - handleSync'); + return this._apiHandler.getItems().then((items) => { + let discoveredDevicesList = []; + items.forEach((item) => { + item.members = items.filter((member) => member.groupNames && member.groupNames.includes(item.name)); + const DeviceType = OpenHAB.getDeviceForItem(item); + if (DeviceType) { + console.log(`openhabGoogleAssistant - handleSync - SYNC is adding: ${item.type}:${item.name} with type: ${DeviceType.type}`); + discoveredDevicesList.push(DeviceType.getMetadata(item)); + } + }); + return { devices: discoveredDevicesList }; + }); + } - /** - * @param {array} devices - */ - handleQuery(devices) { - console.log(`openhabGoogleAssistant - handleQuery - devices: ${JSON.stringify(devices)}`); - const payload = { - devices: {} - }; - const promises = devices.map((device) => { - return this._apiHandler.getItem(device.id).then((item) => { - const DeviceType = OpenHAB.getDeviceForItem(item); - if (!DeviceType) { - throw { statusCode: 404 }; - } - if (item.state === 'NULL' && !('getMembers' in DeviceType)) { - throw { statusCode: 406 }; - } - payload.devices[device.id] = Object.assign({ status: 'SUCCESS', online: true }, DeviceType.getState(item)); - }).catch((error) => { - payload.devices[device.id] = { - status: 'ERROR', - errorCode: error.statusCode == 404 ? 'deviceNotFound' : error.statusCode == 400 ? 'notSupported' : error.statusCode == 406 ? 'deviceNotReady' : 'deviceOffline' - }; - }); - }); - return Promise.all(promises).then(() => payload); - } + /** + * @param {array} devices + */ + handleQuery(devices) { + console.log(`openhabGoogleAssistant - handleQuery - devices: ${JSON.stringify(devices)}`); + const payload = { + devices: {} + }; + const promises = devices.map((device) => { + return this._apiHandler.getItem(device.id).then((item) => { + const DeviceType = OpenHAB.getDeviceForItem(item); + if (!DeviceType) { + throw { statusCode: 404 }; + } + if (item.state === 'NULL' && !('getMembers' in DeviceType)) { + throw { statusCode: 406 }; + } + payload.devices[device.id] = Object.assign({ status: 'SUCCESS', online: true }, DeviceType.getState(item)); + }).catch((error) => { + payload.devices[device.id] = { + status: 'ERROR', + errorCode: error.statusCode == 404 ? 'deviceNotFound' : error.statusCode == 400 ? 'notSupported' : error.statusCode == 406 ? 'deviceNotReady' : 'deviceOffline' + }; + }); + }); + return Promise.all(promises).then(() => payload); + } - /** - * @param {array} commands - */ - handleExecute(commands) { - console.log(`openhabGoogleAssistant - handleExecute - commands: ${JSON.stringify(commands)}`); - const payload = { - commands: [] - }; - const promises = []; - commands.forEach((command) => { - command.execution.forEach((execution) => { - // Special handling of ThermostatTemperatureSetRange that requires updating two values - if (execution.command === 'action.devices.commands.ThermostatTemperatureSetRange') { - const SetHigh = OpenHAB.getCommandType('action.devices.commands.ThermostatTemperatureSetpointHigh', execution.params); - const SetLow = OpenHAB.getCommandType('action.devices.commands.ThermostatTemperatureSetpointLow', execution.params); - if (SetHigh && SetLow) { - promises.push(SetHigh.execute(this._apiHandler, command.devices, execution.params, execution.challenge).then(() => { - return SetLow.execute(this._apiHandler, command.devices, execution.params, execution.challenge); - })); - return; - } - } - const CommandType = OpenHAB.getCommandType(execution.command, execution.params); - if (!CommandType) { - promises.push(Promise.resolve({ - ids: command.devices.map((device) => device.id), - status: 'ERROR', - errorCode: 'functionNotSupported' - })); - return; - } - promises.push(CommandType.execute(this._apiHandler, command.devices, execution.params, execution.challenge)); - }); - }); - return Promise.all(promises).then((result) => { - result.forEach((entry) => (payload.commands = payload.commands.concat(entry))); - return payload; - }); - } + /** + * @param {array} commands + */ + handleExecute(commands) { + console.log(`openhabGoogleAssistant - handleExecute - commands: ${JSON.stringify(commands)}`); + const payload = { + commands: [] + }; + const promises = []; + commands.forEach((command) => { + command.execution.forEach((execution) => { + // Special handling of ThermostatTemperatureSetRange that requires updating two values + if (execution.command === 'action.devices.commands.ThermostatTemperatureSetRange') { + const SetHigh = OpenHAB.getCommandType('action.devices.commands.ThermostatTemperatureSetpointHigh', execution.params); + const SetLow = OpenHAB.getCommandType('action.devices.commands.ThermostatTemperatureSetpointLow', execution.params); + if (SetHigh && SetLow) { + promises.push(SetHigh.execute(this._apiHandler, command.devices, execution.params, execution.challenge).then(() => { + return SetLow.execute(this._apiHandler, command.devices, execution.params, execution.challenge); + })); + return; + } + } + const CommandType = OpenHAB.getCommandType(execution.command, execution.params); + if (!CommandType) { + promises.push(Promise.resolve({ + ids: command.devices.map((device) => device.id), + status: 'ERROR', + errorCode: 'functionNotSupported' + })); + return; + } + promises.push(CommandType.execute(this._apiHandler, command.devices, execution.params, execution.challenge)); + }); + }); + return Promise.all(promises).then((result) => { + result.forEach((entry) => (payload.commands = payload.commands.concat(entry))); + return payload; + }); + } } module.exports = OpenHAB; diff --git a/functions/utilities.js b/functions/utilities.js index a21cd1ee..48750fd3 100644 --- a/functions/utilities.js +++ b/functions/utilities.js @@ -20,20 +20,20 @@ module.exports = { /** - * @param {number} value - */ + * @param {number} value + */ convertToCelsius: (value) => { return Number(((value - 32) * 5 / 9).toFixed(1)); }, /** * @param {number} value - */ + */ convertToFahrenheit: (value) => { return Math.round(value * 9 / 5 + 32); }, /** * @param {number} kelvin - */ + */ kelvin2rgb: (kelvin) => { const temp = kelvin / 100; const r = temp <= 66 ? 255 : 329.698727446 * Math.pow(temp - 60, -0.1332047592); @@ -47,7 +47,7 @@ module.exports = { }, /** * @param {object} rgb - */ + */ rgb2hsv: ({ r, g, b }) => { r = r / 255; g = g / 255; From 8422e0210ab42880ceafdda149b6ce8af9f141d5 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 3 Oct 2020 21:45:30 +0200 Subject: [PATCH 66/94] Add ApiHandler tests Signed-off-by: Michael Krug --- functions/apihandler.js | 26 +++++- package-lock.json | 41 +++++++++ package.json | 3 +- tests/apihandler.test.js | 174 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 tests/apihandler.test.js diff --git a/functions/apihandler.js b/functions/apihandler.js index d9db8de5..dc803e76 100644 --- a/functions/apihandler.js +++ b/functions/apihandler.js @@ -21,7 +21,14 @@ const https = require('https'); class ApiHandler { - constructor(config = {}, authToken = '') { + /** + * @param {object} config + * @param {string} authToken + */ + constructor(config = { host: '', path: '/rest/items/', port: 80 }, authToken = '') { + if (!config.path.startsWith('/')) { + config.path = '/' + config.path; + } if (!config.path.endsWith('/')) { config.path += '/'; } @@ -29,7 +36,12 @@ class ApiHandler { this._authToken = authToken; } - getOptions(method = 'GET', itemName = '', length = 0) { + /** + * @param {string} method + * @param {string} itemName + * @param {number} length + */ + getOptions(method = 'GET', itemName = '', length = 0) { const queryString = '?metadata=ga,synonyms' + (itemName ? '' : '&fields=groupNames,groupType,name,label,metadata,tags,type'); const options = { hostname: this._config.host, @@ -55,7 +67,9 @@ class ApiHandler { return options; } - + /** + * @param {string} itemName + */ getItem(itemName = '') { const options = this.getOptions('GET', itemName); return new Promise((resolve, reject) => { @@ -86,7 +100,11 @@ class ApiHandler { return this.getItem(); } - sendCommand(itemName = '', payload = '') { + /** + * @param {string} itemName + * @param {string} payload + */ + sendCommand(itemName, payload) { const options = this.getOptions('POST', itemName, payload.length); return new Promise((resolve, reject) => { const req = https.request(options, (response) => { diff --git a/package-lock.json b/package-lock.json index 6b0b2c0f..e19d36aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4838,6 +4838,12 @@ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "dev": true }, + "lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", + "dev": true + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -5017,6 +5023,35 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "nock": { + "version": "13.0.4", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.4.tgz", + "integrity": "sha512-alqTV8Qt7TUbc74x1pKRLSENzfjp4nywovcJgi/1aXDiUxXdt7TkruSTF5MDWPP7UoPVgea4F9ghVdmX0xxnSA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash.set": "^4.3.2", + "propagate": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5419,6 +5454,12 @@ "sisteransi": "^1.0.4" } }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", diff --git a/package.json b/package.json index 54233717..f7e260a9 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1", - "jest": "^26.4.2" + "jest": "^26.4.2", + "nock": "^13.0.4" } } diff --git a/tests/apihandler.test.js b/tests/apihandler.test.js new file mode 100644 index 00000000..c33fa2c9 --- /dev/null +++ b/tests/apihandler.test.js @@ -0,0 +1,174 @@ +const ApiHandler = require('../functions/apihandler.js'); +const nock = require('nock'); + +describe('ApiHandler', () => { + const config = { + "host": "example.org", + "path": "items", + "port": 443 + } + + const apiHander = new ApiHandler(config, "token"); + + test('constructor', () => { + expect(apiHander._config).toStrictEqual({ + "host": "example.org", + "path": "/items/", + "port": 443 + }); + expect(apiHander._authToken).toBe("token"); + }); + + describe('getOptions', () => { + test('getOptions GET all items', () => { + expect(apiHander.getOptions("GET", "", 0)).toStrictEqual({ + "headers": { + "Accept": "application/json", + "Authorization": "Bearer token" + }, + "hostname": "example.org", + "method": "GET", + "path": "/items/?metadata=ga,synonyms&fields=groupNames,groupType,name,label,metadata,tags,type", + "port": 443 + }); + }); + + test('getOptions GET single items', () => { + expect(apiHander.getOptions("GET", "TestItem", 0)).toStrictEqual({ + "headers": { + "Accept": "application/json", + "Authorization": "Bearer token" + }, + "hostname": "example.org", + "method": "GET", + "path": "/items/TestItem?metadata=ga,synonyms", + "port": 443 + }); + }); + + test('getOptions POST', () => { + expect(apiHander.getOptions("POST", "TestItem", 10)).toStrictEqual({ + "headers": { + "Accept": "application/json", + "Authorization": "Bearer token", + "Content-Length": 10, + "Content-Type": "text/plain" + }, + "hostname": "example.org", + "method": "POST", + "path": "/items/TestItem?metadata=ga,synonyms", + "port": 443 + }); + }); + + test('getOptions GET userpass', () => { + apiHander._config.userpass = "tester:test"; + expect(apiHander.getOptions("GET", "TestItem", 0)).toStrictEqual({ + "auth": "tester:test", + "headers": { + "Accept": "application/json" + }, + "hostname": "example.org", + "method": "GET", + "path": "/items/TestItem?metadata=ga,synonyms", + "port": 443 + }); + }); + }); + + describe('getItem', () => { + afterEach(() => { + nock.cleanAll(); + }); + + test('getItem', async () => { + const scope = nock('https://example.org') + .get('/items/TestItem?metadata=ga,synonyms') + .reply(200, [{ "name": "TestItem" }]); + const result = await apiHander.getItem("TestItem"); + expect(result).toStrictEqual([{ "name": "TestItem" }]); + expect(scope.isDone()).toBe(true); + }); + + test('getItem failed', async () => { + const scope = nock('https://example.org') + .get('/items/TestItem?metadata=ga,synonyms') + .reply(400, {}); + let error = {}; + try { + await apiHander.getItem("TestItem"); + } catch (e) { + error = e; + } + expect(error).toStrictEqual({ + "message": "getItem failed", + "statusCode": 400 + }); + expect(scope.isDone()).toBe(true); + }); + }); + + describe('getItems', () => { + afterEach(() => { + nock.cleanAll(); + }); + + test('getItems', async () => { + const scope = nock('https://example.org') + .get('/items/?metadata=ga,synonyms&fields=groupNames,groupType,name,label,metadata,tags,type') + .reply(200, [{ "name": "TestItem" }]); + const result = await apiHander.getItems(); + expect(result).toStrictEqual([{ "name": "TestItem" }]); + expect(scope.isDone()).toBe(true); + }); + + test('getItems failed', async () => { + const scope = nock('https://example.org') + .get('/items/?metadata=ga,synonyms&fields=groupNames,groupType,name,label,metadata,tags,type') + .reply(400, {}); + let error = {}; + try { + await apiHander.getItems(); + } catch (e) { + error = e; + } + expect(error).toStrictEqual({ + "message": "getItem failed", + "statusCode": 400 + }); + expect(scope.isDone()).toBe(true); + }); + }); + + describe('sendCommand', () => { + afterEach(() => { + nock.cleanAll(); + }); + + test('sendCommand', async () => { + const scope = nock('https://example.org') + .post('/items/TestItem?metadata=ga,synonyms') + .reply(200, [{ "name": "TestItem" }]); + const result = await apiHander.sendCommand("TestItem", "OFF"); + expect(result).toBeUndefined(); + expect(scope.isDone()).toBe(true); + }); + + test('sendCommand failed', async () => { + const scope = nock('https://example.org') + .post('/items/TestItem?metadata=ga,synonyms') + .reply(400, {}); + let error = {}; + try { + await apiHander.sendCommand("TestItem", "OFF"); + } catch (e) { + error = e; + } + expect(error).toStrictEqual({ + "message": "sendCommand failed", + "statusCode": 400 + }); + expect(scope.isDone()).toBe(true); + }); + }); +}); From 72aafdc28869368cb4de0b04fd9658d48b7d6984 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 1 Oct 2020 12:36:49 +0200 Subject: [PATCH 67/94] Create codeql-analysis.yml Signed-off-by: Michael Krug --- .github/workflows/codeql-analysis.yml | 64 +++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..56bae904 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,64 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +name: "CodeQL" + +on: [push, pull_request] + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + # Override automatic language detection by changing the below list + # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] + language: ['javascript'] + # Learn more... + # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From e21b3e4895003d3a8ec99ea0ddddb891fb597f8e Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Mon, 5 Oct 2020 01:18:29 +0200 Subject: [PATCH 68/94] Rework index & openhab to be more testable Signed-off-by: Michael Krug --- functions/apihandler.js | 12 +- functions/index.js | 52 +----- functions/openhab.js | 113 ++++++++++--- tests/apihandler.test.js | 11 +- tests/openhab.test.js | 347 +++++++++++++++++++++++++++------------ 5 files changed, 364 insertions(+), 171 deletions(-) diff --git a/functions/apihandler.js b/functions/apihandler.js index dc803e76..cf506b1e 100644 --- a/functions/apihandler.js +++ b/functions/apihandler.js @@ -23,9 +23,8 @@ const https = require('https'); class ApiHandler { /** * @param {object} config - * @param {string} authToken */ - constructor(config = { host: '', path: '/rest/items/', port: 80 }, authToken = '') { + constructor(config = { host: '', path: '/rest/items/', port: 80 }) { if (!config.path.startsWith('/')) { config.path = '/' + config.path; } @@ -33,6 +32,13 @@ class ApiHandler { config.path += '/'; } this._config = config; + this._authToken = ''; + } + + /** + * @param {string} authToken + */ + set authToken(authToken) { this._authToken = authToken; } @@ -55,7 +61,7 @@ class ApiHandler { if (this._config.userpass) { options.auth = this._config.userpass; - } else { + } else if (this._authToken) { options.headers['Authorization'] = 'Bearer ' + this._authToken; } diff --git a/functions/index.js b/functions/index.js index a54c8bde..7c82f765 100644 --- a/functions/index.js +++ b/functions/index.js @@ -23,52 +23,12 @@ const ApiHandler = require('./apihandler.js'); const config = require('./config.js'); const app = require('actions-on-google').smarthome(); -app.onDisconnect(() => { - return {}; -}); +const apiHandler = new ApiHandler(config); +const openHAB = new OpenHAB(apiHandler); -app.onExecute(async (body, headers) => { - const authToken = headers.authorization ? headers.authorization.split(' ')[1] : null; - const apiHandler = new ApiHandler(config, authToken); - const payload = await new OpenHAB(apiHandler).handleExecute(body.inputs[0].payload.commands).catch(() => ({ - errorCode: 'actionNotAvailable', - status: 'ERROR', - commands: [] - })); - - return { - requestId: body.requestId, - payload: payload - }; -}); - -app.onQuery(async (body, headers) => { - const authToken = headers.authorization ? headers.authorization.split(' ')[1] : null; - const apiHandler = new ApiHandler(config, authToken); - const payload = await new OpenHAB(apiHandler).handleQuery(body.inputs[0].payload.devices).catch(() => ({ - errorCode: 'actionNotAvailable', - status: 'ERROR', - devices: {} - })); - - return { - requestId: body.requestId, - payload: payload - }; -}); - -app.onSync(async (body, headers) => { - const authToken = headers.authorization ? headers.authorization.split(' ')[1] : null; - const apiHandler = new ApiHandler(config, authToken); - const payload = await new OpenHAB(apiHandler).handleSync().catch(() => ({ - errorCode: 'actionNotAvailable', - status : 'ERROR' - })); - - return { - requestId: body.requestId, - payload: payload - }; -}); +app.onDisconnect(openHAB.onDisconnect); +app.onExecute(openHAB.onExecute); +app.onQuery(openHAB.onQuery); +app.onSync(openHAB.onSync); exports.openhabGoogleAssistant = app; diff --git a/functions/openhab.js b/functions/openhab.js index 0185ca21..53b6adce 100644 --- a/functions/openhab.js +++ b/functions/openhab.js @@ -60,8 +60,88 @@ class OpenHAB { return Devices.find((device) => device.matchesItemType(item) && device.isCompatible(item)); } - handleSync() { + /** + * @param {object} headers + */ + setTokenFromHeader(headers) { + this._apiHandler.authToken = headers.authorization ? headers.authorization.split(' ')[1] : null; + } + + onDisconnect() { + return {}; + } + + /** + * @param {object} body + * @param {object} headers + */ + async onSync(body, headers) { console.log('openhabGoogleAssistant - handleSync'); + + this.setTokenFromHeader(headers); + + const payload = await this.handleSync() + .catch(() => ({ + errorCode: 'actionNotAvailable', + status: 'ERROR', + devices: [] + })); + + return { + requestId: body.requestId, + payload: payload + }; + } + + /** + * @param {object} body + * @param {object} headers + */ + async onQuery(body, headers) { + const devices = body && body.inputs && body.inputs[0] && body.inputs[0].payload && body.inputs[0].payload.devices || []; + + console.log(`openhabGoogleAssistant - handleQuery - devices: ${JSON.stringify(devices)}`); + + this.setTokenFromHeader(headers); + + const payload = await this.handleQuery(devices) + .catch(() => ({ + errorCode: 'actionNotAvailable', + status: 'ERROR', + devices: {} + })); + + return { + requestId: body.requestId, + payload: payload + }; + } + + /** + * @param {object} body + * @param {object} headers + */ + async onExecute(body, headers) { + const commands = body && body.inputs && body.inputs[0] && body.inputs[0].payload && body.inputs[0].payload.commands || []; + + console.log(`openhabGoogleAssistant - handleExecute - commands: ${JSON.stringify(commands)}`); + + this.setTokenFromHeader(headers); + + const payload = await this.handleExecute(commands) + .catch(() => ({ + errorCode: 'actionNotAvailable', + status: 'ERROR', + commands: [] + })); + + return { + requestId: body.requestId, + payload: payload + }; + } + + handleSync() { return this._apiHandler.getItems().then((items) => { let discoveredDevicesList = []; items.forEach((item) => { @@ -80,12 +160,9 @@ class OpenHAB { * @param {array} devices */ handleQuery(devices) { - console.log(`openhabGoogleAssistant - handleQuery - devices: ${JSON.stringify(devices)}`); - const payload = { - devices: {} - }; - const promises = devices.map((device) => { - return this._apiHandler.getItem(device.id).then((item) => { + const payload = { devices: {} }; + const promises = devices.map((device) => ( + this._apiHandler.getItem(device.id).then((item) => { const DeviceType = OpenHAB.getDeviceForItem(item); if (!DeviceType) { throw { statusCode: 404 }; @@ -94,25 +171,21 @@ class OpenHAB { throw { statusCode: 406 }; } payload.devices[device.id] = Object.assign({ status: 'SUCCESS', online: true }, DeviceType.getState(item)); - }).catch((error) => { + }).catch((error) => ( payload.devices[device.id] = { status: 'ERROR', errorCode: error.statusCode == 404 ? 'deviceNotFound' : error.statusCode == 400 ? 'notSupported' : error.statusCode == 406 ? 'deviceNotReady' : 'deviceOffline' - }; - }); - }); + } + )) + )); + return Promise.all(promises).then(() => payload); } - /** * @param {array} commands */ handleExecute(commands) { - console.log(`openhabGoogleAssistant - handleExecute - commands: ${JSON.stringify(commands)}`); - const payload = { - commands: [] - }; const promises = []; commands.forEach((command) => { command.execution.forEach((execution) => { @@ -139,9 +212,11 @@ class OpenHAB { promises.push(CommandType.execute(this._apiHandler, command.devices, execution.params, execution.challenge)); }); }); - return Promise.all(promises).then((result) => { - result.forEach((entry) => (payload.commands = payload.commands.concat(entry))); - return payload; + + return Promise.all(promises).then((responseDetails) => { + let responses = []; + responseDetails.forEach((response) => (responses = responses.concat(response))); + return { commands: responses }; }); } } diff --git a/tests/apihandler.test.js b/tests/apihandler.test.js index c33fa2c9..82bdd606 100644 --- a/tests/apihandler.test.js +++ b/tests/apihandler.test.js @@ -8,7 +8,11 @@ describe('ApiHandler', () => { "port": 443 } - const apiHander = new ApiHandler(config, "token"); + const apiHander = new ApiHandler(config); + + beforeEach(() => { + apiHander.authToken = "token"; + }); test('constructor', () => { expect(apiHander._config).toStrictEqual({ @@ -19,6 +23,11 @@ describe('ApiHandler', () => { expect(apiHander._authToken).toBe("token"); }); + test('authToken', () => { + apiHander.authToken = "1234"; + expect(apiHander._authToken).toBe("1234"); + }); + describe('getOptions', () => { test('getOptions GET all items', () => { expect(apiHander.getOptions("GET", "", 0)).toStrictEqual({ diff --git a/tests/openhab.test.js b/tests/openhab.test.js index 69e5336d..b0f8bc60 100644 --- a/tests/openhab.test.js +++ b/tests/openhab.test.js @@ -21,6 +21,54 @@ describe('OpenHAB', () => { }); }); + test('setTokenFromHeader', () => { + const openHAB = new OpenHAB({ authToken: "" }); + openHAB.setTokenFromHeader({}); + expect(openHAB._apiHandler.authToken).toBe(null); + openHAB.setTokenFromHeader({ "authorization": "Bearer token" }); + expect(openHAB._apiHandler.authToken).toBe("token"); + }); + + test('onDisconnect', () => { + const openHAB = new OpenHAB({}); + expect(openHAB.onDisconnect()).toStrictEqual({}); + }); + + describe('onSync', () => { + const openHAB = new OpenHAB({}); + + beforeEach(() => { + jest.spyOn(openHAB, 'handleSync').mockReset(); + }); + + test('onSync failure', async () => { + const handleSyncMock = jest.spyOn(openHAB, 'handleSync'); + handleSyncMock.mockReturnValue(Promise.reject()); + const result = await openHAB.onSync({ "requestId": "1234" }, {}); + expect(handleSyncMock).toBeCalledTimes(1); + expect(result).toStrictEqual({ + "requestId": "1234", + "payload": { + "devices": [], + "errorCode": "actionNotAvailable", + "status": "ERROR" + } + }); + }); + + test('onSync empty', async () => { + const handleSyncMock = jest.spyOn(openHAB, 'handleSync'); + const payload = { "devices": [] } + handleSyncMock.mockReturnValue(Promise.resolve(payload)); + const result = await openHAB.onSync({ "requestId": "1234" }, {}); + expect(handleSyncMock).toBeCalledTimes(1); + expect(result).toStrictEqual({ + "requestId": "1234", + "payload": payload + }); + }); + }); + describe('handleSync', () => { const getItemsMock = jest.fn(); @@ -28,13 +76,14 @@ describe('OpenHAB', () => { getItems: getItemsMock }; + const openHAB = new OpenHAB(apiHandler); + beforeEach(() => { getItemsMock.mockClear(); }); test('handleSync no matching items', async () => { getItemsMock.mockReturnValue(Promise.resolve([{ "name": "TestItem" }])); - const openHAB = new OpenHAB(apiHandler); const result = await openHAB.handleSync(); expect(getItemsMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ "devices": [] }); @@ -47,42 +96,33 @@ describe('OpenHAB', () => { "label": "Switch Item", "metadata": { "ga": { "value": "Switch" } } }])); - const openHAB = new OpenHAB(apiHandler); const result = await openHAB.handleSync(); expect(getItemsMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ - "devices": [ - { - "attributes": {}, - "customData": { - "deviceType": "Switch", - "itemType": "Switch", - }, - "deviceInfo": { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Switch:SwitchItem", - "swVersion": "2.5.0", - }, - "id": "SwitchItem", - "name": { - "defaultNames": [ - "Switch Item", - ], - "name": "Switch Item", - "nicknames": [ - "Switch Item", - ], - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": [ - "action.devices.traits.OnOff", - ], - "type": "action.devices.types.SWITCH", - "willReportState": false, + "devices": [{ + "attributes": {}, + "customData": { + "deviceType": "Switch", + "itemType": "Switch", }, - ], + "deviceInfo": { + "hwVersion": "2.5.0", + "manufacturer": "openHAB", + "model": "Switch:SwitchItem", + "swVersion": "2.5.0", + }, + "id": "SwitchItem", + "name": { + "defaultNames": ["Switch Item"], + "name": "Switch Item", + "nicknames": ["Switch Item"] + }, + "roomHint": undefined, + "structureHint": undefined, + "traits": ["action.devices.traits.OnOff"], + "type": "action.devices.types.SWITCH", + "willReportState": false + }] }); }); @@ -114,7 +154,6 @@ describe('OpenHAB', () => { "metadata": { "ga": { "value": "tvPower" } } } ])); - const openHAB = new OpenHAB(apiHandler); const result = await openHAB.handleSync(); expect(getItemsMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ @@ -133,19 +172,13 @@ describe('OpenHAB', () => { }, "id": "SwitchItem", "name": { - "defaultNames": [ - "Switch Item" - ], + "defaultNames": ["Switch Item"], "name": "Switch Item", - "nicknames": [ - "Switch Item" - ], + "nicknames": ["Switch Item"], }, "roomHint": undefined, "structureHint": undefined, - "traits": [ - "action.devices.traits.OnOff" - ], + "traits": ["action.devices.traits.OnOff"], "type": "action.devices.types.SWITCH", "willReportState": false }, @@ -165,13 +198,9 @@ describe('OpenHAB', () => { }, "id": "TVItem", "name": { - "defaultNames": [ - "TV Item" - ], + "defaultNames": ["TV Item"], "name": "TV Item", - "nicknames": [ - "TV Item" - ], + "nicknames": ["TV Item"], }, "roomHint": undefined, "structureHint": undefined, @@ -187,6 +216,68 @@ describe('OpenHAB', () => { }); }); + describe('onQuery', () => { + const openHAB = new OpenHAB({}); + + beforeEach(() => { + jest.spyOn(openHAB, 'handleQuery').mockReset(); + }); + + test('onQuery failure', async () => { + const handleQueryMock = jest.spyOn(openHAB, 'handleQuery'); + handleQueryMock.mockReturnValue(Promise.reject()); + const result = await openHAB.onQuery({ "requestId": "1234" }, {}); + expect(handleQueryMock).toBeCalledTimes(1); + expect(handleQueryMock).toBeCalledWith([]); + expect(result).toStrictEqual({ + "requestId": "1234", + "payload": { + "devices": {}, + "errorCode": "actionNotAvailable", + "status": "ERROR" + } + }); + }); + + test('onQuery empty', async () => { + const handleQueryMock = jest.spyOn(openHAB, 'handleQuery'); + const payload = { "devices": {} }; + handleQueryMock.mockReturnValue(Promise.resolve(payload)); + const result = await openHAB.onQuery({ "requestId": "1234" }, {}); + expect(handleQueryMock).toBeCalledTimes(1); + expect(handleQueryMock).toBeCalledWith([]); + expect(result).toStrictEqual({ + "requestId": "1234", + "payload": payload + }); + }); + + test('onQuery', async () => { + const handleQueryMock = jest.spyOn(openHAB, 'handleQuery'); + const payload = { "devices": {} }; + handleQueryMock.mockReturnValue(Promise.resolve(payload)); + const devices = [{ "id": "TestItem1" }, { "id": "TestItem2" }]; + const body = { + "requestId": "1234", + "inputs": [ + { + "intent": "action.devices.QUERY", + "payload": { + "devices": devices + } + } + ] + }; + const result = await openHAB.onQuery(body, {}); + expect(handleQueryMock).toBeCalledTimes(1); + expect(handleQueryMock).toBeCalledWith(devices); + expect(result).toStrictEqual({ + "requestId": "1234", + "payload": payload + }); + }); + }); + describe('handleQuery', () => { const getItemMock = jest.fn(); @@ -194,13 +285,14 @@ describe('OpenHAB', () => { getItem: getItemMock }; + const openHAB = new OpenHAB(apiHandler); + beforeEach(() => { getItemMock.mockReset(); }); test('handleQuery device offline', async () => { getItemMock.mockReturnValue(Promise.reject({ "statusCode": 500 })); - const openHAB = new OpenHAB(apiHandler); const result = await openHAB.handleQuery([{ "id": "TestItem" }]); expect(getItemMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ @@ -215,7 +307,6 @@ describe('OpenHAB', () => { test('handleQuery device not found', async () => { getItemMock.mockReturnValue(Promise.resolve({ "name": "TestItem" })); - const openHAB = new OpenHAB(apiHandler); const result = await openHAB.handleQuery([{ "id": "TestItem" }]); expect(getItemMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ @@ -236,7 +327,6 @@ describe('OpenHAB', () => { "state": "NULL", "metadata": { "ga": { "value": "Switch" } } })); - const openHAB = new OpenHAB(apiHandler); const result = await openHAB.handleQuery([{ "id": "TestItem" }]); expect(getItemMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ @@ -270,7 +360,6 @@ describe('OpenHAB', () => { } ] })); - const openHAB = new OpenHAB(apiHandler); const result = await openHAB.handleQuery([{ "id": "TestItem" }]); expect(getItemMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ @@ -290,7 +379,6 @@ describe('OpenHAB', () => { "state": "ON", "metadata": { "ga": { "value": "Switch" } } })); - const openHAB = new OpenHAB(apiHandler); const result = await openHAB.handleQuery([{ "id": "TestItem" }]); expect(getItemMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ @@ -317,7 +405,6 @@ describe('OpenHAB', () => { "state": "50", "metadata": { "ga": { "value": "Light" } } })); - const openHAB = new OpenHAB(apiHandler); const result = await openHAB.handleQuery([{ "id": "TestItem" }, { "id": "TestItem2" }]); expect(getItemMock).toHaveBeenCalledTimes(2); expect(result).toStrictEqual({ @@ -338,6 +425,72 @@ describe('OpenHAB', () => { }); }); + describe('onExecute', () => { + const openHAB = new OpenHAB({}); + + beforeEach(() => { + jest.spyOn(openHAB, 'handleExecute').mockReset(); + }); + + test('onExecute failure', async () => { + const handleExecuteMock = jest.spyOn(openHAB, 'handleExecute'); + handleExecuteMock.mockReturnValue(Promise.reject()); + const result = await openHAB.onExecute({ "requestId": "1234" }, {}); + expect(handleExecuteMock).toBeCalledTimes(1); + expect(handleExecuteMock).toBeCalledWith([]); + expect(result).toStrictEqual({ + "requestId": "1234", + "payload": { + "commands": [], + "errorCode": "actionNotAvailable", + "status": "ERROR" + } + }); + }); + + test('onExecute empty', async () => { + const handleExecuteMock = jest.spyOn(openHAB, 'handleExecute'); + const payload = { "commands": [] }; + handleExecuteMock.mockReturnValue(Promise.resolve(payload)); + const result = await openHAB.onExecute({ "requestId": "1234" }, {}); + expect(handleExecuteMock).toBeCalledTimes(1); + expect(handleExecuteMock).toBeCalledWith([]); + expect(result).toStrictEqual({ + "requestId": "1234", + "payload": payload + }); + }); + + test('onExecute', async () => { + const handleExecuteMock = jest.spyOn(openHAB, 'handleExecute'); + const payload = { "commands": [] }; + handleExecuteMock.mockReturnValue(Promise.resolve(payload)); + const commands = [{ + "devices": [{ "id": "123" }, { "id": "456" }], + "execution": [{ + "command": "action.devices.commands.OnOff", + "params": { "on": true } + }] + }]; + const body = { + "requestId": "1234", + "inputs": [{ + "intent": "action.devices.EXECUTE", + "payload": { + "commands": commands + } + }] + }; + const result = await openHAB.onExecute(body, {}); + expect(handleExecuteMock).toBeCalledTimes(1); + expect(handleExecuteMock).toBeCalledWith(commands); + expect(result).toStrictEqual({ + "requestId": "1234", + "payload": payload + }); + }); + }); + describe('handleExecute', () => { const getItemMock = jest.fn(); const sendCommandMock = jest.fn(); @@ -347,6 +500,8 @@ describe('OpenHAB', () => { sendCommand: sendCommandMock }; + const openHAB = new OpenHAB(apiHandler); + beforeEach(() => { getItemMock.mockReset(); sendCommandMock.mockReset(); @@ -354,21 +509,17 @@ describe('OpenHAB', () => { test('handleExecute OnOff', async () => { sendCommandMock.mockReturnValue(Promise.resolve()); - const openHAB = new OpenHAB(apiHandler); - const result = await openHAB.handleExecute([{ - "devices": [ - { - "id": "TestItem", - "customData": {} - }, - ], - "execution": [ - { - "command": "action.devices.commands.OnOff", - "params": { "on": true } - } - ] - }]); + const commands = [{ + "devices": [{ + "id": "TestItem", + "customData": {} + }], + "execution": [{ + "command": "action.devices.commands.OnOff", + "params": { "on": true } + }] + }]; + const result = await openHAB.handleExecute(commands); expect(getItemMock).toHaveBeenCalledTimes(0); expect(sendCommandMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ @@ -386,21 +537,17 @@ describe('OpenHAB', () => { }); test('handleExecute function not supported', async () => { - const openHAB = new OpenHAB(apiHandler); - const result = await openHAB.handleExecute([{ - "devices": [ - { - "id": "TestItem", - "customData": {} - }, - ], - "execution": [ - { - "command": "action.devices.commands.Invalid", - "params": {} - } - ] - }]); + const commands = [{ + "devices": [{ + "id": "TestItem", + "customData": {} + }], + "execution": [{ + "command": "action.devices.commands.Invalid", + "params": {} + }] + }]; + const result = await openHAB.handleExecute(commands); expect(getItemMock).toHaveBeenCalledTimes(0); expect(sendCommandMock).toHaveBeenCalledTimes(0); expect(result).toStrictEqual({ @@ -445,24 +592,20 @@ describe('OpenHAB', () => { ] })); sendCommandMock.mockReturnValue(Promise.resolve()); - const openHAB = new OpenHAB(apiHandler); - const result = await openHAB.handleExecute([{ - "devices": [ - { - "id": "TestItem", - "customData": {} - }, - ], - "execution": [ - { - "command": "action.devices.commands.ThermostatTemperatureSetRange", - "params": { - "thermostatTemperatureSetpointLow": 10, - "thermostatTemperatureSetpointHigh": 20 - } + const commands = [{ + "devices": [{ + "id": "TestItem", + "customData": {} + }], + "execution": [{ + "command": "action.devices.commands.ThermostatTemperatureSetRange", + "params": { + "thermostatTemperatureSetpointLow": 10, + "thermostatTemperatureSetpointHigh": 20 } - ] - }]); + }] + }]; + const result = await openHAB.handleExecute(commands); expect(getItemMock).toHaveBeenCalledTimes(2); expect(sendCommandMock).toHaveBeenCalledTimes(2); expect(result).toStrictEqual({ From 638f17e2647018e8b649d849d8bd585291e22460 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Tue, 6 Oct 2020 22:04:54 +0200 Subject: [PATCH 69/94] Add test workflow Signed-off-by: Michael Krug --- .github/workflows/test.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..31e018db --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,19 @@ +name: "Test" + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [10.x, 12.x, 14.x] + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci + - run: npm run build --if-present + - run: npm test From 8f10aeb210863a901b8fdc0fe81bcce7789eead2 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Tue, 6 Oct 2020 22:12:36 +0200 Subject: [PATCH 70/94] Upload coverage as artifact Signed-off-by: Michael Krug --- .github/workflows/test.yml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 31e018db..ccc8ca76 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,11 +9,15 @@ jobs: matrix: node-version: [10.x, 12.x, 14.x] steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - run: npm ci - - run: npm run build --if-present - - run: npm test + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci + - run: npm run build --if-present + - run: npm test + - uses: actions/upload-artifact@v2 + with: + name: coverage + path: coverage/ From f385984e5d33d4a92acaf78fd04cf846a83dda2c Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Tue, 6 Oct 2020 22:44:23 +0200 Subject: [PATCH 71/94] Fix intent handler calls Signed-off-by: Michael Krug --- functions/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/functions/index.js b/functions/index.js index 7c82f765..e50faacf 100644 --- a/functions/index.js +++ b/functions/index.js @@ -26,9 +26,9 @@ const app = require('actions-on-google').smarthome(); const apiHandler = new ApiHandler(config); const openHAB = new OpenHAB(apiHandler); -app.onDisconnect(openHAB.onDisconnect); -app.onExecute(openHAB.onExecute); -app.onQuery(openHAB.onQuery); -app.onSync(openHAB.onSync); +app.onDisconnect(() => openHAB.onDisconnect()); +app.onExecute((body, headers) => openHAB.onExecute(body, headers)); +app.onQuery((body, headers) => openHAB.onQuery(body, headers)); +app.onSync((body, headers) => openHAB.onSync(body, headers)); exports.openhabGoogleAssistant = app; From e9e7049268dbfabb24c3a1ec008cdee23ff2e604 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Wed, 7 Oct 2020 00:23:14 +0200 Subject: [PATCH 72/94] Only run workflows for pushes on master and PRs, Comment test coverage on PRs Signed-off-by: Michael Krug --- .github/workflows/codeql-analysis.yml | 95 +++++++++++++-------------- .github/workflows/test.yml | 16 ++++- 2 files changed, 60 insertions(+), 51 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 56bae904..7fe88f75 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,11 +1,10 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -name: "CodeQL" +name: CodeQL -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: jobs: analyze: @@ -17,48 +16,48 @@ jobs: matrix: # Override automatic language detection by changing the below list # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['javascript'] + language: ["javascript"] # Learn more... # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ccc8ca76..6e176cba 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,10 @@ -name: "Test" +name: Tests -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: jobs: build: @@ -17,7 +21,13 @@ jobs: - run: npm ci - run: npm run build --if-present - run: npm test - - uses: actions/upload-artifact@v2 + - name: Upload Test Coverage + uses: actions/upload-artifact@v2 with: name: coverage path: coverage/ + - name: Comment Test Coverage + if: ${{ github.event_name == 'pull_request' }} + uses: romeovs/lcov-reporter-action@v0.2.11 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} From 22a7d47d9b0570c64de7ab93b9cd29563acd7aae Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Wed, 7 Oct 2020 01:05:41 +0200 Subject: [PATCH 73/94] Update jest dependency Signed-off-by: Michael Krug --- package-lock.json | 839 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 421 insertions(+), 420 deletions(-) diff --git a/package-lock.json b/package-lock.json index e19d36aa..648a8401 100644 --- a/package-lock.json +++ b/package-lock.json @@ -515,23 +515,23 @@ "dev": true }, "@jest/console": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.3.0.tgz", - "integrity": "sha512-/5Pn6sJev0nPUcAdpJHMVIsA8sKizL2ZkcKPE5+dJrCccks7tcM7c9wbgHudBJbxXLoTbqsHkG1Dofoem4F09w==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.5.2.tgz", + "integrity": "sha512-lJELzKINpF1v74DXHbCRIkQ/+nUV1M+ntj+X1J8LxCgpmJZjfLmhFejiMSbjjD66fayxl5Z06tbs3HMyuik6rw==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^26.3.0", - "jest-util": "^26.3.0", + "jest-message-util": "^26.5.2", + "jest-util": "^26.5.2", "slash": "^3.0.0" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -563,34 +563,34 @@ } }, "@jest/core": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.4.2.tgz", - "integrity": "sha512-sDva7YkeNprxJfepOctzS8cAk9TOekldh+5FhVuXS40+94SHbiicRO1VV2tSoRtgIo+POs/Cdyf8p76vPTd6dg==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.5.2.tgz", + "integrity": "sha512-LLTo1LQMg7eJjG/+P1NYqFof2B25EV1EqzD5FonklihG4UJKiK2JBIvWonunws6W7e+DhNLoFD+g05tCY03eyA==", "dev": true, "requires": { - "@jest/console": "^26.3.0", - "@jest/reporters": "^26.4.1", - "@jest/test-result": "^26.3.0", - "@jest/transform": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/console": "^26.5.2", + "@jest/reporters": "^26.5.2", + "@jest/test-result": "^26.5.2", + "@jest/transform": "^26.5.2", + "@jest/types": "^26.5.2", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.4", - "jest-changed-files": "^26.3.0", - "jest-config": "^26.4.2", - "jest-haste-map": "^26.3.0", - "jest-message-util": "^26.3.0", + "jest-changed-files": "^26.5.2", + "jest-config": "^26.5.2", + "jest-haste-map": "^26.5.2", + "jest-message-util": "^26.5.2", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.4.0", - "jest-resolve-dependencies": "^26.4.2", - "jest-runner": "^26.4.2", - "jest-runtime": "^26.4.2", - "jest-snapshot": "^26.4.2", - "jest-util": "^26.3.0", - "jest-validate": "^26.4.2", - "jest-watcher": "^26.3.0", + "jest-resolve": "^26.5.2", + "jest-resolve-dependencies": "^26.5.2", + "jest-runner": "^26.5.2", + "jest-runtime": "^26.5.2", + "jest-snapshot": "^26.5.2", + "jest-util": "^26.5.2", + "jest-validate": "^26.5.2", + "jest-watcher": "^26.5.2", "micromatch": "^4.0.2", "p-each-series": "^2.1.0", "rimraf": "^3.0.0", @@ -599,9 +599,9 @@ }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -633,21 +633,21 @@ } }, "@jest/environment": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.3.0.tgz", - "integrity": "sha512-EW+MFEo0DGHahf83RAaiqQx688qpXgl99wdb8Fy67ybyzHwR1a58LHcO376xQJHfmoXTu89M09dH3J509cx2AA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.5.2.tgz", + "integrity": "sha512-YjhCD/Zhkz0/1vdlS/QN6QmuUdDkpgBdK4SdiVg4Y19e29g4VQYN5Xg8+YuHjdoWGY7wJHMxc79uDTeTOy9Ngw==", "dev": true, "requires": { - "@jest/fake-timers": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/fake-timers": "^26.5.2", + "@jest/types": "^26.5.2", "@types/node": "*", - "jest-mock": "^26.3.0" + "jest-mock": "^26.5.2" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -679,23 +679,23 @@ } }, "@jest/fake-timers": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.3.0.tgz", - "integrity": "sha512-ZL9ytUiRwVP8ujfRepffokBvD2KbxbqMhrXSBhSdAhISCw3gOkuntisiSFv+A6HN0n0fF4cxzICEKZENLmW+1A==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.5.2.tgz", + "integrity": "sha512-09Hn5Oraqt36V1akxQeWMVL0fR9c6PnEhpgLaYvREXZJAh2H2Y+QLCsl0g7uMoJeoWJAuz4tozk1prbR1Fc1sw==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "@sinonjs/fake-timers": "^6.0.1", "@types/node": "*", - "jest-message-util": "^26.3.0", - "jest-mock": "^26.3.0", - "jest-util": "^26.3.0" + "jest-message-util": "^26.5.2", + "jest-mock": "^26.5.2", + "jest-util": "^26.5.2" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -727,20 +727,20 @@ } }, "@jest/globals": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.4.2.tgz", - "integrity": "sha512-Ot5ouAlehhHLRhc+sDz2/9bmNv9p5ZWZ9LE1pXGGTCXBasmi5jnYjlgYcYt03FBwLmZXCZ7GrL29c33/XRQiow==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.5.2.tgz", + "integrity": "sha512-9PmnFsAUJxpPt1s/stq02acS1YHliVBDNfAWMe1bwdRr1iTCfhbNt3ERQXrO/ZfZSweftoA26Q/2yhSVSWQ3sw==", "dev": true, "requires": { - "@jest/environment": "^26.3.0", - "@jest/types": "^26.3.0", - "expect": "^26.4.2" + "@jest/environment": "^26.5.2", + "@jest/types": "^26.5.2", + "expect": "^26.5.2" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -772,16 +772,16 @@ } }, "@jest/reporters": { - "version": "26.4.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.4.1.tgz", - "integrity": "sha512-aROTkCLU8++yiRGVxLsuDmZsQEKO6LprlrxtAuzvtpbIFl3eIjgIf3EUxDKgomkS25R9ZzwGEdB5weCcBZlrpQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.5.2.tgz", + "integrity": "sha512-zvq6Wvy6MmJq/0QY0YfOPb49CXKSf42wkJbrBPkeypVa8I+XDxijvFuywo6TJBX/ILPrdrlE/FW9vJZh6Rf9vA==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.3.0", - "@jest/test-result": "^26.3.0", - "@jest/transform": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/console": "^26.5.2", + "@jest/test-result": "^26.5.2", + "@jest/transform": "^26.5.2", + "@jest/types": "^26.5.2", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", @@ -792,10 +792,10 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.3.0", - "jest-resolve": "^26.4.0", - "jest-util": "^26.3.0", - "jest-worker": "^26.3.0", + "jest-haste-map": "^26.5.2", + "jest-resolve": "^26.5.2", + "jest-util": "^26.5.2", + "jest-worker": "^26.5.0", "node-notifier": "^8.0.0", "slash": "^3.0.0", "source-map": "^0.6.0", @@ -805,9 +805,9 @@ }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -839,9 +839,9 @@ } }, "@jest/source-map": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.3.0.tgz", - "integrity": "sha512-hWX5IHmMDWe1kyrKl7IhFwqOuAreIwHhbe44+XH2ZRHjrKIh0LO5eLQ/vxHFeAfRwJapmxuqlGAEYLadDq6ZGQ==", + "version": "26.5.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.5.0.tgz", + "integrity": "sha512-jWAw9ZwYHJMe9eZq/WrsHlwF8E3hM9gynlcDpOyCb9bR8wEd9ZNBZCi7/jZyzHxC7t3thZ10gO2IDhu0bPKS5g==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -850,21 +850,21 @@ } }, "@jest/test-result": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.3.0.tgz", - "integrity": "sha512-a8rbLqzW/q7HWheFVMtghXV79Xk+GWwOK1FrtimpI5n1la2SY0qHri3/b0/1F0Ve0/yJmV8pEhxDfVwiUBGtgg==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.5.2.tgz", + "integrity": "sha512-E/Zp6LURJEGSCWpoMGmCFuuEI1OWuI3hmZwmULV0GsgJBh7u0rwqioxhRU95euUuviqBDN8ruX/vP/4bwYolXw==", "dev": true, "requires": { - "@jest/console": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/console": "^26.5.2", + "@jest/types": "^26.5.2", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -896,34 +896,34 @@ } }, "@jest/test-sequencer": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.4.2.tgz", - "integrity": "sha512-83DRD8N3M0tOhz9h0bn6Kl6dSp+US6DazuVF8J9m21WAp5x7CqSMaNycMP0aemC/SH/pDQQddbsfHRTBXVUgog==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.5.2.tgz", + "integrity": "sha512-XmGEh7hh07H2B8mHLFCIgr7gA5Y6Hw1ZATIsbz2fOhpnQ5AnQtZk0gmP0Q5/+mVB2xygO64tVFQxOajzoptkNA==", "dev": true, "requires": { - "@jest/test-result": "^26.3.0", + "@jest/test-result": "^26.5.2", "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.3.0", - "jest-runner": "^26.4.2", - "jest-runtime": "^26.4.2" + "jest-haste-map": "^26.5.2", + "jest-runner": "^26.5.2", + "jest-runtime": "^26.5.2" } }, "@jest/transform": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.3.0.tgz", - "integrity": "sha512-Isj6NB68QorGoFWvcOjlUhpkT56PqNIsXKR7XfvoDlCANn/IANlh8DrKAA2l2JKC3yWSMH5wS0GwuQM20w3b2A==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.5.2.tgz", + "integrity": "sha512-AUNjvexh+APhhmS8S+KboPz+D3pCxPvEAGduffaAJYxIFxGi/ytZQkrqcKDUU0ERBAo5R7087fyOYr2oms1seg==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "babel-plugin-istanbul": "^6.0.0", "chalk": "^4.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.3.0", + "jest-haste-map": "^26.5.2", "jest-regex-util": "^26.0.0", - "jest-util": "^26.3.0", + "jest-util": "^26.5.2", "micromatch": "^4.0.2", "pirates": "^4.0.1", "slash": "^3.0.0", @@ -932,9 +932,9 @@ }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -1093,9 +1093,9 @@ "dev": true }, "@types/node": { - "version": "14.11.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz", - "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==", + "version": "14.11.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.5.tgz", + "integrity": "sha512-jVFzDV6NTbrLMxm4xDSIW/gKnk8rQLF9wAzLWIOg+5nU6ACrIMndeBdXci0FGtqJbP9tQvm6V39eshc96TO2wQ==", "dev": true }, "@types/normalize-package-data": { @@ -1111,9 +1111,9 @@ "dev": true }, "@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", "dev": true }, "@types/yargs": { @@ -1147,9 +1147,9 @@ } }, "acorn": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", - "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true }, "acorn-globals": { @@ -1328,25 +1328,25 @@ "dev": true }, "babel-jest": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.3.0.tgz", - "integrity": "sha512-sxPnQGEyHAOPF8NcUsD0g7hDCnvLL2XyblRBcgrzTWBB/mAIpWow3n1bEL+VghnnZfreLhFSBsFluRoK2tRK4g==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.5.2.tgz", + "integrity": "sha512-U3KvymF3SczA3vOL/cgiUFOznfMET+XDIXiWnoJV45siAp2pLMG8i2+/MGZlAC3f/F6Q40LR4M4qDrWZ9wkK8A==", "dev": true, "requires": { - "@jest/transform": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/transform": "^26.5.2", + "@jest/types": "^26.5.2", "@types/babel__core": "^7.1.7", "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.3.0", + "babel-preset-jest": "^26.5.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "slash": "^3.0.0" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -1391,9 +1391,9 @@ } }, "babel-plugin-jest-hoist": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.2.0.tgz", - "integrity": "sha512-B/hVMRv8Nh1sQ1a3EY8I0n4Y1Wty3NrR5ebOyVT302op+DOAau+xNEImGMsUWOC3++ZlMooCytKz+NgN8aKGbA==", + "version": "26.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.5.0.tgz", + "integrity": "sha512-ck17uZFD3CDfuwCLATWZxkkuGGFhMij8quP8CNhwj8ek1mqFgbFzRJ30xwC04LLscj/aKsVFfRST+b5PT7rSuw==", "dev": true, "requires": { "@babel/template": "^7.3.3", @@ -1403,9 +1403,9 @@ } }, "babel-preset-current-node-syntax": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.3.tgz", - "integrity": "sha512-uyexu1sVwcdFnyq9o8UQYsXwXflIh8LvrF5+cKrYam93ned1CStffB3+BEcsxGSgagoA3GEyjDqO4a/58hyPYQ==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.4.tgz", + "integrity": "sha512-5/INNCYhUGqw7VbVjT/hb3ucjgkVHKXY7lX3ZjlN4gm565VyFmJUrJ/h+h16ECVB38R/9SF6aACydpKMLZ/c9w==", "dev": true, "requires": { "@babel/plugin-syntax-async-generators": "^7.8.4", @@ -1422,12 +1422,12 @@ } }, "babel-preset-jest": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.3.0.tgz", - "integrity": "sha512-5WPdf7nyYi2/eRxCbVrE1kKCWxgWY4RsPEbdJWFm7QsesFGqjdkyLeu1zRkwM1cxK6EPIlNd6d2AxLk7J+t4pw==", + "version": "26.5.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.5.0.tgz", + "integrity": "sha512-F2vTluljhqkiGSJGBg/jOruA8vIIIL11YrxRcO7nviNTMbbofPSHwnm8mgP7d/wS7wRSexRoI6X1A6T74d4LQA==", "dev": true, "requires": { - "babel-plugin-jest-hoist": "^26.2.0", + "babel-plugin-jest-hoist": "^26.5.0", "babel-preset-current-node-syntax": "^0.1.3" } }, @@ -2277,23 +2277,23 @@ } }, "expect": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.4.2.tgz", - "integrity": "sha512-IlJ3X52Z0lDHm7gjEp+m76uX46ldH5VpqmU0006vqDju/285twh7zaWMRhs67VpQhBwjjMchk+p5aA0VkERCAA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.5.2.tgz", + "integrity": "sha512-ccTGrXZd8DZCcvCz4htGXTkd/LOoy6OEtiDS38x3/VVf6E4AQL0QoeksBiw7BtGR5xDNiRYPB8GN6pfbuTOi7w==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "ansi-styles": "^4.0.0", "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.4.2", - "jest-message-util": "^26.3.0", + "jest-matcher-utils": "^26.5.2", + "jest-message-util": "^26.5.2", "jest-regex-util": "^26.0.0" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -3211,20 +3211,20 @@ } }, "jest": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/jest/-/jest-26.4.2.tgz", - "integrity": "sha512-LLCjPrUh98Ik8CzW8LLVnSCfLaiY+wbK53U7VxnFSX7Q+kWC4noVeDvGWIFw0Amfq1lq2VfGm7YHWSLBV62MJw==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.5.2.tgz", + "integrity": "sha512-4HFabJVwsgDwul/7rhXJ3yFAF/aUkVIXiJWmgFxb+WMdZG39fVvOwYAs8/3r4AlFPc4m/n5sTMtuMbOL3kNtrQ==", "dev": true, "requires": { - "@jest/core": "^26.4.2", + "@jest/core": "^26.5.2", "import-local": "^3.0.2", - "jest-cli": "^26.4.2" + "jest-cli": "^26.5.2" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -3254,43 +3254,43 @@ } }, "jest-cli": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.4.2.tgz", - "integrity": "sha512-zb+lGd/SfrPvoRSC/0LWdaWCnscXc1mGYW//NP4/tmBvRPT3VntZ2jtKUONsRi59zc5JqmsSajA9ewJKFYp8Cw==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.5.2.tgz", + "integrity": "sha512-usm48COuUvRp8YEG5OWOaxbSM0my7eHn3QeBWxiGUuFhvkGVBvl1fic4UjC02EAEQtDv8KrNQUXdQTV6ZZBsoA==", "dev": true, "requires": { - "@jest/core": "^26.4.2", - "@jest/test-result": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/core": "^26.5.2", + "@jest/test-result": "^26.5.2", + "@jest/types": "^26.5.2", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.4", "import-local": "^3.0.2", "is-ci": "^2.0.0", - "jest-config": "^26.4.2", - "jest-util": "^26.3.0", - "jest-validate": "^26.4.2", + "jest-config": "^26.5.2", + "jest-util": "^26.5.2", + "jest-validate": "^26.5.2", "prompts": "^2.0.1", - "yargs": "^15.3.1" + "yargs": "^15.4.1" } } } }, "jest-changed-files": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.3.0.tgz", - "integrity": "sha512-1C4R4nijgPltX6fugKxM4oQ18zimS7LqQ+zTTY8lMCMFPrxqBFb7KJH0Z2fRQJvw2Slbaipsqq7s1mgX5Iot+g==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.5.2.tgz", + "integrity": "sha512-qSmssmiIdvM5BWVtyK/nqVpN3spR5YyvkvPqz1x3BR1bwIxsWmU/MGwLoCrPNLbkG2ASAKfvmJpOduEApBPh2w==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "execa": "^4.0.0", "throat": "^5.0.0" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -3404,35 +3404,35 @@ } }, "jest-config": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.4.2.tgz", - "integrity": "sha512-QBf7YGLuToiM8PmTnJEdRxyYy3mHWLh24LJZKVdXZ2PNdizSe1B/E8bVm+HYcjbEzGuVXDv/di+EzdO/6Gq80A==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.5.2.tgz", + "integrity": "sha512-dqJOnSegNdE5yDiuGHsjTM5gec7Z4AcAMHiW+YscbOYJAlb3LEtDSobXCq0or9EmGQI5SFmKy4T7P1FxetJOfg==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.4.2", - "@jest/types": "^26.3.0", - "babel-jest": "^26.3.0", + "@jest/test-sequencer": "^26.5.2", + "@jest/types": "^26.5.2", + "babel-jest": "^26.5.2", "chalk": "^4.0.0", "deepmerge": "^4.2.2", "glob": "^7.1.1", "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.3.0", - "jest-environment-node": "^26.3.0", + "jest-environment-jsdom": "^26.5.2", + "jest-environment-node": "^26.5.2", "jest-get-type": "^26.3.0", - "jest-jasmine2": "^26.4.2", + "jest-jasmine2": "^26.5.2", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.4.0", - "jest-util": "^26.3.0", - "jest-validate": "^26.4.2", + "jest-resolve": "^26.5.2", + "jest-util": "^26.5.2", + "jest-validate": "^26.5.2", "micromatch": "^4.0.2", - "pretty-format": "^26.4.2" + "pretty-format": "^26.5.2" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -3468,12 +3468,12 @@ "dev": true }, "pretty-format": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", - "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", + "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -3503,22 +3503,22 @@ } }, "jest-each": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.4.2.tgz", - "integrity": "sha512-p15rt8r8cUcRY0Mvo1fpkOGYm7iI8S6ySxgIdfh3oOIv+gHwrHTy5VWCGOecWUhDsit4Nz8avJWdT07WLpbwDA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.5.2.tgz", + "integrity": "sha512-w7D9FNe0m2D3yZ0Drj9CLkyF/mGhmBSULMQTypzAKR746xXnjUrK8GUJdlLTWUF6dd0ks3MtvGP7/xNFr9Aphg==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "chalk": "^4.0.0", "jest-get-type": "^26.3.0", - "jest-util": "^26.3.0", - "pretty-format": "^26.4.2" + "jest-util": "^26.5.2", + "pretty-format": "^26.5.2" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -3554,12 +3554,12 @@ "dev": true }, "pretty-format": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", - "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", + "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -3568,24 +3568,24 @@ } }, "jest-environment-jsdom": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.3.0.tgz", - "integrity": "sha512-zra8He2btIMJkAzvLaiZ9QwEPGEetbxqmjEBQwhH3CA+Hhhu0jSiEJxnJMbX28TGUvPLxBt/zyaTLrOPF4yMJA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.5.2.tgz", + "integrity": "sha512-fWZPx0bluJaTQ36+PmRpvUtUlUFlGGBNyGX1SN3dLUHHMcQ4WseNEzcGGKOw4U5towXgxI4qDoI3vwR18H0RTw==", "dev": true, "requires": { - "@jest/environment": "^26.3.0", - "@jest/fake-timers": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/environment": "^26.5.2", + "@jest/fake-timers": "^26.5.2", + "@jest/types": "^26.5.2", "@types/node": "*", - "jest-mock": "^26.3.0", - "jest-util": "^26.3.0", - "jsdom": "^16.2.2" + "jest-mock": "^26.5.2", + "jest-util": "^26.5.2", + "jsdom": "^16.4.0" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -3617,23 +3617,23 @@ } }, "jest-environment-node": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.3.0.tgz", - "integrity": "sha512-c9BvYoo+FGcMj5FunbBgtBnbR5qk3uky8PKyRVpSfe2/8+LrNQMiXX53z6q2kY+j15SkjQCOSL/6LHnCPLVHNw==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.5.2.tgz", + "integrity": "sha512-YHjnDsf/GKFCYMGF1V+6HF7jhY1fcLfLNBDjhAOvFGvt6d8vXvNdJGVM7uTZ2VO/TuIyEFhPGaXMX5j3h7fsrA==", "dev": true, "requires": { - "@jest/environment": "^26.3.0", - "@jest/fake-timers": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/environment": "^26.5.2", + "@jest/fake-timers": "^26.5.2", + "@jest/types": "^26.5.2", "@types/node": "*", - "jest-mock": "^26.3.0", - "jest-util": "^26.3.0" + "jest-mock": "^26.5.2", + "jest-util": "^26.5.2" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -3671,12 +3671,12 @@ "dev": true }, "jest-haste-map": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.3.0.tgz", - "integrity": "sha512-DHWBpTJgJhLLGwE5Z1ZaqLTYqeODQIZpby0zMBsCU9iRFHYyhklYqP4EiG73j5dkbaAdSZhgB938mL51Q5LeZA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.5.2.tgz", + "integrity": "sha512-lJIAVJN3gtO3k4xy+7i2Xjtwh8CfPcH08WYjZpe9xzveDaqGw9fVNCpkYu6M525wKFVkLmyi7ku+DxCAP1lyMA==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "@types/graceful-fs": "^4.1.2", "@types/node": "*", "anymatch": "^3.0.3", @@ -3684,18 +3684,18 @@ "fsevents": "^2.1.2", "graceful-fs": "^4.2.4", "jest-regex-util": "^26.0.0", - "jest-serializer": "^26.3.0", - "jest-util": "^26.3.0", - "jest-worker": "^26.3.0", + "jest-serializer": "^26.5.0", + "jest-util": "^26.5.2", + "jest-worker": "^26.5.0", "micromatch": "^4.0.2", "sane": "^4.0.3", "walker": "^1.0.7" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -3727,35 +3727,35 @@ } }, "jest-jasmine2": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.4.2.tgz", - "integrity": "sha512-z7H4EpCldHN1J8fNgsja58QftxBSL+JcwZmaXIvV9WKIM+x49F4GLHu/+BQh2kzRKHAgaN/E82od+8rTOBPyPA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.5.2.tgz", + "integrity": "sha512-2J+GYcgLVPTkpmvHEj0/IDTIAuyblGNGlyGe4fLfDT2aktEPBYvoxUwFiOmDDxxzuuEAD2uxcYXr0+1Yw4tjFA==", "dev": true, "requires": { "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.3.0", - "@jest/source-map": "^26.3.0", - "@jest/test-result": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/environment": "^26.5.2", + "@jest/source-map": "^26.5.0", + "@jest/test-result": "^26.5.2", + "@jest/types": "^26.5.2", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", - "expect": "^26.4.2", + "expect": "^26.5.2", "is-generator-fn": "^2.0.0", - "jest-each": "^26.4.2", - "jest-matcher-utils": "^26.4.2", - "jest-message-util": "^26.3.0", - "jest-runtime": "^26.4.2", - "jest-snapshot": "^26.4.2", - "jest-util": "^26.3.0", - "pretty-format": "^26.4.2", + "jest-each": "^26.5.2", + "jest-matcher-utils": "^26.5.2", + "jest-message-util": "^26.5.2", + "jest-runtime": "^26.5.2", + "jest-snapshot": "^26.5.2", + "jest-util": "^26.5.2", + "pretty-format": "^26.5.2", "throat": "^5.0.0" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -3785,12 +3785,12 @@ } }, "pretty-format": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", - "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", + "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -3799,19 +3799,19 @@ } }, "jest-leak-detector": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.4.2.tgz", - "integrity": "sha512-akzGcxwxtE+9ZJZRW+M2o+nTNnmQZxrHJxX/HjgDaU5+PLmY1qnQPnMjgADPGCRPhB+Yawe1iij0REe+k/aHoA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.5.2.tgz", + "integrity": "sha512-h7ia3dLzBFItmYERaLPEtEKxy3YlcbcRSjj0XRNJgBEyODuu+3DM2o62kvIFvs3PsaYoIIv+e+nLRI61Dj1CNw==", "dev": true, "requires": { "jest-get-type": "^26.3.0", - "pretty-format": "^26.4.2" + "pretty-format": "^26.5.2" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -3847,12 +3847,12 @@ "dev": true }, "pretty-format": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", - "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", + "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -3861,21 +3861,21 @@ } }, "jest-matcher-utils": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.4.2.tgz", - "integrity": "sha512-KcbNqWfWUG24R7tu9WcAOKKdiXiXCbMvQYT6iodZ9k1f7065k0keUOW6XpJMMvah+hTfqkhJhRXmA3r3zMAg0Q==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.5.2.tgz", + "integrity": "sha512-W9GO9KBIC4gIArsNqDUKsLnhivaqf8MSs6ujO/JDcPIQrmY+aasewweXVET8KdrJ6ADQaUne5UzysvF/RR7JYA==", "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^26.4.2", + "jest-diff": "^26.5.2", "jest-get-type": "^26.3.0", - "pretty-format": "^26.4.2" + "pretty-format": "^26.5.2" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -3905,21 +3905,21 @@ } }, "diff-sequences": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.3.0.tgz", - "integrity": "sha512-5j5vdRcw3CNctePNYN0Wy2e/JbWT6cAYnXv5OuqPhDpyCGc0uLu2TK0zOCJWNB9kOIfYMSpIulRaDgIi4HJ6Ig==", + "version": "26.5.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.5.0.tgz", + "integrity": "sha512-ZXx86srb/iYy6jG71k++wBN9P9J05UNQ5hQHQd9MtMPvcqXPx/vKU69jfHV637D00Q2gSgPk2D+jSx3l1lDW/Q==", "dev": true }, "jest-diff": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.4.2.tgz", - "integrity": "sha512-6T1XQY8U28WH0Z5rGpQ+VqZSZz8EN8rZcBtfvXaOkbwxIEeRre6qnuZQlbY1AJ4MKDxQF8EkrCvK+hL/VkyYLQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.5.2.tgz", + "integrity": "sha512-HCSWDUGwsov5oTlGzrRM+UPJI/Dpqi9jzeV0fdRNi3Ch5bnoXhnyJMmVg2juv9081zLIy3HGPI5mcuGgXM2xRA==", "dev": true, "requires": { "chalk": "^4.0.0", - "diff-sequences": "^26.3.0", + "diff-sequences": "^26.5.0", "jest-get-type": "^26.3.0", - "pretty-format": "^26.4.2" + "pretty-format": "^26.5.2" } }, "jest-get-type": { @@ -3929,12 +3929,12 @@ "dev": true }, "pretty-format": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", - "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", + "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -3943,14 +3943,14 @@ } }, "jest-message-util": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.3.0.tgz", - "integrity": "sha512-xIavRYqr4/otGOiLxLZGj3ieMmjcNE73Ui+LdSW/Y790j5acqCsAdDiLIbzHCZMpN07JOENRWX5DcU+OQ+TjTA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.5.2.tgz", + "integrity": "sha512-Ocp9UYZ5Jl15C5PNsoDiGEk14A4NG0zZKknpWdZGoMzJuGAkVt10e97tnEVMYpk7LnQHZOfuK2j/izLBMcuCZw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.3.0", - "@types/stack-utils": "^1.0.1", + "@jest/types": "^26.5.2", + "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "micromatch": "^4.0.2", @@ -3959,9 +3959,9 @@ }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -3993,19 +3993,19 @@ } }, "jest-mock": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.3.0.tgz", - "integrity": "sha512-PeaRrg8Dc6mnS35gOo/CbZovoDPKAeB1FICZiuagAgGvbWdNNyjQjkOaGUa/3N3JtpQ/Mh9P4A2D4Fv51NnP8Q==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.5.2.tgz", + "integrity": "sha512-9SiU4b5PtO51v0MtJwVRqeGEroH66Bnwtq4ARdNP7jNXbpT7+ByeWNAk4NeT/uHfNSVDXEXgQo1XRuwEqS6Rdw==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "@types/node": "*" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -4049,25 +4049,25 @@ "dev": true }, "jest-resolve": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.4.0.tgz", - "integrity": "sha512-bn/JoZTEXRSlEx3+SfgZcJAVuTMOksYq9xe9O6s4Ekg84aKBObEaVXKOEilULRqviSLAYJldnoWV9c07kwtiCg==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.5.2.tgz", + "integrity": "sha512-XsPxojXGRA0CoDD7Vis59ucz2p3cQFU5C+19tz3tLEAlhYKkK77IL0cjYjikY9wXnOaBeEdm1rOgSJjbZWpcZg==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.3.0", + "jest-util": "^26.5.2", "read-pkg-up": "^7.0.1", "resolve": "^1.17.0", "slash": "^3.0.0" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -4200,20 +4200,20 @@ } }, "jest-resolve-dependencies": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.4.2.tgz", - "integrity": "sha512-ADHaOwqEcVc71uTfySzSowA/RdxUpCxhxa2FNLiin9vWLB1uLPad3we+JSSROq5+SrL9iYPdZZF8bdKM7XABTQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.5.2.tgz", + "integrity": "sha512-LLkc8LuRtxqOx0AtX/Npa2C4I23WcIrwUgNtHYXg4owYF/ZDQShcwBAHjYZIFR06+HpQcZ43+kCTMlQ3aDCYTg==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "jest-regex-util": "^26.0.0", - "jest-snapshot": "^26.4.2" + "jest-snapshot": "^26.5.2" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -4245,37 +4245,37 @@ } }, "jest-runner": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.4.2.tgz", - "integrity": "sha512-FgjDHeVknDjw1gRAYaoUoShe1K3XUuFMkIaXbdhEys+1O4bEJS8Avmn4lBwoMfL8O5oFTdWYKcf3tEJyyYyk8g==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.5.2.tgz", + "integrity": "sha512-GKhYxtSX5+tXZsd2QwfkDqPIj5C2HqOdXLRc2x2qYqWE26OJh17xo58/fN/mLhRkO4y6o60ZVloan7Kk5YA6hg==", "dev": true, "requires": { - "@jest/console": "^26.3.0", - "@jest/environment": "^26.3.0", - "@jest/test-result": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/console": "^26.5.2", + "@jest/environment": "^26.5.2", + "@jest/test-result": "^26.5.2", + "@jest/types": "^26.5.2", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.7.1", "exit": "^0.1.2", "graceful-fs": "^4.2.4", - "jest-config": "^26.4.2", + "jest-config": "^26.5.2", "jest-docblock": "^26.0.0", - "jest-haste-map": "^26.3.0", - "jest-leak-detector": "^26.4.2", - "jest-message-util": "^26.3.0", - "jest-resolve": "^26.4.0", - "jest-runtime": "^26.4.2", - "jest-util": "^26.3.0", - "jest-worker": "^26.3.0", + "jest-haste-map": "^26.5.2", + "jest-leak-detector": "^26.5.2", + "jest-message-util": "^26.5.2", + "jest-resolve": "^26.5.2", + "jest-runtime": "^26.5.2", + "jest-util": "^26.5.2", + "jest-worker": "^26.5.0", "source-map-support": "^0.5.6", "throat": "^5.0.0" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -4307,43 +4307,43 @@ } }, "jest-runtime": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.4.2.tgz", - "integrity": "sha512-4Pe7Uk5a80FnbHwSOk7ojNCJvz3Ks2CNQWT5Z7MJo4tX0jb3V/LThKvD9tKPNVNyeMH98J/nzGlcwc00R2dSHQ==", - "dev": true, - "requires": { - "@jest/console": "^26.3.0", - "@jest/environment": "^26.3.0", - "@jest/fake-timers": "^26.3.0", - "@jest/globals": "^26.4.2", - "@jest/source-map": "^26.3.0", - "@jest/test-result": "^26.3.0", - "@jest/transform": "^26.3.0", - "@jest/types": "^26.3.0", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.5.2.tgz", + "integrity": "sha512-zArr4DatX/Sn0wswX/AnAuJgmwgAR5rNtrUz36HR8BfMuysHYNq5sDbYHuLC4ICyRdy5ae/KQ+sczxyS9G6Qvw==", + "dev": true, + "requires": { + "@jest/console": "^26.5.2", + "@jest/environment": "^26.5.2", + "@jest/fake-timers": "^26.5.2", + "@jest/globals": "^26.5.2", + "@jest/source-map": "^26.5.0", + "@jest/test-result": "^26.5.2", + "@jest/transform": "^26.5.2", + "@jest/types": "^26.5.2", "@types/yargs": "^15.0.0", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.4", - "jest-config": "^26.4.2", - "jest-haste-map": "^26.3.0", - "jest-message-util": "^26.3.0", - "jest-mock": "^26.3.0", + "jest-config": "^26.5.2", + "jest-haste-map": "^26.5.2", + "jest-message-util": "^26.5.2", + "jest-mock": "^26.5.2", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.4.0", - "jest-snapshot": "^26.4.2", - "jest-util": "^26.3.0", - "jest-validate": "^26.4.2", + "jest-resolve": "^26.5.2", + "jest-snapshot": "^26.5.2", + "jest-util": "^26.5.2", + "jest-validate": "^26.5.2", "slash": "^3.0.0", "strip-bom": "^4.0.0", - "yargs": "^15.3.1" + "yargs": "^15.4.1" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -4381,9 +4381,9 @@ } }, "jest-serializer": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.3.0.tgz", - "integrity": "sha512-IDRBQBLPlKa4flg77fqg0n/pH87tcRKwe8zxOVTWISxGpPHYkRZ1dXKyh04JOja7gppc60+soKVZ791mruVdow==", + "version": "26.5.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.5.0.tgz", + "integrity": "sha512-+h3Gf5CDRlSLdgTv7y0vPIAoLgX/SI7T4v6hy+TEXMgYbv+ztzbg5PSN6mUXAT/hXYHvZRWm+MaObVfqkhCGxA==", "dev": true, "requires": { "@types/node": "*", @@ -4391,32 +4391,33 @@ } }, "jest-snapshot": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.4.2.tgz", - "integrity": "sha512-N6Uub8FccKlf5SBFnL2Ri/xofbaA68Cc3MGjP/NuwgnsvWh+9hLIR/DhrxbSiKXMY9vUW5dI6EW1eHaDHqe9sg==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.5.2.tgz", + "integrity": "sha512-MkXIDvEefzDubI/WaDVSRH4xnkuirP/Pz8LhAIDXcVQTmcEfwxywj5LGwBmhz+kAAIldA7XM4l96vbpzltSjqg==", "dev": true, "requires": { "@babel/types": "^7.0.0", - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", + "@types/babel__traverse": "^7.0.4", "@types/prettier": "^2.0.0", "chalk": "^4.0.0", - "expect": "^26.4.2", + "expect": "^26.5.2", "graceful-fs": "^4.2.4", - "jest-diff": "^26.4.2", + "jest-diff": "^26.5.2", "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.3.0", - "jest-matcher-utils": "^26.4.2", - "jest-message-util": "^26.3.0", - "jest-resolve": "^26.4.0", + "jest-haste-map": "^26.5.2", + "jest-matcher-utils": "^26.5.2", + "jest-message-util": "^26.5.2", + "jest-resolve": "^26.5.2", "natural-compare": "^1.4.0", - "pretty-format": "^26.4.2", + "pretty-format": "^26.5.2", "semver": "^7.3.2" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -4446,21 +4447,21 @@ } }, "diff-sequences": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.3.0.tgz", - "integrity": "sha512-5j5vdRcw3CNctePNYN0Wy2e/JbWT6cAYnXv5OuqPhDpyCGc0uLu2TK0zOCJWNB9kOIfYMSpIulRaDgIi4HJ6Ig==", + "version": "26.5.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.5.0.tgz", + "integrity": "sha512-ZXx86srb/iYy6jG71k++wBN9P9J05UNQ5hQHQd9MtMPvcqXPx/vKU69jfHV637D00Q2gSgPk2D+jSx3l1lDW/Q==", "dev": true }, "jest-diff": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.4.2.tgz", - "integrity": "sha512-6T1XQY8U28WH0Z5rGpQ+VqZSZz8EN8rZcBtfvXaOkbwxIEeRre6qnuZQlbY1AJ4MKDxQF8EkrCvK+hL/VkyYLQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.5.2.tgz", + "integrity": "sha512-HCSWDUGwsov5oTlGzrRM+UPJI/Dpqi9jzeV0fdRNi3Ch5bnoXhnyJMmVg2juv9081zLIy3HGPI5mcuGgXM2xRA==", "dev": true, "requires": { "chalk": "^4.0.0", - "diff-sequences": "^26.3.0", + "diff-sequences": "^26.5.0", "jest-get-type": "^26.3.0", - "pretty-format": "^26.4.2" + "pretty-format": "^26.5.2" } }, "jest-get-type": { @@ -4470,12 +4471,12 @@ "dev": true }, "pretty-format": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", - "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", + "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -4490,12 +4491,12 @@ } }, "jest-util": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.3.0.tgz", - "integrity": "sha512-4zpn6bwV0+AMFN0IYhH/wnzIQzRaYVrz1A8sYnRnj4UXDXbOVtWmlaZkO9mipFqZ13okIfN87aDoJWB7VH6hcw==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.5.2.tgz", + "integrity": "sha512-WTL675bK+GSSAYgS8z9FWdCT2nccO1yTIplNLPlP0OD8tUk/H5IrWKMMRudIQQ0qp8bb4k+1Qa8CxGKq9qnYdg==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "@types/node": "*", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", @@ -4504,9 +4505,9 @@ }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -4538,23 +4539,23 @@ } }, "jest-validate": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.4.2.tgz", - "integrity": "sha512-blft+xDX7XXghfhY0mrsBCYhX365n8K5wNDC4XAcNKqqjEzsRUSXP44m6PL0QJEW2crxQFLLztVnJ4j7oPlQrQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.5.2.tgz", + "integrity": "sha512-FmJks0zY36mp6Af/5sqO6CTL9bNMU45yKCJk3hrz8d2aIqQIlN1pr9HPIwZE8blLaewOla134nt5+xAmWsx3SQ==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "camelcase": "^6.0.0", "chalk": "^4.0.0", "jest-get-type": "^26.3.0", "leven": "^3.1.0", - "pretty-format": "^26.4.2" + "pretty-format": "^26.5.2" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -4596,12 +4597,12 @@ "dev": true }, "pretty-format": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz", - "integrity": "sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", + "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.5.2", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -4610,24 +4611,24 @@ } }, "jest-watcher": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.3.0.tgz", - "integrity": "sha512-XnLdKmyCGJ3VoF6G/p5ohbJ04q/vv5aH9ENI+i6BL0uu9WWB6Z7Z2lhQQk0d2AVZcRGp1yW+/TsoToMhBFPRdQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.5.2.tgz", + "integrity": "sha512-i3m1NtWzF+FXfJ3ljLBB/WQEp4uaNhX7QcQUWMokcifFTUQBDFyUMEwk0JkJ1kopHbx7Een3KX0Q7+9koGM/Pw==", "dev": true, "requires": { - "@jest/test-result": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/test-result": "^26.5.2", + "@jest/types": "^26.5.2", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "jest-util": "^26.3.0", + "jest-util": "^26.5.2", "string-length": "^4.0.1" }, "dependencies": { "@jest/types": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz", - "integrity": "sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==", + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -4659,9 +4660,9 @@ } }, "jest-worker": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.3.0.tgz", - "integrity": "sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw==", + "version": "26.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.5.0.tgz", + "integrity": "sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug==", "dev": true, "requires": { "@types/node": "*", @@ -6616,9 +6617,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz", - "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==", + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", "dev": true, "optional": true }, diff --git a/package.json b/package.json index f7e260a9..8daa0812 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1", - "jest": "^26.4.2", + "jest": "^26.5.2", "nock": "^13.0.4" } } From 7c365cc9b34c9173e27b01c77fef7ea87a0c455a Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Wed, 7 Oct 2020 01:05:55 +0200 Subject: [PATCH 74/94] Do not use matrix builds Signed-off-by: Michael Krug --- .github/workflows/codeql-analysis.yml | 2 ++ .github/workflows/test.yml | 9 +++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7fe88f75..fd29e5dc 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -5,6 +5,8 @@ on: branches: - master pull_request: + branches: + - master jobs: analyze: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6e176cba..19faaa63 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,19 +5,16 @@ on: branches: - master pull_request: + branches: + - master jobs: build: runs-on: ubuntu-latest - strategy: - matrix: - node-version: [10.x, 12.x, 14.x] steps: - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} + - name: Use Node.js uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - run: npm ci - run: npm run build --if-present - run: npm test From a3812468bfd0050bf71670dff0f5bdacf2062cad Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Wed, 7 Oct 2020 11:57:54 +0200 Subject: [PATCH 75/94] Use Node 12.x explicitly for tests Signed-off-by: Michael Krug --- .github/workflows/test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 19faaa63..e877c80e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,8 +13,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Use Node.js + - name: Use Node.js 12.x uses: actions/setup-node@v1 + with: + node-version: 12.x - run: npm ci - run: npm run build --if-present - run: npm test From ce2d866c632f1c1db9ea087d76dd7cd4c2995744 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Wed, 30 Dec 2020 19:31:41 +0100 Subject: [PATCH 76/94] Fix two-factor auth Signed-off-by: Michael Krug --- functions/commands/default.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/functions/commands/default.js b/functions/commands/default.js index 53d04c0e..fbbf9493 100644 --- a/functions/commands/default.js +++ b/functions/commands/default.js @@ -61,7 +61,7 @@ class DefaultCommand { * @param {object} challenge */ static handleAuthPin(device, challenge) { - if (!device.customData || !device.customData.pinNeeded || challenge.pin === device.customData.pinNeeded) { + if (!device.customData || !device.customData.pinNeeded || challenge && challenge.pin === device.customData.pinNeeded) { return; } return { @@ -69,7 +69,7 @@ class DefaultCommand { status: 'ERROR', errorCode: 'challengeNeeded', challengeNeeded: { - type: !challenge.pin ? 'pinNeeded' : 'challengeFailedPinNeeded' + type: !challenge || !challenge.pin ? 'pinNeeded' : 'challengeFailedPinNeeded' } }; } @@ -80,7 +80,7 @@ class DefaultCommand { * @param {object} responseStates */ static handleAuthAck(device, challenge, responseStates) { - if (!device.customData || !device.customData.ackNeeded || challenge.ack === true) { + if (!device.customData || !device.customData.ackNeeded || challenge && challenge.ack === true) { return; } return { From 01716ee42aad629ed043ca3b988f2f148c2cf3e4 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Wed, 30 Dec 2020 20:40:10 +0100 Subject: [PATCH 77/94] Adjust test for two-factor auth Signed-off-by: Michael Krug --- tests/commands/default.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/commands/default.test.js b/tests/commands/default.test.js index ad0393c6..bbe62a90 100644 --- a/tests/commands/default.test.js +++ b/tests/commands/default.test.js @@ -58,9 +58,9 @@ describe('Default Command', () => { }); test('handleAuthPin', () => { - expect(Command.handleAuthPin({ "id": "Item", "customData": {} }, {})).toBeUndefined(); + expect(Command.handleAuthPin({ "id": "Item", "customData": {} }, undefined)).toBeUndefined(); expect(Command.handleAuthPin({ "id": "Item", "customData": { "pinNeeded": "1234" } }, { "pin": "1234" })).toBeUndefined(); - expect(Command.handleAuthPin({ "id": "Item", "customData": { "pinNeeded": "1234" } }, {})).toStrictEqual({ + expect(Command.handleAuthPin({ "id": "Item", "customData": { "pinNeeded": "1234" } }, undefined)).toStrictEqual({ ids: ["Item"], status: 'ERROR', errorCode: 'challengeNeeded', @@ -79,8 +79,8 @@ describe('Default Command', () => { }); test('handleAuthAck', () => { - expect(Command.handleAuthAck({ "id": "Item", "customData": {} }, {}, {})).toBeUndefined(); - expect(Command.handleAuthAck({ "id": "Item", "customData": { "ackNeeded": true } }, { "ack": true }, {})).toBeUndefined(); + expect(Command.handleAuthAck({ "id": "Item", "customData": {} }, {}, undefined)).toBeUndefined(); + expect(Command.handleAuthAck({ "id": "Item", "customData": { "ackNeeded": true } }, { "ack": true }, undefined)).toBeUndefined(); expect(Command.handleAuthAck({ "id": "Item", "customData": { "ackNeeded": true } }, {}, { "key": "value" })).toStrictEqual({ ids: ["Item"], status: 'ERROR', From 98c56b20811569b3d0090d923705a16d230d961a Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Fri, 1 Jan 2021 16:25:53 +0100 Subject: [PATCH 78/94] Add useKelvin option for SpecialColorLight Signed-off-by: Michael Krug --- .../commands/colorabsolutetemperature.js | 3 + functions/devices/specialcolorlight.js | 7 +- .../commands/colorabsolutetemperature.test.js | 15 +++ tests/devices/specialcolorlight.test.js | 97 +++++++++++++------ 4 files changed, 93 insertions(+), 29 deletions(-) diff --git a/functions/commands/colorabsolutetemperature.js b/functions/commands/colorabsolutetemperature.js index 167b2503..fd5003df 100644 --- a/functions/commands/colorabsolutetemperature.js +++ b/functions/commands/colorabsolutetemperature.js @@ -31,6 +31,9 @@ class ColorAbsoluteTemperature extends DefaultCommand { static convertParamsToValue(params, item, device) { if (device.customData && device.customData.deviceType === 'SpecialColorLight') { try { + if (SpecialColorLight.useKelvin(item)) { + return params.color.temperature.toString(); + } const { temperatureMinK, temperatureMaxK } = SpecialColorLight.getAttributes(item).colorTemperatureRange; return (100 - ((params.color.temperature - temperatureMinK) / (temperatureMaxK - temperatureMinK) * 100)).toString(); } catch { diff --git a/functions/devices/specialcolorlight.js b/functions/devices/specialcolorlight.js index 8e09c332..987666f6 100644 --- a/functions/devices/specialcolorlight.js +++ b/functions/devices/specialcolorlight.js @@ -45,7 +45,7 @@ class SpecialColorLight extends DefaultDevice { try { const { temperatureMinK, temperatureMaxK } = this.getAttributes(item).colorTemperatureRange; state.color = {}; - state.color.temperatureK = temperatureMinK + ((temperatureMaxK - temperatureMinK) / 100 * (100 - Number(members[member].state)) || 0); + state.color.temperatureK = this.useKelvin(item) ? Number(members[member].state) : temperatureMinK + ((temperatureMaxK - temperatureMinK) / 100 * (100 - Number(members[member].state)) || 0); } catch { } break; } @@ -71,6 +71,11 @@ class SpecialColorLight extends DefaultDevice { } return members; } + + static useKelvin(item) { + const config = this.getConfig(item); + return config.useKelvin === true; + } } module.exports = SpecialColorLight; diff --git a/tests/commands/colorabsolutetemperature.test.js b/tests/commands/colorabsolutetemperature.test.js index fd871d44..d9e67ffe 100644 --- a/tests/commands/colorabsolutetemperature.test.js +++ b/tests/commands/colorabsolutetemperature.test.js @@ -56,6 +56,21 @@ describe('ColorAbsoluteTemperature Command', () => { expect(Command.convertParamsToValue(params, item, device)).toBe("75"); expect(Command.convertParamsToValue(params, { "state": "100,100,50" }, device)).toBe("0"); }); + + test('convertParamsToValue SpecialColorLight Kelvin', () => { + const item = { + "metadata": { + "ga": { + "config": { + "colorTemperatureRange": "1000,5000", + "useKelvin": true + } + } + } + }; + const device = { "customData": { "deviceType": "SpecialColorLight" } }; + expect(Command.convertParamsToValue(params, item, device)).toBe("2000"); + }); }); test('getResponseStates', () => { diff --git a/tests/devices/specialcolorlight.test.js b/tests/devices/specialcolorlight.test.js index 80bf2146..67d2cb4a 100644 --- a/tests/devices/specialcolorlight.test.js +++ b/tests/devices/specialcolorlight.test.js @@ -78,42 +78,83 @@ describe('SpecialColorLight Device', () => { }); }); - test('getState', () => { - const item = { - "type": "Group", - "metadata": { - "ga": { - "value": "LIGHT", - "config": { - "colorTemperatureRange": "1000,4000" + describe('getState', () => { + test('getState', () => { + const item = { + "type": "Group", + "metadata": { + "ga": { + "value": "LIGHT", + "config": { + "colorTemperatureRange": "1000,4000" + } + } + }, + "members": [ + { + "state": "50", + "metadata": { + "ga": { + "value": "lightBrightness" + } + } + }, + { + "state": "20", + "metadata": { + "ga": { + "value": "lightColorTemperature" + } + } } + ] + }; + expect(Device.getState(item)).toStrictEqual({ + "on": true, + "brightness": 50, + "color": { + "temperatureK": 3400 } - }, - "members": [ - { - "state": "50", - "metadata": { - "ga": { - "value": "lightBrightness" + }); + }); + test('getState use kelvin', () => { + const item = { + "type": "Group", + "metadata": { + "ga": { + "value": "LIGHT", + "config": { + "colorTemperatureRange": "1000,4000", + "useKelvin": true } } }, - { - "state": "20", - "metadata": { - "ga": { - "value": "lightColorTemperature" + "members": [ + { + "state": "50", + "metadata": { + "ga": { + "value": "lightBrightness" + } + } + }, + { + "state": "2000", + "metadata": { + "ga": { + "value": "lightColorTemperature" + } } } + ] + }; + expect(Device.getState(item)).toStrictEqual({ + "on": true, + "brightness": 50, + "color": { + "temperatureK": 2000 } - ] - }; - expect(Device.getState(item)).toStrictEqual({ - "on": true, - "brightness": 50, - "color": { - "temperatureK": 3400 - } + }); }); }); }); From 3894cd550d31734e6c9f3eecc7d20c5ff05acd47 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Fri, 1 Jan 2021 16:26:16 +0100 Subject: [PATCH 79/94] Update documentation Signed-off-by: Michael Krug --- README.md | 46 ++++++++++++++++++++++++++++++++++++++++------ docs/USAGE.md | 47 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 81 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 09050d56..2fe7c41d 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,16 @@ Number HK_Basement_Humid "Basement Humidity" (g_HK_Basement_TSTAT) { ga="thermos Currently the following metadata values are supported (also depending on Googles API capabilities): -* `Switch / Dimmer / Color { ga="Light" }` +* `Switch / Dimmer / Color { ga="Light" }` (Depending on the item type controlling power, brightness and color is supported) + +--- + +* `Group { ga="Light" [ useKelvin=true ] }` (Light with separate brightness and color items) +* `Dimmer / Number { ga="lightBrightness" }` as part of Light group +* `Dimmer / Number { ga="lightColorTemperature" }` as part of Light group + +--- + * `Switch { ga="Switch" [ inverted=true ] }` (all Switch items can use the inverted option) * `Switch { ga="Outlet" }` * `Switch { ga="Coffee_Maker" }` @@ -208,12 +217,32 @@ Currently the following metadata values are supported (also depending on Googles * `Switch { ga="Sprinkler" }` * `Switch { ga="Vacuum" }` * `Switch { ga="Scene" }` + +--- + * `Switch / Contact { ga="Lock" [ ackNeeded=true ] }` * `Switch { ga="SecuritySystem" [ pinNeeded="1234" ] }` -* `Dimmer { ga="Speaker" }` +* `String { ga="Camera" [ protocols="hls,dash" ] }` +* `Dimmer { ga="Speaker" }` (Volume control) + +--- + +* `Group { ga="TV" [ volumeDefaultPercentage="20", levelStepSize="10", volumeMaxLevel="100", transportControlSupportedCommands="NEXT,PREVIOUS,PAUSE,RESUME", availableInputs="hdmi1=xbox,hdmi2=settopbox", availableChannels="1=Channel1=NBC,2=Channel2=CBS" ] }` +* `Switch { ga="tvPower" }` as part of TV group (optional) +* `Switch { ga="tvMute" }` as part of TV group (optional) +* `Dimmer { ga="tvVolume" }` as part of TV group (optional) +* `String { ga="tvChannel" }` as part of TV group (optional) +* `String { ga="tvInput" }` as part of TV group (optional) +* `Player { ga="tvTransport" }` as part of TV group (optional) + +--- + * `Switch / Dimmer { ga="Fan" [ speeds="0=away:zero,50=default:standard:one,100=high:two", lang="en", ordered=true ] }` (for Dimmer the options have to be set) * `Switch / Dimmer { ga="Hood" }` * `Switch / Dimmer { ga="AirPurifier" }` + +--- + * `Rollershutter { ga="Awning" [ inverted=true ] }` (all Rollershutter items can use the inverted option) * `Rollershutter { ga="Blinds" }` * `Rollershutter { ga="Curtain" }` @@ -223,15 +252,20 @@ Currently the following metadata values are supported (also depending on Googles * `Rollershutter { ga="Pergola" }` * `Rollershutter { ga="Shutter" }` * `Rollershutter { ga="Window" }` -* `Group { ga="Thermostat" [ modes="...", useFahrenheit=true ] }` + +_\* All Rollershutter devices can also be used with a Switch or Contact item with the limitation of only supporting open and close states._ + +--- + +* `Group { ga="Thermostat" [ modes="...", thermostatTemperatureRange="10,30", useFahrenheit=true ] }` * `Number { ga="thermostatTemperatureAmbient" }` as part of Thermostat group * `Number { ga="thermostatHumidityAmbient" }` as part of Thermostat group * `Number { ga="thermostatTemperatureSetpoint" }` as part of Thermostat group * `Number / String { ga="thermostatMode" }` as part of Thermostat group -* `Number { ga="TemperatureSensor" } [ useFahrenheit=true ] ` -* `String { ga="Camera" [ protocols="hls,dash" ] }` -_\* All Rollershutter devices can also be used with a Switch or Contact item with the limitation of only supporting open and close states._ +--- + +* `Number { ga="TemperatureSensor" } [ useFahrenheit=true ] ` Item labels are not mandatory in openHAB, but for the Google Assistant Action they are absolutely necessary! diff --git a/docs/USAGE.md b/docs/USAGE.md index f478d03d..a95794e0 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -26,7 +26,16 @@ In openHAB 2 items are exposed via [metadata](https://www.openhab.org/docs/confi Currently the following metadata values are supported (also depending on Googles API capabilities): -* `Switch / Dimmer / Color { ga="Light" }` +* `Switch / Dimmer / Color { ga="Light" }` (Depending on the item type controlling power, brightness and color is supported) + +--- + +* `Group { ga="Light" [ useKelvin=true ] }` (Light with separate brightness and color items) +* `Dimmer / Number { ga="lightBrightness" }` as part of Light group +* `Dimmer / Number { ga="lightColorTemperature" }` as part of Light group + +--- + * `Switch { ga="Switch" [ inverted=true ] }` (all Switch items can use the inverted option) * `Switch { ga="Outlet" }` * `Switch { ga="Coffee_Maker" }` @@ -36,12 +45,32 @@ Currently the following metadata values are supported (also depending on Googles * `Switch { ga="Sprinkler" }` * `Switch { ga="Vacuum" }` * `Switch { ga="Scene" }` + +--- + * `Switch / Contact { ga="Lock" [ ackNeeded=true ] }` * `Switch { ga="SecuritySystem" [ pinNeeded="1234" ] }` -* `Dimmer { ga="Speaker" }` +* `String { ga="Camera" [ protocols="hls,dash" ] }` +* `Dimmer { ga="Speaker" }` (Volume control) + +--- + +* `Group { ga="TV" [ volumeDefaultPercentage="20", levelStepSize="10", volumeMaxLevel="100", transportControlSupportedCommands="NEXT,PREVIOUS,PAUSE,RESUME", availableInputs="hdmi1=xbox,hdmi2=settopbox", availableChannels="1=Channel1=NBC,2=Channel2=CBS" ] }` +* `Switch { ga="tvPower" }` as part of TV group (optional) +* `Switch { ga="tvMute" }` as part of TV group (optional) +* `Dimmer { ga="tvVolume" }` as part of TV group (optional) +* `String { ga="tvChannel" }` as part of TV group (optional) +* `String { ga="tvInput" }` as part of TV group (optional) +* `Player { ga="tvTransport" }` as part of TV group (optional) + +--- + * `Switch / Dimmer { ga="Fan" [ speeds="0=away:zero,50=default:standard:one,100=high:two", lang="en", ordered=true ] }` (for Dimmer the options have to be set) * `Switch / Dimmer { ga="Hood" }` * `Switch / Dimmer { ga="AirPurifier" }` + +--- + * `Rollershutter { ga="Awning" [ inverted=true ] }` (all Rollershutter items can use the inverted option) * `Rollershutter { ga="Blinds" }` * `Rollershutter { ga="Curtain" }` @@ -51,15 +80,21 @@ Currently the following metadata values are supported (also depending on Googles * `Rollershutter { ga="Pergola" }` * `Rollershutter { ga="Shutter" }` * `Rollershutter { ga="Window" }` -* `Group { ga="Thermostat" [ modes="...", useFahrenheit=true ] }` + +_\* All Rollershutter devices can also be used with a Switch or Contact item with the limitation of only supporting open and close states._ + +--- + +* `Group { ga="Thermostat" [ modes="...", thermostatTemperatureRange="10,30", useFahrenheit=true ] }` * `Number { ga="thermostatTemperatureAmbient" }` as part of Thermostat group * `Number { ga="thermostatHumidityAmbient" }` as part of Thermostat group * `Number { ga="thermostatTemperatureSetpoint" }` as part of Thermostat group * `Number / String { ga="thermostatMode" }` as part of Thermostat group + +--- + * `Number { ga="TemperatureSensor" } [ useFahrenheit=true ] ` -* `String { ga="Camera" [ protocols="hls,dash" ] }` -_\* All Rollershutter devices can also be used with a Switch or Contact item with the limitation of only supporting open and close states._ Example item configuration: ``` @@ -140,7 +175,7 @@ To set the temperature range your thermostat supports, add the config option `[ There must be at least three items as members of the group: -* (Mandatory) Mode: Number (Zwave THERMOSTAT_MODE Format) or String (off, heat, cool, on, ...). `{ ga="thermostatMode" }` +* (Mandatory) Mode: Number or String (off, heat, cool, on, ...). `{ ga="thermostatMode" }` * (Mandatory) Temperature Ambient: Number. `{ ga="thermostatTemperatureAmbient" }` * (Mandatory) Temperature Setpoint: Number. `{ ga="thermostatTemperatureSetpoint" }` * (Optional) Temperature Setpoint High: Number. `{ ga="thermostatTemperatureSetpointHigh" }` From 7806b39c3c17dc9c3c95da5860e8ab1bfea02926 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Fri, 1 Jan 2021 16:27:11 +0100 Subject: [PATCH 80/94] Consistent naming of useFahrenheit Signed-off-by: Michael Krug --- .../commands/thermostattemperaturesetpoint.js | 2 +- .../commands/thermostattemperaturesetpointhigh.js | 2 +- .../commands/thermostattemperaturesetpointlow.js | 2 +- functions/devices/thermostat.js | 6 +++--- tests/devices/thermostat.test.js | 14 +++++++------- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/functions/commands/thermostattemperaturesetpoint.js b/functions/commands/thermostattemperaturesetpoint.js index 863fb4cc..7733d6a5 100644 --- a/functions/commands/thermostattemperaturesetpoint.js +++ b/functions/commands/thermostattemperaturesetpoint.js @@ -25,7 +25,7 @@ class ThermostatTemperatureSetpoint extends DefaultCommand { static convertParamsToValue(params, item) { let value = params.thermostatTemperatureSetpoint; - if (Thermostat.usesFahrenheit(item)) { + if (Thermostat.useFahrenheit(item)) { value = convertToFahrenheit(value); } return value.toString(); diff --git a/functions/commands/thermostattemperaturesetpointhigh.js b/functions/commands/thermostattemperaturesetpointhigh.js index 71af26df..7941f23b 100644 --- a/functions/commands/thermostattemperaturesetpointhigh.js +++ b/functions/commands/thermostattemperaturesetpointhigh.js @@ -25,7 +25,7 @@ class ThermostatTemperatureSetpointHigh extends DefaultCommand { static convertParamsToValue(params, item) { let value = params.thermostatTemperatureSetpointHigh; - if (Thermostat.usesFahrenheit(item)) { + if (Thermostat.useFahrenheit(item)) { value = convertToFahrenheit(value); } return value.toString(); diff --git a/functions/commands/thermostattemperaturesetpointlow.js b/functions/commands/thermostattemperaturesetpointlow.js index d79202c1..62eec24a 100644 --- a/functions/commands/thermostattemperaturesetpointlow.js +++ b/functions/commands/thermostattemperaturesetpointlow.js @@ -25,7 +25,7 @@ class ThermostatTemperatureSetpointLow extends DefaultCommand { static convertParamsToValue(params, item) { let value = params.thermostatTemperatureSetpointLow; - if (Thermostat.usesFahrenheit(item)) { + if (Thermostat.useFahrenheit(item)) { value = convertToFahrenheit(value); } return value.toString(); diff --git a/functions/devices/thermostat.js b/functions/devices/thermostat.js index c4182e94..471d8ccb 100644 --- a/functions/devices/thermostat.js +++ b/functions/devices/thermostat.js @@ -19,7 +19,7 @@ class Thermostat extends DefaultDevice { static getAttributes(item) { const config = this.getConfig(item); const attributes = { - thermostatTemperatureUnit: this.usesFahrenheit(item) ? 'F' : 'C' + thermostatTemperatureUnit: this.useFahrenheit(item) ? 'F' : 'C' }; if ('thermostatTemperatureRange' in config) { const [min, max] = config.thermostatTemperatureRange.split(',').map(s => parseFloat(s.trim())); @@ -49,7 +49,7 @@ class Thermostat extends DefaultDevice { state[member] = this.translateModeToGoogle(item, members[member].state); } else { state[member] = Number(parseFloat(members[member].state).toFixed(1)); - if (member.indexOf('Temperature') > 0 && this.usesFahrenheit(item)) { + if (member.indexOf('Temperature') > 0 && this.useFahrenheit(item)) { state[member] = convertToCelsius(state[member]); } } @@ -93,7 +93,7 @@ class Thermostat extends DefaultDevice { return members; } - static usesFahrenheit(item) { + static useFahrenheit(item) { const config = this.getConfig(item); return config.thermostatTemperatureUnit === 'F' || config.useFahrenheit === true || this.hasTag(item, 'Fahrenheit'); } diff --git a/tests/devices/thermostat.test.js b/tests/devices/thermostat.test.js index 7bb0c6e1..6f5ba17b 100644 --- a/tests/devices/thermostat.test.js +++ b/tests/devices/thermostat.test.js @@ -28,8 +28,8 @@ describe('Thermostat Device', () => { expect(Device.matchesItemType({ "type": "Group" })).toBe(false); }); - describe('usesFahrenheit', () => { - test('usesFahrenheit unit', () => { + describe('useFahrenheit', () => { + test('useFahrenheit thermostatTemperatureUnit', () => { const item = { "metadata": { "ga": { @@ -39,9 +39,9 @@ describe('Thermostat Device', () => { } } }; - expect(Device.usesFahrenheit(item)).toBe(true); + expect(Device.useFahrenheit(item)).toBe(true); }); - test('usesFahrenheit unit', () => { + test('useFahrenheit useFahrenheit', () => { const item = { "metadata": { "ga": { @@ -51,13 +51,13 @@ describe('Thermostat Device', () => { } } }; - expect(Device.usesFahrenheit(item)).toBe(true); + expect(Device.useFahrenheit(item)).toBe(true); }); - test('usesFahrenheit unit', () => { + test('useFahrenheit tag', () => { const item = { "tags": ["Fahrenheit"] }; - expect(Device.usesFahrenheit(item)).toBe(true); + expect(Device.useFahrenheit(item)).toBe(true); }); }); From 822bd0c4e2217d981d42acb55510da372315cbf0 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Fri, 1 Jan 2021 22:30:02 +0100 Subject: [PATCH 81/94] Refactor commands Signed-off-by: Michael Krug --- functions/commands/activatescene.js | 4 ++-- functions/commands/armdisarm.js | 4 ++-- functions/commands/brightnessabsolute.js | 4 ++-- functions/commands/colorabsolute.js | 4 ++-- .../commands/colorabsolutetemperature.js | 4 ++-- functions/commands/default.js | 22 +++++++++++++++++++ functions/commands/getcamerastream.js | 2 +- functions/commands/lockunlock.js | 6 ++--- functions/commands/mute.js | 10 ++++----- functions/commands/onoff.js | 13 +++++------ functions/commands/openclose.js | 11 +++++----- functions/commands/selectchannel.js | 5 ++--- functions/commands/setvolume.js | 4 ++-- functions/commands/startstop.js | 7 +++--- functions/commands/volumerelative.js | 7 +++--- tests/commands/colorabsolute.test.js | 2 +- 16 files changed, 65 insertions(+), 44 deletions(-) diff --git a/functions/commands/activatescene.js b/functions/commands/activatescene.js index dac738f2..8a1c345e 100644 --- a/functions/commands/activatescene.js +++ b/functions/commands/activatescene.js @@ -9,9 +9,9 @@ class ActivateScene extends DefaultCommand { return (('deactivate' in params) && typeof params.deactivate === 'boolean') || !('deactivate' in params); } - static convertParamsToValue(params, item, device) { + static convertParamsToValue(params, _, device) { let deactivate = params.deactivate; - if (device.customData && device.customData.inverted === true) { + if (this.isInverted(device)) { deactivate = !deactivate; } return !deactivate ? 'ON' : 'OFF'; diff --git a/functions/commands/armdisarm.js b/functions/commands/armdisarm.js index 97c0ea7c..5204200b 100644 --- a/functions/commands/armdisarm.js +++ b/functions/commands/armdisarm.js @@ -9,9 +9,9 @@ class ArmDisarm extends DefaultCommand { return ('arm' in params) && typeof params.arm === 'boolean'; } - static convertParamsToValue(params, item, device) { + static convertParamsToValue(params, _, device) { let arm = params.arm; - if (device.customData && device.customData.inverted === true) { + if (this.isInverted(device)) { arm = !arm; } return arm ? 'ON' : 'OFF'; diff --git a/functions/commands/brightnessabsolute.js b/functions/commands/brightnessabsolute.js index 7307ac6c..b0902309 100644 --- a/functions/commands/brightnessabsolute.js +++ b/functions/commands/brightnessabsolute.js @@ -11,11 +11,11 @@ class BrightnessAbsolute extends DefaultCommand { } static requiresItem(device) { - return !!device.customData && device.customData.deviceType === 'SpecialColorLight'; + return this.getDeviceType(device) === 'SpecialColorLight'; } static getItemName(item, device) { - if (device.customData && device.customData.deviceType === 'SpecialColorLight') { + if (this.getDeviceType(device) === 'SpecialColorLight') { const members = SpecialColorLight.getMembers(item); if ('lightBrightness' in members) { return members.lightBrightness.name; diff --git a/functions/commands/colorabsolute.js b/functions/commands/colorabsolute.js index a967bfce..d3b2289c 100644 --- a/functions/commands/colorabsolute.js +++ b/functions/commands/colorabsolute.js @@ -10,8 +10,8 @@ class ColorAbsolute extends DefaultCommand { ('spectrumHSV' in params.color) && typeof params.color.spectrumHSV === 'object'; } - static convertParamsToValue(params, item, device) { - if (device.customData && device.customData.deviceType !== 'ColorLight') { + static convertParamsToValue(params, _, device) { + if (this.getDeviceType(device) !== 'ColorLight') { throw { statusCode: 400 }; } const hsv = params.color.spectrumHSV; diff --git a/functions/commands/colorabsolutetemperature.js b/functions/commands/colorabsolutetemperature.js index fd5003df..8f3b13a3 100644 --- a/functions/commands/colorabsolutetemperature.js +++ b/functions/commands/colorabsolutetemperature.js @@ -18,7 +18,7 @@ class ColorAbsoluteTemperature extends DefaultCommand { } static getItemName(item, device) { - if (device.customData && device.customData.deviceType === 'SpecialColorLight') { + if (this.getDeviceType(device) === 'SpecialColorLight') { const members = SpecialColorLight.getMembers(item); if ('lightColorTemperature' in members) { return members.lightColorTemperature.name; @@ -29,7 +29,7 @@ class ColorAbsoluteTemperature extends DefaultCommand { } static convertParamsToValue(params, item, device) { - if (device.customData && device.customData.deviceType === 'SpecialColorLight') { + if (this.getDeviceType(device) === 'SpecialColorLight') { try { if (SpecialColorLight.useKelvin(item)) { return params.color.temperature.toString(); diff --git a/functions/commands/default.js b/functions/commands/default.js index fbbf9493..358391ae 100644 --- a/functions/commands/default.js +++ b/functions/commands/default.js @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars */ const ackSupported = [ 'action.devices.commands.ArmDisarm', 'action.devices.commands.Fill', @@ -49,6 +50,27 @@ class DefaultCommand { return item.name; } + /** + * @param {object} device + */ + static getDeviceType(device) { + return device.customData && device.customData.deviceType || ''; + } + + /** + * @param {object} device + */ + static getItemType(device) { + return device.customData && device.customData.itemType || ''; + } + + /** + * @param {object} device + */ + static isInverted(device) { + return device.customData && device.customData.inverted === true; + } + /** * @param {object} device */ diff --git a/functions/commands/getcamerastream.js b/functions/commands/getcamerastream.js index 35c8769d..63658ef6 100644 --- a/functions/commands/getcamerastream.js +++ b/functions/commands/getcamerastream.js @@ -18,7 +18,7 @@ class GetCameraStream extends DefaultCommand { return null; } - static getResponseStates(params, item) { + static getResponseStates(_, item) { return { cameraStreamAccessUrl: item.state }; diff --git a/functions/commands/lockunlock.js b/functions/commands/lockunlock.js index fc29ee5b..81baecbe 100644 --- a/functions/commands/lockunlock.js +++ b/functions/commands/lockunlock.js @@ -9,12 +9,12 @@ class LockUnlock extends DefaultCommand { return ('lock' in params) && typeof params.lock === 'boolean'; } - static convertParamsToValue(params, item, device) { - if (device.customData && device.customData.itemType === 'Contact') { + static convertParamsToValue(params, _, device) { + if (this.getItemType(device) === 'Contact') { throw { statusCode: 400 }; } let lock = params.lock; - if (device.customData && device.customData.inverted === true) { + if (this.isInverted(device)) { lock = !lock; } return lock ? 'ON' : 'OFF'; diff --git a/functions/commands/mute.js b/functions/commands/mute.js index 865e48d6..4bffa70d 100644 --- a/functions/commands/mute.js +++ b/functions/commands/mute.js @@ -11,11 +11,11 @@ class Mute extends DefaultCommand { } static requiresItem(device) { - return !!device.customData && device.customData.deviceType === 'TV'; + return this.getDeviceType(device) === 'TV'; } static getItemName(item, device) { - if (device.customData && device.customData.deviceType === 'TV') { + if (this.getDeviceType(device) === 'TV') { const members = TV.getMembers(item); if ('tvMute' in members) { return members.tvMute.name; @@ -29,8 +29,8 @@ class Mute extends DefaultCommand { } static convertParamsToValue(params, item, device) { - let itemType = device.customData && device.customData.itemType; - if (device.customData && device.customData.deviceType === 'TV') { + let itemType = this.getItemType(device); + if (this.getDeviceType(device) === 'TV') { const members = TV.getMembers(item); if ('tvMute' in members) { itemType = 'Switch'; @@ -42,7 +42,7 @@ class Mute extends DefaultCommand { if (itemType !== 'Switch') { return mute ? '0' : undefined; } - if (device.customData && device.customData.inverted === true) { + if (this.isInverted(device) === true) { mute = !mute; } return mute ? 'ON' : 'OFF'; diff --git a/functions/commands/onoff.js b/functions/commands/onoff.js index 8a49a014..e6812dde 100644 --- a/functions/commands/onoff.js +++ b/functions/commands/onoff.js @@ -12,20 +12,19 @@ class OnOff extends DefaultCommand { } static requiresItem(device) { - return !!device.customData && ( - device.customData.deviceType === 'SpecialColorLight' || device.customData.deviceType === 'TV' - ); + return ['SpecialColorLight', 'TV'].includes(this.getDeviceType(device)); } static getItemName(item, device) { - if (device.customData && device.customData.deviceType === 'SpecialColorLight') { + const deviceType = this.getDeviceType(device); + if (deviceType === 'SpecialColorLight') { const members = SpecialColorLight.getMembers(item); if ('lightBrightness' in members) { return members.lightBrightness.name; } throw { statusCode: 400 }; } - if (device.customData && device.customData.deviceType === 'TV') { + if (deviceType === 'TV') { const members = TV.getMembers(item); if ('tvPower' in members) { return members.tvPower.name; @@ -35,9 +34,9 @@ class OnOff extends DefaultCommand { return item.name; } - static convertParamsToValue(params, item, device) { + static convertParamsToValue(params, _, device) { let on = params.on; - if (device.customData && device.customData.inverted === true) { + if (this.isInverted(device) === true) { on = !on; } return on ? 'ON' : 'OFF'; diff --git a/functions/commands/openclose.js b/functions/commands/openclose.js index 27e2ad50..aaca9bd4 100644 --- a/functions/commands/openclose.js +++ b/functions/commands/openclose.js @@ -9,18 +9,19 @@ class OpenClose extends DefaultCommand { return ('openPercent' in params) && typeof params.openPercent === 'number'; } - static convertParamsToValue(params, item, device) { - if (device.customData && device.customData.itemType === 'Contact') { + static convertParamsToValue(params, _, device) { + const itemType = this.getItemType(device); + if (itemType === 'Contact') { throw { statusCode: 400 }; } let openPercent = params.openPercent; - if (device.customData && device.customData.inverted === true) { + if (this.isInverted(device) === true) { openPercent = 100 - openPercent; } - if (device.customData && device.customData.itemType === 'Rollershutter') { + if (itemType === 'Rollershutter') { return openPercent === 0 ? 'DOWN' : openPercent === 100 ? 'UP' : (100 - openPercent).toString(); } - if (device.customData && device.customData.itemType === 'Switch') { + if (itemType === 'Switch') { return openPercent === 0 ? 'OFF' : 'ON'; } return openPercent.toString(); diff --git a/functions/commands/selectchannel.js b/functions/commands/selectchannel.js index 6d7adc23..e043bc8c 100644 --- a/functions/commands/selectchannel.js +++ b/functions/commands/selectchannel.js @@ -28,7 +28,7 @@ class SelectChannel extends DefaultCommand { if (params.channelNumber) { return params.channelNumber; } - const search = params.channelName || params.channelCode; + const search = params.channelName || params.channelCode; const channelMap = TV.getChannelMap(item); for (const number in channelMap) { if (channelMap[number].includes(search)) { @@ -38,9 +38,8 @@ class SelectChannel extends DefaultCommand { } static getResponseStates(params, item) { - const state = this.convertParamsToValue(params, item); return { - channelNumber: state + channelNumber: this.convertParamsToValue(params, item) }; } } diff --git a/functions/commands/setvolume.js b/functions/commands/setvolume.js index d2e0df40..f74edd58 100644 --- a/functions/commands/setvolume.js +++ b/functions/commands/setvolume.js @@ -11,11 +11,11 @@ class SetVolume extends DefaultCommand { } static requiresItem(device) { - return !!device.customData && device.customData.deviceType === 'TV'; + return this.getDeviceType(device) === 'TV'; } static getItemName(item, device) { - if (device.customData && device.customData.deviceType === 'TV') { + if (this.getDeviceType(device) === 'TV') { const members = TV.getMembers(item); if ('tvVolume' in members) { return members.tvVolume.name; diff --git a/functions/commands/startstop.js b/functions/commands/startstop.js index 92b334f0..ac471b5e 100644 --- a/functions/commands/startstop.js +++ b/functions/commands/startstop.js @@ -9,11 +9,12 @@ class StartStop extends DefaultCommand { return ('start' in params) && typeof params.start === 'boolean'; } - static convertParamsToValue(params, item, device) { - if (device.customData && device.customData.itemType === 'Contact') { + static convertParamsToValue(params, _, device) { + const itemType = this.getItemType(device); + if (itemType === 'Contact') { throw { statusCode: 400 }; } - if (device.customData && device.customData.itemType === 'Rollershutter') { + if (itemType === 'Rollershutter') { return params.start ? 'MOVE' : 'STOP'; } return params.start ? 'ON' : 'OFF'; diff --git a/functions/commands/volumerelative.js b/functions/commands/volumerelative.js index e5e38f21..a6146fbf 100644 --- a/functions/commands/volumerelative.js +++ b/functions/commands/volumerelative.js @@ -15,7 +15,7 @@ class VolumeRelative extends DefaultCommand { } static getItemName(item, device) { - if (device.customData && device.customData.deviceType === 'TV') { + if (this.getDeviceType(device) === 'TV') { const members = TV.getMembers(item); if ('tvVolume' in members) { return members.tvVolume.name; @@ -27,7 +27,7 @@ class VolumeRelative extends DefaultCommand { static convertParamsToValue(params, item, device) { let state = item.state; - if (device.customData && device.customData.deviceType === 'TV') { + if (this.getDeviceType(device) === 'TV') { const members = TV.getMembers(item); if ('tvVolume' in members) { state = members.tvVolume.state; @@ -40,9 +40,8 @@ class VolumeRelative extends DefaultCommand { } static getResponseStates(params, item, device) { - const state = parseInt(this.convertParamsToValue(params, item, device)); return { - currentVolume: state + currentVolume: parseInt(this.convertParamsToValue(params, item, device)) }; } } diff --git a/tests/commands/colorabsolute.test.js b/tests/commands/colorabsolute.test.js index 453da3d9..0e2cae4f 100644 --- a/tests/commands/colorabsolute.test.js +++ b/tests/commands/colorabsolute.test.js @@ -14,7 +14,7 @@ describe('ColorAbsolute Command', () => { }); test('convertParamsToValue', () => { - expect(Command.convertParamsToValue(params, {}, {})).toBe("10,20,30"); + expect(Command.convertParamsToValue(params, {}, { "customData": { "deviceType": "ColorLight" } })).toBe("10,20,30"); expect(() => (Command.convertParamsToValue(params, {}, { "customData": { "deviceType": "Light" } }))).toThrow(); }); From 6c22c93d0365be97ae8c6979f6a9206ce94f5f24 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Fri, 1 Jan 2021 22:31:40 +0100 Subject: [PATCH 82/94] Update dependencies, add linting Signed-off-by: Michael Krug --- .eslintrc | 3 - .eslintrc.json | 24 + .github/workflows/test.yml | 4 +- .gitignore | 4 +- functions/devices/default.js | 1 + functions/utilities.js | 12 +- package-lock.json | 2696 +++++++++++++--------------------- package.json | 15 +- 8 files changed, 1072 insertions(+), 1687 deletions(-) delete mode 100644 .eslintrc create mode 100644 .eslintrc.json diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 4307cd08..00000000 --- a/.eslintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "standard" -} diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..2b561f19 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,24 @@ +{ + "env": { + "es6": true, + "jest": true, + "node": true + }, + "extends": "eslint:recommended", + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parserOptions": { + "ecmaVersion": 2019, + "sourceType": "module" + }, + "rules": { + "no-empty": [ + "error", + { + "allowEmptyCatch": true + } + ] + } +} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e877c80e..4dc37b7b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,8 +18,8 @@ jobs: with: node-version: 12.x - run: npm ci - - run: npm run build --if-present - - run: npm test + - run: npm lint + - run: npm test-ci - name: Upload Test Coverage uses: actions/upload-artifact@v2 with: diff --git a/.gitignore b/.gitignore index 3186c134..900020f1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ /node_modules/ /creds.data /gactions -/functions/node_modules/ \ No newline at end of file +/functions/node_modules/ +/coverage +/.eslintcache diff --git a/functions/devices/default.js b/functions/devices/default.js index b4f96098..393e6224 100644 --- a/functions/devices/default.js +++ b/functions/devices/default.js @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars */ class DefaultDevice { static get type() { return ''; diff --git a/functions/utilities.js b/functions/utilities.js index 48750fd3..bf42f3ee 100644 --- a/functions/utilities.js +++ b/functions/utilities.js @@ -20,19 +20,22 @@ module.exports = { /** - * @param {number} value + * @param {number} value temperature in Fahrenheit + * @returns {number} temperature value converted to Celcius */ convertToCelsius: (value) => { return Number(((value - 32) * 5 / 9).toFixed(1)); }, /** - * @param {number} value + * @param {number} value temperature in Celcius + * @returns {number} temperature value converted to Fahrenheit */ convertToFahrenheit: (value) => { return Math.round(value * 9 / 5 + 32); }, /** - * @param {number} kelvin + * @param {number} kelvin color temperature as Kelvin + * @returns {object} color temperature value converted to RGB */ kelvin2rgb: (kelvin) => { const temp = kelvin / 100; @@ -46,7 +49,8 @@ module.exports = { }; }, /** - * @param {object} rgb + * @param {object} rgb color as RGB + * @returns {object} color value converted to HSV */ rgb2hsv: ({ r, g, b }) => { r = r / 255; diff --git a/package-lock.json b/package-lock.json index 648a8401..3e078456 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,33 +14,32 @@ } }, "@babel/core": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.6.tgz", - "integrity": "sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", + "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.6", - "@babel/helper-module-transforms": "^7.11.0", - "@babel/helpers": "^7.10.4", - "@babel/parser": "^7.11.5", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.11.5", - "@babel/types": "^7.11.5", + "@babel/generator": "^7.12.10", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.10", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.10", + "@babel/types": "^7.12.10", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", "json5": "^2.1.2", "lodash": "^4.17.19", - "resolve": "^1.3.2", "semver": "^5.4.1", "source-map": "^0.5.0" }, "dependencies": { "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -70,12 +69,12 @@ } }, "@babel/generator": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", - "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", + "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", "dev": true, "requires": { - "@babel/types": "^7.11.5", + "@babel/types": "^7.12.11", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -89,65 +88,67 @@ } }, "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", + "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-get-function-arity": "^7.12.10", + "@babel/template": "^7.12.7", + "@babel/types": "^7.12.11" } }, "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", + "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.10" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", - "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", + "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", "dev": true, "requires": { - "@babel/types": "^7.11.0" + "@babel/types": "^7.12.7" } }, "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.5" } }, "@babel/helper-module-transforms": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", - "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", "@babel/template": "^7.10.4", - "@babel/types": "^7.11.0", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", "lodash": "^4.17.19" } }, "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", + "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.10" } }, "@babel/helper-plugin-utils": { @@ -157,34 +158,33 @@ "dev": true }, "@babel/helper-replace-supers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", - "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", + "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-member-expression-to-functions": "^7.12.7", + "@babel/helper-optimise-call-expression": "^7.12.10", + "@babel/traverse": "^7.12.10", + "@babel/types": "^7.12.11" } }, "@babel/helper-simple-access": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", - "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", "dev": true, "requires": { - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.1" } }, "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", + "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", "dev": true, "requires": { - "@babel/types": "^7.11.0" + "@babel/types": "^7.12.11" } }, "@babel/helper-validator-identifier": { @@ -194,14 +194,14 @@ "dev": true }, "@babel/helpers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", - "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", "dev": true, "requires": { "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" } }, "@babel/highlight": { @@ -268,9 +268,9 @@ } }, "@babel/parser": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", - "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", + "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -292,9 +292,9 @@ } }, "@babel/plugin-syntax-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", - "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -372,38 +372,56 @@ "@babel/helper-plugin-utils": "^7.8.0" } }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", + "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", + "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7" } }, "@babel/traverse": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", - "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", + "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.5", - "@babel/types": "^7.11.5", + "@babel/code-frame": "^7.12.11", + "@babel/generator": "^7.12.11", + "@babel/helper-function-name": "^7.12.11", + "@babel/helper-split-export-declaration": "^7.12.11", + "@babel/parser": "^7.12.11", + "@babel/types": "^7.12.12", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" }, "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -418,14 +436,22 @@ } }, "@babel/types": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", - "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", + "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + } } }, "@bcoe/v8-coverage": { @@ -444,6 +470,56 @@ "minimist": "^1.2.0" } }, + "@eslint/eslintrc": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz", + "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -515,273 +591,103 @@ "dev": true }, "@jest/console": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.5.2.tgz", - "integrity": "sha512-lJELzKINpF1v74DXHbCRIkQ/+nUV1M+ntj+X1J8LxCgpmJZjfLmhFejiMSbjjD66fayxl5Z06tbs3HMyuik6rw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", "dev": true, "requires": { - "@jest/types": "^26.5.2", + "@jest/types": "^26.6.2", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^26.5.2", - "jest-util": "^26.5.2", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", "slash": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "@jest/core": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.5.2.tgz", - "integrity": "sha512-LLTo1LQMg7eJjG/+P1NYqFof2B25EV1EqzD5FonklihG4UJKiK2JBIvWonunws6W7e+DhNLoFD+g05tCY03eyA==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", "dev": true, "requires": { - "@jest/console": "^26.5.2", - "@jest/reporters": "^26.5.2", - "@jest/test-result": "^26.5.2", - "@jest/transform": "^26.5.2", - "@jest/types": "^26.5.2", + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.4", - "jest-changed-files": "^26.5.2", - "jest-config": "^26.5.2", - "jest-haste-map": "^26.5.2", - "jest-message-util": "^26.5.2", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.5.2", - "jest-resolve-dependencies": "^26.5.2", - "jest-runner": "^26.5.2", - "jest-runtime": "^26.5.2", - "jest-snapshot": "^26.5.2", - "jest-util": "^26.5.2", - "jest-validate": "^26.5.2", - "jest-watcher": "^26.5.2", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", "micromatch": "^4.0.2", "p-each-series": "^2.1.0", "rimraf": "^3.0.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "@jest/environment": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.5.2.tgz", - "integrity": "sha512-YjhCD/Zhkz0/1vdlS/QN6QmuUdDkpgBdK4SdiVg4Y19e29g4VQYN5Xg8+YuHjdoWGY7wJHMxc79uDTeTOy9Ngw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", "dev": true, "requires": { - "@jest/fake-timers": "^26.5.2", - "@jest/types": "^26.5.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", - "jest-mock": "^26.5.2" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } + "jest-mock": "^26.6.2" } }, "@jest/fake-timers": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.5.2.tgz", - "integrity": "sha512-09Hn5Oraqt36V1akxQeWMVL0fR9c6PnEhpgLaYvREXZJAh2H2Y+QLCsl0g7uMoJeoWJAuz4tozk1prbR1Fc1sw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", "dev": true, "requires": { - "@jest/types": "^26.5.2", + "@jest/types": "^26.6.2", "@sinonjs/fake-timers": "^6.0.1", "@types/node": "*", - "jest-message-util": "^26.5.2", - "jest-mock": "^26.5.2", - "jest-util": "^26.5.2" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" } }, "@jest/globals": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.5.2.tgz", - "integrity": "sha512-9PmnFsAUJxpPt1s/stq02acS1YHliVBDNfAWMe1bwdRr1iTCfhbNt3ERQXrO/ZfZSweftoA26Q/2yhSVSWQ3sw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", "dev": true, "requires": { - "@jest/environment": "^26.5.2", - "@jest/types": "^26.5.2", - "expect": "^26.5.2" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" } }, "@jest/reporters": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.5.2.tgz", - "integrity": "sha512-zvq6Wvy6MmJq/0QY0YfOPb49CXKSf42wkJbrBPkeypVa8I+XDxijvFuywo6TJBX/ILPrdrlE/FW9vJZh6Rf9vA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.5.2", - "@jest/test-result": "^26.5.2", - "@jest/transform": "^26.5.2", - "@jest/types": "^26.5.2", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", @@ -792,56 +698,22 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.5.2", - "jest-resolve": "^26.5.2", - "jest-util": "^26.5.2", - "jest-worker": "^26.5.0", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", "node-notifier": "^8.0.0", "slash": "^3.0.0", "source-map": "^0.6.0", "string-length": "^4.0.1", "terminal-link": "^2.0.0", - "v8-to-istanbul": "^5.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } + "v8-to-istanbul": "^7.0.0" } }, "@jest/source-map": { - "version": "26.5.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.5.0.tgz", - "integrity": "sha512-jWAw9ZwYHJMe9eZq/WrsHlwF8E3hM9gynlcDpOyCb9bR8wEd9ZNBZCi7/jZyzHxC7t3thZ10gO2IDhu0bPKS5g==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -850,131 +722,64 @@ } }, "@jest/test-result": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.5.2.tgz", - "integrity": "sha512-E/Zp6LURJEGSCWpoMGmCFuuEI1OWuI3hmZwmULV0GsgJBh7u0rwqioxhRU95euUuviqBDN8ruX/vP/4bwYolXw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", "dev": true, "requires": { - "@jest/console": "^26.5.2", - "@jest/types": "^26.5.2", + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "@jest/test-sequencer": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.5.2.tgz", - "integrity": "sha512-XmGEh7hh07H2B8mHLFCIgr7gA5Y6Hw1ZATIsbz2fOhpnQ5AnQtZk0gmP0Q5/+mVB2xygO64tVFQxOajzoptkNA==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", "dev": true, "requires": { - "@jest/test-result": "^26.5.2", + "@jest/test-result": "^26.6.2", "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.5.2", - "jest-runner": "^26.5.2", - "jest-runtime": "^26.5.2" + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" } }, "@jest/transform": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.5.2.tgz", - "integrity": "sha512-AUNjvexh+APhhmS8S+KboPz+D3pCxPvEAGduffaAJYxIFxGi/ytZQkrqcKDUU0ERBAo5R7087fyOYr2oms1seg==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^26.5.2", + "@jest/types": "^26.6.2", "babel-plugin-istanbul": "^6.0.0", "chalk": "^4.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.5.2", + "jest-haste-map": "^26.6.2", "jest-regex-util": "^26.0.0", - "jest-util": "^26.5.2", + "jest-util": "^26.6.2", "micromatch": "^4.0.2", "pirates": "^4.0.1", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" + "chalk": "^4.0.0" } }, "@sinonjs/commons": { @@ -996,9 +801,9 @@ } }, "@types/babel__core": { - "version": "7.1.10", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.10.tgz", - "integrity": "sha512-x8OM8XzITIMyiwl5Vmo2B1cR1S1Ipkyv4mdlbJjMa1lmuKvKY9FrBbEANIaMlnWn5Rf7uO+rC/VgYabNkE17Hw==", + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", + "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -1018,9 +823,9 @@ } }, "@types/babel__template": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.3.tgz", - "integrity": "sha512-uCoznIPDmnickEi6D0v11SBpW0OuVqHJCa7syXqQHy5uktSCreIlt0iglsCnmvz8yCb38hGcWeseA8cWJSwv5Q==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", + "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -1028,9 +833,9 @@ } }, "@types/babel__traverse": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.15.tgz", - "integrity": "sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.0.tgz", + "integrity": "sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg==", "dev": true, "requires": { "@babel/types": "^7.3.0" @@ -1043,9 +848,9 @@ "dev": true }, "@types/graceful-fs": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.3.tgz", - "integrity": "sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", + "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", "dev": true, "requires": { "@types/node": "*" @@ -1067,23 +872,22 @@ } }, "@types/istanbul-reports": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "*", "@types/istanbul-lib-report": "*" } }, "@types/jest": { - "version": "26.0.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.14.tgz", - "integrity": "sha512-Hz5q8Vu0D288x3iWXePSn53W7hAjP0H7EQ6QvDO9c7t46mR0lNOLlfuwQ+JkVxuhygHzlzPX+0jKdA3ZgSh+Vg==", + "version": "26.0.19", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.19.tgz", + "integrity": "sha512-jqHoirTG61fee6v6rwbnEuKhpSKih0tuhqeFbCmMmErhtu3BYlOZaXWjffgOstMM4S/3iQD31lI5bGLTrs97yQ==", "dev": true, "requires": { - "jest-diff": "^25.2.1", - "pretty-format": "^25.2.1" + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" } }, "@types/json5": { @@ -1093,9 +897,9 @@ "dev": true }, "@types/node": { - "version": "14.11.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.5.tgz", - "integrity": "sha512-jVFzDV6NTbrLMxm4xDSIW/gKnk8rQLF9wAzLWIOg+5nU6ACrIMndeBdXci0FGtqJbP9tQvm6V39eshc96TO2wQ==", + "version": "14.14.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.19.tgz", + "integrity": "sha512-4nhBPStMK04rruRVtVc6cDqhu7S9GZai0fpXgPXrFpcPX6Xul8xnrjSdGB4KPBVYG/R5+fXWdCM8qBoiULWGPQ==", "dev": true }, "@types/normalize-package-data": { @@ -1105,9 +909,9 @@ "dev": true }, "@types/prettier": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.1.tgz", - "integrity": "sha512-2zs+O+UkDsJ1Vcp667pd3f8xearMdopz/z54i99wtRDI5KLmngk7vlrYZD0ZjKHaROR03EznlBbVY9PfAEyJIQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.6.tgz", + "integrity": "sha512-6gOkRe7OIioWAXfnO/2lFiv+SJichKVSys1mSsgyrYHSEjk8Ctv4tSR/Odvnu+HWlH2C8j53dahU03XmQdd5fA==", "dev": true }, "@types/stack-utils": { @@ -1117,18 +921,18 @@ "dev": true }, "@types/yargs": { - "version": "15.0.7", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.7.tgz", - "integrity": "sha512-Gf4u3EjaPNcC9cTu4/j2oN14nSVhr8PQ+BvBcBQHAhDZfl0bVIiLgvnRXv/dn58XhTm9UXvBpvJpDlwV65QxOA==", + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.12.tgz", + "integrity": "sha512-f+fD/fQAo3BCbCDlrUpznF1A5Zp9rB0noS5vnoormHSIPFKL0Z2DcUJ3Gxp5ytH4uLRNxy7AwYUC9exZzqGMAw==", "dev": true, "requires": { "@types/yargs-parser": "*" } }, "@types/yargs-parser": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", - "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", + "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", "dev": true }, "abab": { @@ -1162,6 +966,12 @@ "acorn-walk": "^7.1.1" } }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true + }, "acorn-walk": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", @@ -1180,6 +990,12 @@ "uri-js": "^4.2.2" } }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, "ansi-escapes": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", @@ -1303,6 +1119,12 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1322,59 +1144,25 @@ "dev": true }, "aws4": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", - "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, "babel-jest": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.5.2.tgz", - "integrity": "sha512-U3KvymF3SczA3vOL/cgiUFOznfMET+XDIXiWnoJV45siAp2pLMG8i2+/MGZlAC3f/F6Q40LR4M4qDrWZ9wkK8A==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", "dev": true, "requires": { - "@jest/transform": "^26.5.2", - "@jest/types": "^26.5.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", "@types/babel__core": "^7.1.7", "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.5.0", + "babel-preset-jest": "^26.6.2", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "slash": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "babel-plugin-istanbul": { @@ -1391,9 +1179,9 @@ } }, "babel-plugin-jest-hoist": { - "version": "26.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.5.0.tgz", - "integrity": "sha512-ck17uZFD3CDfuwCLATWZxkkuGGFhMij8quP8CNhwj8ek1mqFgbFzRJ30xwC04LLscj/aKsVFfRST+b5PT7rSuw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", + "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", "dev": true, "requires": { "@babel/template": "^7.3.3", @@ -1403,9 +1191,9 @@ } }, "babel-preset-current-node-syntax": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.4.tgz", - "integrity": "sha512-5/INNCYhUGqw7VbVjT/hb3ucjgkVHKXY7lX3ZjlN4gm565VyFmJUrJ/h+h16ECVB38R/9SF6aACydpKMLZ/c9w==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", "dev": true, "requires": { "@babel/plugin-syntax-async-generators": "^7.8.4", @@ -1418,17 +1206,18 @@ "@babel/plugin-syntax-numeric-separator": "^7.8.3", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" } }, "babel-preset-jest": { - "version": "26.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.5.0.tgz", - "integrity": "sha512-F2vTluljhqkiGSJGBg/jOruA8vIIIL11YrxRcO7nviNTMbbofPSHwnm8mgP7d/wS7wRSexRoI6X1A6T74d4LQA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", + "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", "dev": true, "requires": { - "babel-plugin-jest-hoist": "^26.5.0", - "babel-preset-current-node-syntax": "^0.1.3" + "babel-plugin-jest-hoist": "^26.6.2", + "babel-preset-current-node-syntax": "^1.0.0" } }, "balanced-match": { @@ -1608,9 +1397,9 @@ "dev": true }, "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -1629,6 +1418,12 @@ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true }, + "cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", + "dev": true + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -1938,9 +1733,9 @@ "dev": true }, "diff-sequences": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz", - "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", "dev": true }, "doctrine": { @@ -1986,9 +1781,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "emittery": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.1.tgz", - "integrity": "sha512-d34LN4L6h18Bzz9xpoku2nPwKxCPlPMr3EEKTkoEBi+1/+b0lcRkRJ1UVyyZaKNeqGR3swcGl6s390DNO4YVgQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", "dev": true }, "emoji-regex": { @@ -2011,6 +1806,15 @@ "once": "^1.4.0" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2074,10 +1878,201 @@ "source-map": "~0.6.1" } }, + "eslint": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.16.0.tgz", + "integrity": "sha512-iVWPS785RuDA4dWuhhgXTNrGxHHK3a8HLSMBgbbU59ruJDubUraXN8N5rn7kb8tG6sjg74eE0RA3YWT51eusEw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.2.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.0", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.4", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "eslint-config-standard": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz", - "integrity": "sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg==", + "version": "16.0.2", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.2.tgz", + "integrity": "sha512-fx3f1rJDsl9bY7qzyX8SAtP8GBSk6MfXFaTfaGgk12aAYW4gJSyRm7dM790L6cbXv63fvjY4XeSzXnb4WM+SKw==", "dev": true }, "eslint-import-resolver-node": { @@ -2171,11 +2166,21 @@ "dev": true }, "eslint-plugin-standard": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", - "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.1.0.tgz", + "integrity": "sha512-ZL7+QRixjTR6/528YNGyDotyffm5OQst/sGxKDwGb9Uqs4In5Egi4+jbobhqJoyoCM6/7v/1A5fhQ7ScMtDjaQ==", "dev": true }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, "eslint-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", @@ -2191,12 +2196,57 @@ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", @@ -2277,57 +2327,17 @@ } }, "expect": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.5.2.tgz", - "integrity": "sha512-ccTGrXZd8DZCcvCz4htGXTkd/LOoy6OEtiDS38x3/VVf6E4AQL0QoeksBiw7BtGR5xDNiRYPB8GN6pfbuTOi7w==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", "dev": true, "requires": { - "@jest/types": "^26.5.2", + "@jest/types": "^26.6.2", "ansi-styles": "^4.0.0", "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.5.2", - "jest-message-util": "^26.5.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", "jest-regex-util": "^26.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - } } }, "express": { @@ -2492,6 +2502,15 @@ "bser": "2.1.1" } }, + "file-entry-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz", + "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2524,6 +2543,22 @@ "locate-path": "^2.0.0" } }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz", + "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", + "dev": true + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -2573,9 +2608,9 @@ "dev": true }, "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz", + "integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==", "dev": true, "optional": true }, @@ -2585,10 +2620,16 @@ "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 + }, "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, "get-caller-file": { @@ -2641,6 +2682,15 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -2813,6 +2863,24 @@ "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "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 + } + } + }, "import-local": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", @@ -2962,6 +3030,15 @@ "ci-info": "^2.0.0" } }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -3020,6 +3097,12 @@ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, + "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 + }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -3032,6 +3115,15 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, "is-negative-zero": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", @@ -3184,9 +3276,9 @@ }, "dependencies": { "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -3211,65 +3303,33 @@ } }, "jest": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest/-/jest-26.5.2.tgz", - "integrity": "sha512-4HFabJVwsgDwul/7rhXJ3yFAF/aUkVIXiJWmgFxb+WMdZG39fVvOwYAs8/3r4AlFPc4m/n5sTMtuMbOL3kNtrQ==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", "dev": true, "requires": { - "@jest/core": "^26.5.2", + "@jest/core": "^26.6.3", "import-local": "^3.0.2", - "jest-cli": "^26.5.2" + "jest-cli": "^26.6.3" }, "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, "jest-cli": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.5.2.tgz", - "integrity": "sha512-usm48COuUvRp8YEG5OWOaxbSM0my7eHn3QeBWxiGUuFhvkGVBvl1fic4UjC02EAEQtDv8KrNQUXdQTV6ZZBsoA==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", "dev": true, "requires": { - "@jest/core": "^26.5.2", - "@jest/test-result": "^26.5.2", - "@jest/types": "^26.5.2", + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.4", "import-local": "^3.0.2", "is-ci": "^2.0.0", - "jest-config": "^26.5.2", - "jest-util": "^26.5.2", - "jest-validate": "^26.5.2", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", "prompts": "^2.0.1", "yargs": "^15.4.1" } @@ -3277,48 +3337,16 @@ } }, "jest-changed-files": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.5.2.tgz", - "integrity": "sha512-qSmssmiIdvM5BWVtyK/nqVpN3spR5YyvkvPqz1x3BR1bwIxsWmU/MGwLoCrPNLbkG2ASAKfvmJpOduEApBPh2w==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", "dev": true, "requires": { - "@jest/types": "^26.5.2", + "@jest/types": "^26.6.2", "execa": "^4.0.0", "throat": "^5.0.0" }, "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3331,9 +3359,9 @@ } }, "execa": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", - "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -3404,93 +3432,41 @@ } }, "jest-config": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.5.2.tgz", - "integrity": "sha512-dqJOnSegNdE5yDiuGHsjTM5gec7Z4AcAMHiW+YscbOYJAlb3LEtDSobXCq0or9EmGQI5SFmKy4T7P1FxetJOfg==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.5.2", - "@jest/types": "^26.5.2", - "babel-jest": "^26.5.2", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", "chalk": "^4.0.0", "deepmerge": "^4.2.2", "glob": "^7.1.1", "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.5.2", - "jest-environment-node": "^26.5.2", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", "jest-get-type": "^26.3.0", - "jest-jasmine2": "^26.5.2", + "jest-jasmine2": "^26.6.3", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.5.2", - "jest-util": "^26.5.2", - "jest-validate": "^26.5.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", "micromatch": "^4.0.2", - "pretty-format": "^26.5.2" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "pretty-format": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", - "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", - "dev": true, - "requires": { - "@jest/types": "^26.5.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - } + "pretty-format": "^26.6.2" } }, "jest-diff": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", - "integrity": "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", "dev": true, "requires": { - "chalk": "^3.0.0", - "diff-sequences": "^25.2.6", - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" } }, "jest-docblock": { @@ -3503,180 +3479,60 @@ } }, "jest-each": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.5.2.tgz", - "integrity": "sha512-w7D9FNe0m2D3yZ0Drj9CLkyF/mGhmBSULMQTypzAKR746xXnjUrK8GUJdlLTWUF6dd0ks3MtvGP7/xNFr9Aphg==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", "dev": true, "requires": { - "@jest/types": "^26.5.2", + "@jest/types": "^26.6.2", "chalk": "^4.0.0", "jest-get-type": "^26.3.0", - "jest-util": "^26.5.2", - "pretty-format": "^26.5.2" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "pretty-format": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", - "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", - "dev": true, - "requires": { - "@jest/types": "^26.5.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - } + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" } }, "jest-environment-jsdom": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.5.2.tgz", - "integrity": "sha512-fWZPx0bluJaTQ36+PmRpvUtUlUFlGGBNyGX1SN3dLUHHMcQ4WseNEzcGGKOw4U5towXgxI4qDoI3vwR18H0RTw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", "dev": true, "requires": { - "@jest/environment": "^26.5.2", - "@jest/fake-timers": "^26.5.2", - "@jest/types": "^26.5.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", - "jest-mock": "^26.5.2", - "jest-util": "^26.5.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", "jsdom": "^16.4.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "jest-environment-node": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.5.2.tgz", - "integrity": "sha512-YHjnDsf/GKFCYMGF1V+6HF7jhY1fcLfLNBDjhAOvFGvt6d8vXvNdJGVM7uTZ2VO/TuIyEFhPGaXMX5j3h7fsrA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", "dev": true, "requires": { - "@jest/environment": "^26.5.2", - "@jest/fake-timers": "^26.5.2", - "@jest/types": "^26.5.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", - "jest-mock": "^26.5.2", - "jest-util": "^26.5.2" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" } }, "jest-get-type": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", - "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", "dev": true }, "jest-haste-map": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.5.2.tgz", - "integrity": "sha512-lJIAVJN3gtO3k4xy+7i2Xjtwh8CfPcH08WYjZpe9xzveDaqGw9fVNCpkYu6M525wKFVkLmyi7ku+DxCAP1lyMA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", "dev": true, "requires": { - "@jest/types": "^26.5.2", + "@jest/types": "^26.6.2", "@types/graceful-fs": "^4.1.2", "@types/node": "*", "anymatch": "^3.0.3", @@ -3684,356 +3540,87 @@ "fsevents": "^2.1.2", "graceful-fs": "^4.2.4", "jest-regex-util": "^26.0.0", - "jest-serializer": "^26.5.0", - "jest-util": "^26.5.2", - "jest-worker": "^26.5.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", "micromatch": "^4.0.2", "sane": "^4.0.3", "walker": "^1.0.7" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "jest-jasmine2": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.5.2.tgz", - "integrity": "sha512-2J+GYcgLVPTkpmvHEj0/IDTIAuyblGNGlyGe4fLfDT2aktEPBYvoxUwFiOmDDxxzuuEAD2uxcYXr0+1Yw4tjFA==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", "dev": true, "requires": { "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.5.2", - "@jest/source-map": "^26.5.0", - "@jest/test-result": "^26.5.2", - "@jest/types": "^26.5.2", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", - "expect": "^26.5.2", + "expect": "^26.6.2", "is-generator-fn": "^2.0.0", - "jest-each": "^26.5.2", - "jest-matcher-utils": "^26.5.2", - "jest-message-util": "^26.5.2", - "jest-runtime": "^26.5.2", - "jest-snapshot": "^26.5.2", - "jest-util": "^26.5.2", - "pretty-format": "^26.5.2", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", "throat": "^5.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "pretty-format": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", - "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", - "dev": true, - "requires": { - "@jest/types": "^26.5.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - } } }, "jest-leak-detector": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.5.2.tgz", - "integrity": "sha512-h7ia3dLzBFItmYERaLPEtEKxy3YlcbcRSjj0XRNJgBEyODuu+3DM2o62kvIFvs3PsaYoIIv+e+nLRI61Dj1CNw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", "dev": true, "requires": { "jest-get-type": "^26.3.0", - "pretty-format": "^26.5.2" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "pretty-format": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", - "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", - "dev": true, - "requires": { - "@jest/types": "^26.5.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - } + "pretty-format": "^26.6.2" } }, "jest-matcher-utils": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.5.2.tgz", - "integrity": "sha512-W9GO9KBIC4gIArsNqDUKsLnhivaqf8MSs6ujO/JDcPIQrmY+aasewweXVET8KdrJ6ADQaUne5UzysvF/RR7JYA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^26.5.2", + "jest-diff": "^26.6.2", "jest-get-type": "^26.3.0", - "pretty-format": "^26.5.2" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "diff-sequences": { - "version": "26.5.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.5.0.tgz", - "integrity": "sha512-ZXx86srb/iYy6jG71k++wBN9P9J05UNQ5hQHQd9MtMPvcqXPx/vKU69jfHV637D00Q2gSgPk2D+jSx3l1lDW/Q==", - "dev": true - }, - "jest-diff": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.5.2.tgz", - "integrity": "sha512-HCSWDUGwsov5oTlGzrRM+UPJI/Dpqi9jzeV0fdRNi3Ch5bnoXhnyJMmVg2juv9081zLIy3HGPI5mcuGgXM2xRA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.5.0", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.5.2" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "pretty-format": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", - "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", - "dev": true, - "requires": { - "@jest/types": "^26.5.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - } + "pretty-format": "^26.6.2" } }, "jest-message-util": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.5.2.tgz", - "integrity": "sha512-Ocp9UYZ5Jl15C5PNsoDiGEk14A4NG0zZKknpWdZGoMzJuGAkVt10e97tnEVMYpk7LnQHZOfuK2j/izLBMcuCZw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.5.2", + "@jest/types": "^26.6.2", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } + "pretty-format": "^26.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" } }, "jest-mock": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.5.2.tgz", - "integrity": "sha512-9SiU4b5PtO51v0MtJwVRqeGEroH66Bnwtq4ARdNP7jNXbpT7+ByeWNAk4NeT/uHfNSVDXEXgQo1XRuwEqS6Rdw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", "dev": true, "requires": { - "@jest/types": "^26.5.2", + "@jest/types": "^26.6.2", "@types/node": "*" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "jest-pnp-resolver": { @@ -4049,53 +3636,21 @@ "dev": true }, "jest-resolve": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.5.2.tgz", - "integrity": "sha512-XsPxojXGRA0CoDD7Vis59ucz2p3cQFU5C+19tz3tLEAlhYKkK77IL0cjYjikY9wXnOaBeEdm1rOgSJjbZWpcZg==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", "dev": true, "requires": { - "@jest/types": "^26.5.2", + "@jest/types": "^26.6.2", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.5.2", + "jest-util": "^26.6.2", "read-pkg-up": "^7.0.1", - "resolve": "^1.17.0", + "resolve": "^1.18.1", "slash": "^3.0.0" }, "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -4189,189 +3744,91 @@ } }, "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, "requires": { + "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } } } }, "jest-resolve-dependencies": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.5.2.tgz", - "integrity": "sha512-LLkc8LuRtxqOx0AtX/Npa2C4I23WcIrwUgNtHYXg4owYF/ZDQShcwBAHjYZIFR06+HpQcZ43+kCTMlQ3aDCYTg==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", "dev": true, "requires": { - "@jest/types": "^26.5.2", + "@jest/types": "^26.6.2", "jest-regex-util": "^26.0.0", - "jest-snapshot": "^26.5.2" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } + "jest-snapshot": "^26.6.2" } }, "jest-runner": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.5.2.tgz", - "integrity": "sha512-GKhYxtSX5+tXZsd2QwfkDqPIj5C2HqOdXLRc2x2qYqWE26OJh17xo58/fN/mLhRkO4y6o60ZVloan7Kk5YA6hg==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", "dev": true, "requires": { - "@jest/console": "^26.5.2", - "@jest/environment": "^26.5.2", - "@jest/test-result": "^26.5.2", - "@jest/types": "^26.5.2", + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.7.1", "exit": "^0.1.2", "graceful-fs": "^4.2.4", - "jest-config": "^26.5.2", + "jest-config": "^26.6.3", "jest-docblock": "^26.0.0", - "jest-haste-map": "^26.5.2", - "jest-leak-detector": "^26.5.2", - "jest-message-util": "^26.5.2", - "jest-resolve": "^26.5.2", - "jest-runtime": "^26.5.2", - "jest-util": "^26.5.2", - "jest-worker": "^26.5.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", "source-map-support": "^0.5.6", "throat": "^5.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "jest-runtime": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.5.2.tgz", - "integrity": "sha512-zArr4DatX/Sn0wswX/AnAuJgmwgAR5rNtrUz36HR8BfMuysHYNq5sDbYHuLC4ICyRdy5ae/KQ+sczxyS9G6Qvw==", - "dev": true, - "requires": { - "@jest/console": "^26.5.2", - "@jest/environment": "^26.5.2", - "@jest/fake-timers": "^26.5.2", - "@jest/globals": "^26.5.2", - "@jest/source-map": "^26.5.0", - "@jest/test-result": "^26.5.2", - "@jest/transform": "^26.5.2", - "@jest/types": "^26.5.2", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", "@types/yargs": "^15.0.0", "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.4", - "jest-config": "^26.5.2", - "jest-haste-map": "^26.5.2", - "jest-message-util": "^26.5.2", - "jest-mock": "^26.5.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.5.2", - "jest-snapshot": "^26.5.2", - "jest-util": "^26.5.2", - "jest-validate": "^26.5.2", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", "slash": "^3.0.0", "strip-bom": "^4.0.0", "yargs": "^15.4.1" }, "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -4381,9 +3838,9 @@ } }, "jest-serializer": { - "version": "26.5.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.5.0.tgz", - "integrity": "sha512-+h3Gf5CDRlSLdgTv7y0vPIAoLgX/SI7T4v6hy+TEXMgYbv+ztzbg5PSN6mUXAT/hXYHvZRWm+MaObVfqkhCGxA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", "dev": true, "requires": { "@types/node": "*", @@ -4391,278 +3848,95 @@ } }, "jest-snapshot": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.5.2.tgz", - "integrity": "sha512-MkXIDvEefzDubI/WaDVSRH4xnkuirP/Pz8LhAIDXcVQTmcEfwxywj5LGwBmhz+kAAIldA7XM4l96vbpzltSjqg==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", "dev": true, "requires": { "@babel/types": "^7.0.0", - "@jest/types": "^26.5.2", + "@jest/types": "^26.6.2", "@types/babel__traverse": "^7.0.4", "@types/prettier": "^2.0.0", "chalk": "^4.0.0", - "expect": "^26.5.2", + "expect": "^26.6.2", "graceful-fs": "^4.2.4", - "jest-diff": "^26.5.2", + "jest-diff": "^26.6.2", "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.5.2", - "jest-matcher-utils": "^26.5.2", - "jest-message-util": "^26.5.2", - "jest-resolve": "^26.5.2", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", "natural-compare": "^1.4.0", - "pretty-format": "^26.5.2", + "pretty-format": "^26.6.2", "semver": "^7.3.2" }, "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "diff-sequences": { - "version": "26.5.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.5.0.tgz", - "integrity": "sha512-ZXx86srb/iYy6jG71k++wBN9P9J05UNQ5hQHQd9MtMPvcqXPx/vKU69jfHV637D00Q2gSgPk2D+jSx3l1lDW/Q==", - "dev": true - }, - "jest-diff": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.5.2.tgz", - "integrity": "sha512-HCSWDUGwsov5oTlGzrRM+UPJI/Dpqi9jzeV0fdRNi3Ch5bnoXhnyJMmVg2juv9081zLIy3HGPI5mcuGgXM2xRA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.5.0", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.5.2" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "pretty-format": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", - "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, "requires": { - "@jest/types": "^26.5.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" + "lru-cache": "^6.0.0" } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true } } }, "jest-util": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.5.2.tgz", - "integrity": "sha512-WTL675bK+GSSAYgS8z9FWdCT2nccO1yTIplNLPlP0OD8tUk/H5IrWKMMRudIQQ0qp8bb4k+1Qa8CxGKq9qnYdg==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", "dev": true, "requires": { - "@jest/types": "^26.5.2", + "@jest/types": "^26.6.2", "@types/node": "*", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "is-ci": "^2.0.0", "micromatch": "^4.0.2" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "jest-validate": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.5.2.tgz", - "integrity": "sha512-FmJks0zY36mp6Af/5sqO6CTL9bNMU45yKCJk3hrz8d2aIqQIlN1pr9HPIwZE8blLaewOla134nt5+xAmWsx3SQ==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", "dev": true, "requires": { - "@jest/types": "^26.5.2", + "@jest/types": "^26.6.2", "camelcase": "^6.0.0", "chalk": "^4.0.0", "jest-get-type": "^26.3.0", "leven": "^3.1.0", - "pretty-format": "^26.5.2" + "pretty-format": "^26.6.2" }, "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, "camelcase": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz", - "integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==", - "dev": true - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true - }, - "pretty-format": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", - "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", - "dev": true, - "requires": { - "@jest/types": "^26.5.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } } } }, "jest-watcher": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.5.2.tgz", - "integrity": "sha512-i3m1NtWzF+FXfJ3ljLBB/WQEp4uaNhX7QcQUWMokcifFTUQBDFyUMEwk0JkJ1kopHbx7Een3KX0Q7+9koGM/Pw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", "dev": true, "requires": { - "@jest/test-result": "^26.5.2", - "@jest/types": "^26.5.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "jest-util": "^26.5.2", + "jest-util": "^26.6.2", "string-length": "^4.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", - "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "jest-worker": { - "version": "26.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.5.0.tgz", - "integrity": "sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", "dev": true, "requires": { "@types/node": "*", @@ -4750,6 +4024,12 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -4851,6 +4131,15 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -5025,9 +4314,9 @@ "dev": true }, "nock": { - "version": "13.0.4", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.4.tgz", - "integrity": "sha512-alqTV8Qt7TUbc74x1pKRLSENzfjp4nywovcJgi/1aXDiUxXdt7TkruSTF5MDWPP7UoPVgea4F9ghVdmX0xxnSA==", + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.5.tgz", + "integrity": "sha512-1ILZl0zfFm2G4TIeJFW0iHknxr2NyA+aGCMTjDVUsBY4CkMRispF1pfIYkTRdAR/3Bg+UzdEuK0B6HczMQZcCg==", "dev": true, "requires": { "debug": "^4.1.0", @@ -5037,9 +4326,9 @@ }, "dependencies": { "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -5066,9 +4355,9 @@ "dev": true }, "node-notifier": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.0.tgz", - "integrity": "sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", + "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", "dev": true, "optional": true, "requires": { @@ -5081,11 +4370,14 @@ }, "dependencies": { "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, - "optional": true + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "which": { "version": "2.0.2", @@ -5286,9 +4578,9 @@ } }, "p-each-series": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", - "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", "dev": true }, "p-finally": { @@ -5321,6 +4613,15 @@ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -5434,25 +4735,31 @@ "dev": true }, "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", "dev": true, "requires": { - "@jest/types": "^25.5.0", + "@jest/types": "^26.6.2", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" + "react-is": "^17.0.1" } }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "prompts": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz", - "integrity": "sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", + "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", "dev": true, "requires": { "kleur": "^3.0.3", - "sisteransi": "^1.0.4" + "sisteransi": "^1.0.5" } }, "propagate": { @@ -5514,9 +4821,9 @@ } }, "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", "dev": true }, "read-pkg": { @@ -6008,6 +5315,17 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -6221,9 +5539,9 @@ } }, "stack-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.2.tgz", - "integrity": "sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", "dev": true, "requires": { "escape-string-regexp": "^2.0.0" @@ -6337,6 +5655,12 @@ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -6362,6 +5686,18 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "table": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.4.tgz", + "integrity": "sha512-sBT4xRLdALd+NFBvwOz8bw4b15htyythha+q+DVZqy2RS08PPC8O2sZFgJYEY7bJvbCFKccs+WIZ/cd+xxTWCw==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "lodash": "^4.17.20", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0" + } + }, "terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -6383,6 +5719,12 @@ "minimatch": "^3.0.4" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", @@ -6617,16 +5959,22 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "optional": true }, + "v8-compile-cache": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "dev": true + }, "v8-to-istanbul": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-5.0.1.tgz", - "integrity": "sha512-mbDNjuDajqYe3TXFk5qxcQy8L1msXNE37WTlLoqqpBfRsimbNcrlhQlDPntmECEcUvdC+AQ8CyMMf6EUx1r74Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.0.tgz", + "integrity": "sha512-uXUVqNUCLa0AH1vuVxzi+MI4RfxEOKt9pBgKwHbgH7st8Kv2P1m+jvWNnektzBh5QShF3ODgKmUFCf38LnVz1g==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.1", @@ -6717,9 +6065,9 @@ "dev": true }, "whatwg-url": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.3.0.tgz", - "integrity": "sha512-BQRf/ej5Rp3+n7k0grQXZj9a1cHtsp4lqj01p59xBWFKdezR8sO37XnpafwNqiFac/v2Il12EIMjX/Y4VZtT8Q==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", + "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", "dev": true, "requires": { "lodash.sortby": "^4.7.0", @@ -6778,9 +6126,9 @@ } }, "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==", + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", + "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==", "dev": true }, "xml-name-validator": { @@ -6796,9 +6144,15 @@ "dev": true }, "y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, "yargs": { diff --git a/package.json b/package.json index 8daa0812..d00114f9 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,9 @@ ], "scripts": { "start": "node testServer.js", - "test": "jest --silent --coverage" + "test": "jest --silent --coverage", + "test-ci": "npm test --ci", + "lint": "eslint --ext .js --cache ." }, "license": "EPL-2.0", "bugs": { @@ -25,13 +27,14 @@ "express": "^4.17.1" }, "devDependencies": { - "@types/jest": "^26.0.14", - "eslint-config-standard": "^14.1.1", + "@types/jest": "^26.0.19", + "eslint": "^7.16.0", + "eslint-config-standard": "^16.0.2", "eslint-plugin-import": "^2.22.1", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.2.1", - "eslint-plugin-standard": "^4.0.1", - "jest": "^26.5.2", - "nock": "^13.0.4" + "eslint-plugin-standard": "^4.1.0", + "jest": "^26.6.3", + "nock": "^13.0.5" } } From c93725b6f10e955a6ac437997239c6e9e5e551e1 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sat, 7 Nov 2020 00:05:47 +0100 Subject: [PATCH 83/94] Adjust thermostat modes to use array Signed-off-by: Michael Krug --- functions/devices/thermostat.js | 2 +- tests/devices/thermostat.test.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/functions/devices/thermostat.js b/functions/devices/thermostat.js index 471d8ccb..645c7f12 100644 --- a/functions/devices/thermostat.js +++ b/functions/devices/thermostat.js @@ -36,7 +36,7 @@ class Thermostat extends DefaultDevice { !('thermostatTemperatureSetpoint' in members)) { attributes.queryOnlyTemperatureSetting = true; } else { - attributes.availableThermostatModes = Object.keys(this.getModeMap(item)).join(','); + attributes.availableThermostatModes = Object.keys(this.getModeMap(item)); } return attributes; } diff --git a/tests/devices/thermostat.test.js b/tests/devices/thermostat.test.js index 6f5ba17b..1700999c 100644 --- a/tests/devices/thermostat.test.js +++ b/tests/devices/thermostat.test.js @@ -71,7 +71,7 @@ describe('Thermostat Device', () => { } }; expect(Device.getAttributes(item)).toStrictEqual({ - "availableThermostatModes": "off,heat,cool,on,heatcool,auto,eco", + "availableThermostatModes": ["off", "heat", "cool", "on", "heatcool", "auto", "eco"], "thermostatTemperatureUnit": "C" }); }); @@ -88,7 +88,7 @@ describe('Thermostat Device', () => { } }; expect(Device.getAttributes(item)).toStrictEqual({ - "availableThermostatModes": "on,off", + "availableThermostatModes": ["on", "off"], "thermostatTemperatureUnit": "F" }); }); @@ -104,7 +104,7 @@ describe('Thermostat Device', () => { } }; expect(Device.getAttributes(item)).toStrictEqual({ - "availableThermostatModes": "off,heat,cool,on,heatcool,auto,eco", + "availableThermostatModes": ["off", "heat", "cool", "on", "heatcool", "auto", "eco"], "thermostatTemperatureUnit": "C", "thermostatTemperatureRange": { "maxThresholdCelsius": 30, @@ -124,7 +124,7 @@ describe('Thermostat Device', () => { } }; expect(Device.getAttributes(item)).toStrictEqual({ - "availableThermostatModes": "off,heat,cool,on,heatcool,auto,eco", + "availableThermostatModes": ["off", "heat", "cool", "on", "heatcool", "auto", "eco"], "thermostatTemperatureUnit": "C" }); }); From a0c58d53c1211f30859e2d8b7fd6fbc41017ed49 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Fri, 1 Jan 2021 23:00:06 +0100 Subject: [PATCH 84/94] Fix test workflow Signed-off-by: Michael Krug --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4dc37b7b..1039cfd9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,7 @@ on: - master jobs: - build: + unit-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -18,8 +18,8 @@ jobs: with: node-version: 12.x - run: npm ci - - run: npm lint - - run: npm test-ci + - run: npm run lint + - run: npm run test-ci - name: Upload Test Coverage uses: actions/upload-artifact@v2 with: From e1340eb14ce34d80aa80f5e177f8449c82c4e03d Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Fri, 1 Jan 2021 23:09:37 +0100 Subject: [PATCH 85/94] Udate codeql workflow Signed-off-by: Michael Krug --- .github/workflows/codeql-analysis.yml | 61 ++++++--------------------- 1 file changed, 13 insertions(+), 48 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index fd29e5dc..116f5dfd 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -2,11 +2,10 @@ name: CodeQL on: push: - branches: - - master + branches: [ master ] pull_request: - branches: - - master + # The branches below must be a subset of the branches above + branches: [ master ] jobs: analyze: @@ -16,50 +15,16 @@ jobs: strategy: fail-fast: false matrix: - # Override automatic language detection by changing the below list - # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ["javascript"] - # Learn more... - # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection - + language: [ 'javascript' ] steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + - name: Checkout repository + uses: actions/checkout@v2 - #- run: | - # make bootstrap - # make release + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 4f1ca5a8932c052aeffbd912f0bcc5c81506bdf2 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Fri, 1 Jan 2021 23:27:00 +0100 Subject: [PATCH 86/94] Update workflows Signed-off-by: Michael Krug --- .github/workflows/codeql-analysis.yml | 3 +- .github/workflows/test.yml | 54 ++++++++++++++++----------- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 116f5dfd..981fac56 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -4,7 +4,6 @@ on: push: branches: [ master ] pull_request: - # The branches below must be a subset of the branches above branches: [ master ] jobs: @@ -16,11 +15,11 @@ jobs: fail-fast: false matrix: language: [ 'javascript' ] + steps: - name: Checkout repository uses: actions/checkout@v2 - # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1039cfd9..023ae369 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,31 +2,41 @@ name: Tests on: push: - branches: - - master + branches: [ master ] pull_request: - branches: - - master + branches: [ master ] jobs: unit-tests: + name: Unit-Tests runs-on: ubuntu-latest + steps: - - uses: actions/checkout@v2 - - name: Use Node.js 12.x - uses: actions/setup-node@v1 - with: - node-version: 12.x - - run: npm ci - - run: npm run lint - - run: npm run test-ci - - name: Upload Test Coverage - uses: actions/upload-artifact@v2 - with: - name: coverage - path: coverage/ - - name: Comment Test Coverage - if: ${{ github.event_name == 'pull_request' }} - uses: romeovs/lcov-reporter-action@v0.2.11 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Use Node.js 12.x + uses: actions/setup-node@v1 + with: + node-version: 12.x + + - name: Install dependencies + run: npm ci + + - name: Run linting + run: npm run lint + + - name: Run tests + run: npm run test-ci + + - name: Upload Test Coverage + uses: actions/upload-artifact@v2 + with: + name: coverage + path: coverage/ + + - name: Comment Test Coverage + if: ${{ github.event_name == 'pull_request' }} + uses: romeovs/lcov-reporter-action@v0.2.11 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} From 4fa68613f4fd722dc40dc42f1103d3a7ee2d5bf9 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 4 Feb 2021 17:36:40 +0100 Subject: [PATCH 87/94] Update documentation Applied markdown lint Signed-off-by: Michael Krug --- README.md | 121 +++++++++++++++++++++++++++----------------------- docs/USAGE.md | 86 ++++++++++++++++++----------------- 2 files changed, 112 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index 2fe7c41d..db7d5150 100644 --- a/README.md +++ b/README.md @@ -7,21 +7,23 @@ openHAB Google Assistant is based on [Google Cloud Function](https://cloud.googl Google Home Graph: The Google related parts of any Smart Home action rely on Google Home Graph, a database that stores and provides contextual data about the home and its devices. For example, Home Graph can store the concept of a living room that contains multiple types of devices (a light, television, and speaker) from different manufacturers. This information is passed to the Google Assistant in order to execute user requests based on the appropriate context. -# General Instructions +## General Instructions -## Requirements +### Requirements * Google account with "Actions on Google" and "Google Cloud Functions" access * openHAB server that a Google Cloud service endpoint can access -## Google Cloud Functions +### Google Cloud Functions * Enable the Cloud Functions API and install the Google Cloud SDK by following this [quickstart](https://cloud.google.com/functions/docs/quickstart) * gactions CLI (https://developers.google.com/actions/tools/gactions-cli) -``` + +```console curl -O https://dl.google.com/gactions/updates/bin/linux/amd64/gactions/gactions chmod +x gactions ``` + * Modify `functions/config.js` 1. Change `host` to point to your openHAB Cloud instance, for example: `openhab.myserver.com`. Do not include `https`, if you do you'll get DNS errors. 1. Change `path` to the rest API. Defaults to `/rest/items/`. @@ -35,7 +37,7 @@ Deploy the `openhabGoogleAssistant` (openHAB home automation) function: Keep the address somewhere, you'll need it (something like `https://us-central1-.cloudfunctions.net/openhabGoogleAssistant`). -## Create OAuth Credentials +### Create OAuth Credentials You'll need to create OAuth credentials to enable API access. @@ -47,19 +49,19 @@ See [The Client ID and Secret - OAuth](https://www.oauth.com/oauth2-servers/clie 1. Create a client secret (sufficiently random private secret, e.g. minimum 32 char random string) * You'll need these in the next steps. -## Setup your Database +### Setup your Database * SSH into to your openHAB Cloud instance * Open the MongoDB client `mongo` and enter these commands -``` +```console use openhab db.oauth2clients.insert({ clientId: "", clientSecret: ""}) db.oauth2scopes.insert({ name: "any"}) db.oauth2scopes.insert( { name : "google-assistant", description: "Access to openHAB Cloud specific API for Actions on Google Assistant", } ) ``` -## Actions on Google +### Actions on Google Actions on Google is Google's platform for developers to extend Google Assistant. Here you need to develop your actions to engage users on Google Home, Pixel, and other surfaces where the Google Assistant is available. @@ -69,21 +71,21 @@ Here you need to develop your actions to engage users on Google Home, Pixel, and 1. Select "Smart Home Actions". The fulfilment URL is the one saves from the `glcoud beta functions` you saved earlier. 1. Fill out all the App information. Feel free to use fake data and images, you're not actually going to submit this. 1. Move on to Account linking. - * Select Authorization Code - * Enter the client ID and client secret from the OAuth Credentials you created earlier - * Authorization URL should be something like: `https://openhab.myserver.com/oauth2/authorize` - * Token URL should be something like `https://openhab.myserver.com/oauth2/token` - * Set the scope to `google-assistant`. This links to the records that you have inserted into the MongoDB table `oauth2scopes` in [Setup your Database](#setup-your-database). - * Testing instructions: "None" + * Select Authorization Code + * Enter the client ID and client secret from the OAuth Credentials you created earlier + * Authorization URL should be something like: `https://openhab.myserver.com/oauth2/authorize` + * Token URL should be something like `https://openhab.myserver.com/oauth2/token` + * Set the scope to `google-assistant`. This links to the records that you have inserted into the MongoDB table `oauth2scopes` in [Setup your Database](#setup-your-database). + * Testing instructions: "None" 1. Hit save. You're not actually going to submit this for testing, we just need to set it up so we can deploy it later. -## Deploy your action +### Deploy your action When you ask your assistant to “Turn on the light”, it will use the auth bearer Token and call the specified endpoint. To specify which endpoint the Google Assistant should call, you need to create an action.json similar to the one below, with your endpoint URL. * Update the `openhab-google-assistant/action.json` file and specify the Google Cloud Functions endpoint. This is not your server, this is the endpoint given to you from the call to `gcloud beta functions` -``` +```json { "actions": [{ "name": "actions.devices", @@ -102,8 +104,10 @@ When you ask your assistant to “Turn on the light”, it will use the auth bea } } ``` + If you want to deploy your action in a foreign language, add locale parameter to the top of the action.js like : -``` + +```json { "locale": "fr", "actions": [{ @@ -114,7 +118,7 @@ If you want to deploy your action in a foreign language, add locale parameter to * Afterwards deploy this action file using the following command: -``` +```console gactions update --action_package action.json --project ``` @@ -123,13 +127,14 @@ This web service will receive parameters (intents) from Google and will query/mo * You need to Add "App information”, including name and account linking details to the Actions Console * Afterwards please run the following command in the gaction CLI: -``` + +```shell gactions test --action_package action.json --project ``` Note: Anytime you make changes to the settings to your Action on the _Actions By Google_ interface, you'll need to repeat this step. -## Testing & Usage on Google App +### Testing & Usage on Google App * Make sure Google Play Services is up to date * Visit "Google" app entry in Google Play Store on Android @@ -146,22 +151,25 @@ If it didn't work, try the workaround below. To resync changes in the metadata or other openHAB configuration, tell Google Home to `sync my devices`. In a few seconds any changes will appear. -## Workarounds +### Workarounds -### Scope issues +#### Scope issues If you're getting error messages about an unknown scope, first check you've updated the MongoDB correctly in the [Setup your Database](#setup-your-database) step. If you still have issues, you can try this: * SSH into to your openHAB Cloud instance * Edit the file routes/oauth2.js: 1. Comment out line 121: `scope = req.oauth2.req.scope;` and insert the following line above it: `scope = 'any';` - ``` + + ```js //scope = req.oauth2.req.scope; - scope = 'any' + scope = 'any'; ``` - * Restart your server and attempt to authorize again. -### Using a different Google account +* Restart your server and attempt to authorize again. + +#### Using a different Google account + In some cases, you may wish to have your `Google Cloud Function` and `Actions On Google` configured on a different Google account than the one running on your Google Home (eg. you have a work account for GCP services and payments, a home account for assistant). This configuration is still possible, but you need to make some permissions changes. Follow the same process above to setup the function and action, using your _work@gmail.com_ account. By default, when you go to add OpenHAB to the Google Home app using your _home@gmail.com_ account, your `[test] open hab` service will NOT be available to select. @@ -176,11 +184,11 @@ To fix: Return back to the Google Home app and try to add the OpenHAB service again. You should now be able to see `[test] open hab` and add it successfully. -## Item configuration +### Item configuration In openHAB items are exposed using metadata in the namespace `ga`: -``` +```js Switch KitchenLights "Kitchen Lights" (gKitchen) { ga="Switch" } Dimmer BedroomLights "Bedroom Lights" (gBedroom) { ga="Light" } Color LivingroomLights "Livingroom Lights" (gLivingroom) { ga="Light" } @@ -202,7 +210,7 @@ Currently the following metadata values are supported (also depending on Googles --- -* `Group { ga="Light" [ useKelvin=true ] }` (Light with separate brightness and color items) +* `Group { ga="Light" [ colorTemperatureRange="2000,9000", useKelvin=true ] }` (Light with separate brightness and color items) * `Dimmer / Number { ga="lightBrightness" }` as part of Light group * `Dimmer / Number { ga="lightColorTemperature" }` as part of Light group @@ -265,7 +273,7 @@ _\* All Rollershutter devices can also be used with a Switch or Contact item wit --- -* `Number { ga="TemperatureSensor" } [ useFahrenheit=true ] ` +* `Number { ga="TemperatureSensor" } [ useFahrenheit=true ]` Item labels are not mandatory in openHAB, but for the Google Assistant Action they are absolutely necessary! @@ -278,25 +286,29 @@ Furthermore, you can state synonyms for the device name: `Switch KitchenLight "K To ease setting up new devices you can add a room hint: `[ roomHint="Living Room" ]`. For devices supporting the OpenClose trait, the attributes `[ discreteOnlyOpenClose=false, queryOnlyOpenClose=false ]` can be configured. -- discreteOnlyOpenClose defaults to false. When set to true, this indicates that the device must either be fully open or fully closed (that is, it does not support values between 0% and 100%). An example of such a device may be a valve. -- queryOnlyOpenClose defaults to false. Is set to true for `Contact` items. Indicates if the device can only be queried for state information and cannot be controlled. Sensors that can only report open state should set this field to true. + +* discreteOnlyOpenClose defaults to false. When set to true, this indicates that the device must either be fully open or fully closed (that is, it does not support values between 0% and 100%). An example of such a device may be a valve. +* queryOnlyOpenClose defaults to false. Is set to true for `Contact` items. Indicates if the device can only be queried for state information and cannot be controlled. Sensors that can only report open state should set this field to true. --- NOTE: metadata is not (yet?) available via paperUI. Either you create your items via ".items" files, or you can: -- add metadata via console: - ``` - smarthome:metadata add BedroomLights ga Light - ``` -- add metadata using the REST API: - ``` - PUT /rest/items/BedroomLights/metadata/ga +* add metadata via console: + + ```console + smarthome:metadata add BedroomLights ga Light + ``` + +* add metadata using the REST API: + + ```js + PUT /rest/items/BedroomLights/metadata/ga - { - "value": "Light" - } - ``` + { + "value": "Light" + } + ``` NOTE: Please be aware that for backward compatibilty also the former usage of tags (ref. [Google Assistant Action Documentation v2.5](https://www.openhab.org/v2.5/docs/ecosystem/google-assistant/)) to specify items to be exposed to Google Assistent is supported and may cause unexpected behavior. Items that contain tags that refer to a valid Google Assistent device will be exposed regardless of having metadata set. E.g.: `Switch MyBulb ["Lighting"]`. @@ -315,7 +327,7 @@ _pinNeeded_: "A two-factor authentication that requires a personal identificatio Example: -``` +```js Switch DoorLock "Front Door" { ga="Lock" [ ackNeeded=true ] } Switch HouseAlarm "House Alarm" { ga="SecuritySystem" [ pinNeeded="1234" ] } ``` @@ -374,21 +386,21 @@ More details about the setup and the service linkage (https://myopenhab.org) pro Here are some example voice commands: - * Turn on Office Lights - * Dim/Brighten Office Lights (increments 15%) - * Set Office Lights to 35% - * Open/Close the blinds - * Turn off Pool Waterfall - * Turn on House Fan - * Turn on Home Theater Scene - * Set Basement Thermostat to 15 degrees - * What is the current Basement Thermostat Temperature? +* Turn on Office Lights +* Dim/Brighten Office Lights (increments 15%) +* Set Office Lights to 35% +* Open/Close the blinds +* Turn off Pool Waterfall +* Turn on House Fan +* Turn on Home Theater Scene +* Set Basement Thermostat to 15 degrees +* What is the current Basement Thermostat Temperature? ## Logging & Debugging To check your deployed openHAB Google Cloud function app logs and debugging use the following command: -``` +```console gcloud beta functions logs read openhabGoogleAssistant ``` @@ -396,7 +408,6 @@ gcloud beta functions logs read openhabGoogleAssistant * Sometimes the Account Linkage needs to be done twice and repeated * Google Assistant does not respond to querying the current brightness of an item -* More Unit Test & Integration Test will be added soon ## References diff --git a/docs/USAGE.md b/docs/USAGE.md index a95794e0..06ea8457 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -13,15 +13,16 @@ This guide describes step by step how to use the [openHAB Google Assistant Smart With the Action you can voice control your openHAB items and it supports lights, plugs, switches, thermostats and many more. The openHAB Action comes with multiple language support like English, German or French language. -# General Configuration Instructions +## General Configuration Instructions -## Requirements +### Requirements * [openHAB Cloud Connector](http://docs.openhab.org/addons/ios/openhabcloud/readme.html) configured using myopenHAB.org. (Items DO NOT need to be exposed to and will not show up on myopenHAB.org, this is only needed for the IFTTT service!) * Google account. * Google Home, Google Home mini, Google Nest or the Google Assistant on your phone. -## Item configuration +### Item configuration + In openHAB 2 items are exposed via [metadata](https://www.openhab.org/docs/configuration/items.html#item-definition-and-syntax). Currently the following metadata values are supported (also depending on Googles API capabilities): @@ -30,7 +31,7 @@ Currently the following metadata values are supported (also depending on Googles --- -* `Group { ga="Light" [ useKelvin=true ] }` (Light with separate brightness and color items) +* `Group { ga="Light" [ colorTemperatureRange="2000,9000", useKelvin=true ] }` (Light with separate brightness and color items) * `Dimmer / Number { ga="lightBrightness" }` as part of Light group * `Dimmer / Number { ga="lightColorTemperature" }` as part of Light group @@ -93,11 +94,11 @@ _\* All Rollershutter devices can also be used with a Switch or Contact item wit --- -* `Number { ga="TemperatureSensor" } [ useFahrenheit=true ] ` - +* `Number { ga="TemperatureSensor" } [ useFahrenheit=true ]` Example item configuration: - ``` + + ```js Switch KitchenLights "Kitchen Lights" (gKitchen) { ga="Switch" } Dimmer BedroomLights "Bedroom Lights" (gBedroom) { ga="Light" } Color LivingroomLights "Livingroom Lights" (gLivingroom) { ga="Light" } @@ -124,25 +125,29 @@ Furthermore, you can state synonyms for the device name: `Switch KitchenLight "K To ease setting up new devices you can add a room hint: `[ roomHint="Living Room" ]`. For devices supporting the OpenClose trait, the attributes `[ discreteOnlyOpenClose=false, queryOnlyOpenClose=false ]` can be configured. -- discreteOnlyOpenClose defaults to false. When set to true, this indicates that the device must either be fully open or fully closed (that is, it does not support values between 0% and 100%). An example of such a device may be a valve. -- queryOnlyOpenClose defaults to false. Is set to true for `Contact` items. Indicates if the device can only be queried for state information and cannot be controlled. Sensors that can only report open state should set this field to true. + +* discreteOnlyOpenClose defaults to false. When set to true, this indicates that the device must either be fully open or fully closed (that is, it does not support values between 0% and 100%). An example of such a device may be a valve. +* queryOnlyOpenClose defaults to false. Is set to true for `Contact` items. Indicates if the device can only be queried for state information and cannot be controlled. Sensors that can only report open state should set this field to true. --- -NOTE: metadata is not (yet?) available via paperUI. Either you create your items via ".items" files, or you can: -- add metadata via console: - ``` - smarthome:metadata add BedroomLights ga Light - ``` +NOTE: metadata is not available via paperUI in openHAB v2. Either you create your items via ".items" files, or you can: -- add metadata using the REST API: - ``` - PUT /rest/items/BedroomLights/metadata/ga +* add metadata via console: - { - "value": "Light" - } - ``` + ```console + smarthome:metadata add BedroomLights ga Light + ``` + +* add metadata using the REST API: + + ```js + PUT /rest/items/BedroomLights/metadata/ga + + { + "value": "Light" + } + ``` NOTE: Please be aware that for backward compatibilty also the former usage of tags (ref. [Google Assistant Action Documentation v2.5](https://www.openhab.org/v2.5/docs/ecosystem/google-assistant/)) to specify items to be exposed to Google Assistent is supported and may cause unexpected behavior. Items that contain tags that refer to a valid Google Assistent device will be exposed regardless of having metadata set. E.g.: `Switch MyBulb ["Lighting"]`. @@ -161,7 +166,7 @@ _pinNeeded_: "A two-factor authentication that requires a personal identificatio Example: -``` +```js Switch DoorLock "Front Door" { ga="Lock" [ ackNeeded=true ] } Switch HouseAlarm "House Alarm" { ga="SecuritySystem" [ pinNeeded="1234" ] } ``` @@ -212,7 +217,6 @@ If the values are still inverted in your case, you can state the `[ inverted=tru Since Google only tells the open percentage (and not the verb "close" or "down"), it can not be differentiated between saying "set blind to 100%" or "open blind". Therefore, it is not possible to "not invert" the verbs, if the user chooses to invert the numbers. - ## Setup & Usage on Google Assistant App * Make sure Google Play Services is up to date. @@ -255,28 +259,27 @@ Therefore, it is not possible to "not invert" the verbs, if the user chooses to ![openHAB Google App](images/Screenshot_10.png) ![openHAB Google App](images/Screenshot_11.png) - ## Example Voice Commands Here are some example voice commands: - * Turn on Office Lights. - * Dim/Brighten Office Lights (increments 15%). - * Set Office Lights to 35%. - * Open/Close the blinds - * Turn off Pool Waterfall. - * Turn on House Fan. - * Turn on Home Theater Scene. - * Set Basement Thermostat to 15 degrees. - * What is the current Basement Thermostat Temperature? +* Turn on Office Lights. +* Dim/Brighten Office Lights (increments 15%). +* Set Office Lights to 35%. +* Open/Close the blinds +* Turn off Pool Waterfall. +* Turn on House Fan. +* Turn on Home Theater Scene. +* Set Basement Thermostat to 15 degrees. +* What is the current Basement Thermostat Temperature? - ## Frequently Asked Question +## Frequently Asked Question - My New items did not appear in the Google Home app. +My New items did not appear in the Google Home app. - * Say: Hey Google, sync my devices. +* Say: Hey Google, sync my devices. - I'm not able to connect openHAB to Google Home. +I'm not able to connect openHAB to Google Home. * Check, recheck and after that check again your items! * The items that you want to expose to Google Assistant should have the right metadata assigned. @@ -286,17 +289,20 @@ Here are some example voice commands: * A number or string item with the metadata value { ga="thermostatMode" } as part of the thermostat group * A number item with the metadata value { ga="thermostatTemperatureAmbient" } as part of the thermostat group * A number item with the metadata value { ga="thermostatTemperatureSetpoint" } as part of the thermostat group - ``` + + ```js Group g_HK_Basement_TSTAT "Basement Thermostat" { ga="Thermostat" [ useFahrenheit=true ] } Number HK_Basement_Mode "Basement Heating/Cooling Mode" (g_HK_Basement_TSTAT) { ga="thermostatMode" } Number HK_Basement_Setpoint "Basement Setpoint" (g_HK_Basement_TSTAT) { ga="thermostatTemperatureSetpoint" } Number HK_Basement_Temp "Basement Temperature" (g_HK_Basement_TSTAT) { ga="thermostatTemperatureAmbient" } ``` + * If none of the above solutions works for you: * Remove all the metadata. * Make a new .item file with 1 item to expose. - ``` + + ```js Switch TestLight "Test Light" { ga="Switch" } ``` - * Relink your account. + * Relink your account. From 6ffa1c78722b1a3be8222967f8d29615a403fa2e Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 4 Feb 2021 17:38:12 +0100 Subject: [PATCH 88/94] Change test-ci script command Signed-off-by: Michael Krug --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d00114f9..437220bb 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "scripts": { "start": "node testServer.js", "test": "jest --silent --coverage", - "test-ci": "npm test --ci", + "test-ci": "jest --silent --coverage --ci", "lint": "eslint --ext .js --cache ." }, "license": "EPL-2.0", From ea69f4e631a092d93ca853f041073518285c9f06 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Thu, 4 Feb 2021 17:43:55 +0100 Subject: [PATCH 89/94] Make colorTemperatureRange optional if useKelvin is set Signed-off-by: Michael Krug --- functions/devices/specialcolorlight.js | 2 +- tests/devices/specialcolorlight.test.js | 53 +++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/functions/devices/specialcolorlight.js b/functions/devices/specialcolorlight.js index 987666f6..9cd68cda 100644 --- a/functions/devices/specialcolorlight.js +++ b/functions/devices/specialcolorlight.js @@ -14,7 +14,7 @@ class SpecialColorLight extends DefaultDevice { } static matchesItemType(item) { - return item.type === 'Group' && Object.keys(this.getMembers(item)).length > 1 && !!this.getAttributes(item).colorTemperatureRange; + return item.type === 'Group' && Object.keys(this.getMembers(item)).length > 1 && (this.useKelvin(item) || !!this.getAttributes(item).colorTemperatureRange); } static getAttributes(item) { diff --git a/tests/devices/specialcolorlight.test.js b/tests/devices/specialcolorlight.test.js index 67d2cb4a..db03df0d 100644 --- a/tests/devices/specialcolorlight.test.js +++ b/tests/devices/specialcolorlight.test.js @@ -39,7 +39,60 @@ describe('SpecialColorLight Device', () => { } ] }; + const item2 = { + "type": "Group", + "metadata": { + "ga": { + "value": "LIGHT" + } + }, + "members": [ + { + "metadata": { + "ga": { + "value": "lightBrightness" + } + } + }, + { + "metadata": { + "ga": { + "value": "lightColorTemperature" + } + } + } + ] + }; + const item3 = { + "type": "Group", + "metadata": { + "ga": { + "value": "LIGHT", + "config": { + "useKelvin": true + } + } + }, + "members": [ + { + "metadata": { + "ga": { + "value": "lightBrightness" + } + } + }, + { + "metadata": { + "ga": { + "value": "lightColorTemperature" + } + } + } + ] + }; expect(Device.matchesItemType(item)).toBe(true); + expect(Device.matchesItemType(item2)).toBe(false); + expect(Device.matchesItemType(item3)).toBe(true); expect(Device.matchesItemType({ "type": "Color" })).toBe(false); expect(Device.matchesItemType({ "type": "Group", "groupType": "Color" })).toBe(false); expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(false); From fa6c6050249e23b7688fd3d7f4567f8b158b52b7 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Fri, 5 Feb 2021 23:12:23 +0100 Subject: [PATCH 90/94] Switch workflows to main branch Signed-off-by: Michael Krug --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/test.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 981fac56..bba3783d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -2,9 +2,9 @@ name: CodeQL on: push: - branches: [ master ] + branches: [ main ] pull_request: - branches: [ master ] + branches: [ main ] jobs: analyze: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 023ae369..97d130e0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,9 +2,9 @@ name: Tests on: push: - branches: [ master ] + branches: [ main ] pull_request: - branches: [ master ] + branches: [ main ] jobs: unit-tests: From b31d037cf4f64bda3dec4ef14d90549cc0d5a442 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Tue, 9 Feb 2021 12:54:57 +0100 Subject: [PATCH 91/94] Add prettier for code formatting Update dependencies Signed-off-by: Michael Krug --- .eslintrc.json | 25 +- package-lock.json | 7679 ++++++++++++++++++++++++++++++++++++++++++--- package.json | 17 +- 3 files changed, 7221 insertions(+), 500 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 2b561f19..0f1dfd17 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,7 +4,10 @@ "jest": true, "node": true }, - "extends": "eslint:recommended", + "extends": [ + "eslint:recommended", + "prettier" + ], "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly" @@ -13,12 +16,32 @@ "ecmaVersion": 2019, "sourceType": "module" }, + "plugins": [ + "prettier" + ], "rules": { + "prettier/prettier": [ + "error", + { + "singleQuote": true, + "trailingComma": "none", + "tabWidth": 2, + "printWidth": 120 + } + ], "no-empty": [ "error", { "allowEmptyCatch": true } + ], + "max-len": [ + "error", + { + "code": 120, + "tabWidth": 2, + "ignoreUrls": true + } ] } } diff --git a/package-lock.json b/package-lock.json index 3e078456..97b399a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8 +1,7143 @@ { "name": "openhab.google-assistant-smarthome", "version": "2.1.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "openhab.google-assistant-smarthome", + "version": "2.1.0", + "license": "EPL-2.0", + "dependencies": { + "body-parser": "^1.19.0", + "express": "^4.17.1" + }, + "devDependencies": { + "@types/jest": "^26.0.20", + "eslint": "^7.19.0", + "eslint-config-prettier": "^7.2.0", + "eslint-plugin-prettier": "^3.3.1", + "jest": "^26.6.3", + "nock": "^13.0.7", + "prettier": "2.2.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/core": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", + "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.10", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.10", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.10", + "@babel/types": "^7.12.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@babel/core/node_modules/json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/core/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@babel/core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", + "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.11", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", + "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "^7.12.10", + "@babel/template": "^7.12.7", + "@babel/types": "^7.12.11" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", + "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.10" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", + "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.7" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.5" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", + "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.10" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", + "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.12.7", + "@babel/helper-optimise-call-expression": "^7.12.10", + "@babel/traverse": "^7.12.10", + "@babel/types": "^7.12.11" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.1" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", + "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.11" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "node_modules/@babel/helpers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, + "node_modules/@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", + "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", + "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "node_modules/@babel/template": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", + "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7" + } + }, + "node_modules/@babel/traverse": { + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", + "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.11", + "@babel/generator": "^7.12.11", + "@babel/helper-function-name": "^7.12.11", + "@babel/helper-split-export-declaration": "^7.12.11", + "@babel/parser": "^7.12.11", + "@babel/types": "^7.12.12", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@babel/traverse/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@babel/types": { + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", + "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/types/node_modules/@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "dependencies": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + }, + "bin": { + "watch": "cli.js" + }, + "engines": { + "node": ">=0.1.95" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", + "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.20", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "dependencies": { + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/core": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/environment": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/fake-timers": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/globals": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "node-notifier": "^8.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + }, + "engines": { + "node": ">= 10.14.2" + }, + "optionalDependencies": { + "node-notifier": "^8.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", + "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", + "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.0.tgz", + "integrity": "sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", + "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "26.0.20", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.20.tgz", + "integrity": "sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA==", + "dev": true, + "dependencies": { + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" + } + }, + "node_modules/@types/node": { + "version": "14.14.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.19.tgz", + "integrity": "sha512-4nhBPStMK04rruRVtVc6cDqhu7S9GZai0fpXgPXrFpcPX6Xul8xnrjSdGB4KPBVYG/R5+fXWdCM8qBoiULWGPQ==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.6.tgz", + "integrity": "sha512-6gOkRe7OIioWAXfnO/2lFiv+SJichKVSys1mSsgyrYHSEjk8Ctv4tSR/Odvnu+HWlH2C8j53dahU03XmQdd5fA==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.12.tgz", + "integrity": "sha512-f+fD/fQAo3BCbCDlrUpznF1A5Zp9rB0noS5vnoormHSIPFKL0Z2DcUJ3Gxp5ytH4uLRNxy7AwYUC9exZzqGMAw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", + "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.5", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", + "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "dependencies": { + "type-fest": "^0.11.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "dependencies": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/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, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "node_modules/babel-jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", + "dev": true, + "dependencies": { + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/babel__core": "^7.1.7", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", + "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "node_modules/babel-preset-jest": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", + "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^26.6.2", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "dependencies": { + "rsvp": "^4.8.4" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/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 + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/emittery": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/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, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.19.0.tgz", + "integrity": "sha512-CGlMgJY56JZ9ZSYhJuhow61lMPPjUzWmChFya71Z/jilVos7mR/jPgaEfVGgMBY5DshbKdG8Ezb8FDCHcoMEMg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.3.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.0", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.20", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.4", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-7.2.0.tgz", + "integrity": "sha512-rV4Qu0C3nfJKPOAhFujFxB7RMP+URFyQqqOZW9DMRD7ZDTFyjaIlETU3xzHELt++4ugC0+Jm084HQYkkJe+Ivg==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz", + "integrity": "sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "eslint": ">=5.0.0", + "prettier": ">=1.13.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/eslint/node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "dependencies": { + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/eslint/node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow/node_modules/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, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz", + "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz", + "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", + "dev": true + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz", + "integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==", + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true, + "optional": true + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-fresh/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/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, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/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, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "dev": true, + "optional": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/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, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", + "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", + "dev": true + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "optional": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", + "dev": true, + "dependencies": { + "@jest/core": "^26.6.3", + "import-local": "^3.0.2", + "jest-cli": "^26.6.3" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-changed-files/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/jest-changed-files/node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-changed-files/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-changed-files/node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-changed-files/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-changed-files/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-changed-files/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-changed-files/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-changed-files/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/jest-config": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.6.3", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-docblock": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", + "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-each": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", + "jsdom": "^16.4.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-environment-node": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-haste-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "engines": { + "node": ">= 10.14.2" + }, + "optionalDependencies": { + "fsevents": "^2.1.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^26.6.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", + "dev": true, + "dependencies": { + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-message-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-mock": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-snapshot": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-resolve/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-resolve/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-resolve/node_modules/parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.7.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-docblock": "^26.0.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-runtime": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.4.1" + }, + "bin": { + "jest-runtime": "bin/jest-runtime.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-runtime/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-serializer": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", + "dev": true, + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.0.0", + "chalk": "^4.0.0", + "expect": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "natural-compare": "^1.4.0", + "pretty-format": "^26.6.2", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-validate": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "leven": "^3.1.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-watcher": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^26.6.2", + "string-length": "^4.0.1" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest/node_modules/jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "dev": true, + "dependencies": { + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/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 + }, + "node_modules/js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "node_modules/jsdom": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", + "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "acorn": "^7.1.1", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.2.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.0", + "domexception": "^2.0.1", + "escodegen": "^1.14.1", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", + "nwsapi": "^2.2.0", + "parse5": "5.1.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.8", + "saxes": "^5.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^3.0.1", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0", + "ws": "^7.2.3", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "node_modules/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==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "node_modules/jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "node_modules/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "node_modules/lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", + "dev": true + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "dependencies": { + "tmpl": "1.0.x" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "dependencies": { + "mime-db": "1.43.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/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, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/nock": { + "version": "13.0.7", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.7.tgz", + "integrity": "sha512-WBz73VYIjdbO6BwmXODRQLtn7B5tldA9pNpWJe5QTtTEscQlY5KXU4srnGzBOK2fWakkXj69gfTnXGzmrsaRWw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash.set": "^4.3.2", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/nock/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/nock/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node_modules/node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-notifier": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", + "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", + "dev": true, + "optional": true, + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + } + }, + "node_modules/node-notifier/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "optional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-notifier/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "optional": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/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, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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 + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "dependencies": { + "node-modules-regexp": "^1.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/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, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/prompts": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", + "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "dev": true, + "dependencies": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/request-promise-native/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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 + }, + "node_modules/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, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true, + "engines": { + "node": "6.* || >= 7.*" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "dependencies": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "bin": { + "sane": "src/cli.js" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/sane/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/sane/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/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, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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 + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, + "optional": true + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/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, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/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, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/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, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/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 + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/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, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-length": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", + "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/table": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.4.tgz", + "integrity": "sha512-sBT4xRLdALd+NFBvwOz8bw4b15htyythha+q+DVZqy2RS08PPC8O2sZFgJYEY7bJvbCFKccs+WIZ/cd+xxTWCw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "lodash": "^4.17.20", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true + }, + "node_modules/tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "dev": true, + "dependencies": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/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, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.0.tgz", + "integrity": "sha512-uXUVqNUCLa0AH1vuVxzi+MI4RfxEOKt9pBgKwHbgH7st8Kv2P1m+jvWNnektzBh5QShF3ODgKmUFCf38LnVz1g==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/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, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "dependencies": { + "makeerror": "1.0.x" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true, + "engines": { + "node": ">=10.4" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", + "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", + "dev": true, + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/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 + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", + "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==", + "dev": true, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + } + }, "dependencies": { "@babel/code-frame": { "version": "7.10.4", @@ -471,9 +7606,9 @@ } }, "@eslint/eslintrc": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz", - "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", + "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -483,7 +7618,7 @@ "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, @@ -506,12 +7641,6 @@ "type-fest": "^0.8.1" } }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -881,21 +8010,15 @@ } }, "@types/jest": { - "version": "26.0.19", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.19.tgz", - "integrity": "sha512-jqHoirTG61fee6v6rwbnEuKhpSKih0tuhqeFbCmMmErhtu3BYlOZaXWjffgOstMM4S/3iQD31lI5bGLTrs97yQ==", + "version": "26.0.20", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.20.tgz", + "integrity": "sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA==", "dev": true, "requires": { "jest-diff": "^26.0.0", "pretty-format": "^26.0.0" } }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true - }, "@types/node": { "version": "14.14.19", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.19.tgz", @@ -1071,33 +8194,12 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "array-includes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", - "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0", - "is-string": "^1.0.5" - } - }, "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 }, - "array.prototype.flat": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", - "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -1516,12 +8618,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -1660,15 +8756,6 @@ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -1738,16 +8825,6 @@ "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", "dev": true }, - "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" - } - }, "domexception": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", @@ -1824,36 +8901,6 @@ "is-arrayish": "^0.2.1" } }, - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1879,13 +8926,13 @@ } }, "eslint": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.16.0.tgz", - "integrity": "sha512-iVWPS785RuDA4dWuhhgXTNrGxHHK3a8HLSMBgbbU59ruJDubUraXN8N5rn7kb8tG6sjg74eE0RA3YWT51eusEw==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.19.0.tgz", + "integrity": "sha512-CGlMgJY56JZ9ZSYhJuhow61lMPPjUzWmChFya71Z/jilVos7mR/jPgaEfVGgMBY5DshbKdG8Ezb8FDCHcoMEMg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.2.2", + "@eslint/eslintrc": "^0.3.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -1909,7 +8956,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -1977,12 +9024,6 @@ "type-fest": "^0.8.1" } }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2069,108 +9110,22 @@ } } }, - "eslint-config-standard": { - "version": "16.0.2", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.2.tgz", - "integrity": "sha512-fx3f1rJDsl9bY7qzyX8SAtP8GBSk6MfXFaTfaGgk12aAYW4gJSyRm7dM790L6cbXv63fvjY4XeSzXnb4WM+SKw==", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - } - }, - "eslint-module-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", - "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" - } - }, - "eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "dev": true, - "requires": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - } - }, - "eslint-plugin-import": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", - "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "eslint-config-prettier": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-7.2.0.tgz", + "integrity": "sha512-rV4Qu0C3nfJKPOAhFujFxB7RMP+URFyQqqOZW9DMRD7ZDTFyjaIlETU3xzHELt++4ugC0+Jm084HQYkkJe+Ivg==", "dev": true, - "requires": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-module-utils": "^2.6.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" - }, - "dependencies": { - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } - } + "requires": {} }, - "eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "eslint-plugin-prettier": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz", + "integrity": "sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==", "dev": true, "requires": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "prettier-linter-helpers": "^1.0.0" } }, - "eslint-plugin-promise": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", - "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", - "dev": true - }, - "eslint-plugin-standard": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.1.0.tgz", - "integrity": "sha512-ZL7+QRixjTR6/528YNGyDotyffm5OQst/sGxKDwGb9Uqs4In5Egi4+jbobhqJoyoCM6/7v/1A5fhQ7ScMtDjaQ==", - "dev": true - }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -2481,6 +9436,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "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-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2534,15 +9495,6 @@ "unpipe": "~1.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": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -2741,12 +9693,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -2858,9 +9804,9 @@ } }, "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, "import-fresh": { @@ -3015,12 +9961,6 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, - "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", - "dev": true - }, "is-ci": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", @@ -3059,12 +9999,6 @@ } } }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -3124,12 +10058,6 @@ "is-extglob": "^2.1.1" } }, - "is-negative-zero": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", - "dev": true - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3151,36 +10079,12 @@ "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", "dev": true }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -3742,16 +10646,6 @@ "read-pkg": "^5.2.0", "type-fest": "^0.8.1" } - }, - "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "dev": true, - "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - } } } }, @@ -4036,15 +10930,6 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -4091,28 +10976,6 @@ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", "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.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", @@ -4314,9 +11177,9 @@ "dev": true }, "nock": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.5.tgz", - "integrity": "sha512-1ILZl0zfFm2G4TIeJFW0iHknxr2NyA+aGCMTjDVUsBY4CkMRispF1pfIYkTRdAR/3Bg+UzdEuK0B6HczMQZcCg==", + "version": "13.0.7", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.7.tgz", + "integrity": "sha512-WBz73VYIjdbO6BwmXODRQLtn7B5tldA9pNpWJe5QTtTEscQlY5KXU4srnGzBOK2fWakkXj69gfTnXGzmrsaRWw==", "dev": true, "requires": { "debug": "^4.1.0", @@ -4461,18 +11324,6 @@ } } }, - "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -4482,40 +11333,6 @@ "isobject": "^3.0.0" } }, - "object.assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", - "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.0", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } - } - }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -4525,18 +11342,6 @@ "isobject": "^3.0.1" } }, - "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -4589,30 +11394,6 @@ "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 - }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4622,15 +11403,6 @@ "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" - } - }, "parse5": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", @@ -4648,12 +11420,6 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -4677,15 +11443,6 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, - "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" - } - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -4698,12 +11455,6 @@ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, "pirates": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", @@ -4713,15 +11464,6 @@ "node-modules-regexp": "^1.0.0" } }, - "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" - } - }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -4734,6 +11476,21 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "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" + } + }, "pretty-format": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", @@ -4826,27 +11583,6 @@ "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", "dev": true }, - "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" - } - }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -4978,11 +11714,12 @@ "dev": true }, "resolve": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.16.0.tgz", - "integrity": "sha512-LarL/PIKJvc09k1jaeT4kQb/8/7P+qV4qSnN2K80AES+OHdfZELAKVOBjxsvtToT/uLOfFbvYvKfZmV8cee7nA==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, "requires": { + "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } }, @@ -5608,26 +12345,6 @@ "strip-ansi": "^6.0.0" } }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -5637,12 +12354,6 @@ "ansi-regex": "^5.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", @@ -5809,18 +12520,6 @@ "punycode": "^2.1.1" } }, - "tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - } - }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", diff --git a/package.json b/package.json index 437220bb..d41b2a8b 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "start": "node testServer.js", "test": "jest --silent --coverage", "test-ci": "jest --silent --coverage --ci", - "lint": "eslint --ext .js --cache ." + "lint": "eslint --ext .js --cache .", + "fix": "eslint --fix --ext .js --cache ." }, "license": "EPL-2.0", "bugs": { @@ -27,14 +28,12 @@ "express": "^4.17.1" }, "devDependencies": { - "@types/jest": "^26.0.19", - "eslint": "^7.16.0", - "eslint-config-standard": "^16.0.2", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.2.1", - "eslint-plugin-standard": "^4.1.0", + "@types/jest": "^26.0.20", + "eslint": "^7.19.0", + "eslint-config-prettier": "^7.2.0", + "eslint-plugin-prettier": "^3.3.1", "jest": "^26.6.3", - "nock": "^13.0.5" + "nock": "^13.0.7", + "prettier": "2.2.1" } } From cd6b822f7c23dcf89b98f16b8856f59498ccec39 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Tue, 9 Feb 2021 13:05:09 +0100 Subject: [PATCH 92/94] Apply code formatting Signed-off-by: Michael Krug --- functions/apihandler.js | 7 +- functions/commands/activatescene.js | 2 +- functions/commands/armdisarm.js | 2 +- functions/commands/brightnessabsolute.js | 2 +- functions/commands/colorabsolute.js | 8 +- .../commands/colorabsolutetemperature.js | 15 +- functions/commands/default.js | 79 +- functions/commands/getcamerastream.js | 8 +- functions/commands/lockunlock.js | 2 +- functions/commands/mute.js | 2 +- functions/commands/onoff.js | 2 +- functions/commands/openclose.js | 2 +- functions/commands/selectchannel.js | 8 +- functions/commands/setfanspeed.js | 2 +- functions/commands/setinput.js | 2 +- functions/commands/setvolume.js | 2 +- functions/commands/startstop.js | 2 +- functions/commands/thermostatsetmode.js | 2 +- .../commands/thermostattemperaturesetpoint.js | 2 +- .../thermostattemperaturesetpointhigh.js | 4 +- .../thermostattemperaturesetpointlow.js | 2 +- functions/commands/volumerelative.js | 2 +- functions/config.js | 8 +- functions/devices/camera.js | 6 +- functions/devices/colorlight.js | 10 +- functions/devices/default.js | 20 +- functions/devices/dimmablelight.js | 5 +- functions/devices/fan.js | 22 +- functions/devices/lock.js | 4 +- functions/devices/openclosedevice.js | 5 +- functions/devices/scene.js | 4 +- functions/devices/securitysystem.js | 4 +- functions/devices/sensor.js | 10 +- functions/devices/simpleairpurifier.js | 2 +- functions/devices/speaker.js | 4 +- functions/devices/specialcolorlight.js | 30 +- functions/devices/startstopswitch.js | 4 +- functions/devices/switch.js | 4 +- functions/devices/temperaturesensor.js | 6 +- functions/devices/thermostat.js | 33 +- functions/devices/tv.js | 35 +- functions/devices/valve.js | 4 +- functions/openhab.js | 123 ++-- functions/utilities.js | 20 +- testServer.js | 2 +- tests/apihandler.test.js | 130 ++-- tests/commands/activatescene.test.js | 10 +- tests/commands/armdisarm.test.js | 16 +- tests/commands/brightnessabsolute.test.js | 38 +- tests/commands/colorabsolute.test.js | 14 +- .../commands/colorabsolutetemperature.test.js | 62 +- tests/commands/default.test.js | 184 ++--- tests/commands/getcamerastream.test.js | 8 +- tests/commands/lockunlock.test.js | 18 +- tests/commands/medianext.test.js | 20 +- tests/commands/mediapause.test.js | 20 +- tests/commands/mediaprevious.test.js | 20 +- tests/commands/mediaresume.test.js | 20 +- tests/commands/mute.test.js | 82 ++- tests/commands/onoff.test.js | 50 +- tests/commands/openclose.test.js | 50 +- tests/commands/selectchannel.test.js | 46 +- tests/commands/setfanspeed.test.js | 6 +- tests/commands/setinput.test.js | 22 +- tests/commands/setvolume.test.js | 28 +- tests/commands/startstop.test.js | 24 +- tests/commands/thermostatsetmode.test.js | 38 +- .../thermostattemperaturesetpoint.test.js | 40 +- .../thermostattemperaturesetpointhigh.test.js | 40 +- .../thermostattemperaturesetpointlow.test.js | 40 +- tests/commands/volumerelative.test.js | 42 +- tests/config.test.js | 2 +- tests/devices/camera.test.js | 50 +- tests/devices/colorlight.test.js | 64 +- tests/devices/default.test.js | 89 ++- tests/devices/dimmablelight.test.js | 34 +- tests/devices/fan.test.js | 78 +- tests/devices/lock.test.js | 66 +- tests/devices/openclosedevice.test.js | 124 ++-- tests/devices/scene.test.js | 24 +- tests/devices/securitysystem.test.js | 43 +- tests/devices/sensor.test.js | 114 ++- tests/devices/simplelight.test.js | 14 +- tests/devices/speaker.test.js | 58 +- tests/devices/specialcolorlight.test.js | 194 ++--- tests/devices/startstopswitch.test.js | 42 +- tests/devices/switch.test.js | 46 +- tests/devices/temperaturesensor.test.js | 64 +- tests/devices/thermostat.test.js | 430 ++++++----- tests/devices/tv.test.js | 503 +++++++------ tests/devices/valve.test.js | 46 +- tests/openhab.test.js | 682 ++++++++++-------- tests/utilities.test.js | 16 +- 93 files changed, 2249 insertions(+), 2126 deletions(-) diff --git a/functions/apihandler.js b/functions/apihandler.js index cf506b1e..ac0117b3 100644 --- a/functions/apihandler.js +++ b/functions/apihandler.js @@ -47,15 +47,16 @@ class ApiHandler { * @param {string} itemName * @param {number} length */ - getOptions(method = 'GET', itemName = '', length = 0) { - const queryString = '?metadata=ga,synonyms' + (itemName ? '' : '&fields=groupNames,groupType,name,label,metadata,tags,type'); + getOptions(method = 'GET', itemName = '', length = 0) { + const queryString = + '?metadata=ga,synonyms' + (itemName ? '' : '&fields=groupNames,groupType,name,label,metadata,tags,type'); const options = { hostname: this._config.host, port: this._config.port, path: this._config.path + (itemName ? itemName : '') + queryString, method: method, headers: { - 'Accept': 'application/json' + Accept: 'application/json' } }; diff --git a/functions/commands/activatescene.js b/functions/commands/activatescene.js index 8a1c345e..3b28d07a 100644 --- a/functions/commands/activatescene.js +++ b/functions/commands/activatescene.js @@ -6,7 +6,7 @@ class ActivateScene extends DefaultCommand { } static validateParams(params) { - return (('deactivate' in params) && typeof params.deactivate === 'boolean') || !('deactivate' in params); + return ('deactivate' in params && typeof params.deactivate === 'boolean') || !('deactivate' in params); } static convertParamsToValue(params, _, device) { diff --git a/functions/commands/armdisarm.js b/functions/commands/armdisarm.js index 5204200b..9578c2a5 100644 --- a/functions/commands/armdisarm.js +++ b/functions/commands/armdisarm.js @@ -6,7 +6,7 @@ class ArmDisarm extends DefaultCommand { } static validateParams(params) { - return ('arm' in params) && typeof params.arm === 'boolean'; + return 'arm' in params && typeof params.arm === 'boolean'; } static convertParamsToValue(params, _, device) { diff --git a/functions/commands/brightnessabsolute.js b/functions/commands/brightnessabsolute.js index b0902309..fa0da7d4 100644 --- a/functions/commands/brightnessabsolute.js +++ b/functions/commands/brightnessabsolute.js @@ -7,7 +7,7 @@ class BrightnessAbsolute extends DefaultCommand { } static validateParams(params) { - return ('brightness' in params) && typeof params.brightness === 'number'; + return 'brightness' in params && typeof params.brightness === 'number'; } static requiresItem(device) { diff --git a/functions/commands/colorabsolute.js b/functions/commands/colorabsolute.js index d3b2289c..5e281003 100644 --- a/functions/commands/colorabsolute.js +++ b/functions/commands/colorabsolute.js @@ -6,8 +6,12 @@ class ColorAbsolute extends DefaultCommand { } static validateParams(params) { - return ('color' in params) && typeof params.color === 'object' && - ('spectrumHSV' in params.color) && typeof params.color.spectrumHSV === 'object'; + return ( + 'color' in params && + typeof params.color === 'object' && + 'spectrumHSV' in params.color && + typeof params.color.spectrumHSV === 'object' + ); } static convertParamsToValue(params, _, device) { diff --git a/functions/commands/colorabsolutetemperature.js b/functions/commands/colorabsolutetemperature.js index 8f3b13a3..73085fde 100644 --- a/functions/commands/colorabsolutetemperature.js +++ b/functions/commands/colorabsolutetemperature.js @@ -9,8 +9,12 @@ class ColorAbsoluteTemperature extends DefaultCommand { } static validateParams(params) { - return ('color' in params) && typeof params.color === 'object' && - ('temperature' in params.color) && typeof params.color.temperature === 'number'; + return ( + 'color' in params && + typeof params.color === 'object' && + 'temperature' in params.color && + typeof params.color.temperature === 'number' + ); } static requiresItem() { @@ -35,13 +39,16 @@ class ColorAbsoluteTemperature extends DefaultCommand { return params.color.temperature.toString(); } const { temperatureMinK, temperatureMaxK } = SpecialColorLight.getAttributes(item).colorTemperatureRange; - return (100 - ((params.color.temperature - temperatureMinK) / (temperatureMaxK - temperatureMinK) * 100)).toString(); + return ( + 100 - + ((params.color.temperature - temperatureMinK) / (temperatureMaxK - temperatureMinK)) * 100 + ).toString(); } catch { return '0'; } } const hsv = rgb2hsv(kelvin2rgb(params.color.temperature)); - const hsvArray = item.state.split(",").map((val) => Number(val)); + const hsvArray = item.state.split(',').map((val) => Number(val)); return [Math.round(hsv.hue * 100) / 100, Math.round(hsv.saturation * 1000) / 10, hsvArray[2]].join(','); } diff --git a/functions/commands/default.js b/functions/commands/default.js index 358391ae..1a2f9e8f 100644 --- a/functions/commands/default.js +++ b/functions/commands/default.js @@ -54,14 +54,14 @@ class DefaultCommand { * @param {object} device */ static getDeviceType(device) { - return device.customData && device.customData.deviceType || ''; + return (device.customData && device.customData.deviceType) || ''; } /** * @param {object} device */ static getItemType(device) { - return device.customData && device.customData.itemType || ''; + return (device.customData && device.customData.itemType) || ''; } /** @@ -83,7 +83,11 @@ class DefaultCommand { * @param {object} challenge */ static handleAuthPin(device, challenge) { - if (!device.customData || !device.customData.pinNeeded || challenge && challenge.pin === device.customData.pinNeeded) { + if ( + !device.customData || + !device.customData.pinNeeded || + (challenge && challenge.pin === device.customData.pinNeeded) + ) { return; } return { @@ -102,7 +106,7 @@ class DefaultCommand { * @param {object} responseStates */ static handleAuthAck(device, challenge, responseStates) { - if (!device.customData || !device.customData.ackNeeded || challenge && challenge.ack === true) { + if (!device.customData || !device.customData.ackNeeded || (challenge && challenge.ack === true)) { return; } return { @@ -126,53 +130,56 @@ class DefaultCommand { console.log(`openhabGoogleAssistant - ${this.type}: ${JSON.stringify({ devices: devices, params: params })}`); const commandsResponse = []; const promises = devices.map((device) => { - const authPinResponse = this.handleAuthPin(device, challenge); if (authPinResponse) { commandsResponse.push(authPinResponse); return Promise.resolve(); } - const ackWithState = ackSupported.includes(this.type) && device.customData && device.customData.ackNeeded && !challenge.ack; + const ackWithState = + ackSupported.includes(this.type) && device.customData && device.customData.ackNeeded && !challenge.ack; - let getItemPromise = Promise.resolve(({ name: device.id })); + let getItemPromise = Promise.resolve({ name: device.id }); if (this.requiresItem(device) || ackWithState) { getItemPromise = apiHandler.getItem(device.id); } - return getItemPromise.then((item) => { - const responseStates = this.getResponseStates(params, item, device); - if (Object.keys(responseStates).length) { - responseStates.online = true; - } - - const authAckResponse = this.handleAuthAck(device, challenge, responseStates); - if (authAckResponse) { - commandsResponse.push(authAckResponse); - return; - } - - const targetItem = this.getItemName(item, device); - const targetValue = this.convertParamsToValue(params, item, device); - let sendCommandPromise = Promise.resolve(); - if (typeof targetItem === 'string' && typeof targetValue === 'string') { - sendCommandPromise = apiHandler.sendCommand(targetItem, targetValue); - } - return sendCommandPromise.then(() => { + return getItemPromise + .then((item) => { + const responseStates = this.getResponseStates(params, item, device); + if (Object.keys(responseStates).length) { + responseStates.online = true; + } + + const authAckResponse = this.handleAuthAck(device, challenge, responseStates); + if (authAckResponse) { + commandsResponse.push(authAckResponse); + return; + } + + const targetItem = this.getItemName(item, device); + const targetValue = this.convertParamsToValue(params, item, device); + let sendCommandPromise = Promise.resolve(); + if (typeof targetItem === 'string' && typeof targetValue === 'string') { + sendCommandPromise = apiHandler.sendCommand(targetItem, targetValue); + } + return sendCommandPromise.then(() => { + commandsResponse.push({ + ids: [device.id], + status: 'SUCCESS', + states: responseStates + }); + }); + }) + .catch((error) => { + console.error(`openhabGoogleAssistant - ${this.type}: ERROR ${JSON.stringify(error)}`); commandsResponse.push({ ids: [device.id], - status: 'SUCCESS', - states: responseStates + status: 'ERROR', + errorCode: + error.statusCode == 404 ? 'deviceNotFound' : error.statusCode == 400 ? 'notSupported' : 'deviceOffline' }); }); - }).catch((error) => { - console.error(`openhabGoogleAssistant - ${this.type}: ERROR ${JSON.stringify(error)}`); - commandsResponse.push({ - ids: [device.id], - status: 'ERROR', - errorCode: error.statusCode == 404 ? 'deviceNotFound' : error.statusCode == 400 ? 'notSupported' : 'deviceOffline' - }); - }); }); return Promise.all(promises).then(() => commandsResponse); } diff --git a/functions/commands/getcamerastream.js b/functions/commands/getcamerastream.js index 63658ef6..188d24ac 100644 --- a/functions/commands/getcamerastream.js +++ b/functions/commands/getcamerastream.js @@ -6,8 +6,12 @@ class GetCameraStream extends DefaultCommand { } static validateParams(params) { - return ('StreamToChromecast' in params) && typeof params.StreamToChromecast === 'boolean' && - ('SupportedStreamProtocols' in params) && typeof params.SupportedStreamProtocols === 'object'; + return ( + 'StreamToChromecast' in params && + typeof params.StreamToChromecast === 'boolean' && + 'SupportedStreamProtocols' in params && + typeof params.SupportedStreamProtocols === 'object' + ); } static requiresItem() { diff --git a/functions/commands/lockunlock.js b/functions/commands/lockunlock.js index 81baecbe..e3d05d69 100644 --- a/functions/commands/lockunlock.js +++ b/functions/commands/lockunlock.js @@ -6,7 +6,7 @@ class LockUnlock extends DefaultCommand { } static validateParams(params) { - return ('lock' in params) && typeof params.lock === 'boolean'; + return 'lock' in params && typeof params.lock === 'boolean'; } static convertParamsToValue(params, _, device) { diff --git a/functions/commands/mute.js b/functions/commands/mute.js index 4bffa70d..ef537cca 100644 --- a/functions/commands/mute.js +++ b/functions/commands/mute.js @@ -7,7 +7,7 @@ class Mute extends DefaultCommand { } static validateParams(params) { - return ('mute' in params) && typeof params.mute === 'boolean'; + return 'mute' in params && typeof params.mute === 'boolean'; } static requiresItem(device) { diff --git a/functions/commands/onoff.js b/functions/commands/onoff.js index e6812dde..cc4a6670 100644 --- a/functions/commands/onoff.js +++ b/functions/commands/onoff.js @@ -8,7 +8,7 @@ class OnOff extends DefaultCommand { } static validateParams(params) { - return ('on' in params) && typeof params.on === 'boolean'; + return 'on' in params && typeof params.on === 'boolean'; } static requiresItem(device) { diff --git a/functions/commands/openclose.js b/functions/commands/openclose.js index aaca9bd4..bb7ae865 100644 --- a/functions/commands/openclose.js +++ b/functions/commands/openclose.js @@ -6,7 +6,7 @@ class OpenClose extends DefaultCommand { } static validateParams(params) { - return ('openPercent' in params) && typeof params.openPercent === 'number'; + return 'openPercent' in params && typeof params.openPercent === 'number'; } static convertParamsToValue(params, _, device) { diff --git a/functions/commands/selectchannel.js b/functions/commands/selectchannel.js index e043bc8c..f47556fb 100644 --- a/functions/commands/selectchannel.js +++ b/functions/commands/selectchannel.js @@ -7,9 +7,11 @@ class SelectChannel extends DefaultCommand { } static validateParams(params) { - return (('channelCode' in params) && typeof params.channelCode === 'string') || - (('channelName' in params) && typeof params.channelName === 'string') || - (('channelNumber' in params) && typeof params.channelNumber === 'string'); + return ( + ('channelCode' in params && typeof params.channelCode === 'string') || + ('channelName' in params && typeof params.channelName === 'string') || + ('channelNumber' in params && typeof params.channelNumber === 'string') + ); } static requiresItem() { diff --git a/functions/commands/setfanspeed.js b/functions/commands/setfanspeed.js index 600bd0d9..b45df871 100644 --- a/functions/commands/setfanspeed.js +++ b/functions/commands/setfanspeed.js @@ -6,7 +6,7 @@ class SetFanSpeed extends DefaultCommand { } static validateParams(params) { - return ('fanSpeed' in params) && typeof params.fanSpeed === 'string'; + return 'fanSpeed' in params && typeof params.fanSpeed === 'string'; } static convertParamsToValue(params) { diff --git a/functions/commands/setinput.js b/functions/commands/setinput.js index c2677b86..7459dd3f 100644 --- a/functions/commands/setinput.js +++ b/functions/commands/setinput.js @@ -7,7 +7,7 @@ class SetInput extends DefaultCommand { } static validateParams(params) { - return ('newInput' in params) && typeof params.newInput === 'string'; + return 'newInput' in params && typeof params.newInput === 'string'; } static requiresItem() { diff --git a/functions/commands/setvolume.js b/functions/commands/setvolume.js index f74edd58..079f03d0 100644 --- a/functions/commands/setvolume.js +++ b/functions/commands/setvolume.js @@ -7,7 +7,7 @@ class SetVolume extends DefaultCommand { } static validateParams(params) { - return ('volumeLevel' in params) && typeof params.volumeLevel === 'number'; + return 'volumeLevel' in params && typeof params.volumeLevel === 'number'; } static requiresItem(device) { diff --git a/functions/commands/startstop.js b/functions/commands/startstop.js index ac471b5e..92682114 100644 --- a/functions/commands/startstop.js +++ b/functions/commands/startstop.js @@ -6,7 +6,7 @@ class StartStop extends DefaultCommand { } static validateParams(params) { - return ('start' in params) && typeof params.start === 'boolean'; + return 'start' in params && typeof params.start === 'boolean'; } static convertParamsToValue(params, _, device) { diff --git a/functions/commands/thermostatsetmode.js b/functions/commands/thermostatsetmode.js index d0db344b..13675617 100644 --- a/functions/commands/thermostatsetmode.js +++ b/functions/commands/thermostatsetmode.js @@ -7,7 +7,7 @@ class ThermostatSetMode extends DefaultCommand { } static validateParams(params) { - return ('thermostatMode' in params) && typeof params.thermostatMode === 'string'; + return 'thermostatMode' in params && typeof params.thermostatMode === 'string'; } static requiresItem() { diff --git a/functions/commands/thermostattemperaturesetpoint.js b/functions/commands/thermostattemperaturesetpoint.js index 7733d6a5..7fba7407 100644 --- a/functions/commands/thermostattemperaturesetpoint.js +++ b/functions/commands/thermostattemperaturesetpoint.js @@ -8,7 +8,7 @@ class ThermostatTemperatureSetpoint extends DefaultCommand { } static validateParams(params) { - return ('thermostatTemperatureSetpoint' in params) && typeof params.thermostatTemperatureSetpoint === 'number'; + return 'thermostatTemperatureSetpoint' in params && typeof params.thermostatTemperatureSetpoint === 'number'; } static requiresItem() { diff --git a/functions/commands/thermostattemperaturesetpointhigh.js b/functions/commands/thermostattemperaturesetpointhigh.js index 7941f23b..ec659bda 100644 --- a/functions/commands/thermostattemperaturesetpointhigh.js +++ b/functions/commands/thermostattemperaturesetpointhigh.js @@ -8,7 +8,9 @@ class ThermostatTemperatureSetpointHigh extends DefaultCommand { } static validateParams(params) { - return ('thermostatTemperatureSetpointHigh' in params) && typeof params.thermostatTemperatureSetpointHigh === 'number'; + return ( + 'thermostatTemperatureSetpointHigh' in params && typeof params.thermostatTemperatureSetpointHigh === 'number' + ); } static requiresItem() { diff --git a/functions/commands/thermostattemperaturesetpointlow.js b/functions/commands/thermostattemperaturesetpointlow.js index 62eec24a..1327fa9f 100644 --- a/functions/commands/thermostattemperaturesetpointlow.js +++ b/functions/commands/thermostattemperaturesetpointlow.js @@ -8,7 +8,7 @@ class ThermostatTemperatureSetpointLow extends DefaultCommand { } static validateParams(params) { - return ('thermostatTemperatureSetpointLow' in params) && typeof params.thermostatTemperatureSetpointLow === 'number'; + return 'thermostatTemperatureSetpointLow' in params && typeof params.thermostatTemperatureSetpointLow === 'number'; } static requiresItem() { diff --git a/functions/commands/volumerelative.js b/functions/commands/volumerelative.js index a6146fbf..05762450 100644 --- a/functions/commands/volumerelative.js +++ b/functions/commands/volumerelative.js @@ -7,7 +7,7 @@ class VolumeRelative extends DefaultCommand { } static validateParams(params) { - return ('relativeSteps' in params) && typeof params.relativeSteps === 'number'; + return 'relativeSteps' in params && typeof params.relativeSteps === 'number'; } static requiresItem() { diff --git a/functions/config.js b/functions/config.js index 1ea757b7..5da71776 100644 --- a/functions/config.js +++ b/functions/config.js @@ -33,8 +33,8 @@ * **/ module.exports = { - //userpass: 'user@foo.com:Password1', - host: '', - port: 443, - path: '/YOUR/REST/ENDPOINT', + //userpass: 'user@foo.com:Password1', + host: '', + port: 443, + path: '/YOUR/REST/ENDPOINT' }; diff --git a/functions/devices/camera.js b/functions/devices/camera.js index aeb5063b..21aea8ba 100644 --- a/functions/devices/camera.js +++ b/functions/devices/camera.js @@ -6,15 +6,13 @@ class Camera extends DefaultDevice { } static getTraits() { - return [ - 'action.devices.traits.CameraStream' - ]; + return ['action.devices.traits.CameraStream']; } static getAttributes(item) { const config = this.getConfig(item); return { - cameraStreamSupportedProtocols: (config.protocols || 'hls,dash').split(',').map(s => s.trim()), + cameraStreamSupportedProtocols: (config.protocols || 'hls,dash').split(',').map((s) => s.trim()), cameraStreamNeedAuthToken: config.token ? true : false, cameraStreamNeedDrmEncryption: false }; diff --git a/functions/devices/colorlight.js b/functions/devices/colorlight.js index d015ec54..392f464e 100644 --- a/functions/devices/colorlight.js +++ b/functions/devices/colorlight.js @@ -6,11 +6,7 @@ class ColorLight extends DefaultDevice { } static getTraits() { - return [ - 'action.devices.traits.OnOff', - 'action.devices.traits.Brightness', - 'action.devices.traits.ColorSetting' - ]; + return ['action.devices.traits.OnOff', 'action.devices.traits.Brightness', 'action.devices.traits.ColorSetting']; } static getAttributes(item) { @@ -19,7 +15,7 @@ class ColorLight extends DefaultDevice { }; const config = this.getConfig(item); if ('colorTemperatureRange' in config) { - const [min, max] = config.colorTemperatureRange.split(',').map(s => Number(s.trim())); + const [min, max] = config.colorTemperatureRange.split(',').map((s) => Number(s.trim())); if (!isNaN(min) && !isNaN(max)) { attributes.colorTemperatureRange = { temperatureMinK: min, @@ -35,7 +31,7 @@ class ColorLight extends DefaultDevice { } static getState(item) { - const [hue, sat, val] = item.state.split(',').map(s => Number(s.trim())); + const [hue, sat, val] = item.state.split(',').map((s) => Number(s.trim())); return { on: val > 0, brightness: val, diff --git a/functions/devices/default.js b/functions/devices/default.js index 393e6224..a49a679a 100644 --- a/functions/devices/default.js +++ b/functions/devices/default.js @@ -19,9 +19,12 @@ class DefaultDevice { * @param {object} item */ static isCompatible(item) { - return item.metadata && item.metadata.ga && - this.type.toLowerCase() === `action.devices.types.${item.metadata.ga.value}`.toLowerCase() || + return ( + (item.metadata && + item.metadata.ga && + this.type.toLowerCase() === `action.devices.types.${item.metadata.ga.value}`.toLowerCase()) || this.hasTag(item, this.type.substr(21).replace('SWITCH', 'SWITCHABLE').replace('LIGHT', 'LIGHTING')) + ); } /** @@ -46,7 +49,7 @@ class DefaultDevice { * @param {object} item */ static getConfig(item) { - return item && item.metadata && item.metadata.ga && item.metadata.ga.config || {}; + return (item && item.metadata && item.metadata.ga && item.metadata.ga.config) || {}; } /** @@ -62,7 +65,12 @@ class DefaultDevice { name: { name: config.name || item.label, defaultNames: [config.name || item.label], - nicknames: [config.name || item.label, ...(item.metadata && item.metadata.synonyms ? item.metadata.synonyms.value.split(',').map(s => s.trim()) : [])] + nicknames: [ + config.name || item.label, + ...(item.metadata && item.metadata.synonyms + ? item.metadata.synonyms.value.split(',').map((s) => s.trim()) + : []) + ] }, willReportState: false, roomHint: config.roomHint, @@ -85,7 +93,7 @@ class DefaultDevice { if (config.ackNeeded === true) { metadata.customData.ackNeeded = true; } - if (typeof (config.pinNeeded) === 'string') { + if (typeof config.pinNeeded === 'string') { metadata.customData.pinNeeded = config.pinNeeded; } return metadata; @@ -103,7 +111,7 @@ class DefaultDevice { * @param {string} tag */ static hasTag(item, tag) { - return item.tags && item.tags.map(t => t.toLowerCase()).includes(tag.toLowerCase()) || false; + return (item.tags && item.tags.map((t) => t.toLowerCase()).includes(tag.toLowerCase())) || false; } } diff --git a/functions/devices/dimmablelight.js b/functions/devices/dimmablelight.js index 87d26bf0..326c8a1f 100644 --- a/functions/devices/dimmablelight.js +++ b/functions/devices/dimmablelight.js @@ -6,10 +6,7 @@ class DimmableLight extends DefaultDevice { } static getTraits() { - return [ - 'action.devices.traits.OnOff', - 'action.devices.traits.Brightness' - ]; + return ['action.devices.traits.OnOff', 'action.devices.traits.Brightness']; } static get requiredItemTypes() { diff --git a/functions/devices/fan.js b/functions/devices/fan.js index ca0de02c..ded63f1a 100644 --- a/functions/devices/fan.js +++ b/functions/devices/fan.js @@ -6,10 +6,7 @@ class Fan extends DefaultDevice { } static getTraits() { - return [ - 'action.devices.traits.OnOff', - 'action.devices.traits.FanSpeed' - ]; + return ['action.devices.traits.OnOff', 'action.devices.traits.FanSpeed']; } static getAttributes(item) { @@ -26,15 +23,20 @@ class Fan extends DefaultDevice { }; config.speeds.split(',').forEach((speedEntry) => { try { - const [speedName, speedSynonyms] = speedEntry.trim().split('=').map(s => s.trim()); + const [speedName, speedSynonyms] = speedEntry + .trim() + .split('=') + .map((s) => s.trim()); attributes.availableFanSpeeds.speeds.push({ speed_name: speedName, - speed_values: [{ - speed_synonym: speedSynonyms.split(':').map(s => s.trim()), - lang: config.lang || 'en' - }] + speed_values: [ + { + speed_synonym: speedSynonyms.split(':').map((s) => s.trim()), + lang: config.lang || 'en' + } + ] }); - } catch { } + } catch {} }); return attributes; } diff --git a/functions/devices/lock.js b/functions/devices/lock.js index 3f96ca6a..5a05425c 100644 --- a/functions/devices/lock.js +++ b/functions/devices/lock.js @@ -6,9 +6,7 @@ class Lock extends DefaultDevice { } static getTraits() { - return [ - 'action.devices.traits.LockUnlock' - ]; + return ['action.devices.traits.LockUnlock']; } static get requiredItemTypes() { diff --git a/functions/devices/openclosedevice.js b/functions/devices/openclosedevice.js index 023b9c6b..5b375d70 100644 --- a/functions/devices/openclosedevice.js +++ b/functions/devices/openclosedevice.js @@ -2,10 +2,7 @@ const DefaultDevice = require('./default.js'); class OpenCloseDevice extends DefaultDevice { static getTraits() { - return [ - 'action.devices.traits.OpenClose', - 'action.devices.traits.StartStop' - ]; + return ['action.devices.traits.OpenClose', 'action.devices.traits.StartStop']; } static getAttributes(item) { diff --git a/functions/devices/scene.js b/functions/devices/scene.js index 89ec7f8e..87aad11f 100644 --- a/functions/devices/scene.js +++ b/functions/devices/scene.js @@ -6,9 +6,7 @@ class Scene extends DefaultDevice { } static getTraits() { - return [ - 'action.devices.traits.Scene' - ]; + return ['action.devices.traits.Scene']; } static get requiredItemTypes() { diff --git a/functions/devices/securitysystem.js b/functions/devices/securitysystem.js index 0adfea7b..685dd4b0 100644 --- a/functions/devices/securitysystem.js +++ b/functions/devices/securitysystem.js @@ -6,9 +6,7 @@ class SecuritySystem extends DefaultDevice { } static getTraits() { - return [ - 'action.devices.traits.ArmDisarm' - ]; + return ['action.devices.traits.ArmDisarm']; } static get requiredItemTypes() { diff --git a/functions/devices/sensor.js b/functions/devices/sensor.js index cd9c9fd8..43d4465d 100644 --- a/functions/devices/sensor.js +++ b/functions/devices/sensor.js @@ -6,9 +6,7 @@ class Sensor extends DefaultDevice { } static getTraits() { - return [ - 'action.devices.traits.SensorState' - ]; + return ['action.devices.traits.SensorState']; } static matchesItemType(item) { const config = this.getConfig(item); @@ -30,7 +28,7 @@ class Sensor extends DefaultDevice { } if ('states' in config) { attributes.sensorStatesSupported.descriptiveCapabilities = { - availableStates: config.states.split(',').map(s => s.trim().split('=')[0].trim()) + availableStates: config.states.split(',').map((s) => s.trim().split('=')[0].trim()) }; } return attributes; @@ -50,9 +48,9 @@ class Sensor extends DefaultDevice { static translateStateToGoogle(item) { const config = this.getConfig(item); if ('states' in config) { - const states = config.states.split(',').map(s => s.trim()) + const states = config.states.split(',').map((s) => s.trim()); for (const state of states) { - const [key, value] = state.split('=').map(s => s.trim()); + const [key, value] = state.split('=').map((s) => s.trim()); if (value == item.state) { return key; } diff --git a/functions/devices/simpleairpurifier.js b/functions/devices/simpleairpurifier.js index 4873845c..83ab6fc1 100644 --- a/functions/devices/simpleairpurifier.js +++ b/functions/devices/simpleairpurifier.js @@ -2,7 +2,7 @@ const Switch = require('./switch.js'); class SimpleAirPurifier extends Switch { static get type() { - return 'action.devices.types.AIRPURIFIER' + return 'action.devices.types.AIRPURIFIER'; } } diff --git a/functions/devices/speaker.js b/functions/devices/speaker.js index f3eaed64..b643b57e 100644 --- a/functions/devices/speaker.js +++ b/functions/devices/speaker.js @@ -6,9 +6,7 @@ class Speaker extends DefaultDevice { } static getTraits() { - return [ - 'action.devices.traits.Volume' - ]; + return ['action.devices.traits.Volume']; } static getAttributes(item) { diff --git a/functions/devices/specialcolorlight.js b/functions/devices/specialcolorlight.js index 9cd68cda..22588611 100644 --- a/functions/devices/specialcolorlight.js +++ b/functions/devices/specialcolorlight.js @@ -6,22 +6,22 @@ class SpecialColorLight extends DefaultDevice { } static getTraits() { - return [ - 'action.devices.traits.OnOff', - 'action.devices.traits.Brightness', - 'action.devices.traits.ColorSetting' - ]; + return ['action.devices.traits.OnOff', 'action.devices.traits.Brightness', 'action.devices.traits.ColorSetting']; } static matchesItemType(item) { - return item.type === 'Group' && Object.keys(this.getMembers(item)).length > 1 && (this.useKelvin(item) || !!this.getAttributes(item).colorTemperatureRange); + return ( + item.type === 'Group' && + Object.keys(this.getMembers(item)).length > 1 && + (this.useKelvin(item) || !!this.getAttributes(item).colorTemperatureRange) + ); } static getAttributes(item) { const attributes = {}; const config = this.getConfig(item); if ('colorTemperatureRange' in config) { - const [min, max] = config.colorTemperatureRange.split(',').map(s => Number(s.trim())); + const [min, max] = config.colorTemperatureRange.split(',').map((s) => Number(s.trim())); if (!isNaN(min) && !isNaN(max)) { attributes.colorTemperatureRange = { temperatureMinK: min, @@ -45,8 +45,11 @@ class SpecialColorLight extends DefaultDevice { try { const { temperatureMinK, temperatureMaxK } = this.getAttributes(item).colorTemperatureRange; state.color = {}; - state.color.temperatureK = this.useKelvin(item) ? Number(members[member].state) : temperatureMinK + ((temperatureMaxK - temperatureMinK) / 100 * (100 - Number(members[member].state)) || 0); - } catch { } + state.color.temperatureK = this.useKelvin(item) + ? Number(members[member].state) + : temperatureMinK + + (((temperatureMaxK - temperatureMinK) / 100) * (100 - Number(members[member].state)) || 0); + } catch {} break; } } @@ -54,15 +57,12 @@ class SpecialColorLight extends DefaultDevice { } static getMembers(item) { - const supportedMembers = [ - 'lightBrightness', - 'lightColorTemperature' - ]; + const supportedMembers = ['lightBrightness', 'lightColorTemperature']; const members = Object(); if (item.members && item.members.length) { - item.members.forEach(member => { + item.members.forEach((member) => { if (member.metadata && member.metadata.ga) { - const memberType = supportedMembers.find(m => member.metadata.ga.value.toLowerCase() === m.toLowerCase()); + const memberType = supportedMembers.find((m) => member.metadata.ga.value.toLowerCase() === m.toLowerCase()); if (memberType) { members[memberType] = { name: member.name, state: member.state }; } diff --git a/functions/devices/startstopswitch.js b/functions/devices/startstopswitch.js index ada0e559..0274f7fe 100644 --- a/functions/devices/startstopswitch.js +++ b/functions/devices/startstopswitch.js @@ -2,9 +2,7 @@ const DefaultDevice = require('./default.js'); class StartStopSwitch extends DefaultDevice { static getTraits() { - return [ - 'action.devices.traits.StartStop' - ]; + return ['action.devices.traits.StartStop']; } static get requiredItemTypes() { diff --git a/functions/devices/switch.js b/functions/devices/switch.js index 18ad7e80..aa2397e4 100644 --- a/functions/devices/switch.js +++ b/functions/devices/switch.js @@ -6,9 +6,7 @@ class Switch extends DefaultDevice { } static getTraits() { - return [ - 'action.devices.traits.OnOff' - ]; + return ['action.devices.traits.OnOff']; } static get requiredItemTypes() { diff --git a/functions/devices/temperaturesensor.js b/functions/devices/temperaturesensor.js index 90bac38f..7e58d911 100644 --- a/functions/devices/temperaturesensor.js +++ b/functions/devices/temperaturesensor.js @@ -7,9 +7,7 @@ class TemperatureSensor extends DefaultDevice { } static getTraits() { - return [ - 'action.devices.traits.TemperatureControl' - ]; + return ['action.devices.traits.TemperatureControl']; } static getAttributes(item) { @@ -24,7 +22,7 @@ class TemperatureSensor extends DefaultDevice { } static isCompatible(item = {}) { - return item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() == 'temperaturesensor' + return item.metadata && item.metadata.ga && item.metadata.ga.value.toLowerCase() == 'temperaturesensor'; } static getState(item) { diff --git a/functions/devices/thermostat.js b/functions/devices/thermostat.js index 645c7f12..9988bc03 100644 --- a/functions/devices/thermostat.js +++ b/functions/devices/thermostat.js @@ -7,9 +7,7 @@ class Thermostat extends DefaultDevice { } static getTraits() { - return [ - 'action.devices.traits.TemperatureSetting' - ]; + return ['action.devices.traits.TemperatureSetting']; } static matchesItemType(item) { @@ -22,18 +20,20 @@ class Thermostat extends DefaultDevice { thermostatTemperatureUnit: this.useFahrenheit(item) ? 'F' : 'C' }; if ('thermostatTemperatureRange' in config) { - const [min, max] = config.thermostatTemperatureRange.split(',').map(s => parseFloat(s.trim())); + const [min, max] = config.thermostatTemperatureRange.split(',').map((s) => parseFloat(s.trim())); if (!isNaN(min) && !isNaN(max)) { attributes.thermostatTemperatureRange = { minThresholdCelsius: min, maxThresholdCelsius: max - } + }; } } const members = this.getMembers(item); - if (('thermostatTemperatureAmbient' in members) && + if ( + 'thermostatTemperatureAmbient' in members && !('thermostatMode' in members) && - !('thermostatTemperatureSetpoint' in members)) { + !('thermostatTemperatureSetpoint' in members) + ) { attributes.queryOnlyTemperatureSetting = true; } else { attributes.availableThermostatModes = Object.keys(this.getModeMap(item)); @@ -68,14 +68,19 @@ class Thermostat extends DefaultDevice { ]; const members = Object(); if (item.members && item.members.length) { - item.members.forEach(member => { + item.members.forEach((member) => { if (member.metadata && member.metadata.ga) { - const memberType = supportedMembers.find(m => member.metadata.ga.value.toLowerCase() === m.toLowerCase()); + const memberType = supportedMembers.find((m) => member.metadata.ga.value.toLowerCase() === m.toLowerCase()); if (memberType) { members[memberType] = { name: member.name, state: member.state }; } } else { - if (this.hasTag(member, 'HeatingCoolingMode') || this.hasTag(member, 'homekit:HeatingCoolingMode') || this.hasTag(member, 'homekit:TargetHeatingCoolingMode') || this.hasTag(member, 'homekit:CurrentHeatingCoolingMode')) { + if ( + this.hasTag(member, 'HeatingCoolingMode') || + this.hasTag(member, 'homekit:HeatingCoolingMode') || + this.hasTag(member, 'homekit:TargetHeatingCoolingMode') || + this.hasTag(member, 'homekit:CurrentHeatingCoolingMode') + ) { members.thermostatMode = { name: member.name, state: member.state }; } if (this.hasTag(member, 'TargetTemperature') || this.hasTag(member, 'homekit:TargetTemperature')) { @@ -102,12 +107,12 @@ class Thermostat extends DefaultDevice { const config = this.getConfig(item); let modes = ['off', 'heat', 'cool', 'on', 'heatcool', 'auto', 'eco']; if ('modes' in config) { - modes = config.modes.split(',').map(s => s.trim()); + modes = config.modes.split(',').map((s) => s.trim()); } const modeMap = {}; - modes.forEach(pair => { - const [key, value] = pair.split('=').map(s => s.trim()); - modeMap[key] = value ? value.split(':').map(s => s.trim()) : [key]; + modes.forEach((pair) => { + const [key, value] = pair.split('=').map((s) => s.trim()); + modeMap[key] = value ? value.split(':').map((s) => s.trim()) : [key]; }); return modeMap; } diff --git a/functions/devices/tv.js b/functions/devices/tv.js index f16e989f..3df92c93 100644 --- a/functions/devices/tv.js +++ b/functions/devices/tv.js @@ -38,26 +38,30 @@ class TV extends DefaultDevice { if ('tvTransport' in members) { attributes.transportControlSupportedCommands = ['NEXT', 'PREVIOUS', 'PAUSE', 'RESUME']; if ('transportControlSupportedCommands' in config) { - attributes.transportControlSupportedCommands = config.transportControlSupportedCommands.split(',').map(s => s.toUpperCase()); + attributes.transportControlSupportedCommands = config.transportControlSupportedCommands + .split(',') + .map((s) => s.toUpperCase()); } } if ('tvInput' in members && 'availableInputs' in config) { attributes.availableInputs = []; - config.availableInputs.split(',').forEach(input => { + config.availableInputs.split(',').forEach((input) => { const [key, synonyms] = input.split('='); attributes.availableInputs.push({ key: key, - names: [{ - name_synonym: synonyms.split(':'), - lang: config.lang || 'en' - }] + names: [ + { + name_synonym: synonyms.split(':'), + lang: config.lang || 'en' + } + ] }); }); attributes.orderedInputs = config.orderedInputs === true; } if ('tvChannel' in members && 'availableChannels' in config) { attributes.availableChannels = []; - config.availableChannels.split(',').forEach(channel => { + config.availableChannels.split(',').forEach((channel) => { const [number, key, names] = channel.split('='); attributes.availableChannels.push({ key: key, @@ -90,7 +94,7 @@ class TV extends DefaultDevice { state.channelNumber = members[member].state; try { state.channelName = this.getChannelMap(item)[members[member].state][0]; - } catch { } + } catch {} break; } } @@ -98,19 +102,12 @@ class TV extends DefaultDevice { } static getMembers(item) { - const supportedMembers = [ - 'tvChannel', - 'tvVolume', - 'tvInput', - 'tvTransport', - 'tvPower', - 'tvMute' - ]; + const supportedMembers = ['tvChannel', 'tvVolume', 'tvInput', 'tvTransport', 'tvPower', 'tvMute']; const members = Object(); if (item.members && item.members.length) { - item.members.forEach(member => { + item.members.forEach((member) => { if (member.metadata && member.metadata.ga) { - const memberType = supportedMembers.find(m => member.metadata.ga.value.toLowerCase() === m.toLowerCase()); + const memberType = supportedMembers.find((m) => member.metadata.ga.value.toLowerCase() === m.toLowerCase()); if (memberType) { members[memberType] = { name: member.name, state: member.state }; } @@ -124,7 +121,7 @@ class TV extends DefaultDevice { const config = this.getConfig(item); const channelMap = {}; if ('availableChannels' in config) { - config.availableChannels.split(',').forEach(channel => { + config.availableChannels.split(',').forEach((channel) => { const [number, key, names] = channel.split('='); channelMap[number] = [...names.split(':'), key]; }); diff --git a/functions/devices/valve.js b/functions/devices/valve.js index dc3314d0..50369231 100644 --- a/functions/devices/valve.js +++ b/functions/devices/valve.js @@ -6,9 +6,7 @@ class Valve extends DefaultDevice { } static getTraits() { - return [ - 'action.devices.traits.OpenClose' - ]; + return ['action.devices.traits.OpenClose']; } static get requiredItemTypes() { diff --git a/functions/openhab.js b/functions/openhab.js index 53b6adce..f8d2e765 100644 --- a/functions/openhab.js +++ b/functions/openhab.js @@ -23,14 +23,14 @@ const glob = require('glob'); const Commands = []; const Devices = []; -glob.sync('./commands/*.js', { cwd: __dirname }).forEach(file => { +glob.sync('./commands/*.js', { cwd: __dirname }).forEach((file) => { const command = require(file); if (command.type) { Commands.push(command); } }); -glob.sync('./devices/*.js', { cwd: __dirname }).forEach(file => { +glob.sync('./devices/*.js', { cwd: __dirname }).forEach((file) => { const device = require(file); if (device.type) { Devices.push(device); @@ -80,12 +80,11 @@ class OpenHAB { this.setTokenFromHeader(headers); - const payload = await this.handleSync() - .catch(() => ({ - errorCode: 'actionNotAvailable', - status: 'ERROR', - devices: [] - })); + const payload = await this.handleSync().catch(() => ({ + errorCode: 'actionNotAvailable', + status: 'ERROR', + devices: [] + })); return { requestId: body.requestId, @@ -98,18 +97,18 @@ class OpenHAB { * @param {object} headers */ async onQuery(body, headers) { - const devices = body && body.inputs && body.inputs[0] && body.inputs[0].payload && body.inputs[0].payload.devices || []; + const devices = + (body && body.inputs && body.inputs[0] && body.inputs[0].payload && body.inputs[0].payload.devices) || []; console.log(`openhabGoogleAssistant - handleQuery - devices: ${JSON.stringify(devices)}`); this.setTokenFromHeader(headers); - const payload = await this.handleQuery(devices) - .catch(() => ({ - errorCode: 'actionNotAvailable', - status: 'ERROR', - devices: {} - })); + const payload = await this.handleQuery(devices).catch(() => ({ + errorCode: 'actionNotAvailable', + status: 'ERROR', + devices: {} + })); return { requestId: body.requestId, @@ -122,18 +121,18 @@ class OpenHAB { * @param {object} headers */ async onExecute(body, headers) { - const commands = body && body.inputs && body.inputs[0] && body.inputs[0].payload && body.inputs[0].payload.commands || []; + const commands = + (body && body.inputs && body.inputs[0] && body.inputs[0].payload && body.inputs[0].payload.commands) || []; console.log(`openhabGoogleAssistant - handleExecute - commands: ${JSON.stringify(commands)}`); this.setTokenFromHeader(headers); - const payload = await this.handleExecute(commands) - .catch(() => ({ - errorCode: 'actionNotAvailable', - status: 'ERROR', - commands: [] - })); + const payload = await this.handleExecute(commands).catch(() => ({ + errorCode: 'actionNotAvailable', + status: 'ERROR', + commands: [] + })); return { requestId: body.requestId, @@ -148,7 +147,10 @@ class OpenHAB { item.members = items.filter((member) => member.groupNames && member.groupNames.includes(item.name)); const DeviceType = OpenHAB.getDeviceForItem(item); if (DeviceType) { - console.log(`openhabGoogleAssistant - handleSync - SYNC is adding: ${item.type}:${item.name} with type: ${DeviceType.type}`); + console.log( + `openhabGoogleAssistant - handleSync - SYNC is adding: ${item.type}:${item.name}` + + ` with type: ${DeviceType.type}` + ); discoveredDevicesList.push(DeviceType.getMetadata(item)); } }); @@ -161,23 +163,34 @@ class OpenHAB { */ handleQuery(devices) { const payload = { devices: {} }; - const promises = devices.map((device) => ( - this._apiHandler.getItem(device.id).then((item) => { - const DeviceType = OpenHAB.getDeviceForItem(item); - if (!DeviceType) { - throw { statusCode: 404 }; - } - if (item.state === 'NULL' && !('getMembers' in DeviceType)) { - throw { statusCode: 406 }; - } - payload.devices[device.id] = Object.assign({ status: 'SUCCESS', online: true }, DeviceType.getState(item)); - }).catch((error) => ( - payload.devices[device.id] = { - status: 'ERROR', - errorCode: error.statusCode == 404 ? 'deviceNotFound' : error.statusCode == 400 ? 'notSupported' : error.statusCode == 406 ? 'deviceNotReady' : 'deviceOffline' - } - )) - )); + const promises = devices.map((device) => + this._apiHandler + .getItem(device.id) + .then((item) => { + const DeviceType = OpenHAB.getDeviceForItem(item); + if (!DeviceType) { + throw { statusCode: 404 }; + } + if (item.state === 'NULL' && !('getMembers' in DeviceType)) { + throw { statusCode: 406 }; + } + payload.devices[device.id] = Object.assign({ status: 'SUCCESS', online: true }, DeviceType.getState(item)); + }) + .catch( + (error) => + (payload.devices[device.id] = { + status: 'ERROR', + errorCode: + error.statusCode == 404 + ? 'deviceNotFound' + : error.statusCode == 400 + ? 'notSupported' + : error.statusCode == 406 + ? 'deviceNotReady' + : 'deviceOffline' + }) + ) + ); return Promise.all(promises).then(() => payload); } @@ -191,22 +204,32 @@ class OpenHAB { command.execution.forEach((execution) => { // Special handling of ThermostatTemperatureSetRange that requires updating two values if (execution.command === 'action.devices.commands.ThermostatTemperatureSetRange') { - const SetHigh = OpenHAB.getCommandType('action.devices.commands.ThermostatTemperatureSetpointHigh', execution.params); - const SetLow = OpenHAB.getCommandType('action.devices.commands.ThermostatTemperatureSetpointLow', execution.params); + const SetHigh = OpenHAB.getCommandType( + 'action.devices.commands.ThermostatTemperatureSetpointHigh', + execution.params + ); + const SetLow = OpenHAB.getCommandType( + 'action.devices.commands.ThermostatTemperatureSetpointLow', + execution.params + ); if (SetHigh && SetLow) { - promises.push(SetHigh.execute(this._apiHandler, command.devices, execution.params, execution.challenge).then(() => { - return SetLow.execute(this._apiHandler, command.devices, execution.params, execution.challenge); - })); + promises.push( + SetHigh.execute(this._apiHandler, command.devices, execution.params, execution.challenge).then(() => { + return SetLow.execute(this._apiHandler, command.devices, execution.params, execution.challenge); + }) + ); return; } } const CommandType = OpenHAB.getCommandType(execution.command, execution.params); if (!CommandType) { - promises.push(Promise.resolve({ - ids: command.devices.map((device) => device.id), - status: 'ERROR', - errorCode: 'functionNotSupported' - })); + promises.push( + Promise.resolve({ + ids: command.devices.map((device) => device.id), + status: 'ERROR', + errorCode: 'functionNotSupported' + }) + ); return; } promises.push(CommandType.execute(this._apiHandler, command.devices, execution.params, execution.challenge)); diff --git a/functions/utilities.js b/functions/utilities.js index bf42f3ee..8dbbb548 100644 --- a/functions/utilities.js +++ b/functions/utilities.js @@ -24,14 +24,14 @@ module.exports = { * @returns {number} temperature value converted to Celcius */ convertToCelsius: (value) => { - return Number(((value - 32) * 5 / 9).toFixed(1)); + return Number((((value - 32) * 5) / 9).toFixed(1)); }, /** * @param {number} value temperature in Celcius * @returns {number} temperature value converted to Fahrenheit */ convertToFahrenheit: (value) => { - return Math.round(value * 9 / 5 + 32); + return Math.round((value * 9) / 5 + 32); }, /** * @param {number} kelvin color temperature as Kelvin @@ -40,12 +40,15 @@ module.exports = { kelvin2rgb: (kelvin) => { const temp = kelvin / 100; const r = temp <= 66 ? 255 : 329.698727446 * Math.pow(temp - 60, -0.1332047592); - const g = temp <= 66 ? 99.4708025861 * Math.log(temp) - 161.1195681661 : 288.1221695283 * Math.pow(temp - 60, -0.0755148492); + const g = + temp <= 66 + ? 99.4708025861 * Math.log(temp) - 161.1195681661 + : 288.1221695283 * Math.pow(temp - 60, -0.0755148492); const b = temp <= 66 ? (temp <= 19 ? 0 : 138.5177312231 * Math.log(temp - 10) - 305.0447927307) : 255; return { r: r < 0 ? 0 : r > 255 ? 255 : Math.round(r), g: g < 0 ? 0 : g > 255 ? 255 : Math.round(g), - b: b < 0 ? 0 : b > 255 ? 255 : Math.round(b), + b: b < 0 ? 0 : b > 255 ? 255 : Math.round(b) }; }, /** @@ -56,12 +59,13 @@ module.exports = { r = r / 255; g = g / 255; b = b / 255; - let v = Math.max(r, g, b), n = v - Math.min(r, g, b); - let h = n && ((v == r) ? (g - b) / n : ((v == g) ? 2 + (b - r) / n : 4 + (r - g) / n)); + let v = Math.max(r, g, b), + n = v - Math.min(r, g, b); + let h = n && (v == r ? (g - b) / n : v == g ? 2 + (b - r) / n : 4 + (r - g) / n); return { hue: Math.round(60 * (h < 0 ? h + 6 : h) * 100) / 100, - saturation: Math.round(v && n / v * 100) / 100, + saturation: Math.round(v && (n / v) * 100) / 100, value: Math.round(v * 100) / 100 }; } -} +}; diff --git a/testServer.js b/testServer.js index ecafac1f..182425dd 100644 --- a/testServer.js +++ b/testServer.js @@ -1,6 +1,6 @@ const express = require('express'); const app = express(); -const bodyParser = require('body-parser') +const bodyParser = require('body-parser'); const openhabGA = require('./functions/index.js'); app.use(bodyParser.json()); diff --git a/tests/apihandler.test.js b/tests/apihandler.test.js index 82bdd606..f8ee572d 100644 --- a/tests/apihandler.test.js +++ b/tests/apihandler.test.js @@ -3,84 +3,84 @@ const nock = require('nock'); describe('ApiHandler', () => { const config = { - "host": "example.org", - "path": "items", - "port": 443 - } + host: 'example.org', + path: 'items', + port: 443 + }; const apiHander = new ApiHandler(config); beforeEach(() => { - apiHander.authToken = "token"; + apiHander.authToken = 'token'; }); test('constructor', () => { expect(apiHander._config).toStrictEqual({ - "host": "example.org", - "path": "/items/", - "port": 443 + host: 'example.org', + path: '/items/', + port: 443 }); - expect(apiHander._authToken).toBe("token"); + expect(apiHander._authToken).toBe('token'); }); test('authToken', () => { - apiHander.authToken = "1234"; - expect(apiHander._authToken).toBe("1234"); + apiHander.authToken = '1234'; + expect(apiHander._authToken).toBe('1234'); }); describe('getOptions', () => { test('getOptions GET all items', () => { - expect(apiHander.getOptions("GET", "", 0)).toStrictEqual({ - "headers": { - "Accept": "application/json", - "Authorization": "Bearer token" + expect(apiHander.getOptions('GET', '', 0)).toStrictEqual({ + headers: { + Accept: 'application/json', + Authorization: 'Bearer token' }, - "hostname": "example.org", - "method": "GET", - "path": "/items/?metadata=ga,synonyms&fields=groupNames,groupType,name,label,metadata,tags,type", - "port": 443 + hostname: 'example.org', + method: 'GET', + path: '/items/?metadata=ga,synonyms&fields=groupNames,groupType,name,label,metadata,tags,type', + port: 443 }); }); test('getOptions GET single items', () => { - expect(apiHander.getOptions("GET", "TestItem", 0)).toStrictEqual({ - "headers": { - "Accept": "application/json", - "Authorization": "Bearer token" + expect(apiHander.getOptions('GET', 'TestItem', 0)).toStrictEqual({ + headers: { + Accept: 'application/json', + Authorization: 'Bearer token' }, - "hostname": "example.org", - "method": "GET", - "path": "/items/TestItem?metadata=ga,synonyms", - "port": 443 + hostname: 'example.org', + method: 'GET', + path: '/items/TestItem?metadata=ga,synonyms', + port: 443 }); }); test('getOptions POST', () => { - expect(apiHander.getOptions("POST", "TestItem", 10)).toStrictEqual({ - "headers": { - "Accept": "application/json", - "Authorization": "Bearer token", - "Content-Length": 10, - "Content-Type": "text/plain" + expect(apiHander.getOptions('POST', 'TestItem', 10)).toStrictEqual({ + headers: { + Accept: 'application/json', + Authorization: 'Bearer token', + 'Content-Length': 10, + 'Content-Type': 'text/plain' }, - "hostname": "example.org", - "method": "POST", - "path": "/items/TestItem?metadata=ga,synonyms", - "port": 443 + hostname: 'example.org', + method: 'POST', + path: '/items/TestItem?metadata=ga,synonyms', + port: 443 }); }); test('getOptions GET userpass', () => { - apiHander._config.userpass = "tester:test"; - expect(apiHander.getOptions("GET", "TestItem", 0)).toStrictEqual({ - "auth": "tester:test", - "headers": { - "Accept": "application/json" + apiHander._config.userpass = 'tester:test'; + expect(apiHander.getOptions('GET', 'TestItem', 0)).toStrictEqual({ + auth: 'tester:test', + headers: { + Accept: 'application/json' }, - "hostname": "example.org", - "method": "GET", - "path": "/items/TestItem?metadata=ga,synonyms", - "port": 443 + hostname: 'example.org', + method: 'GET', + path: '/items/TestItem?metadata=ga,synonyms', + port: 443 }); }); }); @@ -93,25 +93,23 @@ describe('ApiHandler', () => { test('getItem', async () => { const scope = nock('https://example.org') .get('/items/TestItem?metadata=ga,synonyms') - .reply(200, [{ "name": "TestItem" }]); - const result = await apiHander.getItem("TestItem"); - expect(result).toStrictEqual([{ "name": "TestItem" }]); + .reply(200, [{ name: 'TestItem' }]); + const result = await apiHander.getItem('TestItem'); + expect(result).toStrictEqual([{ name: 'TestItem' }]); expect(scope.isDone()).toBe(true); }); test('getItem failed', async () => { - const scope = nock('https://example.org') - .get('/items/TestItem?metadata=ga,synonyms') - .reply(400, {}); + const scope = nock('https://example.org').get('/items/TestItem?metadata=ga,synonyms').reply(400, {}); let error = {}; try { - await apiHander.getItem("TestItem"); + await apiHander.getItem('TestItem'); } catch (e) { error = e; } expect(error).toStrictEqual({ - "message": "getItem failed", - "statusCode": 400 + message: 'getItem failed', + statusCode: 400 }); expect(scope.isDone()).toBe(true); }); @@ -125,9 +123,9 @@ describe('ApiHandler', () => { test('getItems', async () => { const scope = nock('https://example.org') .get('/items/?metadata=ga,synonyms&fields=groupNames,groupType,name,label,metadata,tags,type') - .reply(200, [{ "name": "TestItem" }]); + .reply(200, [{ name: 'TestItem' }]); const result = await apiHander.getItems(); - expect(result).toStrictEqual([{ "name": "TestItem" }]); + expect(result).toStrictEqual([{ name: 'TestItem' }]); expect(scope.isDone()).toBe(true); }); @@ -142,8 +140,8 @@ describe('ApiHandler', () => { error = e; } expect(error).toStrictEqual({ - "message": "getItem failed", - "statusCode": 400 + message: 'getItem failed', + statusCode: 400 }); expect(scope.isDone()).toBe(true); }); @@ -157,25 +155,23 @@ describe('ApiHandler', () => { test('sendCommand', async () => { const scope = nock('https://example.org') .post('/items/TestItem?metadata=ga,synonyms') - .reply(200, [{ "name": "TestItem" }]); - const result = await apiHander.sendCommand("TestItem", "OFF"); + .reply(200, [{ name: 'TestItem' }]); + const result = await apiHander.sendCommand('TestItem', 'OFF'); expect(result).toBeUndefined(); expect(scope.isDone()).toBe(true); }); test('sendCommand failed', async () => { - const scope = nock('https://example.org') - .post('/items/TestItem?metadata=ga,synonyms') - .reply(400, {}); + const scope = nock('https://example.org').post('/items/TestItem?metadata=ga,synonyms').reply(400, {}); let error = {}; try { - await apiHander.sendCommand("TestItem", "OFF"); + await apiHander.sendCommand('TestItem', 'OFF'); } catch (e) { error = e; } expect(error).toStrictEqual({ - "message": "sendCommand failed", - "statusCode": 400 + message: 'sendCommand failed', + statusCode: 400 }); expect(scope.isDone()).toBe(true); }); diff --git a/tests/commands/activatescene.test.js b/tests/commands/activatescene.test.js index 94c359b2..c67f9380 100644 --- a/tests/commands/activatescene.test.js +++ b/tests/commands/activatescene.test.js @@ -3,18 +3,18 @@ const Command = require('../../functions/commands/activatescene.js'); describe('ActivateScene Command', () => { test('validateParams', () => { expect(Command.validateParams({})).toBe(true); - expect(Command.validateParams({ "deactivate": true })).toBe(true); + expect(Command.validateParams({ deactivate: true })).toBe(true); }); describe('convertParamsToValue', () => { test('convertParamsToValue', () => { - expect(Command.convertParamsToValue({ "deactivate": true }, {}, {})).toBe("OFF"); - expect(Command.convertParamsToValue({ "deactivate": false }, {}, {})).toBe("ON"); + expect(Command.convertParamsToValue({ deactivate: true }, {}, {})).toBe('OFF'); + expect(Command.convertParamsToValue({ deactivate: false }, {}, {})).toBe('ON'); }); test('convertParamsToValue inverted', () => { - expect(Command.convertParamsToValue({ "deactivate": true }, {}, { "customData": { "inverted": true } })).toBe("ON"); - expect(Command.convertParamsToValue({ "deactivate": false }, {}, { "customData": { "inverted": true } })).toBe("OFF"); + expect(Command.convertParamsToValue({ deactivate: true }, {}, { customData: { inverted: true } })).toBe('ON'); + expect(Command.convertParamsToValue({ deactivate: false }, {}, { customData: { inverted: true } })).toBe('OFF'); }); }); }); diff --git a/tests/commands/armdisarm.test.js b/tests/commands/armdisarm.test.js index 5c55f1ed..bf3ddeea 100644 --- a/tests/commands/armdisarm.test.js +++ b/tests/commands/armdisarm.test.js @@ -3,24 +3,24 @@ const Command = require('../../functions/commands/armdisarm.js'); describe('ArmDisarm Command', () => { test('validateParams', () => { expect(Command.validateParams({})).toBe(false); - expect(Command.validateParams({ "arm": true })).toBe(true); - expect(Command.validateParams({ "arm": "true" })).toBe(false); + expect(Command.validateParams({ arm: true })).toBe(true); + expect(Command.validateParams({ arm: 'true' })).toBe(false); }); describe('convertParamsToValue', () => { test('convertParamsToValue', () => { - expect(Command.convertParamsToValue({ "arm": true }, {}, {})).toBe("ON"); - expect(Command.convertParamsToValue({ "arm": false }, {}, {})).toBe("OFF"); + expect(Command.convertParamsToValue({ arm: true }, {}, {})).toBe('ON'); + expect(Command.convertParamsToValue({ arm: false }, {}, {})).toBe('OFF'); }); test('convertParamsToValue inverted', () => { - expect(Command.convertParamsToValue({ "arm": true }, {}, { "customData": { "inverted": true } })).toBe("OFF"); - expect(Command.convertParamsToValue({ "arm": false }, {}, { "customData": { "inverted": true } })).toBe("ON"); + expect(Command.convertParamsToValue({ arm: true }, {}, { customData: { inverted: true } })).toBe('OFF'); + expect(Command.convertParamsToValue({ arm: false }, {}, { customData: { inverted: true } })).toBe('ON'); }); }); test('getResponseStates', () => { - expect(Command.getResponseStates({ "arm": true })).toStrictEqual({ "isArmed": true }); - expect(Command.getResponseStates({ "arm": false })).toStrictEqual({ "isArmed": false }); + expect(Command.getResponseStates({ arm: true })).toStrictEqual({ isArmed: true }); + expect(Command.getResponseStates({ arm: false })).toStrictEqual({ isArmed: false }); }); }); diff --git a/tests/commands/brightnessabsolute.test.js b/tests/commands/brightnessabsolute.test.js index 9ed00881..01637c10 100644 --- a/tests/commands/brightnessabsolute.test.js +++ b/tests/commands/brightnessabsolute.test.js @@ -3,43 +3,45 @@ const Command = require('../../functions/commands/brightnessabsolute.js'); describe('BrightnessAbsolute Command', () => { test('validateParams', () => { expect(Command.validateParams({})).toBe(false); - expect(Command.validateParams({ "brightness": 100 })).toBe(true); - expect(Command.validateParams({ "brightness": "100" })).toBe(false); + expect(Command.validateParams({ brightness: 100 })).toBe(true); + expect(Command.validateParams({ brightness: '100' })).toBe(false); }); test('requiresItem', () => { expect(Command.requiresItem({})).toBe(false); - expect(Command.requiresItem({ "customData": {} })).toBe(false); - expect(Command.requiresItem({ "customData": { "deviceType": "SpecialColorLight" } })).toBe(true); + expect(Command.requiresItem({ customData: {} })).toBe(false); + expect(Command.requiresItem({ customData: { deviceType: 'SpecialColorLight' } })).toBe(true); }); test('getItemName', () => { - expect(Command.getItemName({ "name": "Item" }, {})).toBe("Item"); - expect(Command.getItemName({ "name": "Item" }, { "customData": {} })).toBe("Item"); - expect(() => { Command.getItemName({ "name": "Item" }, { "customData": { "deviceType": "SpecialColorLight" } }) }).toThrow(); + expect(Command.getItemName({ name: 'Item' }, {})).toBe('Item'); + expect(Command.getItemName({ name: 'Item' }, { customData: {} })).toBe('Item'); + expect(() => { + Command.getItemName({ name: 'Item' }, { customData: { deviceType: 'SpecialColorLight' } }); + }).toThrow(); const item = { - "name": "Item", - "members": [ + name: 'Item', + members: [ { - "name": "BrightnessItem", - "metadata": { - "ga": { - "value": "lightBrightness" + name: 'BrightnessItem', + metadata: { + ga: { + value: 'lightBrightness' } } } ] }; - expect(Command.getItemName(item, { "customData": { "deviceType": "SpecialColorLight" } })).toBe("BrightnessItem"); + expect(Command.getItemName(item, { customData: { deviceType: 'SpecialColorLight' } })).toBe('BrightnessItem'); }); test('convertParamsToValue', () => { - expect(Command.convertParamsToValue({ "brightness": 0 })).toBe("0"); - expect(Command.convertParamsToValue({ "brightness": 100 })).toBe("100"); + expect(Command.convertParamsToValue({ brightness: 0 })).toBe('0'); + expect(Command.convertParamsToValue({ brightness: 100 })).toBe('100'); }); test('getResponseStates', () => { - expect(Command.getResponseStates({ "brightness": 0 })).toStrictEqual({ "brightness": 0 }); - expect(Command.getResponseStates({ "brightness": 100 })).toStrictEqual({ "brightness": 100 }); + expect(Command.getResponseStates({ brightness: 0 })).toStrictEqual({ brightness: 0 }); + expect(Command.getResponseStates({ brightness: 100 })).toStrictEqual({ brightness: 100 }); }); }); diff --git a/tests/commands/colorabsolute.test.js b/tests/commands/colorabsolute.test.js index 0e2cae4f..04fd6ea0 100644 --- a/tests/commands/colorabsolute.test.js +++ b/tests/commands/colorabsolute.test.js @@ -2,26 +2,26 @@ const Command = require('../../functions/commands/colorabsolute.js'); describe('ColorAbsolute Command', () => { const params = { - "color": { - "spectrumHSV": { "hue": 10, "saturation": 0.2, "value": 0.3 } + color: { + spectrumHSV: { hue: 10, saturation: 0.2, value: 0.3 } } }; test('validateParams', () => { expect(Command.validateParams({})).toBe(false); - expect(Command.validateParams({ "color": {} })).toBe(false); + expect(Command.validateParams({ color: {} })).toBe(false); expect(Command.validateParams(params)).toBe(true); }); test('convertParamsToValue', () => { - expect(Command.convertParamsToValue(params, {}, { "customData": { "deviceType": "ColorLight" } })).toBe("10,20,30"); - expect(() => (Command.convertParamsToValue(params, {}, { "customData": { "deviceType": "Light" } }))).toThrow(); + expect(Command.convertParamsToValue(params, {}, { customData: { deviceType: 'ColorLight' } })).toBe('10,20,30'); + expect(() => Command.convertParamsToValue(params, {}, { customData: { deviceType: 'Light' } })).toThrow(); }); test('getResponseStates', () => { expect(Command.getResponseStates(params)).toStrictEqual({ - "color": { - "spectrumHsv": { "hue": 10, "saturation": 0.2, "value": 0.3 } + color: { + spectrumHsv: { hue: 10, saturation: 0.2, value: 0.3 } } }); }); diff --git a/tests/commands/colorabsolutetemperature.test.js b/tests/commands/colorabsolutetemperature.test.js index d9e67ffe..36787c99 100644 --- a/tests/commands/colorabsolutetemperature.test.js +++ b/tests/commands/colorabsolutetemperature.test.js @@ -2,14 +2,14 @@ const Command = require('../../functions/commands/colorabsolutetemperature.js'); describe('ColorAbsoluteTemperature Command', () => { const params = { - "color": { - "temperature": 2000 + color: { + temperature: 2000 } }; test('validateParams', () => { expect(Command.validateParams({})).toBe(false); - expect(Command.validateParams({ "color": {} })).toBe(false); + expect(Command.validateParams({ color: {} })).toBe(false); expect(Command.validateParams(params)).toBe(true); }); @@ -18,65 +18,67 @@ describe('ColorAbsoluteTemperature Command', () => { }); test('getItemName', () => { - expect(Command.getItemName({ "name": "Item" }, {})).toBe("Item"); - expect(Command.getItemName({ "name": "Item" }, { "customData": {} })).toBe("Item"); - expect(() => { Command.getItemName({ "name": "Item" }, { "customData": { "deviceType": "SpecialColorLight" } }) }).toThrow(); + expect(Command.getItemName({ name: 'Item' }, {})).toBe('Item'); + expect(Command.getItemName({ name: 'Item' }, { customData: {} })).toBe('Item'); + expect(() => { + Command.getItemName({ name: 'Item' }, { customData: { deviceType: 'SpecialColorLight' } }); + }).toThrow(); const item = { - "name": "Item", - "members": [ + name: 'Item', + members: [ { - "name": "ColorItem", - "metadata": { - "ga": { - "value": "lightColorTemperature" + name: 'ColorItem', + metadata: { + ga: { + value: 'lightColorTemperature' } } } ] }; - expect(Command.getItemName(item, { "customData": { "deviceType": "SpecialColorLight" } })).toBe("ColorItem"); + expect(Command.getItemName(item, { customData: { deviceType: 'SpecialColorLight' } })).toBe('ColorItem'); }); describe('convertParamsToValue', () => { test('convertParamsToValue', () => { - expect(Command.convertParamsToValue(params, { "state": "100,100,50" }, {})).toBe("30.62,95,50"); + expect(Command.convertParamsToValue(params, { state: '100,100,50' }, {})).toBe('30.62,95,50'); }); test('convertParamsToValue SpecialColorLight', () => { const item = { - "metadata": { - "ga": { - "config": { - "colorTemperatureRange": "1000,5000" + metadata: { + ga: { + config: { + colorTemperatureRange: '1000,5000' } } } }; - const device = { "customData": { "deviceType": "SpecialColorLight" } }; - expect(Command.convertParamsToValue(params, item, device)).toBe("75"); - expect(Command.convertParamsToValue(params, { "state": "100,100,50" }, device)).toBe("0"); + const device = { customData: { deviceType: 'SpecialColorLight' } }; + expect(Command.convertParamsToValue(params, item, device)).toBe('75'); + expect(Command.convertParamsToValue(params, { state: '100,100,50' }, device)).toBe('0'); }); test('convertParamsToValue SpecialColorLight Kelvin', () => { const item = { - "metadata": { - "ga": { - "config": { - "colorTemperatureRange": "1000,5000", - "useKelvin": true + metadata: { + ga: { + config: { + colorTemperatureRange: '1000,5000', + useKelvin: true } } } }; - const device = { "customData": { "deviceType": "SpecialColorLight" } }; - expect(Command.convertParamsToValue(params, item, device)).toBe("2000"); + const device = { customData: { deviceType: 'SpecialColorLight' } }; + expect(Command.convertParamsToValue(params, item, device)).toBe('2000'); }); }); test('getResponseStates', () => { expect(Command.getResponseStates(params)).toStrictEqual({ - "color": { - "temperatureK": 2000 + color: { + temperatureK: 2000 } }); }); diff --git a/tests/commands/default.test.js b/tests/commands/default.test.js index bbe62a90..f36a4803 100644 --- a/tests/commands/default.test.js +++ b/tests/commands/default.test.js @@ -50,7 +50,7 @@ describe('Default Command', () => { }); test('getItemName', () => { - expect(Command.getItemName({ "name": "Item" }, {})).toBe("Item"); + expect(Command.getItemName({ name: 'Item' }, {})).toBe('Item'); }); test('requiresItem', () => { @@ -58,18 +58,18 @@ describe('Default Command', () => { }); test('handleAuthPin', () => { - expect(Command.handleAuthPin({ "id": "Item", "customData": {} }, undefined)).toBeUndefined(); - expect(Command.handleAuthPin({ "id": "Item", "customData": { "pinNeeded": "1234" } }, { "pin": "1234" })).toBeUndefined(); - expect(Command.handleAuthPin({ "id": "Item", "customData": { "pinNeeded": "1234" } }, undefined)).toStrictEqual({ - ids: ["Item"], + expect(Command.handleAuthPin({ id: 'Item', customData: {} }, undefined)).toBeUndefined(); + expect(Command.handleAuthPin({ id: 'Item', customData: { pinNeeded: '1234' } }, { pin: '1234' })).toBeUndefined(); + expect(Command.handleAuthPin({ id: 'Item', customData: { pinNeeded: '1234' } }, undefined)).toStrictEqual({ + ids: ['Item'], status: 'ERROR', errorCode: 'challengeNeeded', challengeNeeded: { type: 'pinNeeded' } }); - expect(Command.handleAuthPin({ "id": "Item", "customData": { "pinNeeded": "1234" } }, { "pin": "5678" })).toStrictEqual({ - ids: ["Item"], + expect(Command.handleAuthPin({ id: 'Item', customData: { pinNeeded: '1234' } }, { pin: '5678' })).toStrictEqual({ + ids: ['Item'], status: 'ERROR', errorCode: 'challengeNeeded', challengeNeeded: { @@ -79,12 +79,14 @@ describe('Default Command', () => { }); test('handleAuthAck', () => { - expect(Command.handleAuthAck({ "id": "Item", "customData": {} }, {}, undefined)).toBeUndefined(); - expect(Command.handleAuthAck({ "id": "Item", "customData": { "ackNeeded": true } }, { "ack": true }, undefined)).toBeUndefined(); - expect(Command.handleAuthAck({ "id": "Item", "customData": { "ackNeeded": true } }, {}, { "key": "value" })).toStrictEqual({ - ids: ["Item"], + expect(Command.handleAuthAck({ id: 'Item', customData: {} }, {}, undefined)).toBeUndefined(); + expect( + Command.handleAuthAck({ id: 'Item', customData: { ackNeeded: true } }, { ack: true }, undefined) + ).toBeUndefined(); + expect(Command.handleAuthAck({ id: 'Item', customData: { ackNeeded: true } }, {}, { key: 'value' })).toStrictEqual({ + ids: ['Item'], status: 'ERROR', - states: { "key": "value" }, + states: { key: 'value' }, errorCode: 'challengeNeeded', challengeNeeded: { type: 'ackNeeded' @@ -96,7 +98,7 @@ describe('Default Command', () => { const getItemMock = jest.fn(); const sendCommandMock = jest.fn(); sendCommandMock.mockReturnValue(Promise.resolve()); - getItemMock.mockReturnValue(Promise.resolve({ "name": "TestItem" })); + getItemMock.mockReturnValue(Promise.resolve({ name: 'TestItem' })); const apiHandler = { getItem: getItemMock, @@ -104,12 +106,12 @@ describe('Default Command', () => { }; const successResponse = { - "ids": ["Item1"], - "states": { - "on": true, - "online": true, + ids: ['Item1'], + states: { + on: true, + online: true }, - "status": "SUCCESS" + status: 'SUCCESS' }; beforeEach(() => { @@ -118,38 +120,38 @@ describe('Default Command', () => { }); test('execute without responseStates', async () => { - const devices = [{ "id": "Item1" }]; + const devices = [{ id: 'Item1' }]; const result = await TestCommand1.execute(apiHandler, devices, {}, {}); expect(getItemMock).toHaveBeenCalledTimes(0); expect(sendCommandMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual([ { - "ids": ["Item1"], - "states": {}, - "status": "SUCCESS" + ids: ['Item1'], + states: {}, + status: 'SUCCESS' } ]); }); test('execute without sent command', async () => { - const devices = [{ "id": "Item1" }]; - const result = await TestCommand3.execute(apiHandler, devices, { "on": true }, {}); + const devices = [{ id: 'Item1' }]; + const result = await TestCommand3.execute(apiHandler, devices, { on: true }, {}); expect(getItemMock).toHaveBeenCalledTimes(0); expect(sendCommandMock).toHaveBeenCalledTimes(0); expect(result).toStrictEqual([successResponse]); }); test('execute without getItem', async () => { - const devices = [{ "id": "Item1" }]; - const result = await TestCommand1.execute(apiHandler, devices, { "on": true }, {}); + const devices = [{ id: 'Item1' }]; + const result = await TestCommand1.execute(apiHandler, devices, { on: true }, {}); expect(getItemMock).toHaveBeenCalledTimes(0); expect(sendCommandMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual([successResponse]); }); test('execute with getItem', async () => { - const devices = [{ "id": "Item1" }]; - const result = await TestCommand2.execute(apiHandler, devices, { "on": true }, {}); + const devices = [{ id: 'Item1' }]; + const result = await TestCommand2.execute(apiHandler, devices, { on: true }, {}); expect(getItemMock).toHaveBeenCalledTimes(1); expect(sendCommandMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual([successResponse]); @@ -157,131 +159,131 @@ describe('Default Command', () => { test('execute with multiple getItem', async () => { const successResponse2 = Object.assign({}, successResponse); - successResponse2.ids = ["Item2"]; - const devices = [{ "id": "Item1" }, { "id": "Item2" }]; - const result = await TestCommand2.execute(apiHandler, devices, { "on": true }, {}); + successResponse2.ids = ['Item2']; + const devices = [{ id: 'Item1' }, { id: 'Item2' }]; + const result = await TestCommand2.execute(apiHandler, devices, { on: true }, {}); expect(getItemMock).toHaveBeenCalledTimes(2); expect(sendCommandMock).toHaveBeenCalledTimes(2); expect(result).toStrictEqual([successResponse, successResponse2]); }); test('execute with pinNeeded', async () => { - const devices = [{ "id": "Item1", "customData": { "pinNeeded": "1234" } }]; - const result = await TestCommand1.execute(apiHandler, devices, { "on": true }, {}); + const devices = [{ id: 'Item1', customData: { pinNeeded: '1234' } }]; + const result = await TestCommand1.execute(apiHandler, devices, { on: true }, {}); expect(getItemMock).toHaveBeenCalledTimes(0); expect(sendCommandMock).toHaveBeenCalledTimes(0); expect(result).toStrictEqual([ { - "ids": ["Item1"], - "challengeNeeded": { - "type": "pinNeeded", + ids: ['Item1'], + challengeNeeded: { + type: 'pinNeeded' }, - "errorCode": "challengeNeeded", - "status": "ERROR" + errorCode: 'challengeNeeded', + status: 'ERROR' } ]); }); test('execute with corrrect pin', async () => { - const devices = [{ "id": "Item1", "customData": { "pinNeeded": "1234" } }]; - const result = await TestCommand1.execute(apiHandler, devices, { "on": true }, { "pin": "1234" }); + const devices = [{ id: 'Item1', customData: { pinNeeded: '1234' } }]; + const result = await TestCommand1.execute(apiHandler, devices, { on: true }, { pin: '1234' }); expect(getItemMock).toHaveBeenCalledTimes(0); expect(sendCommandMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual([successResponse]); }); test('execute with ackNeeded', async () => { - const devices = [{ "id": "Item1", "customData": { "ackNeeded": true } }]; - const result = await TestCommand1.execute(apiHandler, devices, { "on": true }, {}); + const devices = [{ id: 'Item1', customData: { ackNeeded: true } }]; + const result = await TestCommand1.execute(apiHandler, devices, { on: true }, {}); expect(getItemMock).toHaveBeenCalledTimes(0); expect(sendCommandMock).toHaveBeenCalledTimes(0); expect(result).toStrictEqual([ { - "ids": ["Item1"], - "challengeNeeded": { - "type": "ackNeeded", + ids: ['Item1'], + challengeNeeded: { + type: 'ackNeeded' }, - "errorCode": "challengeNeeded", - "states": { - "on": true, - "online": true, + errorCode: 'challengeNeeded', + states: { + on: true, + online: true }, - "status": "ERROR" + status: 'ERROR' } ]); }); test('execute with ackNeeded and state', async () => { - const devices = [{ "id": "Item1", "customData": { "ackNeeded": true } }]; - const result = await TestCommand2.execute(apiHandler, devices, { "on": true }, {}); + const devices = [{ id: 'Item1', customData: { ackNeeded: true } }]; + const result = await TestCommand2.execute(apiHandler, devices, { on: true }, {}); expect(getItemMock).toHaveBeenCalledTimes(1); expect(sendCommandMock).toHaveBeenCalledTimes(0); expect(result).toStrictEqual([ { - "ids": ["Item1"], - "challengeNeeded": { - "type": "ackNeeded", + ids: ['Item1'], + challengeNeeded: { + type: 'ackNeeded' }, - "errorCode": "challengeNeeded", - "states": { - "on": true, - "online": true, + errorCode: 'challengeNeeded', + states: { + on: true, + online: true }, - "status": "ERROR" + status: 'ERROR' } ]); }); test('execute with ack', async () => { - const devices = [{ "id": "Item1", "customData": { "ackNeeded": true } }]; - const result = await TestCommand1.execute(apiHandler, devices, { "on": true }, { "ack": true }); + const devices = [{ id: 'Item1', customData: { ackNeeded: true } }]; + const result = await TestCommand1.execute(apiHandler, devices, { on: true }, { ack: true }); expect(getItemMock).toHaveBeenCalledTimes(0); expect(sendCommandMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual([successResponse]); }); test('execute with device not found', async () => { - getItemMock.mockReturnValue(Promise.reject({ "statusCode": "404" })); - const devices = [{ "id": "Item1" }]; - const result = await TestCommand2.execute(apiHandler, devices, { "on": true }, {}); + getItemMock.mockReturnValue(Promise.reject({ statusCode: '404' })); + const devices = [{ id: 'Item1' }]; + const result = await TestCommand2.execute(apiHandler, devices, { on: true }, {}); expect(getItemMock).toHaveBeenCalledTimes(1); expect(sendCommandMock).toHaveBeenCalledTimes(0); - expect(result).toStrictEqual([{ - "errorCode": "deviceNotFound", - "ids": [ - "Item1", - ], - "status": "ERROR" - }]); + expect(result).toStrictEqual([ + { + errorCode: 'deviceNotFound', + ids: ['Item1'], + status: 'ERROR' + } + ]); }); test('execute with not supported', async () => { - const devices = [{ "id": "Item1" }]; - const result = await TestCommand4.execute(apiHandler, devices, { "on": true }, {}); + const devices = [{ id: 'Item1' }]; + const result = await TestCommand4.execute(apiHandler, devices, { on: true }, {}); expect(getItemMock).toHaveBeenCalledTimes(0); expect(sendCommandMock).toHaveBeenCalledTimes(0); - expect(result).toStrictEqual([{ - "errorCode": "notSupported", - "ids": [ - "Item1", - ], - "status": "ERROR" - }]); + expect(result).toStrictEqual([ + { + errorCode: 'notSupported', + ids: ['Item1'], + status: 'ERROR' + } + ]); }); test('execute with device offline', async () => { - sendCommandMock.mockReturnValue(Promise.reject({ "statusCode": 500 })); - const devices = [{ "id": "Item1" }]; - const result = await TestCommand1.execute(apiHandler, devices, { "on": true }, {}); + sendCommandMock.mockReturnValue(Promise.reject({ statusCode: 500 })); + const devices = [{ id: 'Item1' }]; + const result = await TestCommand1.execute(apiHandler, devices, { on: true }, {}); expect(getItemMock).toHaveBeenCalledTimes(0); expect(sendCommandMock).toHaveBeenCalledTimes(1); - expect(result).toStrictEqual([{ - "errorCode": "deviceOffline", - "ids": [ - "Item1", - ], - "status": "ERROR" - }]); + expect(result).toStrictEqual([ + { + errorCode: 'deviceOffline', + ids: ['Item1'], + status: 'ERROR' + } + ]); }); }); }); diff --git a/tests/commands/getcamerastream.test.js b/tests/commands/getcamerastream.test.js index 34e412d3..f7683b8b 100644 --- a/tests/commands/getcamerastream.test.js +++ b/tests/commands/getcamerastream.test.js @@ -3,8 +3,8 @@ const Command = require('../../functions/commands/getcamerastream.js'); describe('GetCameraStream Command', () => { test('validateParams', () => { expect(Command.validateParams({})).toBe(false); - expect(Command.validateParams({ "StreamToChromecast": true })).toBe(false); - expect(Command.validateParams({ "StreamToChromecast": true, "SupportedStreamProtocols": {} })).toBe(true); + expect(Command.validateParams({ StreamToChromecast: true })).toBe(false); + expect(Command.validateParams({ StreamToChromecast: true, SupportedStreamProtocols: {} })).toBe(true); }); test('requiresItem', () => { @@ -16,8 +16,8 @@ describe('GetCameraStream Command', () => { }); test('getResponseStates', () => { - expect(Command.getResponseStates({}, { "state": "https://example.org" })).toStrictEqual({ - "cameraStreamAccessUrl": "https://example.org" + expect(Command.getResponseStates({}, { state: 'https://example.org' })).toStrictEqual({ + cameraStreamAccessUrl: 'https://example.org' }); }); }); diff --git a/tests/commands/lockunlock.test.js b/tests/commands/lockunlock.test.js index 37637ee5..dc52df5b 100644 --- a/tests/commands/lockunlock.test.js +++ b/tests/commands/lockunlock.test.js @@ -3,25 +3,27 @@ const Command = require('../../functions/commands/lockunlock.js'); describe('LockUnlock Command', () => { test('validateParams', () => { expect(Command.validateParams({})).toBe(false); - expect(Command.validateParams({ "lock": true })).toBe(true); + expect(Command.validateParams({ lock: true })).toBe(true); }); describe('convertParamsToValue', () => { test('convertParamsToValue', () => { - expect(Command.convertParamsToValue({ "lock": true }, {}, {})).toBe("ON"); - expect(Command.convertParamsToValue({ "lock": false }, {}, {})).toBe("OFF"); + expect(Command.convertParamsToValue({ lock: true }, {}, {})).toBe('ON'); + expect(Command.convertParamsToValue({ lock: false }, {}, {})).toBe('OFF'); }); test('convertParamsToValue inverted', () => { - expect(Command.convertParamsToValue({ "lock": true }, {}, { "customData": { "inverted": true } })).toBe("OFF"); - expect(Command.convertParamsToValue({ "lock": false }, {}, { "customData": { "inverted": true } })).toBe("ON"); + expect(Command.convertParamsToValue({ lock: true }, {}, { customData: { inverted: true } })).toBe('OFF'); + expect(Command.convertParamsToValue({ lock: false }, {}, { customData: { inverted: true } })).toBe('ON'); }); test('convertParamsToValue Contact', () => { - expect(() => { Command.convertParamsToValue({ "lock": true }, {}, { "customData": { "itemType": "Contact" } }) }).toThrow(); + expect(() => { + Command.convertParamsToValue({ lock: true }, {}, { customData: { itemType: 'Contact' } }); + }).toThrow(); }); }); test('getResponseStates', () => { - expect(Command.getResponseStates({ "lock": true })).toStrictEqual({ "isLocked": true }); - expect(Command.getResponseStates({ "lock": false })).toStrictEqual({ "isLocked": false }); + expect(Command.getResponseStates({ lock: true })).toStrictEqual({ isLocked: true }); + expect(Command.getResponseStates({ lock: false })).toStrictEqual({ isLocked: false }); }); }); diff --git a/tests/commands/medianext.test.js b/tests/commands/medianext.test.js index cb04f902..9e35479c 100644 --- a/tests/commands/medianext.test.js +++ b/tests/commands/medianext.test.js @@ -12,29 +12,31 @@ describe('mediaNext Command', () => { describe('getItemName', () => { test('getItemName', () => { const item = { - "members": [ + members: [ { - "name": "TransportItem", - "metadata": { - "ga": { - "value": "tvTransport" + name: 'TransportItem', + metadata: { + ga: { + value: 'tvTransport' } } } ] }; - expect(Command.getItemName(item)).toBe("TransportItem"); + expect(Command.getItemName(item)).toBe('TransportItem'); }); test('getItemName no transport', () => { const item = { - "members": [] + members: [] }; - expect(() => { Command.getItemName(item) }).toThrow(); + expect(() => { + Command.getItemName(item); + }).toThrow(); }); }); test('convertParamsToValue', () => { - expect(Command.convertParamsToValue()).toBe("NEXT"); + expect(Command.convertParamsToValue()).toBe('NEXT'); }); }); diff --git a/tests/commands/mediapause.test.js b/tests/commands/mediapause.test.js index b221c0cf..45e063d9 100644 --- a/tests/commands/mediapause.test.js +++ b/tests/commands/mediapause.test.js @@ -12,29 +12,31 @@ describe('mediaPause Command', () => { describe('getItemName', () => { test('getItemName', () => { const item = { - "members": [ + members: [ { - "name": "TransportItem", - "metadata": { - "ga": { - "value": "tvTransport" + name: 'TransportItem', + metadata: { + ga: { + value: 'tvTransport' } } } ] }; - expect(Command.getItemName(item)).toBe("TransportItem"); + expect(Command.getItemName(item)).toBe('TransportItem'); }); test('getItemName no transport', () => { const item = { - "members": [] + members: [] }; - expect(() => { Command.getItemName(item) }).toThrow(); + expect(() => { + Command.getItemName(item); + }).toThrow(); }); }); test('convertParamsToValue', () => { - expect(Command.convertParamsToValue()).toBe("PAUSE"); + expect(Command.convertParamsToValue()).toBe('PAUSE'); }); }); diff --git a/tests/commands/mediaprevious.test.js b/tests/commands/mediaprevious.test.js index c160b4d0..71c1c86d 100644 --- a/tests/commands/mediaprevious.test.js +++ b/tests/commands/mediaprevious.test.js @@ -12,29 +12,31 @@ describe('mediaPrevious Command', () => { describe('getItemName', () => { test('getItemName', () => { const item = { - "members": [ + members: [ { - "name": "TransportItem", - "metadata": { - "ga": { - "value": "tvTransport" + name: 'TransportItem', + metadata: { + ga: { + value: 'tvTransport' } } } ] }; - expect(Command.getItemName(item)).toBe("TransportItem"); + expect(Command.getItemName(item)).toBe('TransportItem'); }); test('getItemName no transport', () => { const item = { - "members": [] + members: [] }; - expect(() => { Command.getItemName(item) }).toThrow(); + expect(() => { + Command.getItemName(item); + }).toThrow(); }); }); test('convertParamsToValue', () => { - expect(Command.convertParamsToValue()).toBe("PREVIOUS"); + expect(Command.convertParamsToValue()).toBe('PREVIOUS'); }); }); diff --git a/tests/commands/mediaresume.test.js b/tests/commands/mediaresume.test.js index a76b67c9..1a689a9e 100644 --- a/tests/commands/mediaresume.test.js +++ b/tests/commands/mediaresume.test.js @@ -12,29 +12,31 @@ describe('mediaResume Command', () => { describe('getItemName', () => { test('getItemName', () => { const item = { - "members": [ + members: [ { - "name": "TransportItem", - "metadata": { - "ga": { - "value": "tvTransport" + name: 'TransportItem', + metadata: { + ga: { + value: 'tvTransport' } } } ] }; - expect(Command.getItemName(item)).toBe("TransportItem"); + expect(Command.getItemName(item)).toBe('TransportItem'); }); test('getItemName no transport', () => { const item = { - "members": [] + members: [] }; - expect(() => { Command.getItemName(item) }).toThrow(); + expect(() => { + Command.getItemName(item); + }).toThrow(); }); }); test('convertParamsToValue', () => { - expect(Command.convertParamsToValue()).toBe("PLAY"); + expect(Command.convertParamsToValue()).toBe('PLAY'); }); }); diff --git a/tests/commands/mute.test.js b/tests/commands/mute.test.js index 0c036908..aa61f8a1 100644 --- a/tests/commands/mute.test.js +++ b/tests/commands/mute.test.js @@ -3,109 +3,115 @@ const Command = require('../../functions/commands/mute.js'); describe('Mute Command', () => { test('validateParams', () => { expect(Command.validateParams({})).toBe(false); - expect(Command.validateParams({ "mute": true })).toBe(true); + expect(Command.validateParams({ mute: true })).toBe(true); }); test('requiresItem', () => { expect(Command.requiresItem({})).toBe(false); - expect(Command.requiresItem({ "customData": {} })).toBe(false); - expect(Command.requiresItem({ "customData": { "deviceType": "TV" } })).toBe(true); + expect(Command.requiresItem({ customData: {} })).toBe(false); + expect(Command.requiresItem({ customData: { deviceType: 'TV' } })).toBe(true); }); describe('getItemName', () => { test('getItemName', () => { - expect(Command.getItemName({ "name": "Item" }, {})).toBe("Item"); - expect(Command.getItemName({ "name": "Item" }, { "customData": {} })).toBe("Item"); + expect(Command.getItemName({ name: 'Item' }, {})).toBe('Item'); + expect(Command.getItemName({ name: 'Item' }, { customData: {} })).toBe('Item'); }); test('getItemName TV no members', () => { - expect(() => { Command.getItemName({ "name": "Item" }, { "customData": { "deviceType": "TV" } }) }).toThrow(); + expect(() => { + Command.getItemName({ name: 'Item' }, { customData: { deviceType: 'TV' } }); + }).toThrow(); }); test('getItemName TV mute', () => { const item = { - "name": "Item", - "members": [ + name: 'Item', + members: [ { - "name": "MuteItem", - "metadata": { - "ga": { - "value": "tvMute" + name: 'MuteItem', + metadata: { + ga: { + value: 'tvMute' } } } ] }; - expect(Command.getItemName(item, { "customData": { "deviceType": "TV" } })).toBe("MuteItem"); + expect(Command.getItemName(item, { customData: { deviceType: 'TV' } })).toBe('MuteItem'); }); test('getItemName TV volume', () => { const item = { - "name": "Item", - "members": [ + name: 'Item', + members: [ { - "name": "VolumeItem", - "metadata": { - "ga": { - "value": "tvVolume" + name: 'VolumeItem', + metadata: { + ga: { + value: 'tvVolume' } } } ] }; - expect(Command.getItemName(item, { "customData": { "deviceType": "TV" } })).toBe("VolumeItem"); + expect(Command.getItemName(item, { customData: { deviceType: 'TV' } })).toBe('VolumeItem'); }); }); describe('convertParamsToValue', () => { test('convertParamsToValue no Switch', () => { - expect(Command.convertParamsToValue({ "mute": true }, {}, {})).toBe("0"); - expect(Command.convertParamsToValue({ "mute": false }, {}, {})).toBeUndefined(); + expect(Command.convertParamsToValue({ mute: true }, {}, {})).toBe('0'); + expect(Command.convertParamsToValue({ mute: false }, {}, {})).toBeUndefined(); }); test('convertParamsToValue Switch', () => { - expect(Command.convertParamsToValue({ "mute": true }, {}, { "customData": { "itemType": "Switch" } })).toBe("ON"); - expect(Command.convertParamsToValue({ "mute": false }, {}, { "customData": { "itemType": "Switch" } })).toBe("OFF"); + expect(Command.convertParamsToValue({ mute: true }, {}, { customData: { itemType: 'Switch' } })).toBe('ON'); + expect(Command.convertParamsToValue({ mute: false }, {}, { customData: { itemType: 'Switch' } })).toBe('OFF'); }); test('convertParamsToValue inverted', () => { - expect(Command.convertParamsToValue({ "mute": true }, {}, { "customData": { "itemType": "Switch", "inverted": true } })).toBe("OFF"); - expect(Command.convertParamsToValue({ "mute": false }, {}, { "customData": { "itemType": "Switch", "inverted": true } })).toBe("ON"); + expect( + Command.convertParamsToValue({ mute: true }, {}, { customData: { itemType: 'Switch', inverted: true } }) + ).toBe('OFF'); + expect( + Command.convertParamsToValue({ mute: false }, {}, { customData: { itemType: 'Switch', inverted: true } }) + ).toBe('ON'); }); test('convertParamsToValue TV mute', () => { const item = { - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "tvMute" + metadata: { + ga: { + value: 'tvMute' } } } ] }; - expect(Command.convertParamsToValue({ "mute": true }, item, { "customData": { "deviceType": "TV" } })).toBe("ON"); + expect(Command.convertParamsToValue({ mute: true }, item, { customData: { deviceType: 'TV' } })).toBe('ON'); }); test('convertParamsToValue TV volume', () => { const item = { - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "tvVolume" + metadata: { + ga: { + value: 'tvVolume' } } } ] }; - expect(Command.convertParamsToValue({ "mute": true }, item, { "customData": { "deviceType": "TV" } })).toBe("0"); + expect(Command.convertParamsToValue({ mute: true }, item, { customData: { deviceType: 'TV' } })).toBe('0'); }); }); test('getResponseStates', () => { - expect(Command.getResponseStates({ "mute": true })).toStrictEqual({ "isMuted": true }); - expect(Command.getResponseStates({ "mute": false })).toStrictEqual({ "isMuted": false }); + expect(Command.getResponseStates({ mute: true })).toStrictEqual({ isMuted: true }); + expect(Command.getResponseStates({ mute: false })).toStrictEqual({ isMuted: false }); }); }); diff --git a/tests/commands/onoff.test.js b/tests/commands/onoff.test.js index b91fe4cc..325f3753 100644 --- a/tests/commands/onoff.test.js +++ b/tests/commands/onoff.test.js @@ -3,68 +3,72 @@ const Command = require('../../functions/commands/onoff.js'); describe('OnOff Command', () => { test('validateParams', () => { expect(Command.validateParams({})).toBe(false); - expect(Command.validateParams({ "on": true })).toBe(true); + expect(Command.validateParams({ on: true })).toBe(true); }); test('requiresItem', () => { expect(Command.requiresItem({})).toBe(false); - expect(Command.requiresItem({ "customData": { "deviceType": "SpecialColorLight" } })).toBe(true); - expect(Command.requiresItem({ "customData": { "deviceType": "TV" } })).toBe(true); + expect(Command.requiresItem({ customData: { deviceType: 'SpecialColorLight' } })).toBe(true); + expect(Command.requiresItem({ customData: { deviceType: 'TV' } })).toBe(true); }); describe('getItemName', () => { test('getItemName', () => { - expect(Command.getItemName({ "name": "Item" }, {})).toBe("Item"); - expect(Command.getItemName({ "name": "Item" }, { "customData": {} })).toBe("Item"); + expect(Command.getItemName({ name: 'Item' }, {})).toBe('Item'); + expect(Command.getItemName({ name: 'Item' }, { customData: {} })).toBe('Item'); }); test('getItemName SpecialColorLight', () => { - expect(() => { Command.getItemName({ "name": "Item" }, { "customData": { "deviceType": "SpecialColorLight" } }) }).toThrow(); + expect(() => { + Command.getItemName({ name: 'Item' }, { customData: { deviceType: 'SpecialColorLight' } }); + }).toThrow(); const item = { - "members": [ + members: [ { - "name": "BrightnessItem", - "metadata": { - "ga": { - "value": "lightBrightness" + name: 'BrightnessItem', + metadata: { + ga: { + value: 'lightBrightness' } } } ] }; - expect(Command.getItemName(item, { "customData": { "deviceType": "SpecialColorLight" } })).toBe("BrightnessItem"); + expect(Command.getItemName(item, { customData: { deviceType: 'SpecialColorLight' } })).toBe('BrightnessItem'); }); test('getItemName TV', () => { - expect(() => { Command.getItemName({ "name": "Item" }, { "customData": { "deviceType": "TV" } }) }).toThrow(); + expect(() => { + Command.getItemName({ name: 'Item' }, { customData: { deviceType: 'TV' } }); + }).toThrow(); const item = { - "members": [ + members: [ { - "name": "PowerItem", - "metadata": { - "ga": { - "value": "tvPower" + name: 'PowerItem', + metadata: { + ga: { + value: 'tvPower' } } } ] }; - expect(Command.getItemName(item, { "customData": { "deviceType": "TV" } })).toBe("PowerItem"); + expect(Command.getItemName(item, { customData: { deviceType: 'TV' } })).toBe('PowerItem'); }); }); describe('convertParamsToValue', () => { test('convertParamsToValue', () => { - expect(Command.convertParamsToValue({ "on": true }, {}, {})).toBe("ON"); + expect(Command.convertParamsToValue({ on: true }, {}, {})).toBe('ON'); }); test('convertParamsToValue inverted', () => { - expect(Command.convertParamsToValue({ "on": true }, {}, { "customData": { "inverted": true } })).toBe("OFF"); + expect(Command.convertParamsToValue({ on: true }, {}, { customData: { inverted: true } })).toBe('OFF'); }); }); test('getResponseStates', () => { - expect(Command.getResponseStates({ "on": true })).toStrictEqual({ "on": true }); - expect(Command.getResponseStates({ "on": false })).toStrictEqual({ "on": false }); + expect(Command.getResponseStates({ on: true })).toStrictEqual({ on: true }); + expect(Command.getResponseStates({ on: false })).toStrictEqual({ on: false }); }); }); diff --git a/tests/commands/openclose.test.js b/tests/commands/openclose.test.js index 2b57be6d..e0c368c0 100644 --- a/tests/commands/openclose.test.js +++ b/tests/commands/openclose.test.js @@ -3,49 +3,51 @@ const Command = require('../../functions/commands/openclose.js'); describe('OpenClose Command', () => { test('validateParams', () => { expect(Command.validateParams({})).toBe(false); - expect(Command.validateParams({ "openPercent": 100 })).toBe(true); - expect(Command.validateParams({ "openPercent": "5" })).toBe(false); + expect(Command.validateParams({ openPercent: 100 })).toBe(true); + expect(Command.validateParams({ openPercent: '5' })).toBe(false); }); describe('convertParamsToValue', () => { test('convertParamsToValue', () => { - expect(Command.convertParamsToValue({ "openPercent": 0 }, {}, {})).toBe("0"); - expect(Command.convertParamsToValue({ "openPercent": 20 }, {}, {})).toBe("20"); - expect(Command.convertParamsToValue({ "openPercent": 50 }, {}, {})).toBe("50"); - expect(Command.convertParamsToValue({ "openPercent": 70 }, {}, {})).toBe("70"); - expect(Command.convertParamsToValue({ "openPercent": 100 }, {}, {})).toBe("100"); + expect(Command.convertParamsToValue({ openPercent: 0 }, {}, {})).toBe('0'); + expect(Command.convertParamsToValue({ openPercent: 20 }, {}, {})).toBe('20'); + expect(Command.convertParamsToValue({ openPercent: 50 }, {}, {})).toBe('50'); + expect(Command.convertParamsToValue({ openPercent: 70 }, {}, {})).toBe('70'); + expect(Command.convertParamsToValue({ openPercent: 100 }, {}, {})).toBe('100'); }); test('convertParamsToValue inverted', () => { - const device = { "customData": { "inverted": true } }; - expect(Command.convertParamsToValue({ "openPercent": 0 }, {}, device)).toBe("100"); - expect(Command.convertParamsToValue({ "openPercent": 20 }, {}, device)).toBe("80"); - expect(Command.convertParamsToValue({ "openPercent": 50 }, {}, device)).toBe("50"); - expect(Command.convertParamsToValue({ "openPercent": 70 }, {}, device)).toBe("30"); - expect(Command.convertParamsToValue({ "openPercent": 100 }, {}, device)).toBe("0"); + const device = { customData: { inverted: true } }; + expect(Command.convertParamsToValue({ openPercent: 0 }, {}, device)).toBe('100'); + expect(Command.convertParamsToValue({ openPercent: 20 }, {}, device)).toBe('80'); + expect(Command.convertParamsToValue({ openPercent: 50 }, {}, device)).toBe('50'); + expect(Command.convertParamsToValue({ openPercent: 70 }, {}, device)).toBe('30'); + expect(Command.convertParamsToValue({ openPercent: 100 }, {}, device)).toBe('0'); }); test('convertParamsToValue Rollershutter', () => { - const device = { "customData": { "itemType": "Rollershutter" } }; - expect(Command.convertParamsToValue({ "openPercent": 0 }, {}, device)).toBe("DOWN"); - expect(Command.convertParamsToValue({ "openPercent": 20 }, {}, device)).toBe("80"); - expect(Command.convertParamsToValue({ "openPercent": 100 }, {}, device)).toBe("UP"); + const device = { customData: { itemType: 'Rollershutter' } }; + expect(Command.convertParamsToValue({ openPercent: 0 }, {}, device)).toBe('DOWN'); + expect(Command.convertParamsToValue({ openPercent: 20 }, {}, device)).toBe('80'); + expect(Command.convertParamsToValue({ openPercent: 100 }, {}, device)).toBe('UP'); }); test('convertParamsToValue Switch', () => { - const device = { "customData": { "itemType": "Switch" } }; - expect(Command.convertParamsToValue({ "openPercent": 0 }, {}, device)).toBe("OFF"); - expect(Command.convertParamsToValue({ "openPercent": 20 }, {}, device)).toBe("ON"); - expect(Command.convertParamsToValue({ "openPercent": 100 }, {}, device)).toBe("ON"); + const device = { customData: { itemType: 'Switch' } }; + expect(Command.convertParamsToValue({ openPercent: 0 }, {}, device)).toBe('OFF'); + expect(Command.convertParamsToValue({ openPercent: 20 }, {}, device)).toBe('ON'); + expect(Command.convertParamsToValue({ openPercent: 100 }, {}, device)).toBe('ON'); }); test('convertParamsToValue Contact', () => { - const device = { "customData": { "itemType": "Contact" } }; - expect(() => { Command.convertParamsToValue({}, {}, device) }).toThrow(); + const device = { customData: { itemType: 'Contact' } }; + expect(() => { + Command.convertParamsToValue({}, {}, device); + }).toThrow(); }); }); test('getResponseStates', () => { - expect(Command.getResponseStates({ "openPercent": 10 })).toStrictEqual({ "openPercent": 10 }); + expect(Command.getResponseStates({ openPercent: 10 })).toStrictEqual({ openPercent: 10 }); }); }); diff --git a/tests/commands/selectchannel.test.js b/tests/commands/selectchannel.test.js index 3c11ec81..6684ded7 100644 --- a/tests/commands/selectchannel.test.js +++ b/tests/commands/selectchannel.test.js @@ -3,9 +3,9 @@ const Command = require('../../functions/commands/selectchannel.js'); describe('selectChannel Command', () => { test('validateParams', () => { expect(Command.validateParams({})).toBe(false); - expect(Command.validateParams({ "channelCode": "channel1" })).toBe(true); - expect(Command.validateParams({ "channelName": "Channel 1" })).toBe(true); - expect(Command.validateParams({ "channelNumber": "1" })).toBe(true); + expect(Command.validateParams({ channelCode: 'channel1' })).toBe(true); + expect(Command.validateParams({ channelName: 'Channel 1' })).toBe(true); + expect(Command.validateParams({ channelNumber: '1' })).toBe(true); }); test('requiresItem', () => { @@ -13,47 +13,49 @@ describe('selectChannel Command', () => { }); test('getItemName', () => { - expect(() => { Command.getItemName({ "name": "Item" }) }).toThrow(); + expect(() => { + Command.getItemName({ name: 'Item' }); + }).toThrow(); const item = { - "members": [ + members: [ { - "name": "ChannelItem", - "metadata": { - "ga": { - "value": "tvChannel" + name: 'ChannelItem', + metadata: { + ga: { + value: 'tvChannel' } } } ] }; - expect(Command.getItemName(item)).toBe("ChannelItem"); + expect(Command.getItemName(item)).toBe('ChannelItem'); }); test('convertParamsToValue', () => { const item = { - "metadata": { - "ga": { - "config": { - "availableChannels": "1=channel1=ARD,2=channel2=ZDF" + metadata: { + ga: { + config: { + availableChannels: '1=channel1=ARD,2=channel2=ZDF' } } } }; - expect(Command.convertParamsToValue({ "channelCode": "channel1" }, item)).toBe("1"); - expect(Command.convertParamsToValue({ "channelName": "ARD" }, item)).toBe("1"); - expect(Command.convertParamsToValue({ "channelNumber": "1" }, item)).toBe("1"); + expect(Command.convertParamsToValue({ channelCode: 'channel1' }, item)).toBe('1'); + expect(Command.convertParamsToValue({ channelName: 'ARD' }, item)).toBe('1'); + expect(Command.convertParamsToValue({ channelNumber: '1' }, item)).toBe('1'); }); test('getResponseStates', () => { const item = { - "metadata": { - "ga": { - "config": { - "availableChannels": "1=channel1=ARD,2=channel2=ZDF" + metadata: { + ga: { + config: { + availableChannels: '1=channel1=ARD,2=channel2=ZDF' } } } }; - expect(Command.getResponseStates({ "channelName": "ZDF" }, item)).toStrictEqual({ "channelNumber": "2" }); + expect(Command.getResponseStates({ channelName: 'ZDF' }, item)).toStrictEqual({ channelNumber: '2' }); }); }); diff --git a/tests/commands/setfanspeed.test.js b/tests/commands/setfanspeed.test.js index 8e26fd4e..adc7a105 100644 --- a/tests/commands/setfanspeed.test.js +++ b/tests/commands/setfanspeed.test.js @@ -1,7 +1,7 @@ const Command = require('../../functions/commands/setfanspeed.js'); describe('SetFanSpeed Command', () => { - const params = { "fanSpeed": "50" }; + const params = { fanSpeed: '50' }; test('validateParams', () => { expect(Command.validateParams({})).toBe(false); @@ -9,10 +9,10 @@ describe('SetFanSpeed Command', () => { }); test('convertParamsToValue', () => { - expect(Command.convertParamsToValue(params)).toBe("50"); + expect(Command.convertParamsToValue(params)).toBe('50'); }); test('getResponseStates', () => { - expect(Command.getResponseStates(params)).toStrictEqual({ "currentFanSpeedSetting": "50" }); + expect(Command.getResponseStates(params)).toStrictEqual({ currentFanSpeedSetting: '50' }); }); }); diff --git a/tests/commands/setinput.test.js b/tests/commands/setinput.test.js index de5903de..507a7cc0 100644 --- a/tests/commands/setinput.test.js +++ b/tests/commands/setinput.test.js @@ -1,7 +1,7 @@ const Command = require('../../functions/commands/setinput.js'); describe('SetInput Command', () => { - const params = { "newInput": "hdmi1" }; + const params = { newInput: 'hdmi1' }; test('validateParams', () => { expect(Command.validateParams({})).toBe(false); @@ -13,27 +13,29 @@ describe('SetInput Command', () => { }); test('getItemName', () => { - expect(() => { Command.getItemName({ "name": "Item" }) }).toThrow(); + expect(() => { + Command.getItemName({ name: 'Item' }); + }).toThrow(); const item = { - "members": [ + members: [ { - "name": "InputItem", - "metadata": { - "ga": { - "value": "tvInput" + name: 'InputItem', + metadata: { + ga: { + value: 'tvInput' } } } ] }; - expect(Command.getItemName(item)).toBe("InputItem"); + expect(Command.getItemName(item)).toBe('InputItem'); }); test('convertParamsToValue', () => { - expect(Command.convertParamsToValue(params)).toBe("hdmi1"); + expect(Command.convertParamsToValue(params)).toBe('hdmi1'); }); test('getResponseStates', () => { - expect(Command.getResponseStates(params)).toStrictEqual({ "currentInput": "hdmi1" }); + expect(Command.getResponseStates(params)).toStrictEqual({ currentInput: 'hdmi1' }); }); }); diff --git a/tests/commands/setvolume.test.js b/tests/commands/setvolume.test.js index e75e3b2f..9f43b3dc 100644 --- a/tests/commands/setvolume.test.js +++ b/tests/commands/setvolume.test.js @@ -1,7 +1,7 @@ const Command = require('../../functions/commands/setvolume.js'); describe('setVolume Command', () => { - const params = { "volumeLevel": 20 }; + const params = { volumeLevel: 20 }; test('validateParams', () => { expect(Command.validateParams({})).toBe(false); @@ -10,38 +10,40 @@ describe('setVolume Command', () => { test('requiresItem', () => { expect(Command.requiresItem({})).toBe(false); - expect(Command.requiresItem({ "customData": { "deviceType": "TV" } })).toBe(true); + expect(Command.requiresItem({ customData: { deviceType: 'TV' } })).toBe(true); }); describe('getItemName', () => { test('getItemName', () => { - expect(Command.getItemName({ "name": "Item" }, {})).toBe("Item"); - expect(Command.getItemName({ "name": "Item" }, { "customData": {} })).toBe("Item"); + expect(Command.getItemName({ name: 'Item' }, {})).toBe('Item'); + expect(Command.getItemName({ name: 'Item' }, { customData: {} })).toBe('Item'); }); test('getItemName TV', () => { - expect(() => { Command.getItemName({ "name": "Item" }, { "customData": { "deviceType": "TV" } }) }).toThrow(); + expect(() => { + Command.getItemName({ name: 'Item' }, { customData: { deviceType: 'TV' } }); + }).toThrow(); const item = { - "members": [ + members: [ { - "name": "VolumeItem", - "metadata": { - "ga": { - "value": "tvVolume" + name: 'VolumeItem', + metadata: { + ga: { + value: 'tvVolume' } } } ] }; - expect(Command.getItemName(item, { "customData": { "deviceType": "TV" } })).toBe("VolumeItem"); + expect(Command.getItemName(item, { customData: { deviceType: 'TV' } })).toBe('VolumeItem'); }); }); test('convertParamsToValue', () => { - expect(Command.convertParamsToValue(params)).toBe("20"); + expect(Command.convertParamsToValue(params)).toBe('20'); }); test('getResponseStates', () => { - expect(Command.getResponseStates(params)).toStrictEqual({ "currentVolume": 20 }); + expect(Command.getResponseStates(params)).toStrictEqual({ currentVolume: 20 }); }); }); diff --git a/tests/commands/startstop.test.js b/tests/commands/startstop.test.js index 3dcf1073..43bb8e67 100644 --- a/tests/commands/startstop.test.js +++ b/tests/commands/startstop.test.js @@ -3,30 +3,32 @@ const Command = require('../../functions/commands/startstop.js'); describe('StartStop Command', () => { test('validateParams', () => { expect(Command.validateParams({})).toBe(false); - expect(Command.validateParams({ "start": true })).toBe(true); - expect(Command.validateParams({ "start": "1" })).toBe(false); + expect(Command.validateParams({ start: true })).toBe(true); + expect(Command.validateParams({ start: '1' })).toBe(false); }); describe('convertParamsToValue', () => { test('convertParamsToValue', () => { - expect(Command.convertParamsToValue({ "start": true }, {}, {})).toBe("ON"); - expect(Command.convertParamsToValue({ "start": false }, {}, {})).toBe("OFF"); + expect(Command.convertParamsToValue({ start: true }, {}, {})).toBe('ON'); + expect(Command.convertParamsToValue({ start: false }, {}, {})).toBe('OFF'); }); test('convertParamsToValue Rollershutter', () => { - const device = { "customData": { "itemType": "Rollershutter" } }; - expect(Command.convertParamsToValue({ "start": true }, {}, device)).toBe("MOVE"); - expect(Command.convertParamsToValue({ "start": false }, {}, device)).toBe("STOP"); + const device = { customData: { itemType: 'Rollershutter' } }; + expect(Command.convertParamsToValue({ start: true }, {}, device)).toBe('MOVE'); + expect(Command.convertParamsToValue({ start: false }, {}, device)).toBe('STOP'); }); test('convertParamsToValue Contact', () => { - const device = { "customData": { "itemType": "Contact" } }; - expect(() => { Command.convertParamsToValue({}, {}, device) }).toThrow(); + const device = { customData: { itemType: 'Contact' } }; + expect(() => { + Command.convertParamsToValue({}, {}, device); + }).toThrow(); }); }); test('getResponseStates', () => { - expect(Command.getResponseStates({ "start": true })).toStrictEqual({ "isRunning": true, "isPaused": false }); - expect(Command.getResponseStates({ "start": false })).toStrictEqual({ "isRunning": false, "isPaused": true }); + expect(Command.getResponseStates({ start: true })).toStrictEqual({ isRunning: true, isPaused: false }); + expect(Command.getResponseStates({ start: false })).toStrictEqual({ isRunning: false, isPaused: true }); }); }); diff --git a/tests/commands/thermostatsetmode.test.js b/tests/commands/thermostatsetmode.test.js index 6c1b3f31..66db2b63 100644 --- a/tests/commands/thermostatsetmode.test.js +++ b/tests/commands/thermostatsetmode.test.js @@ -1,7 +1,7 @@ const Command = require('../../functions/commands/thermostatsetmode.js'); describe('ThermostatSetMode Command', () => { - const params = { "thermostatMode": "eco" }; + const params = { thermostatMode: 'eco' }; test('validateParams', () => { expect(Command.validateParams({})).toBe(false); @@ -13,47 +13,49 @@ describe('ThermostatSetMode Command', () => { }); test('getItemName', () => { - expect(() => { Command.getItemName({ "name": "Item" }) }).toThrow(); + expect(() => { + Command.getItemName({ name: 'Item' }); + }).toThrow(); const item = { - "members": [ + members: [ { - "name": "ModeItem", - "metadata": { - "ga": { - "value": "thermostatMode" + name: 'ModeItem', + metadata: { + ga: { + value: 'thermostatMode' } } } ] }; - expect(Command.getItemName(item)).toBe("ModeItem"); + expect(Command.getItemName(item)).toBe('ModeItem'); }); test('convertParamsToValue', () => { const item = { - "metadata": { - "ga": { - "config": { - "modes": "eco=ECO" + metadata: { + ga: { + config: { + modes: 'eco=ECO' } } } }; - expect(Command.convertParamsToValue(params, item)).toBe("ECO"); + expect(Command.convertParamsToValue(params, item)).toBe('ECO'); }); test('getResponseStates', () => { const item = { - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "thermostatMode" + metadata: { + ga: { + value: 'thermostatMode' } } } ] }; - expect(Command.getResponseStates(params, item)).toStrictEqual({ "thermostatMode": "eco" }); + expect(Command.getResponseStates(params, item)).toStrictEqual({ thermostatMode: 'eco' }); }); }); diff --git a/tests/commands/thermostattemperaturesetpoint.test.js b/tests/commands/thermostattemperaturesetpoint.test.js index 17114097..095d4e22 100644 --- a/tests/commands/thermostattemperaturesetpoint.test.js +++ b/tests/commands/thermostattemperaturesetpoint.test.js @@ -1,7 +1,7 @@ const Command = require('../../functions/commands/thermostattemperaturesetpoint.js'); describe('ThermostatTemperatureSetpoint Command', () => { - const params = { "thermostatTemperatureSetpoint": 20 }; + const params = { thermostatTemperatureSetpoint: 20 }; test('validateParams', () => { expect(Command.validateParams({})).toBe(false); @@ -13,48 +13,50 @@ describe('ThermostatTemperatureSetpoint Command', () => { }); test('getItemName', () => { - expect(() => { Command.getItemName({ "name": "Item" }) }).toThrow(); + expect(() => { + Command.getItemName({ name: 'Item' }); + }).toThrow(); const item = { - "members": [ + members: [ { - "name": "SetpointItem", - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpoint" + name: 'SetpointItem', + metadata: { + ga: { + value: 'thermostatTemperatureSetpoint' } } } ] }; - expect(Command.getItemName(item)).toBe("SetpointItem"); + expect(Command.getItemName(item)).toBe('SetpointItem'); }); test('convertParamsToValue', () => { const item = { - "metadata": { - "ga": { - "config": { - "useFahrenheit": true + metadata: { + ga: { + config: { + useFahrenheit: true } } } }; - expect(Command.convertParamsToValue(params, item)).toBe("68"); - expect(Command.convertParamsToValue(params, {})).toBe("20"); + expect(Command.convertParamsToValue(params, item)).toBe('68'); + expect(Command.convertParamsToValue(params, {})).toBe('20'); }); test('getResponseStates', () => { const item = { - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpoint" + metadata: { + ga: { + value: 'thermostatTemperatureSetpoint' } } } ] }; - expect(Command.getResponseStates(params, item)).toStrictEqual({ "thermostatTemperatureSetpoint": 20 }); + expect(Command.getResponseStates(params, item)).toStrictEqual({ thermostatTemperatureSetpoint: 20 }); }); }); diff --git a/tests/commands/thermostattemperaturesetpointhigh.test.js b/tests/commands/thermostattemperaturesetpointhigh.test.js index 6c386a94..b499765c 100644 --- a/tests/commands/thermostattemperaturesetpointhigh.test.js +++ b/tests/commands/thermostattemperaturesetpointhigh.test.js @@ -1,7 +1,7 @@ const Command = require('../../functions/commands/thermostattemperaturesetpointhigh.js'); describe('ThermostatTemperatureSetpointHigh Command', () => { - const params = { "thermostatTemperatureSetpointHigh": 20 }; + const params = { thermostatTemperatureSetpointHigh: 20 }; test('validateParams', () => { expect(Command.validateParams({})).toBe(false); @@ -13,48 +13,50 @@ describe('ThermostatTemperatureSetpointHigh Command', () => { }); test('getItemName', () => { - expect(() => { Command.getItemName({ "name": "Item" }) }).toThrow(); + expect(() => { + Command.getItemName({ name: 'Item' }); + }).toThrow(); const item = { - "members": [ + members: [ { - "name": "SetpointItem", - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpointHigh" + name: 'SetpointItem', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointHigh' } } } ] }; - expect(Command.getItemName(item)).toBe("SetpointItem"); + expect(Command.getItemName(item)).toBe('SetpointItem'); }); test('convertParamsToValue', () => { const item = { - "metadata": { - "ga": { - "config": { - "useFahrenheit": true + metadata: { + ga: { + config: { + useFahrenheit: true } } } }; - expect(Command.convertParamsToValue(params, item)).toBe("68"); - expect(Command.convertParamsToValue(params, {})).toBe("20"); + expect(Command.convertParamsToValue(params, item)).toBe('68'); + expect(Command.convertParamsToValue(params, {})).toBe('20'); }); test('getResponseStates', () => { const item = { - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpointHigh" + metadata: { + ga: { + value: 'thermostatTemperatureSetpointHigh' } } } ] }; - expect(Command.getResponseStates(params, item)).toStrictEqual({ "thermostatTemperatureSetpointHigh": 20 }); + expect(Command.getResponseStates(params, item)).toStrictEqual({ thermostatTemperatureSetpointHigh: 20 }); }); }); diff --git a/tests/commands/thermostattemperaturesetpointlow.test.js b/tests/commands/thermostattemperaturesetpointlow.test.js index de05b11a..71b1c599 100644 --- a/tests/commands/thermostattemperaturesetpointlow.test.js +++ b/tests/commands/thermostattemperaturesetpointlow.test.js @@ -1,7 +1,7 @@ const Command = require('../../functions/commands/thermostattemperaturesetpointlow.js'); describe('ThermostatTemperatureSetpointLow Command', () => { - const params = { "thermostatTemperatureSetpointLow": 20 }; + const params = { thermostatTemperatureSetpointLow: 20 }; test('validateParams', () => { expect(Command.validateParams({})).toBe(false); @@ -13,48 +13,50 @@ describe('ThermostatTemperatureSetpointLow Command', () => { }); test('getItemName', () => { - expect(() => { Command.getItemName({ "name": "Item" }) }).toThrow(); + expect(() => { + Command.getItemName({ name: 'Item' }); + }).toThrow(); const item = { - "members": [ + members: [ { - "name": "SetpointItem", - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpointLow" + name: 'SetpointItem', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointLow' } } } ] }; - expect(Command.getItemName(item)).toBe("SetpointItem"); + expect(Command.getItemName(item)).toBe('SetpointItem'); }); test('convertParamsToValue', () => { const item = { - "metadata": { - "ga": { - "config": { - "useFahrenheit": true + metadata: { + ga: { + config: { + useFahrenheit: true } } } }; - expect(Command.convertParamsToValue(params, item)).toBe("68"); - expect(Command.convertParamsToValue(params, {})).toBe("20"); + expect(Command.convertParamsToValue(params, item)).toBe('68'); + expect(Command.convertParamsToValue(params, {})).toBe('20'); }); test('getResponseStates', () => { const item = { - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpointLow" + metadata: { + ga: { + value: 'thermostatTemperatureSetpointLow' } } } ] }; - expect(Command.getResponseStates(params, item)).toStrictEqual({ "thermostatTemperatureSetpointLow": 20 }); + expect(Command.getResponseStates(params, item)).toStrictEqual({ thermostatTemperatureSetpointLow: 20 }); }); }); diff --git a/tests/commands/volumerelative.test.js b/tests/commands/volumerelative.test.js index 01823b32..e851c491 100644 --- a/tests/commands/volumerelative.test.js +++ b/tests/commands/volumerelative.test.js @@ -1,7 +1,7 @@ const Command = require('../../functions/commands/volumerelative.js'); describe('volumeRelative Command', () => { - const params = { "relativeSteps": 10 }; + const params = { relativeSteps: 10 }; test('validateParams', () => { expect(Command.validateParams({})).toBe(false); @@ -14,52 +14,56 @@ describe('volumeRelative Command', () => { describe('getItemName', () => { test('getItemName', () => { - expect(Command.getItemName({ "name": "Item" }, {})).toBe("Item"); - expect(Command.getItemName({ "name": "Item" }, { "customData": {} })).toBe("Item"); + expect(Command.getItemName({ name: 'Item' }, {})).toBe('Item'); + expect(Command.getItemName({ name: 'Item' }, { customData: {} })).toBe('Item'); }); test('getItemName TV', () => { - expect(() => { Command.getItemName({ "name": "Item" }, { "customData": { "deviceType": "TV" } }) }).toThrow(); + expect(() => { + Command.getItemName({ name: 'Item' }, { customData: { deviceType: 'TV' } }); + }).toThrow(); const item = { - "members": [ + members: [ { - "name": "VolumeItem", - "metadata": { - "ga": { - "value": "tvVolume" + name: 'VolumeItem', + metadata: { + ga: { + value: 'tvVolume' } } } ] }; - expect(Command.getItemName(item, { "customData": { "deviceType": "TV" } })).toBe("VolumeItem"); + expect(Command.getItemName(item, { customData: { deviceType: 'TV' } })).toBe('VolumeItem'); }); }); describe('convertParamsToValue', () => { test('convertParamsToValue', () => { - expect(Command.convertParamsToValue(params, { "state": 20 }, {})).toBe("30"); + expect(Command.convertParamsToValue(params, { state: 20 }, {})).toBe('30'); }); test('convertParamsToValue TV', () => { const item = { - "members": [ + members: [ { - "state": "20", - "metadata": { - "ga": { - "value": "tvVolume" + state: '20', + metadata: { + ga: { + value: 'tvVolume' } } } ] }; - expect(Command.convertParamsToValue(params, item, { "customData": { "deviceType": "TV" } })).toBe("30"); - expect(() => { Command.convertParamsToValue(params, {}, { "customData": { "deviceType": "TV" } }) }).toThrow(); + expect(Command.convertParamsToValue(params, item, { customData: { deviceType: 'TV' } })).toBe('30'); + expect(() => { + Command.convertParamsToValue(params, {}, { customData: { deviceType: 'TV' } }); + }).toThrow(); }); }); test('getResponseStates', () => { - expect(Command.getResponseStates(params, { "state": 20 }, {})).toStrictEqual({ "currentVolume": 30 }); + expect(Command.getResponseStates(params, { state: 20 }, {})).toStrictEqual({ currentVolume: 30 }); }); }); diff --git a/tests/config.test.js b/tests/config.test.js index 2aa84f6e..44af12c5 100644 --- a/tests/config.test.js +++ b/tests/config.test.js @@ -2,6 +2,6 @@ const Config = require('../functions/config.js'); describe('Config', () => { test('Config all properties', () => { - expect(Object.keys(Config)).toStrictEqual(["host", "port", "path"]); + expect(Object.keys(Config)).toStrictEqual(['host', 'port', 'path']); }); }); diff --git a/tests/devices/camera.test.js b/tests/devices/camera.test.js index 941ad1f8..c3ff0507 100644 --- a/tests/devices/camera.test.js +++ b/tests/devices/camera.test.js @@ -2,53 +2,55 @@ const Device = require('../../functions/devices/camera.js'); describe('Camera Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "CAMERA" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'CAMERA' + } } - } - })).toBe(true); + }) + ).toBe(true); }); test('matchesItemType', () => { - expect(Device.matchesItemType({ "type": "String" })).toBe(true); - expect(Device.matchesItemType({ "type": "Number" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(true); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Number" })).toBe(false); + expect(Device.matchesItemType({ type: 'String' })).toBe(true); + expect(Device.matchesItemType({ type: 'Number' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'String' })).toBe(true); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Number' })).toBe(false); }); describe('getAttributes', () => { test('getAttributes no config', () => { const item = { - "metadata": { - "ga": { - "config": {} + metadata: { + ga: { + config: {} } } }; expect(Device.getAttributes(item)).toStrictEqual({ - "cameraStreamSupportedProtocols": ["hls", "dash"], - "cameraStreamNeedAuthToken": false, - "cameraStreamNeedDrmEncryption": false + cameraStreamSupportedProtocols: ['hls', 'dash'], + cameraStreamNeedAuthToken: false, + cameraStreamNeedDrmEncryption: false }); }); test('getAttributes protocols, token', () => { const item = { - "metadata": { - "ga": { - "config": { - "protocols": "hls,test", - "token": true + metadata: { + ga: { + config: { + protocols: 'hls,test', + token: true } } } }; expect(Device.getAttributes(item)).toStrictEqual({ - "cameraStreamSupportedProtocols": ["hls", "test"], - "cameraStreamNeedAuthToken": true, - "cameraStreamNeedDrmEncryption": false + cameraStreamSupportedProtocols: ['hls', 'test'], + cameraStreamNeedAuthToken: true, + cameraStreamNeedDrmEncryption: false }); }); }); diff --git a/tests/devices/colorlight.test.js b/tests/devices/colorlight.test.js index 3dc4e7b8..a92714c9 100644 --- a/tests/devices/colorlight.test.js +++ b/tests/devices/colorlight.test.js @@ -2,67 +2,69 @@ const Device = require('../../functions/devices/colorlight.js'); describe('ColorLight Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "LIGHT" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'LIGHT' + } } - } - })).toBe(true); + }) + ).toBe(true); }); test('matchesItemType', () => { - expect(Device.matchesItemType({ "type": "Color" })).toBe(true); - expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Color" })).toBe(true); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(false); + expect(Device.matchesItemType({ type: 'Color' })).toBe(true); + expect(Device.matchesItemType({ type: 'Dimmer' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Color' })).toBe(true); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Dimmer' })).toBe(false); }); describe('getAttributes', () => { test('getAttributes colorTemperatureRange', () => { const item = { - "metadata": { - "ga": { - "config": { - "colorTemperatureRange": "1000,2000" + metadata: { + ga: { + config: { + colorTemperatureRange: '1000,2000' } } } }; expect(Device.getAttributes(item)).toStrictEqual({ - "colorModel": "hsv", - "colorTemperatureRange": { - "temperatureMinK": 1000, - "temperatureMaxK": 2000 + colorModel: 'hsv', + colorTemperatureRange: { + temperatureMinK: 1000, + temperatureMaxK: 2000 } }); }); test('getAttributes invalid colorTemperatureRange', () => { const item = { - "metadata": { - "ga": { - "config": { - "colorTemperatureRange": "a,b" + metadata: { + ga: { + config: { + colorTemperatureRange: 'a,b' } } } }; expect(Device.getAttributes(item)).toStrictEqual({ - "colorModel": "hsv" + colorModel: 'hsv' }); }); }); test('getState', () => { - expect(Device.getState({ "state": "100,50,10" })).toStrictEqual({ - "on": true, - "brightness": 10, - "color": { - "spectrumHSV": { - "hue": 100, - "saturation": 0.5, - "value": 0.1 + expect(Device.getState({ state: '100,50,10' })).toStrictEqual({ + on: true, + brightness: 10, + color: { + spectrumHSV: { + hue: 100, + saturation: 0.5, + value: 0.1 } } }); diff --git a/tests/devices/default.test.js b/tests/devices/default.test.js index 638882e1..46be8f11 100644 --- a/tests/devices/default.test.js +++ b/tests/devices/default.test.js @@ -2,30 +2,30 @@ const Device = require('../../functions/devices/default.js'); describe('Fan Device', () => { const item = { - "type": "Number", - "state": "50", - "name": "DefaultDevice", - "label": "Default Device", - "metadata": { - "ga": { - "value": "", - "config": { - "inverted": true, - "ackNeeded": true, - "pinNeeded": "1234" + type: 'Number', + state: '50', + name: 'DefaultDevice', + label: 'Default Device', + metadata: { + ga: { + value: '', + config: { + inverted: true, + ackNeeded: true, + pinNeeded: '1234' } }, - "synonyms": { - "value": "Standard Device" + synonyms: { + value: 'Standard Device' } } }; test('getConfig', () => { expect(Device.getConfig(item)).toStrictEqual({ - "ackNeeded": true, - "inverted": true, - "pinNeeded": "1234" + ackNeeded: true, + inverted: true, + pinNeeded: '1234' }); }); @@ -35,43 +35,38 @@ describe('Fan Device', () => { test('getMetadata', () => { expect(Device.getMetadata(item)).toStrictEqual({ - "attributes": {}, - "customData": { - "ackNeeded": true, - "deviceType": "DefaultDevice", - "inverted": true, - "itemType": "Number", - "pinNeeded": "1234" + attributes: {}, + customData: { + ackNeeded: true, + deviceType: 'DefaultDevice', + inverted: true, + itemType: 'Number', + pinNeeded: '1234' }, - "deviceInfo": { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Number:DefaultDevice", - "swVersion": "2.5.0", + deviceInfo: { + hwVersion: '2.5.0', + manufacturer: 'openHAB', + model: 'Number:DefaultDevice', + swVersion: '2.5.0' }, - "id": "DefaultDevice", - "name": { - "defaultNames": [ - "Default Device", - ], - "name": "Default Device", - "nicknames": [ - "Default Device", - "Standard Device" - ], + id: 'DefaultDevice', + name: { + defaultNames: ['Default Device'], + name: 'Default Device', + nicknames: ['Default Device', 'Standard Device'] }, - "roomHint": undefined, - "structureHint": undefined, - "traits": [], - "type": "", - "willReportState": false + roomHint: undefined, + structureHint: undefined, + traits: [], + type: '', + willReportState: false }); }); test('hasTag', () => { - expect(Device.hasTag({}, "testtag")).toBe(false); - expect(Device.hasTag({ "tags": ["test"] }, "testtag")).toBe(false); - expect(Device.hasTag({ "tags": ["testtag"] }, "testtag")).toBe(true); - expect(Device.hasTag({ "tags": ["TestTag"] }, "testtag")).toBe(true); + expect(Device.hasTag({}, 'testtag')).toBe(false); + expect(Device.hasTag({ tags: ['test'] }, 'testtag')).toBe(false); + expect(Device.hasTag({ tags: ['testtag'] }, 'testtag')).toBe(true); + expect(Device.hasTag({ tags: ['TestTag'] }, 'testtag')).toBe(true); }); }); diff --git a/tests/devices/dimmablelight.test.js b/tests/devices/dimmablelight.test.js index b48d167e..e346e183 100644 --- a/tests/devices/dimmablelight.test.js +++ b/tests/devices/dimmablelight.test.js @@ -2,30 +2,32 @@ const Device = require('../../functions/devices/dimmablelight.js'); describe('DimmableLight Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "LIGHT" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'LIGHT' + } } - } - })).toBe(true); + }) + ).toBe(true); }); test('matchesItemType', () => { - expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(true); - expect(Device.matchesItemType({ "type": "String" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(true); - expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); + expect(Device.matchesItemType({ type: 'Dimmer' })).toBe(true); + expect(Device.matchesItemType({ type: 'String' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Dimmer' })).toBe(true); + expect(Device.matchesItemType({ type: 'Group', groupType: 'String' })).toBe(false); }); test('getState', () => { - expect(Device.getState({ "state": "50" })).toStrictEqual({ - "on": true, - "brightness": 50 + expect(Device.getState({ state: '50' })).toStrictEqual({ + on: true, + brightness: 50 }); - expect(Device.getState({ "state": "NULL" })).toStrictEqual({ - "on": false, - "brightness": 0 + expect(Device.getState({ state: 'NULL' })).toStrictEqual({ + on: false, + brightness: 0 }); }); }); diff --git a/tests/devices/fan.test.js b/tests/devices/fan.test.js index af3576d9..0aeff842 100644 --- a/tests/devices/fan.test.js +++ b/tests/devices/fan.test.js @@ -2,28 +2,30 @@ const Device = require('../../functions/devices/fan.js'); describe('Fan Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "FAN" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'FAN' + } } - } - })).toBe(true); + }) + ).toBe(true); }); test('matchesItemType', () => { - expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(true); - expect(Device.matchesItemType({ "type": "String" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(true); - expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); + expect(Device.matchesItemType({ type: 'Dimmer' })).toBe(true); + expect(Device.matchesItemType({ type: 'String' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Dimmer' })).toBe(true); + expect(Device.matchesItemType({ type: 'Group', groupType: 'String' })).toBe(false); }); describe('getAttributes', () => { test('getAttributes no config', () => { const item = { - "metadata": { - "ga": { - "config": {} + metadata: { + ga: { + config: {} } } }; @@ -32,58 +34,58 @@ describe('Fan Device', () => { test('getAttributes speeds', () => { const item = { - "metadata": { - "ga": { - "config": { - "ordered": true, - "speeds": "0=null:off,50=slow,100=full:fast", - "lang": "en" + metadata: { + ga: { + config: { + ordered: true, + speeds: '0=null:off,50=slow,100=full:fast', + lang: 'en' } } } }; expect(Device.getAttributes(item)).toStrictEqual({ - "availableFanSpeeds": { - "speeds": [ + availableFanSpeeds: { + speeds: [ { - "speed_name": "0", - "speed_values": [ + speed_name: '0', + speed_values: [ { - "speed_synonym": ["null", "off"], - "lang": "en" + speed_synonym: ['null', 'off'], + lang: 'en' } ] }, { - "speed_name": "50", - "speed_values": [ + speed_name: '50', + speed_values: [ { - "speed_synonym": ["slow"], - "lang": "en" + speed_synonym: ['slow'], + lang: 'en' } ] }, { - "speed_name": "100", - "speed_values": [ + speed_name: '100', + speed_values: [ { - "speed_synonym": ["full", "fast"], - "lang": "en" + speed_synonym: ['full', 'fast'], + lang: 'en' } ] } ], - "ordered": true + ordered: true }, - "reversible": false + reversible: false }); }); }); test('getState', () => { - expect(Device.getState({ "state": "50" })).toStrictEqual({ - "currentFanSpeedSetting": "50", - "on": true + expect(Device.getState({ state: '50' })).toStrictEqual({ + currentFanSpeedSetting: '50', + on: true }); }); }); diff --git a/tests/devices/lock.test.js b/tests/devices/lock.test.js index 162c5ee9..d4395417 100644 --- a/tests/devices/lock.test.js +++ b/tests/devices/lock.test.js @@ -2,72 +2,74 @@ const Device = require('../../functions/devices/lock.js'); describe('Lock Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "LOCK" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'LOCK' + } } - } - })).toBe(true); + }) + ).toBe(true); }); test('matchesItemType', () => { - expect(Device.matchesItemType({ "type": "Switch" })).toBe(true); - expect(Device.matchesItemType({ "type": "Contact" })).toBe(true); - expect(Device.matchesItemType({ "type": "String" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Switch" })).toBe(true); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Contact" })).toBe(true); - expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); + expect(Device.matchesItemType({ type: 'Switch' })).toBe(true); + expect(Device.matchesItemType({ type: 'Contact' })).toBe(true); + expect(Device.matchesItemType({ type: 'String' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Switch' })).toBe(true); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Contact' })).toBe(true); + expect(Device.matchesItemType({ type: 'Group', groupType: 'String' })).toBe(false); }); describe('getState', () => { test('getState Switch', () => { - expect(Device.getState({ "state": "ON" })).toStrictEqual({ - "isLocked": true + expect(Device.getState({ state: 'ON' })).toStrictEqual({ + isLocked: true }); - expect(Device.getState({ "state": "OFF" })).toStrictEqual({ - "isLocked": false + expect(Device.getState({ state: 'OFF' })).toStrictEqual({ + isLocked: false }); }); test('getState Contact', () => { - expect(Device.getState({ "state": "CLOSED" })).toStrictEqual({ - "isLocked": true + expect(Device.getState({ state: 'CLOSED' })).toStrictEqual({ + isLocked: true }); - expect(Device.getState({ "state": "OPEN" })).toStrictEqual({ - "isLocked": false + expect(Device.getState({ state: 'OPEN' })).toStrictEqual({ + isLocked: false }); }); test('getState inverted Swtich', () => { const item1 = { - "state": "ON", - "metadata": { - "ga": { - "config": { - "inverted": true + state: 'ON', + metadata: { + ga: { + config: { + inverted: true } } } }; expect(Device.getState(item1)).toStrictEqual({ - "isLocked": false + isLocked: false }); }); test('getState inverted Contact', () => { const item2 = { - "state": "OPEN", - "metadata": { - "ga": { - "config": { - "inverted": true + state: 'OPEN', + metadata: { + ga: { + config: { + inverted: true } } } }; expect(Device.getState(item2)).toStrictEqual({ - "isLocked": true + isLocked: true }); }); }); diff --git a/tests/devices/openclosedevice.test.js b/tests/devices/openclosedevice.test.js index 5a523046..ec899db7 100644 --- a/tests/devices/openclosedevice.test.js +++ b/tests/devices/openclosedevice.test.js @@ -2,137 +2,137 @@ const Device = require('../../functions/devices/openclosedevice.js'); describe('OpenCloseDevice Device', () => { test('getAttributes', () => { - expect(Device.getAttributes({ "type": "Rollershutter" })).toStrictEqual({ - "pausable": false, - "discreteOnlyOpenClose": false, - "queryOnlyOpenClose": false + expect(Device.getAttributes({ type: 'Rollershutter' })).toStrictEqual({ + pausable: false, + discreteOnlyOpenClose: false, + queryOnlyOpenClose: false }); - expect(Device.getAttributes({ "type": "Switch", })).toStrictEqual({ - "pausable": false, - "discreteOnlyOpenClose": true, - "queryOnlyOpenClose": false + expect(Device.getAttributes({ type: 'Switch' })).toStrictEqual({ + pausable: false, + discreteOnlyOpenClose: true, + queryOnlyOpenClose: false }); - expect(Device.getAttributes({ "type": "Contact" })).toStrictEqual({ - "pausable": false, - "discreteOnlyOpenClose": true, - "queryOnlyOpenClose": true + expect(Device.getAttributes({ type: 'Contact' })).toStrictEqual({ + pausable: false, + discreteOnlyOpenClose: true, + queryOnlyOpenClose: true }); - expect(Device.getAttributes({ "type": "Group" })).toStrictEqual({ - "pausable": false, - "discreteOnlyOpenClose": false, - "queryOnlyOpenClose": false + expect(Device.getAttributes({ type: 'Group' })).toStrictEqual({ + pausable: false, + discreteOnlyOpenClose: false, + queryOnlyOpenClose: false }); - expect(Device.getAttributes({ "type": "Group", "groupType": "Switch" })).toStrictEqual({ - "pausable": false, - "discreteOnlyOpenClose": true, - "queryOnlyOpenClose": false + expect(Device.getAttributes({ type: 'Group', groupType: 'Switch' })).toStrictEqual({ + pausable: false, + discreteOnlyOpenClose: true, + queryOnlyOpenClose: false }); - expect(Device.getAttributes({ "type": "Group", "groupType": "Contact" })).toStrictEqual({ - "pausable": false, - "discreteOnlyOpenClose": true, - "queryOnlyOpenClose": true + expect(Device.getAttributes({ type: 'Group', groupType: 'Contact' })).toStrictEqual({ + pausable: false, + discreteOnlyOpenClose: true, + queryOnlyOpenClose: true }); }); describe('getState', () => { test('getState Contact', () => { const item = { - "type": "Contact", - "state": "OPEN" + type: 'Contact', + state: 'OPEN' }; expect(Device.getState(item)).toStrictEqual({ - "openPercent": 100 + openPercent: 100 }); - item.state = "CLOSED"; + item.state = 'CLOSED'; expect(Device.getState(item)).toStrictEqual({ - "openPercent": 0 + openPercent: 0 }); }); test('getState Switch', () => { const item = { - "type": "Switch", - "state": "ON" + type: 'Switch', + state: 'ON' }; expect(Device.getState(item)).toStrictEqual({ - "openPercent": 100 + openPercent: 100 }); - item.state = "OFF"; + item.state = 'OFF'; expect(Device.getState(item)).toStrictEqual({ - "openPercent": 0 + openPercent: 0 }); }); test('getState Rollershutter', () => { const item = { - "type": "Rollershutter", - "state": "25" + type: 'Rollershutter', + state: '25' }; expect(Device.getState(item)).toStrictEqual({ - "openPercent": 75 + openPercent: 75 }); }); test('getState Group Rollershutter', () => { const item = { - "type": "Group", - "groupType": "Rollershutter", - "state": "25" + type: 'Group', + groupType: 'Rollershutter', + state: '25' }; expect(Device.getState(item)).toStrictEqual({ - "openPercent": 75 + openPercent: 75 }); }); test('getState inverted Contact', () => { const item = { - "type": "Contact", - "state": "CLOSED", - "metadata": { - "ga": { - "config": { - "inverted": true + type: 'Contact', + state: 'CLOSED', + metadata: { + ga: { + config: { + inverted: true } } } }; expect(Device.getState(item)).toStrictEqual({ - "openPercent": 100 + openPercent: 100 }); }); test('getState inverted Switch', () => { const item = { - "type": "Switch", - "state": "ON", - "metadata": { - "ga": { - "config": { - "inverted": true + type: 'Switch', + state: 'ON', + metadata: { + ga: { + config: { + inverted: true } } } }; expect(Device.getState(item)).toStrictEqual({ - "openPercent": 0 + openPercent: 0 }); }); test('getState inverted Rollershutter', () => { const item = { - "type": "Rollershutter", - "state": "25", - "metadata": { - "ga": { - "config": { - "inverted": true + type: 'Rollershutter', + state: '25', + metadata: { + ga: { + config: { + inverted: true } } } }; expect(Device.getState(item)).toStrictEqual({ - "openPercent": 25 + openPercent: 25 }); }); }); diff --git a/tests/devices/scene.test.js b/tests/devices/scene.test.js index 98f889d6..c1dcc4e2 100644 --- a/tests/devices/scene.test.js +++ b/tests/devices/scene.test.js @@ -2,25 +2,27 @@ const Device = require('../../functions/devices/scene.js'); describe('Scene Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "SCENE" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'SCENE' + } } - } - })).toBe(true); + }) + ).toBe(true); }); test('matchesItemType', () => { - expect(Device.matchesItemType({ "type": "Switch" })).toBe(true); - expect(Device.matchesItemType({ "type": "String" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Switch" })).toBe(true); - expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); + expect(Device.matchesItemType({ type: 'Switch' })).toBe(true); + expect(Device.matchesItemType({ type: 'String' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Switch' })).toBe(true); + expect(Device.matchesItemType({ type: 'Group', groupType: 'String' })).toBe(false); }); test('getAttributes', () => { expect(Device.getAttributes()).toStrictEqual({ - "sceneReversible": true + sceneReversible: true }); }); diff --git a/tests/devices/securitysystem.test.js b/tests/devices/securitysystem.test.js index 135c3dbc..166c0a6d 100644 --- a/tests/devices/securitysystem.test.js +++ b/tests/devices/securitysystem.test.js @@ -2,46 +2,47 @@ const Device = require('../../functions/devices/securitysystem.js'); describe('SecuritySystem Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "SECURITYSYSTEM" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'SECURITYSYSTEM' + } } - } - })).toBe(true); + }) + ).toBe(true); }); test('matchesItemType', () => { - expect(Device.matchesItemType({ "type": "Switch" })).toBe(true); - expect(Device.matchesItemType({ "type": "String" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Switch" })).toBe(true); - expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); + expect(Device.matchesItemType({ type: 'Switch' })).toBe(true); + expect(Device.matchesItemType({ type: 'String' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Switch' })).toBe(true); + expect(Device.matchesItemType({ type: 'Group', groupType: 'String' })).toBe(false); }); - describe('getState', () => { test('getState', () => { - expect(Device.getState({ "state": "ON" })).toStrictEqual({ - "isArmed": true + expect(Device.getState({ state: 'ON' })).toStrictEqual({ + isArmed: true }); - expect(Device.getState({ "state": "OFF" })).toStrictEqual({ - "isArmed": false + expect(Device.getState({ state: 'OFF' })).toStrictEqual({ + isArmed: false }); }); test('getState inverted', () => { const item = { - "state": "ON", - "metadata": { - "ga": { - "config": { - "inverted": true + state: 'ON', + metadata: { + ga: { + config: { + inverted: true } } } }; expect(Device.getState(item)).toStrictEqual({ - "isArmed": false + isArmed: false }); }); }); diff --git a/tests/devices/sensor.test.js b/tests/devices/sensor.test.js index 835421a6..3721c97f 100644 --- a/tests/devices/sensor.test.js +++ b/tests/devices/sensor.test.js @@ -2,38 +2,40 @@ const Device = require('../../functions/devices/sensor.js'); describe('Sensor Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "SENSOR" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'SENSOR' + } } - } - })).toBe(true); + }) + ).toBe(true); }); describe('matchesItemType', () => { describe('matchesItemType numeric', () => { const item = { - "metadata": { - "ga": { - "config": { - "sensorName": "Sensor", - "valueUnit": "PERCENT" + metadata: { + ga: { + config: { + sensorName: 'Sensor', + valueUnit: 'PERCENT' } } } }; expect(Device.matchesItemType(item)).toBe(true); - expect(Device.matchesItemType({ "type": "Number" })).toBe(false); + expect(Device.matchesItemType({ type: 'Number' })).toBe(false); }); describe('matchesItemType descriptive', () => { const item = { - "metadata": { - "ga": { - "config": { - "sensorName": "Sensor", - "states": "50=good,100=bad" + metadata: { + ga: { + config: { + sensorName: 'Sensor', + states: '50=good,100=bad' } } } @@ -45,9 +47,9 @@ describe('Sensor Device', () => { describe('getAttributes', () => { test('getAttributes no config', () => { const item = { - "metadata": { - "ga": { - "config": {} + metadata: { + ga: { + config: {} } } }; @@ -56,28 +58,24 @@ describe('Sensor Device', () => { test('getAttributes states', () => { const item = { - "metadata": { - "ga": { - "config": { - "sensorName": "Sensor", - "valueUnit": "AQI", - "states": "good=10,moderate=50,poor=90" + metadata: { + ga: { + config: { + sensorName: 'Sensor', + valueUnit: 'AQI', + states: 'good=10,moderate=50,poor=90' } } } }; expect(Device.getAttributes(item)).toStrictEqual({ - "sensorStatesSupported": { - "descriptiveCapabilities": { - "availableStates": [ - "good", - "moderate", - "poor" - ], + sensorStatesSupported: { + descriptiveCapabilities: { + availableStates: ['good', 'moderate', 'poor'] }, - "name": "Sensor", - "numericCapabilities": { - "rawValueUnit": "AQI" + name: 'Sensor', + numericCapabilities: { + rawValueUnit: 'AQI' } } }); @@ -87,44 +85,44 @@ describe('Sensor Device', () => { describe('getState', () => { test('getState', () => { const item = { - "metadata": { - "ga": { - "config": { - "sensorName": "Sensor", - "valueUnit": "AQI", - "states": "good=10,moderate=50,poor=90" + metadata: { + ga: { + config: { + sensorName: 'Sensor', + valueUnit: 'AQI', + states: 'good=10,moderate=50,poor=90' } } }, - "state": "10" + state: '10' }; expect(Device.getState(item)).toStrictEqual({ - "currentSensorStateData": { - "currentSensorState": "good", - "name": "Sensor", - "rawValue": 10 + currentSensorStateData: { + currentSensorState: 'good', + name: 'Sensor', + rawValue: 10 } }); }); test('getState no matching state', () => { const item = { - "metadata": { - "ga": { - "config": { - "sensorName": "Sensor", - "valueUnit": "AQI", - "states": "good=10,moderate=50,poor=90" + metadata: { + ga: { + config: { + sensorName: 'Sensor', + valueUnit: 'AQI', + states: 'good=10,moderate=50,poor=90' } } }, - "state": "20" + state: '20' }; expect(Device.getState(item)).toStrictEqual({ - "currentSensorStateData": { - "currentSensorState": "", - "name": "Sensor", - "rawValue": 20 + currentSensorStateData: { + currentSensorState: '', + name: 'Sensor', + rawValue: 20 } }); }); diff --git a/tests/devices/simplelight.test.js b/tests/devices/simplelight.test.js index 77b11a3d..6e627aac 100644 --- a/tests/devices/simplelight.test.js +++ b/tests/devices/simplelight.test.js @@ -2,12 +2,14 @@ const Device = require('../../functions/devices/simplelight.js'); describe('SimpleLight Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "LIGHT" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'LIGHT' + } } - } - })).toBe(true); + }) + ).toBe(true); }); }); diff --git a/tests/devices/speaker.test.js b/tests/devices/speaker.test.js index a8a462ce..ce41ef09 100644 --- a/tests/devices/speaker.test.js +++ b/tests/devices/speaker.test.js @@ -2,63 +2,65 @@ const Device = require('../../functions/devices/speaker.js'); describe('Speaker Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "SPEAKER" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'SPEAKER' + } } - } - })).toBe(true); + }) + ).toBe(true); }); test('matchesItemType', () => { - expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(true); - expect(Device.matchesItemType({ "type": "Number" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(true); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Number" })).toBe(false); + expect(Device.matchesItemType({ type: 'Dimmer' })).toBe(true); + expect(Device.matchesItemType({ type: 'Number' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Dimmer' })).toBe(true); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Number' })).toBe(false); }); describe('getAttributes', () => { test('getAttributes no config', () => { const item = { - "metadata": { - "ga": { - "config": {} + metadata: { + ga: { + config: {} } } }; expect(Device.getAttributes(item)).toStrictEqual({ - "volumeCanMuteAndUnmute": false, - "volumeMaxLevel": 100 + volumeCanMuteAndUnmute: false, + volumeMaxLevel: 100 }); }); test('getAttributes volumeDefaultPercentage, levelStepSize', () => { const item = { - "metadata": { - "ga": { - "config": { - "volumeDefaultPercentage": "20", - "levelStepSize": "10" + metadata: { + ga: { + config: { + volumeDefaultPercentage: '20', + levelStepSize: '10' } } } }; expect(Device.getAttributes(item)).toStrictEqual({ - "volumeCanMuteAndUnmute": false, - "volumeMaxLevel": 100, - "volumeDefaultPercentage": 20, - "levelStepSize": 10 + volumeCanMuteAndUnmute: false, + volumeMaxLevel: 100, + volumeDefaultPercentage: 20, + levelStepSize: 10 }); }); }); test('getState', () => { - expect(Device.getState({ "state": "10" })).toStrictEqual({ - "currentVolume": 10 + expect(Device.getState({ state: '10' })).toStrictEqual({ + currentVolume: 10 }); - expect(Device.getState({ "state": "90" })).toStrictEqual({ - "currentVolume": 90 + expect(Device.getState({ state: '90' })).toStrictEqual({ + currentVolume: 90 }); }); }); diff --git a/tests/devices/specialcolorlight.test.js b/tests/devices/specialcolorlight.test.js index db03df0d..419a2724 100644 --- a/tests/devices/specialcolorlight.test.js +++ b/tests/devices/specialcolorlight.test.js @@ -2,89 +2,91 @@ const Device = require('../../functions/devices/specialcolorlight.js'); describe('SpecialColorLight Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "LIGHT" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'LIGHT' + } } - } - })).toBe(true); + }) + ).toBe(true); }); test('matchesItemType', () => { const item = { - "type": "Group", - "metadata": { - "ga": { - "value": "LIGHT", - "config": { - "colorTemperatureRange": "1000,4000" + type: 'Group', + metadata: { + ga: { + value: 'LIGHT', + config: { + colorTemperatureRange: '1000,4000' } } }, - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "lightBrightness" + metadata: { + ga: { + value: 'lightBrightness' } } }, { - "metadata": { - "ga": { - "value": "lightColorTemperature" + metadata: { + ga: { + value: 'lightColorTemperature' } } } ] }; const item2 = { - "type": "Group", - "metadata": { - "ga": { - "value": "LIGHT" + type: 'Group', + metadata: { + ga: { + value: 'LIGHT' } }, - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "lightBrightness" + metadata: { + ga: { + value: 'lightBrightness' } } }, { - "metadata": { - "ga": { - "value": "lightColorTemperature" + metadata: { + ga: { + value: 'lightColorTemperature' } } } ] }; const item3 = { - "type": "Group", - "metadata": { - "ga": { - "value": "LIGHT", - "config": { - "useKelvin": true + type: 'Group', + metadata: { + ga: { + value: 'LIGHT', + config: { + useKelvin: true } } }, - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "lightBrightness" + metadata: { + ga: { + value: 'lightBrightness' } } }, { - "metadata": { - "ga": { - "value": "lightColorTemperature" + metadata: { + ga: { + value: 'lightColorTemperature' } } } @@ -93,36 +95,36 @@ describe('SpecialColorLight Device', () => { expect(Device.matchesItemType(item)).toBe(true); expect(Device.matchesItemType(item2)).toBe(false); expect(Device.matchesItemType(item3)).toBe(true); - expect(Device.matchesItemType({ "type": "Color" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Color" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(false); + expect(Device.matchesItemType({ type: 'Color' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Color' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Dimmer' })).toBe(false); }); describe('getAttributes', () => { test('getAttributes colorTemperatureRange', () => { const item = { - "metadata": { - "ga": { - "config": { - "colorTemperatureRange": "1000,2000" + metadata: { + ga: { + config: { + colorTemperatureRange: '1000,2000' } } } }; expect(Device.getAttributes(item)).toStrictEqual({ - "colorTemperatureRange": { - "temperatureMinK": 1000, - "temperatureMaxK": 2000 + colorTemperatureRange: { + temperatureMinK: 1000, + temperatureMaxK: 2000 } }); }); test('getAttributes invalid colorTemperatureRange', () => { const item1 = { - "metadata": { - "ga": { - "config": { - "colorTemperatureRange": "a,b" + metadata: { + ga: { + config: { + colorTemperatureRange: 'a,b' } } } @@ -134,78 +136,78 @@ describe('SpecialColorLight Device', () => { describe('getState', () => { test('getState', () => { const item = { - "type": "Group", - "metadata": { - "ga": { - "value": "LIGHT", - "config": { - "colorTemperatureRange": "1000,4000" + type: 'Group', + metadata: { + ga: { + value: 'LIGHT', + config: { + colorTemperatureRange: '1000,4000' } } }, - "members": [ + members: [ { - "state": "50", - "metadata": { - "ga": { - "value": "lightBrightness" + state: '50', + metadata: { + ga: { + value: 'lightBrightness' } } }, { - "state": "20", - "metadata": { - "ga": { - "value": "lightColorTemperature" + state: '20', + metadata: { + ga: { + value: 'lightColorTemperature' } } } ] }; expect(Device.getState(item)).toStrictEqual({ - "on": true, - "brightness": 50, - "color": { - "temperatureK": 3400 + on: true, + brightness: 50, + color: { + temperatureK: 3400 } }); }); test('getState use kelvin', () => { const item = { - "type": "Group", - "metadata": { - "ga": { - "value": "LIGHT", - "config": { - "colorTemperatureRange": "1000,4000", - "useKelvin": true + type: 'Group', + metadata: { + ga: { + value: 'LIGHT', + config: { + colorTemperatureRange: '1000,4000', + useKelvin: true } } }, - "members": [ + members: [ { - "state": "50", - "metadata": { - "ga": { - "value": "lightBrightness" + state: '50', + metadata: { + ga: { + value: 'lightBrightness' } } }, { - "state": "2000", - "metadata": { - "ga": { - "value": "lightColorTemperature" + state: '2000', + metadata: { + ga: { + value: 'lightColorTemperature' } } } ] }; expect(Device.getState(item)).toStrictEqual({ - "on": true, - "brightness": 50, - "color": { - "temperatureK": 2000 + on: true, + brightness: 50, + color: { + temperatureK: 2000 } }); }); diff --git a/tests/devices/startstopswitch.test.js b/tests/devices/startstopswitch.test.js index 7a1e99d8..2fcaa1d6 100644 --- a/tests/devices/startstopswitch.test.js +++ b/tests/devices/startstopswitch.test.js @@ -2,47 +2,47 @@ const Device = require('../../functions/devices/startstopswitch.js'); describe('StartStopSwitch Device', () => { test('matchesItemType', () => { - expect(Device.matchesItemType({ "type": "Switch" })).toBe(true); - expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Switch" })).toBe(true); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(false); + expect(Device.matchesItemType({ type: 'Switch' })).toBe(true); + expect(Device.matchesItemType({ type: 'Dimmer' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Switch' })).toBe(true); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Dimmer' })).toBe(false); }); test('getAttributes', () => { - expect(Device.getAttributes()).toStrictEqual({ "pausable": false }); + expect(Device.getAttributes()).toStrictEqual({ pausable: false }); }); describe('getState', () => { test('getState', () => { - expect(Device.getState({ "state": "ON" })).toStrictEqual({ - "isRunning": true, - "isPaused": false + expect(Device.getState({ state: 'ON' })).toStrictEqual({ + isRunning: true, + isPaused: false }); - expect(Device.getState({ "state": "OFF" })).toStrictEqual({ - "isRunning": false, - "isPaused": true + expect(Device.getState({ state: 'OFF' })).toStrictEqual({ + isRunning: false, + isPaused: true }); }); test('getState inverted', () => { const item = { - "state": "ON", - "metadata": { - "ga": { - "config": { - "inverted": true + state: 'ON', + metadata: { + ga: { + config: { + inverted: true } } } }; expect(Device.getState(item)).toStrictEqual({ - "isRunning": false, - "isPaused": true + isRunning: false, + isPaused: true }); - item.state = "OFF"; + item.state = 'OFF'; expect(Device.getState(item)).toStrictEqual({ - "isRunning": true, - "isPaused": false + isRunning: true, + isPaused: false }); }); }); diff --git a/tests/devices/switch.test.js b/tests/devices/switch.test.js index 4e51663b..4ab982d2 100644 --- a/tests/devices/switch.test.js +++ b/tests/devices/switch.test.js @@ -2,48 +2,50 @@ const Device = require('../../functions/devices/switch.js'); describe('Switch Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "SWITCH" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'SWITCH' + } } - } - })).toBe(true); + }) + ).toBe(true); }); test('matchesItemType', () => { - expect(Device.matchesItemType({ "type": "Switch" })).toBe(true); - expect(Device.matchesItemType({ "type": "String" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Switch" })).toBe(true); - expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); + expect(Device.matchesItemType({ type: 'Switch' })).toBe(true); + expect(Device.matchesItemType({ type: 'String' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Switch' })).toBe(true); + expect(Device.matchesItemType({ type: 'Group', groupType: 'String' })).toBe(false); }); test('getState', () => { - expect(Device.getState({ "state": "ON" })).toStrictEqual({ - "on": true + expect(Device.getState({ state: 'ON' })).toStrictEqual({ + on: true }); - expect(Device.getState({ "state": "OFF" })).toStrictEqual({ - "on": false + expect(Device.getState({ state: 'OFF' })).toStrictEqual({ + on: false }); }); test('getState inverted', () => { const item = { - "state": "ON", - "metadata": { - "ga": { - "config": { - "inverted": true + state: 'ON', + metadata: { + ga: { + config: { + inverted: true } } } }; expect(Device.getState(item)).toStrictEqual({ - "on": false + on: false }); - item.state = "OFF"; + item.state = 'OFF'; expect(Device.getState(item)).toStrictEqual({ - "on": true + on: true }); }); }); diff --git a/tests/devices/temperaturesensor.test.js b/tests/devices/temperaturesensor.test.js index 39917adb..f71ea86a 100644 --- a/tests/devices/temperaturesensor.test.js +++ b/tests/devices/temperaturesensor.test.js @@ -2,72 +2,74 @@ const Device = require('../../functions/devices/temperaturesensor.js'); describe('TemperatureSensor Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "temperaturesensor" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'temperaturesensor' + } } - } - })).toBe(true); + }) + ).toBe(true); }); test('matchesItemType', () => { - expect(Device.matchesItemType({ "type": "Number" })).toBe(true); - expect(Device.matchesItemType({ "type": "Dimmer" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Dimmer" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Number" })).toBe(true); + expect(Device.matchesItemType({ type: 'Number' })).toBe(true); + expect(Device.matchesItemType({ type: 'Dimmer' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Dimmer' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Number' })).toBe(true); }); describe('getAttributes', () => { test('getAttributes no config', () => { const item1 = { - "metadata": { - "ga": { - "config": {} + metadata: { + ga: { + config: {} } } }; expect(Device.getAttributes(item1)).toStrictEqual({ - "queryOnlyTemperatureControl": true, - "temperatureUnitForUX": "C" + queryOnlyTemperatureControl: true, + temperatureUnitForUX: 'C' }); }); test('getAttributes useFahrenheit', () => { const item2 = { - "metadata": { - "ga": { - "config": { - "useFahrenheit": true + metadata: { + ga: { + config: { + useFahrenheit: true } } } }; expect(Device.getAttributes(item2)).toStrictEqual({ - "queryOnlyTemperatureControl": true, - "temperatureUnitForUX": "F" + queryOnlyTemperatureControl: true, + temperatureUnitForUX: 'F' }); }); }); test('getState', () => { - expect(Device.getState({ "state": "10" })).toStrictEqual({ - "temperatureSetpointCelsius": 10, - "temperatureAmbientCelsius": 10 + expect(Device.getState({ state: '10' })).toStrictEqual({ + temperatureSetpointCelsius: 10, + temperatureAmbientCelsius: 10 }); const item = { - "state": "10", - "metadata": { - "ga": { - "config": { - "useFahrenheit": true + state: '10', + metadata: { + ga: { + config: { + useFahrenheit: true } } } }; expect(Device.getState(item)).toStrictEqual({ - "temperatureSetpointCelsius": -12.2, - "temperatureAmbientCelsius": -12.2 + temperatureSetpointCelsius: -12.2, + temperatureAmbientCelsius: -12.2 }); }); }); diff --git a/tests/devices/thermostat.test.js b/tests/devices/thermostat.test.js index 1700999c..1e59b6d6 100644 --- a/tests/devices/thermostat.test.js +++ b/tests/devices/thermostat.test.js @@ -2,39 +2,41 @@ const Device = require('../../functions/devices/thermostat.js'); describe('Thermostat Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "THERMOSTAT" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'THERMOSTAT' + } } - } - })).toBe(true); + }) + ).toBe(true); }); test('matchesItemType', () => { const item = { - "type": "Group", - "members": [ + type: 'Group', + members: [ { - "metadata": { - "ga": { - "value": "thermostatTemperatureAmbient" + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' } } } ] }; expect(Device.matchesItemType(item)).toBe(true); - expect(Device.matchesItemType({ "type": "Group" })).toBe(false); + expect(Device.matchesItemType({ type: 'Group' })).toBe(false); }); describe('useFahrenheit', () => { test('useFahrenheit thermostatTemperatureUnit', () => { const item = { - "metadata": { - "ga": { - "config": { - "thermostatTemperatureUnit": "F" + metadata: { + ga: { + config: { + thermostatTemperatureUnit: 'F' } } } @@ -43,10 +45,10 @@ describe('Thermostat Device', () => { }); test('useFahrenheit useFahrenheit', () => { const item = { - "metadata": { - "ga": { - "config": { - "useFahrenheit": true + metadata: { + ga: { + config: { + useFahrenheit: true } } } @@ -55,7 +57,7 @@ describe('Thermostat Device', () => { }); test('useFahrenheit tag', () => { const item = { - "tags": ["Fahrenheit"] + tags: ['Fahrenheit'] }; expect(Device.useFahrenheit(item)).toBe(true); }); @@ -64,226 +66,226 @@ describe('Thermostat Device', () => { describe('getAttributes', () => { test('getAttributes no config', () => { const item = { - "metadata": { - "ga": { - "config": {} + metadata: { + ga: { + config: {} } } }; expect(Device.getAttributes(item)).toStrictEqual({ - "availableThermostatModes": ["off", "heat", "cool", "on", "heatcool", "auto", "eco"], - "thermostatTemperatureUnit": "C" + availableThermostatModes: ['off', 'heat', 'cool', 'on', 'heatcool', 'auto', 'eco'], + thermostatTemperatureUnit: 'C' }); }); test('getAttributes modes, fahrenheit', () => { const item = { - "metadata": { - "ga": { - "config": { - "modes": "on=1,off=2", - "useFahrenheit": true + metadata: { + ga: { + config: { + modes: 'on=1,off=2', + useFahrenheit: true } } } }; expect(Device.getAttributes(item)).toStrictEqual({ - "availableThermostatModes": ["on", "off"], - "thermostatTemperatureUnit": "F" + availableThermostatModes: ['on', 'off'], + thermostatTemperatureUnit: 'F' }); }); test('getAttributes temperaturerange', () => { const item = { - "metadata": { - "ga": { - "config": { - "thermostatTemperatureRange": "10,30" + metadata: { + ga: { + config: { + thermostatTemperatureRange: '10,30' } } } }; expect(Device.getAttributes(item)).toStrictEqual({ - "availableThermostatModes": ["off", "heat", "cool", "on", "heatcool", "auto", "eco"], - "thermostatTemperatureUnit": "C", - "thermostatTemperatureRange": { - "maxThresholdCelsius": 30, - "minThresholdCelsius": 10 + availableThermostatModes: ['off', 'heat', 'cool', 'on', 'heatcool', 'auto', 'eco'], + thermostatTemperatureUnit: 'C', + thermostatTemperatureRange: { + maxThresholdCelsius: 30, + minThresholdCelsius: 10 } }); }); test('getAttributes invalid temperaturerange', () => { const item = { - "metadata": { - "ga": { - "config": { - "thermostatTemperatureRange": "a,b" + metadata: { + ga: { + config: { + thermostatTemperatureRange: 'a,b' } } } }; expect(Device.getAttributes(item)).toStrictEqual({ - "availableThermostatModes": ["off", "heat", "cool", "on", "heatcool", "auto", "eco"], - "thermostatTemperatureUnit": "C" + availableThermostatModes: ['off', 'heat', 'cool', 'on', 'heatcool', 'auto', 'eco'], + thermostatTemperatureUnit: 'C' }); }); test('getAttributes queryOnly', () => { const item = { - "metadata": { - "ga": { - "config": {} + metadata: { + ga: { + config: {} } }, - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "thermostatTemperatureAmbient" + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' } } } ] }; expect(Device.getAttributes(item)).toStrictEqual({ - "thermostatTemperatureUnit": "C", - "queryOnlyTemperatureSetting": true + thermostatTemperatureUnit: 'C', + queryOnlyTemperatureSetting: true }); }); }); describe('getMembers', () => { - expect(Device.getMembers({ "members": [{}] })).toStrictEqual({}); - expect(Device.getMembers({ "members": [{ "metadata": { "ga": { "value": "invalid" } } }] })).toStrictEqual({}); + expect(Device.getMembers({ members: [{}] })).toStrictEqual({}); + expect(Device.getMembers({ members: [{ metadata: { ga: { value: 'invalid' } } }] })).toStrictEqual({}); test('getMembers', () => { const item = { - "members": [ + members: [ { - "name": "Mode", - "state": "on", - "metadata": { - "ga": { - "value": "thermostatMode" + name: 'Mode', + state: 'on', + metadata: { + ga: { + value: 'thermostatMode' } } }, { - "name": "Setpoint", - "state": "20", - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpoint" + name: 'Setpoint', + state: '20', + metadata: { + ga: { + value: 'thermostatTemperatureSetpoint' } } }, { - "name": "High", - "state": "25", - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpointHigh" + name: 'High', + state: '25', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointHigh' } } }, { - "name": "Low", - "state": "5", - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpointLow" + name: 'Low', + state: '5', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointLow' } } }, { - "name": "Temperature", - "state": "20", - "metadata": { - "ga": { - "value": "thermostatTemperatureAmbient" + name: 'Temperature', + state: '20', + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' } } }, { - "name": "Humidity", - "state": "50", - "metadata": { - "ga": { - "value": "thermostatHumidityAmbient" + name: 'Humidity', + state: '50', + metadata: { + ga: { + value: 'thermostatHumidityAmbient' } } } ] }; expect(Device.getMembers(item)).toStrictEqual({ - "thermostatMode": { - "name": "Mode", - "state": "on" + thermostatMode: { + name: 'Mode', + state: 'on' }, - "thermostatTemperatureSetpoint": { - "name": "Setpoint", - "state": "20" + thermostatTemperatureSetpoint: { + name: 'Setpoint', + state: '20' }, - "thermostatTemperatureSetpointHigh": { - "name": "High", - "state": "25" + thermostatTemperatureSetpointHigh: { + name: 'High', + state: '25' }, - "thermostatTemperatureSetpointLow": { - "name": "Low", - "state": "5" + thermostatTemperatureSetpointLow: { + name: 'Low', + state: '5' }, - "thermostatTemperatureAmbient": { - "name": "Temperature", - "state": "20" + thermostatTemperatureAmbient: { + name: 'Temperature', + state: '20' }, - "thermostatHumidityAmbient": { - "name": "Humidity", - "state": "50" + thermostatHumidityAmbient: { + name: 'Humidity', + state: '50' } }); }); test('getMembers with tags', () => { const item = { - "members": [ + members: [ { - "name": "Mode", - "state": "on", - "tags": ["HeatingCoolingMode"] + name: 'Mode', + state: 'on', + tags: ['HeatingCoolingMode'] }, { - "name": "Setpoint", - "state": "20", - "tags": ["TargetTemperature"] + name: 'Setpoint', + state: '20', + tags: ['TargetTemperature'] }, { - "name": "Temperature", - "state": "20", - "tags": ["CurrentTemperature"] + name: 'Temperature', + state: '20', + tags: ['CurrentTemperature'] }, { - "name": "Humidity", - "state": "50", - "tags": ["CurrentHumidity"] + name: 'Humidity', + state: '50', + tags: ['CurrentHumidity'] } ] }; expect(Device.getMembers(item)).toStrictEqual({ - "thermostatMode": { - "name": "Mode", - "state": "on" + thermostatMode: { + name: 'Mode', + state: 'on' }, - "thermostatTemperatureSetpoint": { - "name": "Setpoint", - "state": "20" + thermostatTemperatureSetpoint: { + name: 'Setpoint', + state: '20' }, - "thermostatTemperatureAmbient": { - "name": "Temperature", - "state": "20" + thermostatTemperatureAmbient: { + name: 'Temperature', + state: '20' }, - "thermostatHumidityAmbient": { - "name": "Humidity", - "state": "50" + thermostatHumidityAmbient: { + name: 'Humidity', + state: '50' } }); }); @@ -291,161 +293,155 @@ describe('Thermostat Device', () => { test('getModeMap', () => { const item = { - "metadata": { - "ga": { - "config": { - "modes": "on=ON:1,off=OFF:2,auto=3" + metadata: { + ga: { + config: { + modes: 'on=ON:1,off=OFF:2,auto=3' } } } }; expect(Device.getModeMap(item)).toStrictEqual({ - "on": [ - "ON", - "1" - ], - "off": [ - "OFF", - "2" - ], - "auto": [ - "3" - ] + on: ['ON', '1'], + off: ['OFF', '2'], + auto: ['3'] }); expect(Device.getModeMap({})).toStrictEqual({ - "off": ["off"], - "heat": ["heat"], - "cool": ["cool"], - "on": ["on"], - "heatcool": ["heatcool"], - "auto": ["auto"], - "eco": ["eco"] + off: ['off'], + heat: ['heat'], + cool: ['cool'], + on: ['on'], + heatcool: ['heatcool'], + auto: ['auto'], + eco: ['eco'] }); }); test('translateModeToOpenhab', () => { const item = { - "metadata": { - "ga": { - "config": { - "modes": "on=ON:1,off=OFF:2,auto=3" + metadata: { + ga: { + config: { + modes: 'on=ON:1,off=OFF:2,auto=3' } } } }; - expect(Device.translateModeToOpenhab(item, "off")).toBe("OFF"); - expect(Device.translateModeToOpenhab(item, "auto")).toBe("3"); - expect(() => { Device.translateModeToOpenhab(item, "invalid") }).toThrow(); + expect(Device.translateModeToOpenhab(item, 'off')).toBe('OFF'); + expect(Device.translateModeToOpenhab(item, 'auto')).toBe('3'); + expect(() => { + Device.translateModeToOpenhab(item, 'invalid'); + }).toThrow(); }); test('translateModeToGoogle', () => { const item = { - "metadata": { - "ga": { - "config": { - "modes": "on=ON:1,off=OFF:2,auto=3" + metadata: { + ga: { + config: { + modes: 'on=ON:1,off=OFF:2,auto=3' } } } }; - expect(Device.translateModeToGoogle(item, "OFF")).toBe("off"); - expect(Device.translateModeToGoogle(item, "3")).toBe("auto"); - expect(Device.translateModeToGoogle(item, "invalid")).toBe("on"); + expect(Device.translateModeToGoogle(item, 'OFF')).toBe('off'); + expect(Device.translateModeToGoogle(item, '3')).toBe('auto'); + expect(Device.translateModeToGoogle(item, 'invalid')).toBe('on'); }); describe('getState', () => { test('getState', () => { const item = { - "members": [ + members: [ { - "name": "Mode", - "state": "on", - "metadata": { - "ga": { - "value": "thermostatMode" + name: 'Mode', + state: 'on', + metadata: { + ga: { + value: 'thermostatMode' } } }, { - "name": "Setpoint", - "state": "20", - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpoint" + name: 'Setpoint', + state: '20', + metadata: { + ga: { + value: 'thermostatTemperatureSetpoint' } } }, { - "name": "High", - "state": "25", - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpointHigh" + name: 'High', + state: '25', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointHigh' } } }, { - "name": "Low", - "state": "5", - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpointLow" + name: 'Low', + state: '5', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointLow' } } }, { - "name": "Temperature", - "state": "20", - "metadata": { - "ga": { - "value": "thermostatTemperatureAmbient" + name: 'Temperature', + state: '20', + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' } } }, { - "name": "Humidity", - "state": "50", - "metadata": { - "ga": { - "value": "thermostatHumidityAmbient" + name: 'Humidity', + state: '50', + metadata: { + ga: { + value: 'thermostatHumidityAmbient' } } } ] }; expect(Device.getState(item)).toStrictEqual({ - "thermostatHumidityAmbient": 50, - "thermostatMode": "on", - "thermostatTemperatureAmbient": 20, - "thermostatTemperatureSetpoint": 20, - "thermostatTemperatureSetpointHigh": 25, - "thermostatTemperatureSetpointLow": 5 + thermostatHumidityAmbient: 50, + thermostatMode: 'on', + thermostatTemperatureAmbient: 20, + thermostatTemperatureSetpoint: 20, + thermostatTemperatureSetpointHigh: 25, + thermostatTemperatureSetpointLow: 5 }); }); test('getState only temperature', () => { const item = { - "metadata": { - "ga": { - "config": { - "useFahrenheit": true + metadata: { + ga: { + config: { + useFahrenheit: true } } }, - "members": [ + members: [ { - "name": "Temperature", - "state": "20", - "metadata": { - "ga": { - "value": "thermostatTemperatureAmbient" + name: 'Temperature', + state: '20', + metadata: { + ga: { + value: 'thermostatTemperatureAmbient' } } } ] }; expect(Device.getState(item)).toStrictEqual({ - "thermostatTemperatureAmbient": -6.7 + thermostatTemperatureAmbient: -6.7 }); }); }); diff --git a/tests/devices/tv.test.js b/tests/devices/tv.test.js index 9c6ce153..3d53e5f1 100644 --- a/tests/devices/tv.test.js +++ b/tests/devices/tv.test.js @@ -2,110 +2,110 @@ const Device = require('../../functions/devices/tv.js'); describe('TV Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "TV" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'TV' + } } - } - })).toBe(true); + }) + ).toBe(true); }); test('matchesItemType', () => { const item = { - "type": "Group", - "members": [ + type: 'Group', + members: [ { - "metadata": { - "ga": { - "value": "tvPower" + metadata: { + ga: { + value: 'tvPower' } } } ] }; expect(Device.matchesItemType(item)).toBe(true); - expect(Device.matchesItemType({ "type": "Group" })).toBe(false); + expect(Device.matchesItemType({ type: 'Group' })).toBe(false); }); describe('getTraits', () => { test('getTraits only power', () => { const item = { - "members": [ + members: [ { - "state": "ON", - "metadata": { - "ga": { - "value": "tvPower" + state: 'ON', + metadata: { + ga: { + value: 'tvPower' } } } ] }; - expect(Device.getTraits(item)).toStrictEqual([ - "action.devices.traits.OnOff" - ]); + expect(Device.getTraits(item)).toStrictEqual(['action.devices.traits.OnOff']); }); test('getTraits all members', () => { const item = { - "members": [ + members: [ { - "state": "1", - "metadata": { - "ga": { - "value": "tvChannel" + state: '1', + metadata: { + ga: { + value: 'tvChannel' } } }, { - "state": "50", - "metadata": { - "ga": { - "value": "tvVolume" + state: '50', + metadata: { + ga: { + value: 'tvVolume' } } }, { - "state": "input1", - "metadata": { - "ga": { - "value": "tvInput" + state: 'input1', + metadata: { + ga: { + value: 'tvInput' } } }, { - "state": "PLAY", - "metadata": { - "ga": { - "value": "tvTransport" + state: 'PLAY', + metadata: { + ga: { + value: 'tvTransport' } } }, { - "state": "ON", - "metadata": { - "ga": { - "value": "tvPower" + state: 'ON', + metadata: { + ga: { + value: 'tvPower' } } }, { - "state": "OFF", - "metadata": { - "ga": { - "value": "tvMute" + state: 'OFF', + metadata: { + ga: { + value: 'tvMute' } } } ] }; expect(Device.getTraits(item)).toStrictEqual([ - "action.devices.traits.OnOff", - "action.devices.traits.Volume", - "action.devices.traits.Channel", - "action.devices.traits.InputSelector", - "action.devices.traits.TransportControl" + 'action.devices.traits.OnOff', + 'action.devices.traits.Volume', + 'action.devices.traits.Channel', + 'action.devices.traits.InputSelector', + 'action.devices.traits.TransportControl' ]); }); }); @@ -113,420 +113,407 @@ describe('TV Device', () => { describe('getAttributes', () => { test('getAttributes no config', () => { const item = { - "metadata": { - "ga": { - "config": {} + metadata: { + ga: { + config: {} } }, - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "tvVolume" + metadata: { + ga: { + value: 'tvVolume' } } }, { - "metadata": { - "ga": { - "value": "tvTransport" + metadata: { + ga: { + value: 'tvTransport' } } } ] }; expect(Device.getAttributes(item)).toStrictEqual({ - "transportControlSupportedCommands": [ - "NEXT", - "PREVIOUS", - "PAUSE", - "RESUME", - ], - "volumeCanMuteAndUnmute": false, - "volumeMaxLevel": 100, + transportControlSupportedCommands: ['NEXT', 'PREVIOUS', 'PAUSE', 'RESUME'], + volumeCanMuteAndUnmute: false, + volumeMaxLevel: 100 }); }); test('getAttributes volume', () => { const item = { - "metadata": { - "ga": { - "config": { - "volumeDefaultPercentage": "20", - "levelStepSize": "10" + metadata: { + ga: { + config: { + volumeDefaultPercentage: '20', + levelStepSize: '10' } } }, - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "tvVolume" + metadata: { + ga: { + value: 'tvVolume' } } } ] }; expect(Device.getAttributes(item)).toStrictEqual({ - "levelStepSize": 10, - "volumeCanMuteAndUnmute": false, - "volumeDefaultPercentage": 20, - "volumeMaxLevel": 100 + levelStepSize: 10, + volumeCanMuteAndUnmute: false, + volumeDefaultPercentage: 20, + volumeMaxLevel: 100 }); }); test('getAttributes transport, mute', () => { const item = { - "metadata": { - "ga": { - "config": { - "transportControlSupportedCommands": "PAUSE,RESUME" + metadata: { + ga: { + config: { + transportControlSupportedCommands: 'PAUSE,RESUME' } } }, - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "tvTransport" + metadata: { + ga: { + value: 'tvTransport' } } }, { - "metadata": { - "ga": { - "value": "tvMute" + metadata: { + ga: { + value: 'tvMute' } } } ] }; expect(Device.getAttributes(item)).toStrictEqual({ - "transportControlSupportedCommands": ["PAUSE", "RESUME"], - "volumeCanMuteAndUnmute": true + transportControlSupportedCommands: ['PAUSE', 'RESUME'], + volumeCanMuteAndUnmute: true }); }); test('getAttributes inputs', () => { const item = { - "metadata": { - "ga": { - "config": { - "availableInputs": "input1=hdmi1,input2=hdmi2" + metadata: { + ga: { + config: { + availableInputs: 'input1=hdmi1,input2=hdmi2' } } }, - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "tvInput" + metadata: { + ga: { + value: 'tvInput' } } } ] }; expect(Device.getAttributes(item)).toStrictEqual({ - "availableInputs": [ + availableInputs: [ { - "key": "input1", - "names": [ + key: 'input1', + names: [ { - "lang": "en", - "name_synonym": ["hdmi1"], - }, - ], + lang: 'en', + name_synonym: ['hdmi1'] + } + ] }, { - "key": "input2", - "names": [ + key: 'input2', + names: [ { - "lang": "en", - "name_synonym": ["hdmi2"] + lang: 'en', + name_synonym: ['hdmi2'] } ] } ], - "orderedInputs": false, - "volumeCanMuteAndUnmute": false + orderedInputs: false, + volumeCanMuteAndUnmute: false }); }); test('getAttributes channels', () => { const item = { - "metadata": { - "ga": { - "config": { - "availableChannels": "1=channel1=ARD,2=channel2=ZDF" + metadata: { + ga: { + config: { + availableChannels: '1=channel1=ARD,2=channel2=ZDF' } } }, - "members": [ + members: [ { - "metadata": { - "ga": { - "value": "tvChannel" + metadata: { + ga: { + value: 'tvChannel' } } } ] }; expect(Device.getAttributes(item)).toStrictEqual({ - "availableChannels": [ + availableChannels: [ { - "key": "channel1", - "names": ["ARD"], - "number": "1" + key: 'channel1', + names: ['ARD'], + number: '1' }, { - "key": "channel2", - "names": ["ZDF"], - "number": "2" + key: 'channel2', + names: ['ZDF'], + number: '2' } ], - "volumeCanMuteAndUnmute": false + volumeCanMuteAndUnmute: false }); }); }); test('getMembers', () => { - expect(Device.getMembers({ "members": [{}] })).toStrictEqual({}); - expect(Device.getMembers({ "members": [{ "metadata": { "ga": { "value": "invalid" } } }] })).toStrictEqual({}); + expect(Device.getMembers({ members: [{}] })).toStrictEqual({}); + expect(Device.getMembers({ members: [{ metadata: { ga: { value: 'invalid' } } }] })).toStrictEqual({}); const item = { - "members": [ + members: [ { - "name": "Channel", - "state": "1", - "metadata": { - "ga": { - "value": "tvChannel" + name: 'Channel', + state: '1', + metadata: { + ga: { + value: 'tvChannel' } } }, { - "name": "Volume", - "state": "50", - "metadata": { - "ga": { - "value": "tvVolume" + name: 'Volume', + state: '50', + metadata: { + ga: { + value: 'tvVolume' } } }, { - "name": "Input", - "state": "input1", - "metadata": { - "ga": { - "value": "tvInput" + name: 'Input', + state: 'input1', + metadata: { + ga: { + value: 'tvInput' } } }, { - "name": "Transport", - "state": "PLAY", - "metadata": { - "ga": { - "value": "tvTransport" + name: 'Transport', + state: 'PLAY', + metadata: { + ga: { + value: 'tvTransport' } } }, { - "name": "Power", - "state": "ON", - "metadata": { - "ga": { - "value": "tvPower" + name: 'Power', + state: 'ON', + metadata: { + ga: { + value: 'tvPower' } } }, { - "name": "Mute", - "state": "OFF", - "metadata": { - "ga": { - "value": "tvMute" + name: 'Mute', + state: 'OFF', + metadata: { + ga: { + value: 'tvMute' } } } ] }; expect(Device.getMembers(item)).toStrictEqual({ - "tvChannel": { - "name": "Channel", - "state": "1" + tvChannel: { + name: 'Channel', + state: '1' }, - "tvInput": { - "name": "Input", - "state": "input1" + tvInput: { + name: 'Input', + state: 'input1' }, - "tvMute": { - "name": "Mute", - "state": "OFF" + tvMute: { + name: 'Mute', + state: 'OFF' }, - "tvPower": { - "name": "Power", - "state": "ON" + tvPower: { + name: 'Power', + state: 'ON' }, - "tvTransport": { - "name": "Transport", - "state": "PLAY" + tvTransport: { + name: 'Transport', + state: 'PLAY' }, - "tvVolume": { - "name": "Volume", - "state": "50" + tvVolume: { + name: 'Volume', + state: '50' } }); }); test('getChannelMap', () => { const item = { - "metadata": { - "ga": { - "value": "TV", - "config": { - "availableChannels": "20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2" + metadata: { + ga: { + value: 'TV', + config: { + availableChannels: '20=channel1=Channel 1:Kanal 1,10=channel2=Channel 2:Kanal 2' } } } }; expect(Device.getChannelMap(item)).toStrictEqual({ - "10": [ - "Channel 2", - "Kanal 2", - "channel2" - ], - "20": [ - "Channel 1", - "Kanal 1", - "channel1" - ], + 10: ['Channel 2', 'Kanal 2', 'channel2'], + 20: ['Channel 1', 'Kanal 1', 'channel1'] }); }); describe('getState', () => { test('getState', () => { const item = { - "type": "Group", - "metadata": { - "ga": { - "value": "TV", - "config": { - "transportControlSupportedCommands": "PAUSE,RESUME", - "availableInputs": "input1=hdmi1,input2=hdmi2", - "availableChannels": "1=channel1=ARD,2=channel2=ZDF" + type: 'Group', + metadata: { + ga: { + value: 'TV', + config: { + transportControlSupportedCommands: 'PAUSE,RESUME', + availableInputs: 'input1=hdmi1,input2=hdmi2', + availableChannels: '1=channel1=ARD,2=channel2=ZDF' } } }, - "members": [ + members: [ { - "state": "1", - "metadata": { - "ga": { - "value": "tvChannel" + state: '1', + metadata: { + ga: { + value: 'tvChannel' } } }, { - "state": "50", - "metadata": { - "ga": { - "value": "tvVolume" + state: '50', + metadata: { + ga: { + value: 'tvVolume' } } }, { - "state": "input1", - "metadata": { - "ga": { - "value": "tvInput" + state: 'input1', + metadata: { + ga: { + value: 'tvInput' } } }, { - "state": "PLAY", - "metadata": { - "ga": { - "value": "tvTransport" + state: 'PLAY', + metadata: { + ga: { + value: 'tvTransport' } } }, { - "state": "ON", - "metadata": { - "ga": { - "value": "tvPower" + state: 'ON', + metadata: { + ga: { + value: 'tvPower' } } }, { - "state": "OFF", - "metadata": { - "ga": { - "value": "tvMute" + state: 'OFF', + metadata: { + ga: { + value: 'tvMute' } } } ] }; expect(Device.getState(item)).toStrictEqual({ - "channelName": "ARD", - "channelNumber": "1", - "currentInput": "input1", - "currentVolume": 50, - "isMuted": false, - "on": true, + channelName: 'ARD', + channelNumber: '1', + currentInput: 'input1', + currentVolume: 50, + isMuted: false, + on: true }); }); test('getState only channel without map', () => { const item = { - "type": "Group", - "members": [ + type: 'Group', + members: [ { - "state": "1", - "metadata": { - "ga": { - "value": "tvChannel" + state: '1', + metadata: { + ga: { + value: 'tvChannel' } } } ] }; expect(Device.getState(item)).toStrictEqual({ - "channelNumber": "1" + channelNumber: '1' }); }); test('getState only power, mute', () => { const item = { - "type": "Group", - "metadata": { - "ga": { - "value": "TV" + type: 'Group', + metadata: { + ga: { + value: 'TV' } }, - "members": [ + members: [ { - "state": "50", - "metadata": { - "ga": { - "value": "tvVolume" + state: '50', + metadata: { + ga: { + value: 'tvVolume' } } }, { - "state": "ON", - "metadata": { - "ga": { - "value": "tvPower" + state: 'ON', + metadata: { + ga: { + value: 'tvPower' } } } ] }; expect(Device.getState(item)).toStrictEqual({ - "currentVolume": 50, - "on": true, + currentVolume: 50, + on: true }); }); }); diff --git a/tests/devices/valve.test.js b/tests/devices/valve.test.js index 63579e6d..27c18648 100644 --- a/tests/devices/valve.test.js +++ b/tests/devices/valve.test.js @@ -2,49 +2,51 @@ const Device = require('../../functions/devices/valve.js'); describe('Valve Device', () => { test('isCompatible', () => { - expect(Device.isCompatible({ - "metadata": { - "ga": { - "value": "VALVE" + expect( + Device.isCompatible({ + metadata: { + ga: { + value: 'VALVE' + } } - } - })).toBe(true); + }) + ).toBe(true); }); test('matchesItemType', () => { - expect(Device.matchesItemType({ "type": "Switch" })).toBe(true); - expect(Device.matchesItemType({ "type": "String" })).toBe(false); - expect(Device.matchesItemType({ "type": "Group", "groupType": "Switch" })).toBe(true); - expect(Device.matchesItemType({ "type": "Group", "groupType": "String" })).toBe(false); + expect(Device.matchesItemType({ type: 'Switch' })).toBe(true); + expect(Device.matchesItemType({ type: 'String' })).toBe(false); + expect(Device.matchesItemType({ type: 'Group', groupType: 'Switch' })).toBe(true); + expect(Device.matchesItemType({ type: 'Group', groupType: 'String' })).toBe(false); }); describe('getState', () => { test('getState', () => { - expect(Device.getState({ "state": "ON" })).toStrictEqual({ - "openPercent": 100 + expect(Device.getState({ state: 'ON' })).toStrictEqual({ + openPercent: 100 }); - expect(Device.getState({ "state": "OFF" })).toStrictEqual({ - "openPercent": 0 + expect(Device.getState({ state: 'OFF' })).toStrictEqual({ + openPercent: 0 }); }); test('getState inverted', () => { const item = { - "state": "ON", - "metadata": { - "ga": { - "config": { - "inverted": true + state: 'ON', + metadata: { + ga: { + config: { + inverted: true } } } }; expect(Device.getState(item)).toStrictEqual({ - "openPercent": 0 + openPercent: 0 }); - item.state = "OFF"; + item.state = 'OFF'; expect(Device.getState(item)).toStrictEqual({ - "openPercent": 100 + openPercent: 100 }); }); }); diff --git a/tests/openhab.test.js b/tests/openhab.test.js index b0f8bc60..025cb0f8 100644 --- a/tests/openhab.test.js +++ b/tests/openhab.test.js @@ -2,31 +2,31 @@ const OpenHAB = require('../functions/openhab.js'); describe('OpenHAB', () => { test('getCommandType', () => { - const command = OpenHAB.getCommandType('action.devices.commands.OnOff', { "on": true }); + const command = OpenHAB.getCommandType('action.devices.commands.OnOff', { on: true }); expect(command).not.toBeUndefined(); expect(command.name).toBe('OnOff'); }); describe('getDeviceForItem', () => { test('getDeviceForItem switch tag', () => { - const device = OpenHAB.getDeviceForItem({ "type": "Switch", "metadata": { "ga": { "value": "Switch" } } }); + const device = OpenHAB.getDeviceForItem({ type: 'Switch', metadata: { ga: { value: 'Switch' } } }); expect(device).not.toBeUndefined(); expect(device.name).toBe('Switch'); }); test('getDeviceForItem switch tag', () => { - const device = OpenHAB.getDeviceForItem({ "type": "Switch", "tags": ["Switchable"] }); + const device = OpenHAB.getDeviceForItem({ type: 'Switch', tags: ['Switchable'] }); expect(device).not.toBeUndefined(); expect(device.name).toBe('Switch'); }); }); test('setTokenFromHeader', () => { - const openHAB = new OpenHAB({ authToken: "" }); + const openHAB = new OpenHAB({ authToken: '' }); openHAB.setTokenFromHeader({}); expect(openHAB._apiHandler.authToken).toBe(null); - openHAB.setTokenFromHeader({ "authorization": "Bearer token" }); - expect(openHAB._apiHandler.authToken).toBe("token"); + openHAB.setTokenFromHeader({ authorization: 'Bearer token' }); + expect(openHAB._apiHandler.authToken).toBe('token'); }); test('onDisconnect', () => { @@ -44,27 +44,27 @@ describe('OpenHAB', () => { test('onSync failure', async () => { const handleSyncMock = jest.spyOn(openHAB, 'handleSync'); handleSyncMock.mockReturnValue(Promise.reject()); - const result = await openHAB.onSync({ "requestId": "1234" }, {}); + const result = await openHAB.onSync({ requestId: '1234' }, {}); expect(handleSyncMock).toBeCalledTimes(1); expect(result).toStrictEqual({ - "requestId": "1234", - "payload": { - "devices": [], - "errorCode": "actionNotAvailable", - "status": "ERROR" + requestId: '1234', + payload: { + devices: [], + errorCode: 'actionNotAvailable', + status: 'ERROR' } }); }); test('onSync empty', async () => { const handleSyncMock = jest.spyOn(openHAB, 'handleSync'); - const payload = { "devices": [] } + const payload = { devices: [] }; handleSyncMock.mockReturnValue(Promise.resolve(payload)); - const result = await openHAB.onSync({ "requestId": "1234" }, {}); + const result = await openHAB.onSync({ requestId: '1234' }, {}); expect(handleSyncMock).toBeCalledTimes(1); expect(result).toStrictEqual({ - "requestId": "1234", - "payload": payload + requestId: '1234', + payload: payload }); }); }); @@ -83,133 +83,139 @@ describe('OpenHAB', () => { }); test('handleSync no matching items', async () => { - getItemsMock.mockReturnValue(Promise.resolve([{ "name": "TestItem" }])); + getItemsMock.mockReturnValue(Promise.resolve([{ name: 'TestItem' }])); const result = await openHAB.handleSync(); expect(getItemsMock).toHaveBeenCalledTimes(1); - expect(result).toStrictEqual({ "devices": [] }); + expect(result).toStrictEqual({ devices: [] }); }); test('handleSync single switch', async () => { - getItemsMock.mockReturnValue(Promise.resolve([{ - "type": "Switch", - "name": "SwitchItem", - "label": "Switch Item", - "metadata": { "ga": { "value": "Switch" } } - }])); + getItemsMock.mockReturnValue( + Promise.resolve([ + { + type: 'Switch', + name: 'SwitchItem', + label: 'Switch Item', + metadata: { ga: { value: 'Switch' } } + } + ]) + ); const result = await openHAB.handleSync(); expect(getItemsMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ - "devices": [{ - "attributes": {}, - "customData": { - "deviceType": "Switch", - "itemType": "Switch", - }, - "deviceInfo": { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Switch:SwitchItem", - "swVersion": "2.5.0", - }, - "id": "SwitchItem", - "name": { - "defaultNames": ["Switch Item"], - "name": "Switch Item", - "nicknames": ["Switch Item"] - }, - "roomHint": undefined, - "structureHint": undefined, - "traits": ["action.devices.traits.OnOff"], - "type": "action.devices.types.SWITCH", - "willReportState": false - }] + devices: [ + { + attributes: {}, + customData: { + deviceType: 'Switch', + itemType: 'Switch' + }, + deviceInfo: { + hwVersion: '2.5.0', + manufacturer: 'openHAB', + model: 'Switch:SwitchItem', + swVersion: '2.5.0' + }, + id: 'SwitchItem', + name: { + defaultNames: ['Switch Item'], + name: 'Switch Item', + nicknames: ['Switch Item'] + }, + roomHint: undefined, + structureHint: undefined, + traits: ['action.devices.traits.OnOff'], + type: 'action.devices.types.SWITCH', + willReportState: false + } + ] }); }); test('handleSync switch and light group', async () => { - getItemsMock.mockReturnValue(Promise.resolve([{ - "type": "Switch", - "name": "SwitchItem", - "label": "Switch Item", - "metadata": { "ga": { "value": "Switch" } } - }, - { - "type": "Group", - "name": "TVItem", - "label": "TV Item", - "metadata": { "ga": { "value": "TV" } } - }, - { - "type": "Switch", - "name": "TVMute", - "label": "TV Mute", - "groupNames": ["TVItem"], - "metadata": { "ga": { "value": "tvMute" } } - }, - { - "type": "Switch", - "name": "TVPower", - "label": "TV Power", - "groupNames": ["TVItem"], - "metadata": { "ga": { "value": "tvPower" } } - } - ])); + getItemsMock.mockReturnValue( + Promise.resolve([ + { + type: 'Switch', + name: 'SwitchItem', + label: 'Switch Item', + metadata: { ga: { value: 'Switch' } } + }, + { + type: 'Group', + name: 'TVItem', + label: 'TV Item', + metadata: { ga: { value: 'TV' } } + }, + { + type: 'Switch', + name: 'TVMute', + label: 'TV Mute', + groupNames: ['TVItem'], + metadata: { ga: { value: 'tvMute' } } + }, + { + type: 'Switch', + name: 'TVPower', + label: 'TV Power', + groupNames: ['TVItem'], + metadata: { ga: { value: 'tvPower' } } + } + ]) + ); const result = await openHAB.handleSync(); expect(getItemsMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ - "devices": [ + devices: [ { - "attributes": {}, - "customData": { - "deviceType": "Switch", - "itemType": "Switch" + attributes: {}, + customData: { + deviceType: 'Switch', + itemType: 'Switch' }, - "deviceInfo": { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Switch:SwitchItem", - "swVersion": "2.5.0" + deviceInfo: { + hwVersion: '2.5.0', + manufacturer: 'openHAB', + model: 'Switch:SwitchItem', + swVersion: '2.5.0' }, - "id": "SwitchItem", - "name": { - "defaultNames": ["Switch Item"], - "name": "Switch Item", - "nicknames": ["Switch Item"], + id: 'SwitchItem', + name: { + defaultNames: ['Switch Item'], + name: 'Switch Item', + nicknames: ['Switch Item'] }, - "roomHint": undefined, - "structureHint": undefined, - "traits": ["action.devices.traits.OnOff"], - "type": "action.devices.types.SWITCH", - "willReportState": false + roomHint: undefined, + structureHint: undefined, + traits: ['action.devices.traits.OnOff'], + type: 'action.devices.types.SWITCH', + willReportState: false }, { - "attributes": { - "volumeCanMuteAndUnmute": true + attributes: { + volumeCanMuteAndUnmute: true }, - "customData": { - "deviceType": "TV", - "itemType": "Group" + customData: { + deviceType: 'TV', + itemType: 'Group' }, - "deviceInfo": { - "hwVersion": "2.5.0", - "manufacturer": "openHAB", - "model": "Group:TVItem", - "swVersion": "2.5.0" + deviceInfo: { + hwVersion: '2.5.0', + manufacturer: 'openHAB', + model: 'Group:TVItem', + swVersion: '2.5.0' }, - "id": "TVItem", - "name": { - "defaultNames": ["TV Item"], - "name": "TV Item", - "nicknames": ["TV Item"], + id: 'TVItem', + name: { + defaultNames: ['TV Item'], + name: 'TV Item', + nicknames: ['TV Item'] }, - "roomHint": undefined, - "structureHint": undefined, - "traits": [ - "action.devices.traits.OnOff", - "action.devices.traits.Volume" - ], - "type": "action.devices.types.TV", - "willReportState": false + roomHint: undefined, + structureHint: undefined, + traits: ['action.devices.traits.OnOff', 'action.devices.traits.Volume'], + type: 'action.devices.types.TV', + willReportState: false } ] }); @@ -226,44 +232,44 @@ describe('OpenHAB', () => { test('onQuery failure', async () => { const handleQueryMock = jest.spyOn(openHAB, 'handleQuery'); handleQueryMock.mockReturnValue(Promise.reject()); - const result = await openHAB.onQuery({ "requestId": "1234" }, {}); + const result = await openHAB.onQuery({ requestId: '1234' }, {}); expect(handleQueryMock).toBeCalledTimes(1); expect(handleQueryMock).toBeCalledWith([]); expect(result).toStrictEqual({ - "requestId": "1234", - "payload": { - "devices": {}, - "errorCode": "actionNotAvailable", - "status": "ERROR" + requestId: '1234', + payload: { + devices: {}, + errorCode: 'actionNotAvailable', + status: 'ERROR' } }); }); test('onQuery empty', async () => { const handleQueryMock = jest.spyOn(openHAB, 'handleQuery'); - const payload = { "devices": {} }; + const payload = { devices: {} }; handleQueryMock.mockReturnValue(Promise.resolve(payload)); - const result = await openHAB.onQuery({ "requestId": "1234" }, {}); + const result = await openHAB.onQuery({ requestId: '1234' }, {}); expect(handleQueryMock).toBeCalledTimes(1); expect(handleQueryMock).toBeCalledWith([]); expect(result).toStrictEqual({ - "requestId": "1234", - "payload": payload + requestId: '1234', + payload: payload }); }); test('onQuery', async () => { const handleQueryMock = jest.spyOn(openHAB, 'handleQuery'); - const payload = { "devices": {} }; + const payload = { devices: {} }; handleQueryMock.mockReturnValue(Promise.resolve(payload)); - const devices = [{ "id": "TestItem1" }, { "id": "TestItem2" }]; + const devices = [{ id: 'TestItem1' }, { id: 'TestItem2' }]; const body = { - "requestId": "1234", - "inputs": [ + requestId: '1234', + inputs: [ { - "intent": "action.devices.QUERY", - "payload": { - "devices": devices + intent: 'action.devices.QUERY', + payload: { + devices: devices } } ] @@ -272,8 +278,8 @@ describe('OpenHAB', () => { expect(handleQueryMock).toBeCalledTimes(1); expect(handleQueryMock).toBeCalledWith(devices); expect(result).toStrictEqual({ - "requestId": "1234", - "payload": payload + requestId: '1234', + payload: payload }); }); }); @@ -292,48 +298,50 @@ describe('OpenHAB', () => { }); test('handleQuery device offline', async () => { - getItemMock.mockReturnValue(Promise.reject({ "statusCode": 500 })); - const result = await openHAB.handleQuery([{ "id": "TestItem" }]); + getItemMock.mockReturnValue(Promise.reject({ statusCode: 500 })); + const result = await openHAB.handleQuery([{ id: 'TestItem' }]); expect(getItemMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ - "devices": { - "TestItem": { - "errorCode": "deviceOffline", - "status": "ERROR" + devices: { + TestItem: { + errorCode: 'deviceOffline', + status: 'ERROR' } } }); }); test('handleQuery device not found', async () => { - getItemMock.mockReturnValue(Promise.resolve({ "name": "TestItem" })); - const result = await openHAB.handleQuery([{ "id": "TestItem" }]); + getItemMock.mockReturnValue(Promise.resolve({ name: 'TestItem' })); + const result = await openHAB.handleQuery([{ id: 'TestItem' }]); expect(getItemMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ - "devices": { - "TestItem": { - "errorCode": "deviceNotFound", - "status": "ERROR" + devices: { + TestItem: { + errorCode: 'deviceNotFound', + status: 'ERROR' } } }); }); test('handleQuery device not ready', async () => { - getItemMock.mockReturnValue(Promise.resolve({ - "name": "TestItem", - "type": "Group", - "groupType": "Switch", - "state": "NULL", - "metadata": { "ga": { "value": "Switch" } } - })); - const result = await openHAB.handleQuery([{ "id": "TestItem" }]); + getItemMock.mockReturnValue( + Promise.resolve({ + name: 'TestItem', + type: 'Group', + groupType: 'Switch', + state: 'NULL', + metadata: { ga: { value: 'Switch' } } + }) + ); + const result = await openHAB.handleQuery([{ id: 'TestItem' }]); expect(getItemMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ - "devices": { - "TestItem": { - "errorCode": "deviceNotReady", - "status": "ERROR" + devices: { + TestItem: { + errorCode: 'deviceNotReady', + status: 'ERROR' } } }); @@ -341,84 +349,92 @@ describe('OpenHAB', () => { // there is currently no case xtest('handleQuery notSupported', async () => { - getItemMock.mockReturnValue(Promise.resolve({ - "name": "TestItem", - "type": "Group", - "state": "NULL", - "metadata": { - "ga": { - "value": "Thermostat", - "config": { - "modes": "on=1,off=2" + getItemMock.mockReturnValue( + Promise.resolve({ + name: 'TestItem', + type: 'Group', + state: 'NULL', + metadata: { + ga: { + value: 'Thermostat', + config: { + modes: 'on=1,off=2' + } } - } - }, - "members": [ - { - "state": "3", - "metadata": { "ga": { "value": "thermostatMode" } } - } - ] - })); - const result = await openHAB.handleQuery([{ "id": "TestItem" }]); + }, + members: [ + { + state: '3', + metadata: { ga: { value: 'thermostatMode' } } + } + ] + }) + ); + const result = await openHAB.handleQuery([{ id: 'TestItem' }]); expect(getItemMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ - "devices": { - "TestItem": { - "errorCode": "notSupported", - "status": "ERROR" + devices: { + TestItem: { + errorCode: 'notSupported', + status: 'ERROR' } } }); }); test('handleQuery Switch', async () => { - getItemMock.mockReturnValue(Promise.resolve({ - "name": "TestItem", - "type": "Switch", - "state": "ON", - "metadata": { "ga": { "value": "Switch" } } - })); - const result = await openHAB.handleQuery([{ "id": "TestItem" }]); + getItemMock.mockReturnValue( + Promise.resolve({ + name: 'TestItem', + type: 'Switch', + state: 'ON', + metadata: { ga: { value: 'Switch' } } + }) + ); + const result = await openHAB.handleQuery([{ id: 'TestItem' }]); expect(getItemMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ - "devices": { - "TestItem": { - "status": "SUCCESS", - "on": true, - "online": true + devices: { + TestItem: { + status: 'SUCCESS', + on: true, + online: true } } }); }); test('handleQuery mutliple devices', async () => { - getItemMock.mockReturnValueOnce(Promise.resolve({ - "name": "TestItem", - "type": "Switch", - "state": "ON", - "metadata": { "ga": { "value": "Switch" } } - })); - getItemMock.mockReturnValueOnce(Promise.resolve({ - "name": "TestItem2", - "type": "Dimmer", - "state": "50", - "metadata": { "ga": { "value": "Light" } } - })); - const result = await openHAB.handleQuery([{ "id": "TestItem" }, { "id": "TestItem2" }]); + getItemMock.mockReturnValueOnce( + Promise.resolve({ + name: 'TestItem', + type: 'Switch', + state: 'ON', + metadata: { ga: { value: 'Switch' } } + }) + ); + getItemMock.mockReturnValueOnce( + Promise.resolve({ + name: 'TestItem2', + type: 'Dimmer', + state: '50', + metadata: { ga: { value: 'Light' } } + }) + ); + const result = await openHAB.handleQuery([{ id: 'TestItem' }, { id: 'TestItem2' }]); expect(getItemMock).toHaveBeenCalledTimes(2); expect(result).toStrictEqual({ - "devices": { - "TestItem": { - "status": "SUCCESS", - "on": true, - "online": true + devices: { + TestItem: { + status: 'SUCCESS', + on: true, + online: true }, - "TestItem2": { - "status": "SUCCESS", - "brightness": 50, - "on": true, - "online": true + TestItem2: { + status: 'SUCCESS', + brightness: 50, + on: true, + online: true } } }); @@ -435,58 +451,64 @@ describe('OpenHAB', () => { test('onExecute failure', async () => { const handleExecuteMock = jest.spyOn(openHAB, 'handleExecute'); handleExecuteMock.mockReturnValue(Promise.reject()); - const result = await openHAB.onExecute({ "requestId": "1234" }, {}); + const result = await openHAB.onExecute({ requestId: '1234' }, {}); expect(handleExecuteMock).toBeCalledTimes(1); expect(handleExecuteMock).toBeCalledWith([]); expect(result).toStrictEqual({ - "requestId": "1234", - "payload": { - "commands": [], - "errorCode": "actionNotAvailable", - "status": "ERROR" + requestId: '1234', + payload: { + commands: [], + errorCode: 'actionNotAvailable', + status: 'ERROR' } }); }); test('onExecute empty', async () => { const handleExecuteMock = jest.spyOn(openHAB, 'handleExecute'); - const payload = { "commands": [] }; + const payload = { commands: [] }; handleExecuteMock.mockReturnValue(Promise.resolve(payload)); - const result = await openHAB.onExecute({ "requestId": "1234" }, {}); + const result = await openHAB.onExecute({ requestId: '1234' }, {}); expect(handleExecuteMock).toBeCalledTimes(1); expect(handleExecuteMock).toBeCalledWith([]); expect(result).toStrictEqual({ - "requestId": "1234", - "payload": payload + requestId: '1234', + payload: payload }); }); test('onExecute', async () => { const handleExecuteMock = jest.spyOn(openHAB, 'handleExecute'); - const payload = { "commands": [] }; + const payload = { commands: [] }; handleExecuteMock.mockReturnValue(Promise.resolve(payload)); - const commands = [{ - "devices": [{ "id": "123" }, { "id": "456" }], - "execution": [{ - "command": "action.devices.commands.OnOff", - "params": { "on": true } - }] - }]; + const commands = [ + { + devices: [{ id: '123' }, { id: '456' }], + execution: [ + { + command: 'action.devices.commands.OnOff', + params: { on: true } + } + ] + } + ]; const body = { - "requestId": "1234", - "inputs": [{ - "intent": "action.devices.EXECUTE", - "payload": { - "commands": commands + requestId: '1234', + inputs: [ + { + intent: 'action.devices.EXECUTE', + payload: { + commands: commands + } } - }] + ] }; const result = await openHAB.onExecute(body, {}); expect(handleExecuteMock).toBeCalledTimes(1); expect(handleExecuteMock).toBeCalledWith(commands); expect(result).toStrictEqual({ - "requestId": "1234", - "payload": payload + requestId: '1234', + payload: payload }); }); }); @@ -509,115 +531,135 @@ describe('OpenHAB', () => { test('handleExecute OnOff', async () => { sendCommandMock.mockReturnValue(Promise.resolve()); - const commands = [{ - "devices": [{ - "id": "TestItem", - "customData": {} - }], - "execution": [{ - "command": "action.devices.commands.OnOff", - "params": { "on": true } - }] - }]; + const commands = [ + { + devices: [ + { + id: 'TestItem', + customData: {} + } + ], + execution: [ + { + command: 'action.devices.commands.OnOff', + params: { on: true } + } + ] + } + ]; const result = await openHAB.handleExecute(commands); expect(getItemMock).toHaveBeenCalledTimes(0); expect(sendCommandMock).toHaveBeenCalledTimes(1); expect(result).toStrictEqual({ - "commands": [ + commands: [ { - "ids": ["TestItem"], - "states": { - "on": true, - "online": true + ids: ['TestItem'], + states: { + on: true, + online: true }, - "status": "SUCCESS" + status: 'SUCCESS' } ] }); }); test('handleExecute function not supported', async () => { - const commands = [{ - "devices": [{ - "id": "TestItem", - "customData": {} - }], - "execution": [{ - "command": "action.devices.commands.Invalid", - "params": {} - }] - }]; + const commands = [ + { + devices: [ + { + id: 'TestItem', + customData: {} + } + ], + execution: [ + { + command: 'action.devices.commands.Invalid', + params: {} + } + ] + } + ]; const result = await openHAB.handleExecute(commands); expect(getItemMock).toHaveBeenCalledTimes(0); expect(sendCommandMock).toHaveBeenCalledTimes(0); expect(result).toStrictEqual({ - "commands": [ + commands: [ { - "ids": ["TestItem"], - "errorCode": "functionNotSupported", - "status": "ERROR" + ids: ['TestItem'], + errorCode: 'functionNotSupported', + status: 'ERROR' } ] }); }); test('handleExecute ThermostatTemperatureSetRange', async () => { - getItemMock.mockReturnValue(Promise.resolve({ - "name": "TestItem", - "type": "Group", - "metadata": { - "ga": { - "value": "Thermostat", - } - }, - "members": [ - { - "name": "High", - "state": "25", - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpointHigh" - } + getItemMock.mockReturnValue( + Promise.resolve({ + name: 'TestItem', + type: 'Group', + metadata: { + ga: { + value: 'Thermostat' } }, - { - "name": "Low", - "state": "5", - "metadata": { - "ga": { - "value": "thermostatTemperatureSetpointLow" + members: [ + { + name: 'High', + state: '25', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointHigh' + } + } + }, + { + name: 'Low', + state: '5', + metadata: { + ga: { + value: 'thermostatTemperatureSetpointLow' + } } } - } - ] - })); + ] + }) + ); sendCommandMock.mockReturnValue(Promise.resolve()); - const commands = [{ - "devices": [{ - "id": "TestItem", - "customData": {} - }], - "execution": [{ - "command": "action.devices.commands.ThermostatTemperatureSetRange", - "params": { - "thermostatTemperatureSetpointLow": 10, - "thermostatTemperatureSetpointHigh": 20 - } - }] - }]; + const commands = [ + { + devices: [ + { + id: 'TestItem', + customData: {} + } + ], + execution: [ + { + command: 'action.devices.commands.ThermostatTemperatureSetRange', + params: { + thermostatTemperatureSetpointLow: 10, + thermostatTemperatureSetpointHigh: 20 + } + } + ] + } + ]; const result = await openHAB.handleExecute(commands); expect(getItemMock).toHaveBeenCalledTimes(2); expect(sendCommandMock).toHaveBeenCalledTimes(2); expect(result).toStrictEqual({ - "commands": [ + commands: [ { - "ids": ["TestItem"], - "states": { - "thermostatTemperatureSetpointHigh": 25, - "thermostatTemperatureSetpointLow": 10, - "online": true + ids: ['TestItem'], + states: { + thermostatTemperatureSetpointHigh: 25, + thermostatTemperatureSetpointLow: 10, + online: true }, - "status": "SUCCESS" + status: 'SUCCESS' } ] }); diff --git a/tests/utilities.test.js b/tests/utilities.test.js index 7c9c0894..549d7895 100644 --- a/tests/utilities.test.js +++ b/tests/utilities.test.js @@ -12,16 +12,16 @@ describe('Utilities', () => { }); test('kelvin2rgb', () => { - expect(Utilities.kelvin2rgb(2000)).toStrictEqual({ "b": 14, "g": 137, "r": 255 }); - expect(Utilities.kelvin2rgb(5900)).toEqual({ "b": 234, "g": 244, "r": 255 }); - expect(Utilities.kelvin2rgb(9000)).toEqual({ "b": 255, "g": 223, "r": 210 }); + expect(Utilities.kelvin2rgb(2000)).toStrictEqual({ b: 14, g: 137, r: 255 }); + expect(Utilities.kelvin2rgb(5900)).toEqual({ b: 234, g: 244, r: 255 }); + expect(Utilities.kelvin2rgb(9000)).toEqual({ b: 255, g: 223, r: 210 }); }); test('rgb2hsv', () => { - expect(Utilities.rgb2hsv({ r: 50, g: 10, b: 100 })).toStrictEqual({ "hue": 266.67, "saturation": 0.9, "value": 0.39 }); - expect(Utilities.rgb2hsv({ r: 0, g: 0, b: 0 })).toStrictEqual({ "hue": 0, "saturation": 0, "value": 0 }); - expect(Utilities.rgb2hsv({ r: 255, g: 0, b: 0 })).toStrictEqual({ "hue": 0, "saturation": 1, "value": 1 }); - expect(Utilities.rgb2hsv({ r: 0, g: 255, b: 0 })).toStrictEqual({ "hue": 120, "saturation": 1, "value": 1 }); - expect(Utilities.rgb2hsv({ r: 0, g: 0, b: 255 })).toStrictEqual({ "hue": 240, "saturation": 1, "value": 1 }); + expect(Utilities.rgb2hsv({ r: 50, g: 10, b: 100 })).toStrictEqual({ hue: 266.67, saturation: 0.9, value: 0.39 }); + expect(Utilities.rgb2hsv({ r: 0, g: 0, b: 0 })).toStrictEqual({ hue: 0, saturation: 0, value: 0 }); + expect(Utilities.rgb2hsv({ r: 255, g: 0, b: 0 })).toStrictEqual({ hue: 0, saturation: 1, value: 1 }); + expect(Utilities.rgb2hsv({ r: 0, g: 255, b: 0 })).toStrictEqual({ hue: 120, saturation: 1, value: 1 }); + expect(Utilities.rgb2hsv({ r: 0, g: 0, b: 255 })).toStrictEqual({ hue: 240, saturation: 1, value: 1 }); }); }); From ae6055d111066a8fcf56ff70497f8bf4e36f6fcd Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Tue, 9 Feb 2021 21:04:52 +0100 Subject: [PATCH 93/94] Add compatibility for previous tfa attributes Signed-off-by: Michael Krug --- functions/commands/default.js | 15 +++++++++++---- functions/devices/default.js | 6 +++--- tests/commands/default.test.js | 23 +++++++++++++++++++++++ tests/devices/default.test.js | 17 ++++++++++++++++- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/functions/commands/default.js b/functions/commands/default.js index 1a2f9e8f..4b5f9d73 100644 --- a/functions/commands/default.js +++ b/functions/commands/default.js @@ -85,8 +85,8 @@ class DefaultCommand { static handleAuthPin(device, challenge) { if ( !device.customData || - !device.customData.pinNeeded || - (challenge && challenge.pin === device.customData.pinNeeded) + !(device.customData.pinNeeded || device.customData.tfaPin) || + (challenge && challenge.pin === (device.customData.pinNeeded || device.customData.tfaPin)) ) { return; } @@ -106,7 +106,11 @@ class DefaultCommand { * @param {object} responseStates */ static handleAuthAck(device, challenge, responseStates) { - if (!device.customData || !device.customData.ackNeeded || (challenge && challenge.ack === true)) { + if ( + !device.customData || + !(device.customData.ackNeeded || device.customData.tfaAck) || + (challenge && challenge.ack === true) + ) { return; } return { @@ -137,7 +141,10 @@ class DefaultCommand { } const ackWithState = - ackSupported.includes(this.type) && device.customData && device.customData.ackNeeded && !challenge.ack; + ackSupported.includes(this.type) && + device.customData && + (device.customData.ackNeeded || device.customData.tfaAck) && + !challenge.ack; let getItemPromise = Promise.resolve({ name: device.id }); if (this.requiresItem(device) || ackWithState) { diff --git a/functions/devices/default.js b/functions/devices/default.js index a49a679a..991a14a9 100644 --- a/functions/devices/default.js +++ b/functions/devices/default.js @@ -90,11 +90,11 @@ class DefaultDevice { if (config.inverted === true) { metadata.customData.inverted = true; } - if (config.ackNeeded === true) { + if (config.ackNeeded === true || config.tfaAck === true) { metadata.customData.ackNeeded = true; } - if (typeof config.pinNeeded === 'string') { - metadata.customData.pinNeeded = config.pinNeeded; + if (typeof config.pinNeeded === 'string' || typeof config.tfaPin === 'string') { + metadata.customData.pinNeeded = config.pinNeeded || config.tfaPin; } return metadata; } diff --git a/tests/commands/default.test.js b/tests/commands/default.test.js index f36a4803..e1db8c6f 100644 --- a/tests/commands/default.test.js +++ b/tests/commands/default.test.js @@ -76,6 +76,16 @@ describe('Default Command', () => { type: 'challengeFailedPinNeeded' } }); + // legacy tfa + expect(Command.handleAuthPin({ id: 'Item', customData: { tfaPin: '1234' } }, { pin: '1234' })).toBeUndefined(); + expect(Command.handleAuthPin({ id: 'Item', customData: { tfaPin: '1234' } }, undefined)).toStrictEqual({ + ids: ['Item'], + status: 'ERROR', + errorCode: 'challengeNeeded', + challengeNeeded: { + type: 'pinNeeded' + } + }); }); test('handleAuthAck', () => { @@ -92,6 +102,19 @@ describe('Default Command', () => { type: 'ackNeeded' } }); + // legacy tfa + expect( + Command.handleAuthAck({ id: 'Item', customData: { tfaAck: true } }, { ack: true }, undefined) + ).toBeUndefined(); + expect(Command.handleAuthAck({ id: 'Item', customData: { tfaAck: true } }, {}, { key: 'value' })).toStrictEqual({ + ids: ['Item'], + status: 'ERROR', + states: { key: 'value' }, + errorCode: 'challengeNeeded', + challengeNeeded: { + type: 'ackNeeded' + } + }); }); describe('execute', () => { diff --git a/tests/devices/default.test.js b/tests/devices/default.test.js index 46be8f11..46bd1977 100644 --- a/tests/devices/default.test.js +++ b/tests/devices/default.test.js @@ -1,6 +1,6 @@ const Device = require('../../functions/devices/default.js'); -describe('Fan Device', () => { +describe('Default Device', () => { const item = { type: 'Number', state: '50', @@ -63,6 +63,21 @@ describe('Fan Device', () => { }); }); + test('getMetadata legacy', () => { + const metadata = Device.getMetadata({ + metadata: { + ga: { + config: { + tfaAck: true, + tfaPin: '1234' + } + } + } + }); + expect(metadata.customData.ackNeeded).toBe(true); + expect(metadata.customData.pinNeeded).toBe('1234'); + }); + test('hasTag', () => { expect(Device.hasTag({}, 'testtag')).toBe(false); expect(Device.hasTag({ tags: ['test'] }, 'testtag')).toBe(false); From d78b302ea4a68bd6e8b33ce9c193c9361c985812 Mon Sep 17 00:00:00 2001 From: Michael Krug Date: Sun, 28 Feb 2021 17:14:29 +0100 Subject: [PATCH 94/94] Update dependencies Signed-off-by: Michael Krug --- package-lock.json | 154 +++++++++++++++++++++------------------------- package.json | 6 +- 2 files changed, 74 insertions(+), 86 deletions(-) diff --git a/package-lock.json b/package-lock.json index 97b399a1..843ac24d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,18 +14,18 @@ }, "devDependencies": { "@types/jest": "^26.0.20", - "eslint": "^7.19.0", - "eslint-config-prettier": "^7.2.0", + "eslint": "^7.21.0", + "eslint-config-prettier": "^8.1.0", "eslint-plugin-prettier": "^3.3.1", "jest": "^26.6.3", - "nock": "^13.0.7", + "nock": "^13.0.9", "prettier": "2.2.1" } }, "node_modules/@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "dev": true, "dependencies": { "@babel/highlight": "^7.10.4" @@ -457,15 +457,6 @@ "lodash": "^4.17.19" } }, - "node_modules/@babel/traverse/node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, "node_modules/@babel/traverse/node_modules/debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -524,9 +515,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", - "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", + "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -536,7 +527,6 @@ "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", - "lodash": "^4.17.20", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, @@ -801,7 +791,6 @@ "jest-resolve": "^26.6.2", "jest-util": "^26.6.2", "jest-worker": "^26.6.2", - "node-notifier": "^8.0.0", "slash": "^3.0.0", "source-map": "^0.6.0", "string-length": "^4.0.1", @@ -1093,7 +1082,10 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } }, "node_modules/acorn-walk": { "version": "7.2.0", @@ -2120,13 +2112,13 @@ } }, "node_modules/eslint": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.19.0.tgz", - "integrity": "sha512-CGlMgJY56JZ9ZSYhJuhow61lMPPjUzWmChFya71Z/jilVos7mR/jPgaEfVGgMBY5DshbKdG8Ezb8FDCHcoMEMg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.21.0.tgz", + "integrity": "sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.3.0", + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -2137,9 +2129,9 @@ "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", - "esquery": "^1.2.0", + "esquery": "^1.4.0", "esutils": "^2.0.2", - "file-entry-cache": "^6.0.0", + "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", "globals": "^12.1.0", @@ -2174,9 +2166,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-7.2.0.tgz", - "integrity": "sha512-rV4Qu0C3nfJKPOAhFujFxB7RMP+URFyQqqOZW9DMRD7ZDTFyjaIlETU3xzHELt++4ugC0+Jm084HQYkkJe+Ivg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz", + "integrity": "sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -2457,9 +2449,9 @@ } }, "node_modules/esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -2811,9 +2803,9 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz", - "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "dependencies": { "flat-cache": "^3.0.4" @@ -2865,9 +2857,9 @@ } }, "node_modules/flatted": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz", - "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", "dev": true }, "node_modules/for-in": { @@ -3269,6 +3261,9 @@ }, "engines": { "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/import-fresh/node_modules/resolve-from": { @@ -4034,7 +4029,6 @@ "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", "graceful-fs": "^4.2.4", "jest-regex-util": "^26.0.0", "jest-serializer": "^26.6.2", @@ -4966,9 +4960,9 @@ "dev": true }, "node_modules/nock": { - "version": "13.0.7", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.7.tgz", - "integrity": "sha512-WBz73VYIjdbO6BwmXODRQLtn7B5tldA9pNpWJe5QTtTEscQlY5KXU4srnGzBOK2fWakkXj69gfTnXGzmrsaRWw==", + "version": "13.0.9", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.9.tgz", + "integrity": "sha512-SoGx/J0SsZPOdBFrBC9PP6NwaEgOBQIRPbsOsO9q+OwOPWc5eT6wALSxn3ZNE4Fv2ImIUXM4Hv/07rjq/uWDew==", "dev": true, "dependencies": { "debug": "^4.1.0", @@ -6445,6 +6439,9 @@ "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/supports-color": { @@ -7140,9 +7137,9 @@ }, "dependencies": { "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "dev": true, "requires": { "@babel/highlight": "^7.10.4" @@ -7544,15 +7541,6 @@ "lodash": "^4.17.19" }, "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -7606,9 +7594,9 @@ } }, "@eslint/eslintrc": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", - "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", + "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -7618,7 +7606,6 @@ "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", - "lodash": "^4.17.20", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, @@ -8093,7 +8080,8 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true + "dev": true, + "requires": {} }, "acorn-walk": { "version": "7.2.0", @@ -8926,13 +8914,13 @@ } }, "eslint": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.19.0.tgz", - "integrity": "sha512-CGlMgJY56JZ9ZSYhJuhow61lMPPjUzWmChFya71Z/jilVos7mR/jPgaEfVGgMBY5DshbKdG8Ezb8FDCHcoMEMg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.21.0.tgz", + "integrity": "sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.3.0", + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -8943,9 +8931,9 @@ "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", - "esquery": "^1.2.0", + "esquery": "^1.4.0", "esutils": "^2.0.2", - "file-entry-cache": "^6.0.0", + "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", "globals": "^12.1.0", @@ -9111,9 +9099,9 @@ } }, "eslint-config-prettier": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-7.2.0.tgz", - "integrity": "sha512-rV4Qu0C3nfJKPOAhFujFxB7RMP+URFyQqqOZW9DMRD7ZDTFyjaIlETU3xzHELt++4ugC0+Jm084HQYkkJe+Ivg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz", + "integrity": "sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==", "dev": true, "requires": {} }, @@ -9169,9 +9157,9 @@ "dev": true }, "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -9464,9 +9452,9 @@ } }, "file-entry-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz", - "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "requires": { "flat-cache": "^3.0.4" @@ -9506,9 +9494,9 @@ } }, "flatted": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz", - "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", "dev": true }, "for-in": { @@ -11177,9 +11165,9 @@ "dev": true }, "nock": { - "version": "13.0.7", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.7.tgz", - "integrity": "sha512-WBz73VYIjdbO6BwmXODRQLtn7B5tldA9pNpWJe5QTtTEscQlY5KXU4srnGzBOK2fWakkXj69gfTnXGzmrsaRWw==", + "version": "13.0.9", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.9.tgz", + "integrity": "sha512-SoGx/J0SsZPOdBFrBC9PP6NwaEgOBQIRPbsOsO9q+OwOPWc5eT6wALSxn3ZNE4Fv2ImIUXM4Hv/07rjq/uWDew==", "dev": true, "requires": { "debug": "^4.1.0", diff --git a/package.json b/package.json index d41b2a8b..0bd1091a 100644 --- a/package.json +++ b/package.json @@ -29,11 +29,11 @@ }, "devDependencies": { "@types/jest": "^26.0.20", - "eslint": "^7.19.0", - "eslint-config-prettier": "^7.2.0", + "eslint": "^7.21.0", + "eslint-config-prettier": "^8.1.0", "eslint-plugin-prettier": "^3.3.1", "jest": "^26.6.3", - "nock": "^13.0.7", + "nock": "^13.0.9", "prettier": "2.2.1" } }