misfit-bolt
Advanced tools
Comparing version 0.1.1 to 1.0.0
@@ -6,13 +6,13 @@ Bolt = require('.'); | ||
var i = 0, | ||
colors = [[228,41,15,10], | ||
[216,62,36,10], | ||
[205,55,56,10], | ||
[211,27,76,10], | ||
[166,18,97,10]]; | ||
colors = [['253','100%','49%'], | ||
['117','100%','49%'], | ||
['0','100%','49%'], | ||
['62','100%','49%'], | ||
['304','100%','49%']]; | ||
setInterval(function(){ | ||
var color = colors[i++ % colors.length]; | ||
bolt.setRGBA.apply(bolt, color); | ||
}, 500); | ||
bolt.setHSL(color, 10, function() {}); | ||
}, 2000); | ||
}); | ||
}); |
140
index.js
@@ -6,2 +6,3 @@ 'use strict'; | ||
var Peripheral = require('noble/lib/peripheral'); | ||
var Color = require('color'); | ||
@@ -16,5 +17,29 @@ var advertisementName = 'MFBOLT'; | ||
} | ||
this.id = peripheral.uuid; | ||
this.peripheral = peripheral; | ||
this._connected = false; | ||
} | ||
getLight(done) { | ||
debug('getting light'); | ||
if (this._light) { | ||
debug('got cached light'); | ||
return done(undefined, this._light); | ||
} | ||
this.peripheral.discoverAllServicesAndCharacteristics((error, services, characteristics) => { | ||
debug('got light'); | ||
var characteristic; | ||
for (var i = 0; i < characteristics.length; i ++) { | ||
characteristic = characteristics[i]; | ||
if(characteristic.uuid == 'fff1') { | ||
this._light = characteristic; | ||
} | ||
} | ||
if (!characteristic) { | ||
throw new Error('Bolt#connect : could not find light characteristic'); | ||
} | ||
done(error, this._light); | ||
}); | ||
} | ||
connect(done) { | ||
@@ -25,19 +50,9 @@ if (typeof done !== 'function') { | ||
debug('connecting'); | ||
this.peripheral.connect(() => { | ||
if (this._connected) { | ||
return done(); | ||
} | ||
this.peripheral.connect((error) => { | ||
this._connected = true; | ||
debug(`connected: ${this.peripheral.uuid}`); | ||
this.peripheral.discoverAllServicesAndCharacteristics((error, services, characteristics) => { | ||
debug('got light'); | ||
var characteristic; | ||
for (var i = 0; i < characteristics.length; i ++) { | ||
characteristic = characteristics[i]; | ||
if(characteristic.uuid == 'fff1') { | ||
this.light = characteristic; | ||
} | ||
} | ||
if (!characteristic) { | ||
throw new Error('Bolt#connect : could not find light characteristic'); | ||
} | ||
done(); | ||
}); | ||
this.getLight(done); | ||
}); | ||
@@ -48,8 +63,14 @@ return this; | ||
disconnect(done) { | ||
if (typeof done !== 'function') { | ||
throw new Error('Bolt#disconnect : first argument should be a function'); | ||
} | ||
debug('disconnecting'); | ||
this.peripheral.disconnect(() => { | ||
if (!this._connected) { | ||
return done(); | ||
} | ||
this.peripheral.disconnect((error) => { | ||
this._connected = false; | ||
this._light = undefined; | ||
debug('disconnected'); | ||
if (typeof done == 'function') { | ||
done(); | ||
} | ||
done(error); | ||
}); | ||
@@ -59,6 +80,12 @@ return this; | ||
set(value) { | ||
set(value, done) { | ||
if (typeof value !== 'string' || value.length > 18) { | ||
throw new Error('Bolt#set : first argument should be a string of 18 chars max'); | ||
} | ||
if (typeof done !== 'function') { | ||
throw new Error('Bolt#set : second argument should be a function'); | ||
} | ||
if (!this._connected) { | ||
throw new Error('Bolt#set : bulb is not connected.'); | ||
} | ||
@@ -69,12 +96,32 @@ var length = 18; | ||
var buffer = new Buffer(string); | ||
debug(`set light: ${string}`) | ||
this.light.write(buffer); | ||
this.getLight((error, light) => { | ||
debug(`set light: ${light} with value: ${string}`); | ||
light.write(buffer, undefined, done); | ||
}); | ||
return this; | ||
} | ||
setRGBA(rgba) { | ||
// TODO: validate rgba values. | ||
return this.set(rgba.join(',')); | ||
setRGBA(rgba, done) { | ||
if (!this._connected) { | ||
throw new Error('Bolt#setRGBA : bulb is not connected.'); | ||
} | ||
function error(property, max) { | ||
if (!max) { | ||
max = 255; | ||
} | ||
throw new Error(`Bolt#setRGBA : ${property} should be an integer between 0 and ${max}`); | ||
} | ||
if (rgba[0] < 0 || rgba[0] > 255) { error('red'); } | ||
if (rgba[1] < 0 || rgba[1] > 255) { error('green'); } | ||
if (rgba[2] < 0 || rgba[2] > 255) { error('blue'); } | ||
if (rgba[3] < 0 || rgba[3] > 100) { error('alpha', 100); } | ||
return this.set(rgba.join(','), done); | ||
} | ||
setHSL(hsl, brightness, done) { | ||
var color = new Color(`hsl(${hsl.join(', ')})`); | ||
var rgba = color.rgbArray().concat(brightness); | ||
return this.setRGBA(rgba, done); | ||
} | ||
get(done) { | ||
@@ -84,7 +131,12 @@ if (typeof done !== 'function') { | ||
} | ||
if (!this._connected) { | ||
throw new Error('Bolt#get : bulb is not connected.'); | ||
} | ||
debug('reading'); | ||
this.light.read((error, buffer) => { | ||
debug('read'); | ||
var string = buffer.toString(); | ||
done(string.replace(/,+$/, '')); | ||
this.getLight((error, light) => { | ||
light.read((error, buffer) => { | ||
debug('read'); | ||
var string = buffer.toString(); | ||
done(error, string.replace(/,+$/, '')); | ||
}); | ||
}); | ||
@@ -98,3 +150,6 @@ return this; | ||
} | ||
this.get((value) => { | ||
if (!this._connected) { | ||
throw new Error('Bolt#getRGBA : bulb is not connected.'); | ||
} | ||
this.get((error, value) => { | ||
var r, g, b, a; | ||
@@ -110,3 +165,3 @@ var rgba = value.match(/(\d{1,3}),(\d{1,3}),(\d{1,3}),(\d{1,3})/).slice(1, 5); | ||
} | ||
done([r, g, b, a]); | ||
done(error, [r, g, b, a]); | ||
}); | ||
@@ -116,10 +171,23 @@ return this; | ||
off() { | ||
return this.set("CLTMP 3200,0"); | ||
getState(done) { | ||
if (typeof done !== 'function') { | ||
throw new Error('Bolt#getState : first argument should be a function'); | ||
} | ||
if (!this._connected) { | ||
throw new Error('Bolt#getState : bulb is not connected.'); | ||
} | ||
this.get((error, value) => { | ||
var off = ',0'; | ||
done(error, value.substr(value.length - off.length, off.length) === off); | ||
}); | ||
} | ||
on() { | ||
return this.set("CLTMP 3200,1"); | ||
off(done) { | ||
return this.set("CLTMP 3200,0", done); | ||
} | ||
on(done) { | ||
return this.set("CLTMP 3200,100", done); | ||
} | ||
static discover(done, uuids) { | ||
@@ -126,0 +194,0 @@ if (typeof done !== 'function') { |
@@ -18,2 +18,3 @@ /*jshint expr: true*/ | ||
peripheral = new Peripheral(); | ||
peripheral.id = 'foo'; | ||
bolt = new Bolt(peripheral); | ||
@@ -32,3 +33,7 @@ }); | ||
it('sets peripheral parameter as instance property', () => { | ||
it('sets `peripheral.uuid` as `id` instance property', () => { | ||
chai.expect(peripheral.uuid).to.equal(bolt.id); | ||
}); | ||
it('sets `peripheral` parameter as instance property', () => { | ||
chai.expect(peripheral).to.equal(bolt.peripheral); | ||
@@ -51,11 +56,10 @@ }); | ||
var connectSpy, discoverLightSpy, | ||
characteristics = [{uuid: 'foo'},{uuid:'fff1'}]; | ||
var connectSpy, getLightSpy; | ||
before((done) => { | ||
connectSpy = sinon.stub(peripheral, 'connect', (cb) => { | ||
connectSpy = sinon.sandbox.stub(peripheral, 'connect', (cb) => { | ||
cb(); | ||
}); | ||
discoverLightSpy = sinon.stub(peripheral, 'discoverAllServicesAndCharacteristics', (cb) => { | ||
cb(undefined, undefined, characteristics); | ||
getLightSpy = sinon.sandbox.stub(bolt, 'getLight', (cb) => { | ||
cb(); | ||
}); | ||
@@ -68,13 +72,9 @@ bolt.connect(() => { | ||
it('first calls `connect` on the peripheral object', () => { | ||
connectSpy.calledBefore(discoverLightSpy); | ||
chai.expect(connectSpy.calledBefore(getLightSpy)); | ||
}); | ||
it('then calls `discoverAllServicesAndCharacteristics` on the peripheral object', () => { | ||
discoverLightSpy.calledAfter(connectSpy); | ||
it('then calls `getLight` on the instance object', () => { | ||
chai.expect(getLightSpy.calledAfter(connectSpy)); | ||
}); | ||
it('sets first characteristic as `light` instance property', () => { | ||
chai.expect(bolt.light).to.deep.equal(characteristics[1]); | ||
}); | ||
}); | ||
@@ -86,14 +86,21 @@ | ||
var disconnectSpy; | ||
describe('without callback', () => { | ||
before(() => { | ||
disconnectSpy = sinon.stub(peripheral, 'disconnect', (cb) => { | ||
cb(); | ||
it('throws an error if first parameter is not a function', () => { | ||
chai.expect(() => bolt.connect()).to.throw(/Bolt#connect : first argument should be a function/); | ||
}); | ||
}); | ||
describe('without callback', () => { | ||
describe('with callback', () => { | ||
before(() => { | ||
bolt.disconnect(); | ||
var disconnectSpy; | ||
before((done) => { | ||
disconnectSpy = sinon.sandbox.stub(peripheral, 'disconnect', (cb) => { | ||
cb(); | ||
}); | ||
bolt.disconnect(() => { | ||
done(); | ||
}); | ||
}); | ||
@@ -107,6 +114,21 @@ | ||
describe('with callback', () => { | ||
}); | ||
describe('#getLight', () => { | ||
var discoverLightSpy, | ||
characteristics = [{uuid: 'foo'}, {uuid:'fff1'}]; | ||
before(() => { | ||
discoverLightSpy = sinon.sandbox.stub(peripheral, 'discoverAllServicesAndCharacteristics', (cb) => { | ||
cb(undefined, undefined, characteristics); | ||
}); | ||
}); | ||
describe('with `_light` not defined', () => { | ||
var returnedLight; | ||
before((done) => { | ||
bolt.disconnect(() => { | ||
bolt._light = undefined; | ||
bolt.getLight((error, light) => { | ||
returnedLight = light; | ||
done(); | ||
@@ -116,11 +138,37 @@ }); | ||
it('calls `disconnect` on the peripheral object', () => { | ||
chai.expect(disconnectSpy.called).to.be.true; | ||
it('calls `discoverAllServicesAndCharacteristics` on the peripheral object', () => { | ||
chai.expect(discoverLightSpy.called); | ||
}); | ||
it('sets first characteristic as `_light` instance property', () => { | ||
chai.expect(bolt._light).to.deep.equal(characteristics[1]); | ||
}); | ||
it('passes back the cached `_light` property', () => { | ||
chai.expect(returnedLight).to.equal(bolt._light); | ||
}); | ||
}); | ||
describe('with `_light` defined', () => { | ||
var returnedLight; | ||
before((done) => { | ||
bolt._light = 'foo'; | ||
bolt.getLight((error, light) => { | ||
returnedLight = light; | ||
done(); | ||
}); | ||
}); | ||
it('does not call `discoverAllServicesAndCharacteristics` on the peripheral object', () => { | ||
chai.expect(!discoverLightSpy.called); | ||
}); | ||
it('passes back the cached `_light` property', () => { | ||
chai.expect(returnedLight).to.equal(bolt._light); | ||
}); | ||
}); | ||
}); | ||
describe('#set', () => { | ||
@@ -160,18 +208,55 @@ | ||
var value, spy, expectedBuffer; | ||
var value = 'abc'; | ||
before(() => { | ||
value = "abc"; | ||
expectedBuffer = new Buffer("abc,,,,,,,,,,,,,,,"); | ||
describe('without callback', () => { | ||
// stub light property, and create spy on write method | ||
bolt.light = {write: () => {}}; | ||
spy = sinon.sandbox.stub(bolt.light, 'write'); | ||
it('throws an error', () => { | ||
chai.expect( | ||
() => bolt.set(value) | ||
).to.throw(/Bolt#set : second argument should be a function/); | ||
}); | ||
bolt.set(value); | ||
}); | ||
it('calls the `write` method on the light property with the correct buffer', () => { | ||
chai.expect(spy.called); | ||
chai.expect(spy.args[0][0].toString()).to.equal(expectedBuffer.toString()); | ||
describe('with callback', () => { | ||
describe('when `_connected` is false', () => { | ||
before(() => { | ||
bolt._connected = false; | ||
}); | ||
it('throws an error', () => { | ||
chai.expect( | ||
() => bolt.set(value, () => {}) | ||
).to.throw(/Bolt#set : bulb is not connected/); | ||
}); | ||
}); | ||
describe('when `_connected` is true', () => { | ||
var spy, expectedBuffer; | ||
before((done) => { | ||
bolt._connected = true; | ||
expectedBuffer = new Buffer(`${value},,,,,,,,,,,,,,,`); | ||
// stub light property, and create spy on write method | ||
bolt._light = {write: () => {}}; | ||
spy = sinon.sandbox.stub(bolt._light, 'write', (buffer, withoutResponse, callback) => { | ||
callback(); | ||
}); | ||
bolt.set(value, done); | ||
}); | ||
it('calls the `write` method on the light property with the correct buffer', () => { | ||
chai.expect(spy.called); | ||
chai.expect(spy.args[0][0].toString()).to.equal(expectedBuffer.toString()); | ||
}); | ||
}); | ||
}); | ||
@@ -187,17 +272,52 @@ | ||
before(() => { | ||
var r = 123, | ||
g = 345, | ||
b = 567, | ||
a = 789; | ||
expectedSetValue = [r,g,b,a].join(','); | ||
spy = sinon.sandbox.stub(bolt, 'set'); | ||
bolt.setRGBA(r, g, b, a); | ||
describe('when red is not in [0, 255] range', () => { | ||
it('throws an error', () => { | ||
chai.expect( | ||
() => bolt.setRGBA([256,0,0,0], () => {}) | ||
).to.throw(/Bolt#setRGBA : red should be an integer between 0 and 255/); | ||
}); | ||
}); | ||
it('calls #set method with the right value', () => { | ||
chai.expect(spy.called); | ||
chai.expect(spy.args[0][0]).to.equal(expectedSetValue); | ||
describe('when green is not in [0, 255] range', () => { | ||
it('throws an error', () => { | ||
chai.expect( | ||
() => bolt.setRGBA([0,256,0,0], () => {}) | ||
).to.throw(/Bolt#setRGBA : green should be an integer between 0 and 255/); | ||
}); | ||
}); | ||
describe('when blue is not in [0, 255] range', () => { | ||
it('throws an error', () => { | ||
chai.expect( | ||
() => bolt.setRGBA([0,0,256,0], () => {}) | ||
).to.throw(/Bolt#setRGBA : blue should be an integer between 0 and 255/); | ||
}); | ||
}); | ||
describe('when alpha is not in [0, 100] range', () => { | ||
it('throws an error', () => { | ||
chai.expect( | ||
() => bolt.setRGBA([0,0,0,101], () => {}) | ||
).to.throw(/Bolt#setRGBA : alpha should be an integer between 0 and 100/); | ||
}); | ||
}); | ||
describe('when rgba is valid', () => { | ||
var spy, rgba = [100,100,100,100]; | ||
before((done) => { | ||
spy = sinon.sandbox.stub(bolt, 'set', (value, cb) => { | ||
cb(); | ||
}); | ||
bolt.setRGBA(rgba, done); | ||
}); | ||
it('calls #set method with the right value', () => { | ||
chai.expect(spy.called); | ||
chai.expect(spy.args[0][0]).to.equal(rgba.join(',')); | ||
}); | ||
}); | ||
}); | ||
@@ -223,9 +343,9 @@ | ||
// stub light property, and create spy on read method | ||
bolt.light = {read: () => {}}; | ||
bolt._light = {read: () => {}}; | ||
expectedValue = 'foo'; | ||
spy = sinon.sandbox.stub(bolt.light, 'read', (cb) => { | ||
spy = sinon.sandbox.stub(bolt._light, 'read', (cb) => { | ||
cb(undefined, new Buffer(`${expectedValue},,,,,,,,,,,,,,,`)); | ||
}); | ||
bolt.get((value) => { | ||
bolt.get((error, value) => { | ||
returnedValue = value; | ||
@@ -263,5 +383,5 @@ done(); | ||
spy = sinon.sandbox.stub(bolt, 'get', (cb) => { | ||
cb("123,234,345,0"); | ||
cb(undefined, "123,234,345,0"); | ||
}); | ||
bolt.getRGBA((value) => { | ||
bolt.getRGBA((error, value) => { | ||
returnedValue = value; | ||
@@ -281,3 +401,87 @@ done(); | ||
describe('#getState', () => { | ||
describe('without callback', () => { | ||
it('throws an error', () => { | ||
chai.expect( | ||
() => bolt.getState() | ||
).to.throw(/Bolt#getState : first argument should be a function/); | ||
}); | ||
}); | ||
describe('with callback', () => { | ||
describe('when `_connected` is false', () => { | ||
before(() => { | ||
bolt._connected = false; | ||
}); | ||
it('throws an error', () => { | ||
chai.expect( | ||
() => bolt.getState(() => {}) | ||
).to.throw(/Bolt#getState : bulb is not connected/); | ||
}); | ||
}); | ||
describe('when `_connected` is true', () => { | ||
var spy, getValue, returnedValue; | ||
before(() => { | ||
bolt._connected = true; | ||
}); | ||
describe('when get returns `CLTMP 3200,0`', () => { | ||
before((done) => { | ||
spy = sinon.sandbox.stub(bolt, 'get', (cb) => { | ||
cb(undefined, 'CLTMP 3200,0'); | ||
}); | ||
bolt.getState((error, value) => { | ||
returnedValue = value; | ||
done(); | ||
}); | ||
}); | ||
it('calls #get method', () => { | ||
chai.expect(spy.called); | ||
}); | ||
it('return `false`', () => { | ||
chai.expect(!returnedValue); | ||
}); | ||
}); | ||
describe('when get returns `CLTMP 3200,100`', () => { | ||
before((done) => { | ||
spy = sinon.sandbox.stub(bolt, 'get', (cb) => { | ||
cb(undefined, 'CLTMP 3200,100'); | ||
}); | ||
bolt.getState((error, value) => { | ||
returnedValue = value; | ||
done(); | ||
}); | ||
}); | ||
it('calls #get method', () => { | ||
chai.expect(spy.called); | ||
}); | ||
it('return `true`', () => { | ||
chai.expect(returnedValue); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('#on', () => { | ||
@@ -288,3 +492,3 @@ | ||
before(() => { | ||
expectedSetValue = 'CLTMP 3200,1'; | ||
expectedSetValue = 'CLTMP 3200,100'; | ||
spy = sinon.sandbox.stub(bolt, 'set'); | ||
@@ -303,4 +507,16 @@ bolt.on(); | ||
var expectedSetValue, spy; | ||
before(() => { | ||
expectedSetValue = 'CLTMP 3200,0'; | ||
spy = sinon.sandbox.stub(bolt, 'set'); | ||
bolt.off(); | ||
}); | ||
it('calls #set method with the right value', () => { | ||
chai.expect(spy.called); | ||
chai.expect(spy.args[0][0]).to.equal(expectedSetValue); | ||
}); | ||
}); | ||
}); |
{ | ||
"name": "misfit-bolt", | ||
"version": "0.1.1", | ||
"version": "1.0.0", | ||
"description": "Misfit Bolt Javascript Interface", | ||
@@ -21,4 +21,4 @@ "keywords": [ | ||
"scripts": { | ||
"lint": "./bin/jshint *.js", | ||
"test": "./bin/mocha .", | ||
"lint": "./node_modules/.bin/jshint *.js", | ||
"test": "./node_modules/.bin/mocha .", | ||
"patch-release": "npm version patch && npm publish && git push --follow-tags", | ||
@@ -35,2 +35,3 @@ "minor-release": "npm version minor && npm publish && git push --follow-tags", | ||
"dependencies": { | ||
"color": "^0.10.1", | ||
"debug": "^2.2.0", | ||
@@ -37,0 +38,0 @@ "noble": "^1.2.1" |
@@ -37,3 +37,3 @@ # Misfit Bolt Javascript Interface | ||
Bolt.discover(function(bolt) { | ||
// do something with bolt instance | ||
// do something | ||
}, ['29852E52-67A0-490A-BC55-7FAB809AD0C0']); | ||
@@ -49,3 +49,3 @@ ``` | ||
1. callback (*function*): gets called when connection is complete. | ||
1. callback (*function*) | ||
@@ -60,3 +60,3 @@ **Returns** | ||
bolt.connect(function() { | ||
// do something with bolt instance | ||
// do something | ||
}); | ||
@@ -72,3 +72,3 @@ ``` | ||
1. callback (*function*, optional): gets called when disconnection is complete. | ||
1. callback (*function*) | ||
@@ -82,3 +82,5 @@ **Returns** | ||
```javascript | ||
bolt.disconnect(); | ||
bolt.disconnect(function(){ | ||
// do something | ||
}); | ||
``` | ||
@@ -93,3 +95,3 @@ | ||
none | ||
1. callback (*function*) | ||
@@ -103,3 +105,5 @@ **Returns** | ||
```javascript | ||
bolt.on(); | ||
bolt.on(function(){ | ||
// do something | ||
}); | ||
``` | ||
@@ -114,3 +118,3 @@ | ||
none | ||
1. callback (*function*) | ||
@@ -124,3 +128,5 @@ **Returns** | ||
```javascript | ||
bolt.off(); | ||
bolt.off(function(){ | ||
// do something | ||
}); | ||
``` | ||
@@ -136,2 +142,3 @@ | ||
1. value (*String*): value to set on bulb. Mostly in the form or RGBA. Accepts "CLTMP 3200,0" or "CLTMP 3200,1" (used for toggle on / off). Other undocumented formats might exist. | ||
2. callback (*function*) | ||
@@ -145,3 +152,5 @@ **Returns** | ||
```javascript | ||
bolt.set("228,41,15,10"); | ||
bolt.set("228,41,15,10", function(){ | ||
// do something | ||
}); | ||
``` | ||
@@ -157,2 +166,3 @@ | ||
1. value (*Array*): value to set on bulb. Although not enforced yet, should be in the form of `[red, gree, blue, alpha]`. | ||
2. callback (*function*) | ||
@@ -166,3 +176,5 @@ **Returns** | ||
```javascript | ||
bolt.setRGBA([228,41,15,10]); | ||
bolt.setRGBA([228,41,15,10], function(){ | ||
// do something | ||
}); | ||
``` | ||
@@ -213,5 +225,6 @@ | ||
## Usage | ||
## Example | ||
```javascript | ||
Bolt = require('.'); | ||
@@ -224,15 +237,16 @@ // Discover every nearby Bolt | ||
var i = 0, | ||
colors = [[228,41,15,10], | ||
[216,62,36,10], | ||
[205,55,56,10], | ||
[211,27,76,10], | ||
[166,18,97,10]]; | ||
colors = [['253','100%','49%'], | ||
['117','100%','49%'], | ||
['0','100%','49%'], | ||
['62','100%','49%'], | ||
['304','100%','49%']]; | ||
// Change color every 500 ms | ||
// Change color every 2000 ms | ||
setInterval(function(){ | ||
var color = colors[i++ % colors.length]; | ||
bolt.setRGBA.apply(bolt, color); | ||
}, 500); | ||
bolt.setHSL(color, 10, function() {}); | ||
}, 2000); | ||
}); | ||
}); | ||
``` | ||
@@ -239,0 +253,0 @@ |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
24160
571
1
246
3
+ Addedcolor@^0.10.1
+ Addedcolor@0.10.1(transitive)
+ Addedcolor-convert@0.5.3(transitive)
+ Addedcolor-name@1.1.4(transitive)
+ Addedcolor-string@0.3.0(transitive)