From 1477d2314dbbe49ca45587ecdba7efc2c39978c3 Mon Sep 17 00:00:00 2001 From: Martin Endler Date: Sat, 28 Jan 2017 17:52:30 +0100 Subject: [PATCH] Led control support (#6) Add support for controlling led and buzzer Add support for direct connection to the reader Add support for direct transmit Update dependencies Add yarn.lock Move APDU commands specific to ACR122 to new class Fix typos in README Add Discord badge in README Move and add examples to new folder Update README (add note about usage in Electron) --- .idea/misc.xml | 10 - README.md | 30 +- examples/desfire.js | 212 +++++ {test => examples}/index.js | 8 + examples/led.js | 179 +++++ {test => examples}/pretty.js | 2 +- package.json | 22 +- src/ACR122Reader.js | 235 ++++++ src/NFC.js | 12 + src/Reader.js | 148 ++-- src/errors.js | 12 + yarn.lock | 1400 ++++++++++++++++++++++++++++++++++ 12 files changed, 2192 insertions(+), 78 deletions(-) create mode 100644 examples/desfire.js rename {test => examples}/index.js (92%) create mode 100644 examples/led.js rename {test => examples}/pretty.js (96%) create mode 100644 src/ACR122Reader.js create mode 100644 yarn.lock diff --git a/.idea/misc.xml b/.idea/misc.xml index eabe228..28a804d 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,14 +3,4 @@ - - - - - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 3e81aa4..719b91e 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # nfc-pcsc -[![npm](https://img.shields.io/npm/v/nfc-pcsc.svg)](https://www.npmjs.com/package/nfc-pcsc) +[![npm](https://img.shields.io/npm/v/nfc-pcsc.svg?maxAge=2592000)](https://www.npmjs.com/package/nfc-pcsc) +[![nfc-pcsc channel on discord](https://img.shields.io/badge/discord-join%20chat-61dafb.svg)](https://discord.gg/bg3yazg) A simple wrapper around [pokusew/node-pcsclite](https://github.com/pokusew/node-pcsclite) to work easier with NFC tags. Built-in support for reading **card UIDs** and reading tags emulated with [**Android HCE**](https://developer.android.com/guide/topics/connectivity/nfc/hce.html). > **NOTE:** Reading tag UID and methods for writing and reading tag content **depend on NFC reader commands support**. -It is tested to work with **ARC122 USB reader** but it can work with others too. +It is tested to work with **ACR122 USB reader** but it can work with others too. When detecting tags does not work see [Alternative usage](#alternative-usage). ## Content @@ -22,6 +23,8 @@ When detecting tags does not work see [Alternative usage](#alternative-usage). - [Running examples locally](#running-examples-locally) - [Alternative usage](#alternative-usage) - [Reading and writing data](#reading-and-writing-data) +- [FAQ](#faq) + - [Can I use this library in my Electron app?](#can-i-use-this-library-in-my-electron-app) - [LICENSE](#license) @@ -62,11 +65,11 @@ When a NFC tag (card) is attached to the reader, the following is done: ## Basic usage > ### Running examples locally -> If you want see it in action, clone this repository, install dependencies with npm and run `npm run test`. +> If you want see it in action, clone this repository, install dependencies with npm and run `npm run example`. > ```bash > git clone https://github.com/pokusew/nfc-pcsc.git > npm install -> npm run test +> npm run example > ``` @@ -113,7 +116,7 @@ nfc.on('error', err => { ## Alternative usage You can **disable auto processing of tags** and process them yourself. -It may be useful when you are using other than ARC122 USB reader or non-standard tags. +It may be useful when you are using other than ACR122 USB reader or non-standard tags. ```javascript import NFC from 'nfc-pcsc'; @@ -242,6 +245,23 @@ reader.on('card', async card => { }); ``` + +## FAQ + +### Can I use this library in my [Electron](http://electron.atom.io/) app? + +Yes, you can! It works well. + +**But please note**, that this library uses [Node Native Modules](https://nodejs.org/api/addons.html) (underlying library [pokusew/node-pcsclite](https://github.com/pokusew/node-pcsclite) which provides access to PC/SC API). + +Read carefully **[Using Native Node Modules](http://electron.atom.io/docs/tutorial/using-native-node-modules/) guide in Electron documentation** to fully understand the problematic. + +**Note**, that because of Node Native Modules, you must build your app on target platform (you must run Windows build on Windows machine, etc.). +You can use CI/CD server to build your app for certain platforms. +For Windows, I recommend you to use [AppVeyor](https://appveyor.com/). +For macOS and Linux build, there are plenty of services to choose from, for example [CircleCI](https://circleci.com/), [Travis CI](https://travis-ci.com/) [CodeShip](https://codeship.com/). + + ## LICENSE The nfc node module, documentation, tests, and build scripts are licensed diff --git a/examples/desfire.js b/examples/desfire.js new file mode 100644 index 0000000..65ff065 --- /dev/null +++ b/examples/desfire.js @@ -0,0 +1,212 @@ +"use strict"; + +// ############# +// Example accessing and authenticating Mifare DESFIRE cards +// ############# + +import winston from 'winston'; +import NFC, { TAG_ISO_14443_3, TAG_ISO_14443_4, KEY_TYPE_A, KEY_TYPE_B } from '../src/NFC'; +import pretty from './pretty'; +import crypto from 'crypto'; + + +// config +const desfire = { + key: '00000000000000000000000000000000', + appId: [0x00, 0x00, 0x00], + keyId: [0x00], + read: { + fileId: [0x02], + offset: [0x00, 0x00, 0x00], + length: [14, 0x00, 0x00] + } +}; + + +function decrypt(key, data, iv = Buffer.alloc(8).fill(0)) { + + const decipher = crypto.createDecipheriv('DES-EDE-CBC', key, iv); + decipher.setAutoPadding(false); + + return Buffer.concat([decipher.update(data), decipher.final()]); + +} + +function encrypt(key, data, iv = Buffer.alloc(8).fill(0)) { + + const decipher = crypto.createCipheriv('DES-EDE-CBC', key, iv); + decipher.setAutoPadding(false); + + return Buffer.concat([decipher.update(data), decipher.final()]); + +} + + +const nfc = new NFC(); + +let readers = []; + +nfc.on('reader', async reader => { + + pretty.info(`device attached`, { reader: reader.name }); + + readers.push(reader); + + // we have to handle Mifare DESFIRE + reader.autoProcessing = false; + + // just handy shortcut to send data + const send = async(cmd, comment = null, responseMaxLength = 40) => { + + const b = Buffer.from(cmd); + + console.log((comment ? `[${comment}] ` : '') + `sending`, b); + + const data = await reader.transmit(b, responseMaxLength); + + console.log((comment ? `[${comment}] ` : '') + `received data`, data); + + return data; + + }; + + const wrap = (cmd, dataIn) => ([0x90, cmd, 0x00, 0x00, dataIn.length, ...dataIn, 0x00]); + + reader.on('card', async card => { + + pretty.info(`card detected`, { reader: reader.name, card }); + + const selectApplication = async() => { + + // 1: [0x5A] SelectApplication(appId) [4 bytes] - Selects one specific application for further access + // DataIn: appId (3 bytes) + const res = await send(wrap(0x5a, desfire.appId), 'step 1 - select app'); + + // something went wrong + if (res.slice(-1)[0] !== 0x00) { + throw new Error('error in step 1'); + } + + + }; + + const authenticate = async(key) => { + + // 2: [0x0a] Authenticate(keyId) [2bytes] + // DataIn: keyId (1 byte) + const res1 = await send(wrap(0x0a, desfire.keyId), 'step 2 - authenticate'); + + // something went wrong + if (res1.slice(-1)[0] !== 0xaf) { + throw new Error('error in step 2 - authenticate'); + } + + // encrypted RndB from reader + // cut out status code (last 2 bytes) + const ecRndB = res1.slice(0, -2); + + // decrypt it + const RndB = decrypt(key, ecRndB); + + // rotate RndB + const RndBp = Buffer.concat([RndB.slice(1, 8), RndB.slice(0, 1)]); + + // generate a 8 byte Random Number A + const RndA = crypto.randomBytes(8); + + // concat RndA and RndBp + const msg = encrypt(key, Buffer.concat([RndA, RndBp])); + + // send it back to the reader + const res2 = await send(wrap(0xaf, msg), 'step 2 - set up RndA'); + + // something went wrong + if (res2.slice(-1)[0] !== 0x00) { + throw new Error('error in step 2 - set up RndA'); + } + + // encrypted RndAp from reader + // cut out status code (last 2 bytes) + const ecRndAp = res2.slice(0, -2); + + // decrypt to get rotated value of RndA2 + const RndAp = decrypt(key, ecRndAp); + + // rotate + const RndA2 = Buffer.concat([RndAp.slice(7, 8), RndAp.slice(0, 7)]); + + // compare decrypted RndA2 response from reader with our RndA + // if it equals authentication process was successful + if (!RndA.equals(RndA2)) { + throw new Error('error in step 2 - match RndA random bytes'); + } + + return { + RndA, + RndB + }; + + }; + + const readData = async() => { + + // 3: [0xBD] ReadData(FileNo,Offset,Length) [8bytes] - Reads data from Standard Data Files or Backup Data Files + const res = await send(wrap(0xbd, [desfire.read.fileId, ...desfire.read.offset, ...desfire.read.length]), 'step 3 - read', 255); + + // something went wrong + if (res.slice(-1)[0] !== 0x00) { + throw new Error('error in step 3 - read'); + } + + console.log('data', res); + + }; + + + try { + + // step 1 + await selectApplication(); + + // step 2 + const key = new Buffer(desfire.key, 'hex'); + await authenticate(key); + + // step 3 + await readData(); + + + } catch (err) { + + pretty.error(`error occurred during processing steps`, { reader: reader.name }); + console.log(err); + + } + + + }); + + reader.on('error', err => { + + pretty.error(`an error occurred`, { reader: reader.name, err }); + + }); + + reader.on('end', () => { + + pretty.info(`device removed`, { reader: reader.name }); + + delete readers[readers.indexOf(reader)]; + + console.log(readers); + + }); + + +}); + +nfc.on('error', err => { + + pretty.error(`an error occurred`, err); + +}); diff --git a/test/index.js b/examples/index.js similarity index 92% rename from test/index.js rename to examples/index.js index 5df0935..7702ac3 100644 --- a/test/index.js +++ b/examples/index.js @@ -1,5 +1,13 @@ "use strict"; +// ############# +// Basic example +// - example reading and writing data on from/to card +// - should work well with any compatible PC/SC card reader +// - tested with Mifare Ultralight cards but should work with many others +// - example authentication for Mifare Classic cards +// ############# + import winston from 'winston'; import NFC, { TAG_ISO_14443_3, TAG_ISO_14443_4, KEY_TYPE_A, KEY_TYPE_B } from '../src/NFC'; import pretty from './pretty'; diff --git a/examples/led.js b/examples/led.js new file mode 100644 index 0000000..36e1806 --- /dev/null +++ b/examples/led.js @@ -0,0 +1,179 @@ +"use strict"; + +// ############# +// ACR122U example controlling LED and buzzer +// - custom buzzer output +// - repeated beeping on unsuccessful read/write operation +// ############# + +import winston from 'winston'; +import NFC, { TAG_ISO_14443_3, TAG_ISO_14443_4, KEY_TYPE_A, KEY_TYPE_B, CONNECT_MODE_DIRECT } from '../src/NFC'; +import pretty from './pretty'; + + +// minilogger for debugging + +function log() { + console.log(...arguments); +} + +const minilogger = { + log: log, + debug: log, + info: log, + warn: log, + error: log +}; + +const nfc = new NFC(minilogger); // const nfc = new NFC(minilogger); // optionally you can pass logger to see internal debug logs + +let readers = []; + +nfc.on('reader', async reader => { + + pretty.info(`device attached`, { reader: reader.name }); + + readers.push(reader); + + + // needed for reading tags emulated with Android HCE AID + // see https://developer.android.com/guide/topics/connectivity/nfc/hce.html + reader.aid = 'F222222222'; + + console.log(); + + try { + await reader.connect(CONNECT_MODE_DIRECT); + await reader.setBuzzerOutput(false); + await reader.disconnect(); + } catch (err) { + console.log(err); + } + + reader.on('card', async card => { + + + // standard nfc tags like Mifare + if (card.type === TAG_ISO_14443_3) { + // const uid = card.uid; + pretty.info(`card detected`, { reader: reader.name, card }); + } + // Android HCE + else if (card.type === TAG_ISO_14443_4) { + // process raw Buffer data + const data = card.data.toString('utf8'); + pretty.info(`card detected`, { reader: reader.name, card: { ...card, data } }); + } + // not possible, just to be sure + else { + pretty.info(`card detected`, { reader: reader.name, card }); + } + + // Notice: reading data from Mifare Classic cards (e.g. Mifare 1K) requires, + // that the data block must be authenticated first + // don't forget to fill your keys and types + // reader.authenticate(blockNumber, keyType, key, obsolete = false) + // if you are experiencing problems, you can try using obsolete = true which is compatible with PC/SC V2.01 + // uncomment when you need it + + // try { + // + // const key = 'FFFFFFFFFFFF'; + // const keyType = KEY_TYPE_A; + // + // // we will authenticate block 4, 5, 6, 7 (which we want to read) + // await Promise.all([ + // reader.authenticate(4, keyType, key), + // reader.authenticate(5, keyType, key), + // reader.authenticate(6, keyType, key), + // reader.authenticate(7, keyType, key) + // ]); + // + // pretty.info(`blocks successfully authenticated`); + // + // } catch (err) { + // pretty.error(`error when authenticating data`, { reader: reader.name, card, err }); + // return; + // } + + + // example reading 16 bytes assuming containing 16bit integer + try { + + // reader.read(blockNumber, length, blockSize = 4, packetSize = 16) + // - blockNumber - memory block number where to start reading + // - length - how many bytes to read + // ! Caution! length must be divisible by blockSize + + const data = await reader.read(4, 16); + + pretty.info(`data read`, { reader: reader.name, card, data }); + + const payload = data.readInt16BE(); + + pretty.info(`data converted`, payload); + + } catch (err) { + pretty.error(`error when reading data`, { reader: reader.name, card, err }); + await reader.led(0b01011101, [0x02, 0x01, 0x05, 0x01]); + return; + } + + + // example write 16bit integer + try { + + // reader.write(blockNumber, data, blockSize = 4) + // - blockNumber - memory block number where to start writing + // - data - what to write + // ! Caution! data.length must be divisible by blockSize + + const data = Buffer.allocUnsafe(16); + data.writeInt16BE(800); + + await reader.write(4, data); + + pretty.info(`data written`, { reader: reader.name, card }); + + } catch (err) { + pretty.error(`error when writing data`, { reader: reader.name, card, err }); + await reader.led(0b01011101, [0x02, 0x01, 0x05, 0x01]); + return; + } + + + try { + + await reader.led(0b00101110, [0x01, 0x00, 0x01, 0x01]); + + } catch (err) { + pretty.error(`error when writing led`); + } + + + }); + + reader.on('error', err => { + + pretty.error(`an error occurred`, { reader: reader.name, err }); + + }); + + reader.on('end', () => { + + pretty.info(`device removed`, { reader: reader.name }); + + delete readers[readers.indexOf(reader)]; + + console.log(readers); + + }); + + +}); + +nfc.on('error', err => { + + pretty.error(`an error occurred`, err); + +}); diff --git a/test/pretty.js b/examples/pretty.js similarity index 96% rename from test/pretty.js rename to examples/pretty.js index 6500674..099b5d0 100644 --- a/test/pretty.js +++ b/examples/pretty.js @@ -56,7 +56,7 @@ function formatConsoleLog(options) { delete options.meta.reader; } - let log = time + level + reader + ' ' + let log = time + level + reader + ' ' + (options.message !== undefined ? options.message : '') + (options.meta && Object.keys(options.meta).length ? '\n' + json(options.meta) : '' ); diff --git a/package.json b/package.json index 8d3846e..b95e367 100644 --- a/package.json +++ b/package.json @@ -24,20 +24,22 @@ }, "scripts": { "build": "babel src --out-dir dist", - "test": "node -r babel-register test/index.js" + "example": "node -r babel-register examples/index.js", + "example-led": "node -r babel-register examples/led.js", + "example-desfire": "node -r babel-register examples/desfire.js" }, "dependencies": { - "@pokusew/pcsclite": "^0.4.13" + "@pokusew/pcsclite": "^0.4.15" }, "devDependencies": { - "babel-cli": "^6.14.0", - "babel-plugin-transform-async-to-generator": "^6.8.0", - "babel-plugin-transform-class-properties": "^6.11.5", - "babel-plugin-transform-object-rest-spread": "^6.8.0", - "babel-preset-es2015-node6": "^0.3.0", - "babel-register": "^6.14.0", + "babel-cli": "^6.22.2", + "babel-plugin-transform-async-to-generator": "^6.22.0", + "babel-plugin-transform-class-properties": "^6.22.0", + "babel-plugin-transform-object-rest-spread": "^6.22.0", + "babel-preset-es2015-node6": "^0.4.0", + "babel-register": "^6.22.0", "chalk": "^1.1.3", - "prettyjson": "^1.1.3", - "winston": "^2.2.0" + "prettyjson": "^1.2.1", + "winston": "^2.3.1" } } diff --git a/src/ACR122Reader.js b/src/ACR122Reader.js new file mode 100644 index 0000000..291e3cd --- /dev/null +++ b/src/ACR122Reader.js @@ -0,0 +1,235 @@ +"use strict"; + +import Reader from './Reader'; +import { + ConnectError, + DisconnectError, + TransmitError, + ControlError, + AuthenticationError, + LoadAuthenticationKeyError, + ReadError, + WriteError, + GetUIDError, + CARD_NOT_CONNECTED, + OPERATION_FAILED, + UNKNOWN_ERROR, + FAILURE +} from './errors'; + + +class ACR122Reader extends Reader { + + async inAutoPoll() { + + const payload = [ + 0xD4, + 0x60, + 0xFF, // PollNr (0xFF = Endless polling) + 0x01, // Period (0x01 – 0x0F) indicates the polling period in units of 150 ms + 0x00 // Type 1 0x00 = Generic passive 106 kbps (ISO/IEC14443-4A, Mifare and DEP) + ]; + + // CMD: Direct Transmit (to inner PN532 chip InAutoPoll CMD) + const packet = new Buffer([ + 0xff, // Class + 0x00, // INS + 0x00, // P1 + 0x00, // P2 + payload.length, // Lc: Number of Bytes to send (Maximum 255 bytes) + ...payload + ]); + + console.log(packet); + + let response = null; + + try { + + response = await this.control(packet, 2); + + this.logger.info('response received', response); + + // Red OFF Green OFF 0x00 + // Red ON Green OFF 0x01 + // Red OFF Green ON 0x02 + // Red ON Green ON 0x03 + + console.log(response.slice(1)); + + + } catch (err) { + + throw err; + + } + + // const statusCode = response.readUInt16BE(0); + // + // if (statusCode !== 0x9000) { + // //throw new LoadAuthenticationKeyError(OPERATION_FAILED, `Load authentication key operation failed: Status code: ${statusCode}`); + // } + + } + + async led(led, blinking) { + + // P2: LED State Control (1 byte = 8 bits) + // format: + /* + +-----+----------------------------------+-------------------------------------+ + | Bit | Item | Description | + +-----+----------------------------------+-------------------------------------+ + | 0 | Final Red LED State | 1 = On; 0 = Off | + | 1 | Final Green LED State | 1 = On; 0 = Off | + | 2 | Red LED State Mask | 1 = Update the State; 0 = No change | + | 3 | Green LED State Mask | 1 = Update the State; 0 = No change | + | 4 | Initial Red LED Blinking State | 1 = On; 0 = Off | + | 5 | Initial Green LED Blinking State | 1 = On; 0 = Off | + | 6 | Red LED Blinking Mask | 1 = Blink
; 0 = Not Blink | + | 7 | Green LED Blinking Mask | 1 = Blink
; 0 = Not Blink | + +-----+----------------------------------+-------------------------------------+ + */ + + //const led = 0b00001111; + //const led = 0x50; + + // Data In: Blinking Duration Control (4 bytes) + // Byte 0: T1 Duration Initial Blinking State (Unit = 100 ms) + // Byte 1: T2 Duration Toggle Blinking State (Unit = 100 ms) + // Byte 2: Number of repetition + // Byte 3: Link to Buzzer + // - 00: The buzzer will not turn on + // - 01: The buzzer will turn on during the T1 Duration + // - 02: The buzzer will turn on during the T2 Duration + // - 03: The buzzer will turn on during the T1 and T2 Duration + + // const blinking = [ + // 0x00, + // 0x00, + // 0x00, + // 0x00 + // ]; + + + // CMD: Bi-Color LED and Buzzer Control + const packet = new Buffer([ + 0xff, // Class + 0x00, // INS + 0x40, // P1 + led, // P2: LED State Control + 0x04, // Lc + ...blinking, // Data In: Blinking Duration Control (4 bytes) + ]); + + console.log(packet); + + let response = null; + + try { + + response = await this.control(packet, 2); + + this.logger.info('response received', response); + + // Red OFF Green OFF 0x00 + // Red ON Green OFF 0x01 + // Red OFF Green ON 0x02 + // Red ON Green ON 0x03 + + console.log(response.slice(1)); + + + } catch (err) { + + throw err; + + } + + // const statusCode = response.readUInt16BE(0); + // + // if (statusCode !== 0x9000) { + // //throw new LoadAuthenticationKeyError(OPERATION_FAILED, `Load authentication key operation failed: Status code: ${statusCode}`); + // } + + } + + async setBuzzerOutput(enabled = true) { + + + // CMD: Set Buzzer Output Enable for Card Detection + const packet = new Buffer([ + 0xff, // Class + 0x00, // INS + 0x52, // P1 + enabled ? 0xff : 0x00, // P2: PollBuzzStatus + 0x00, // Le + ]); + + console.log(packet); + + let response = null; + + try { + + response = await this.control(packet, 2); + + this.logger.info('response received', response); + + + } catch (err) { + + throw err; + + } + + const statusCode = response.readUInt16BE(0); + + if (statusCode !== 0x9000) { + //throw new LoadAuthenticationKeyError(OPERATION_FAILED, `Load authentication key operation failed: Status code: ${statusCode}`); + } + + } + + async setPICC(picc) { + + // just enable Auto ATS Generation + // const picc = 0b01000000; + + // CMD: Set PICC Operating Parameter + const packet = new Buffer([ + 0xff, // Class + 0x00, // INS + 0x51, // P1 + picc, // P2: New PICC Operating Parameter + 0x00, // Le + ]); + + console.log(packet); + + let response = null; + + try { + + response = await this.control(packet, 1); + + this.logger.info('response received', response); + + + } catch (err) { + + throw err; + + } + + // const statusCode = response.readUInt16BE(0); + // + // if (statusCode !== 0x9000) { + // //throw new LoadAuthenticationKeyError(OPERATION_FAILED, `Load authentication key operation failed: Status code: ${statusCode}`); + // } + + } + +} + +export default ACR122Reader; diff --git a/src/NFC.js b/src/NFC.js index 23ae48c..d53d269 100644 --- a/src/NFC.js +++ b/src/NFC.js @@ -3,6 +3,7 @@ import pcsclite from '@pokusew/pcsclite'; import EventEmitter from 'events'; import Reader from './Reader'; +import ACR122Reader from './ACR122Reader'; export * from './Reader'; @@ -41,6 +42,17 @@ class NFC extends EventEmitter { this.logger.info('New reader detected', reader.name); + // create special object for ARC122U reader with commands specific to this reader + if (reader.name.toLowerCase().indexOf('acr122') !== -1) { + + const device = new ACR122Reader(reader, this.logger); + + this.emit('reader', device); + + return; + + } + const device = new Reader(reader, this.logger); this.emit('reader', device); diff --git a/src/Reader.js b/src/Reader.js index eba131f..f1e5644 100644 --- a/src/Reader.js +++ b/src/Reader.js @@ -5,6 +5,7 @@ import { ConnectError, DisconnectError, TransmitError, + ControlError, AuthenticationError, LoadAuthenticationKeyError, ReadError, @@ -23,12 +24,16 @@ export const TAG_ISO_14443_4 = 'TAG_ISO_14443_4'; export const KEY_TYPE_A = 0x60; export const KEY_TYPE_B = 0x61; +export const CONNECT_MODE_DIRECT = 'CONNECT_MODE_DIRECT'; +export const CONNECT_MODE_CARD = 'CONNECT_MODE_CARD'; + class Reader extends EventEmitter { reader = null; logger = null; + connection = null; card = null; autoProcessing = true; @@ -46,7 +51,7 @@ class Reader extends EventEmitter { let buffer = new Buffer(src.length); - for (var i = 0, j = src.length - 1; i <= j; ++i, --j) { + for (let i = 0, j = src.length - 1; i <= j; ++i, --j) { buffer[i] = src[j]; buffer[j] = src[i]; } @@ -147,7 +152,10 @@ class Reader extends EventEmitter { try { - await this.disconnect(); + this.card = null; + if (this.connection) { + await this.disconnect(); + } } catch (err) { @@ -205,28 +213,41 @@ class Reader extends EventEmitter { } - connect() { + connect(mode = CONNECT_MODE_CARD) { + + const modes = { + [CONNECT_MODE_DIRECT]: this.reader.SCARD_SHARE_DIRECT, + [CONNECT_MODE_CARD]: this.reader.SCARD_SHARE_SHARED, + }; + + if (!modes[mode]) { + throw new ConnectError('invalid_mode', 'Invalid mode') + } - this.logger.info('trying to connect'); + this.logger.info('trying to connect', mode, modes[mode]); return new Promise((resolve, reject) => { // connect card - this.reader.connect({ share_mode: this.reader.SCARD_SHARE_SHARED }, (err, protocol) => { + this.reader.connect({ + share_mode: modes[mode], + //protocol: this.reader.SCARD_PROTOCOL_UNDEFINED + }, (err, protocol) => { if (err) { - if (err) { - const error = new ConnectError(FAILURE, 'An error occurred while connecting.', err); - this.logger.error(error); - return reject(error); - } + const error = new ConnectError(FAILURE, 'An error occurred while connecting.', err); + this.logger.error(error); + return reject(error); } - this.card.protocol = protocol; + this.connection = { + type: modes[mode], + protocol: protocol + }; - this.logger.info('card connected', protocol); + this.logger.info('connected', this.connection); - return resolve(protocol); + return resolve(this.connection); }); @@ -236,11 +257,11 @@ class Reader extends EventEmitter { disconnect() { - if (!this.card) { - throw new DisconnectError(CARD_NOT_CONNECTED, 'Reader in not connected to any card.') + if (!this.connection) { + throw new DisconnectError('not_connected', 'Reader in not connected. No need for disconnecting.') } - this.logger.info('trying to disconnect card', this.card); + this.logger.info('trying to disconnect', this.connection); return new Promise((resolve, reject) => { @@ -253,9 +274,9 @@ class Reader extends EventEmitter { return reject(error); } - this.card = null; + this.connection = null; - this.logger.info('card disconnected'); + this.logger.info('disconnected'); return resolve(true); @@ -267,15 +288,15 @@ class Reader extends EventEmitter { transmit(data, responseMaxLength) { - if (!this.card || !this.card.protocol) { - throw new TransmitError(CARD_NOT_CONNECTED, 'No card or protocol available.'); + if (!this.card || !this.connection) { + throw new TransmitError(CARD_NOT_CONNECTED, 'No card or connection available.'); } return new Promise((resolve, reject) => { - //console.log('transmitting', data, responseMaxLength); + this.logger.log('transmitting', data, responseMaxLength); - this.reader.transmit(data, responseMaxLength, this.card.protocol, (err, response) => { + this.reader.transmit(data, responseMaxLength, this.connection.protocol, (err, response) => { if (err) { const error = new TransmitError(FAILURE, 'An error occurred while transmitting.', err); @@ -290,6 +311,31 @@ class Reader extends EventEmitter { } + control(data, responseMaxLength) { + + if (!this.connection) { + throw new ControlError('not_connected', 'No connection available.'); + } + + return new Promise((resolve, reject) => { + + this.logger.log('transmitting control', data, responseMaxLength); + + this.reader.control(data, this.reader.IOCTL_CCID_ESCAPE, responseMaxLength, (err, response) => { + + if (err) { + const error = new ControlError(FAILURE, 'An error occurred while transmitting control.', err); + return reject(error); + } + + return resolve(response); + + }); + + }); + + } + async loadAuthenticationKey(keyNumber, key) { if (!(keyNumber === 0 || keyNumber === 1)) { @@ -313,8 +359,6 @@ class Reader extends EventEmitter { ...keyData ]); - // console.log(packet); - let response = null; try { @@ -386,31 +430,31 @@ class Reader extends EventEmitter { } const packet = !obsolete ? ( - // CMD: Authentication - new Buffer([ - 0xff, // Class - 0x86, // INS - 0x00, // P1 - 0x00, // P2 - 0x05, // Lc - // Data In: Authenticate Data Bytes (5 bytes) - 0x01, // Byte 1: Version - 0x00, // Byte 2 - blockNumber, // Byte 3: Block Number - keyType, // Byte 4: Key Type - keyNumber, // Byte 5: Key Number - ]) - ) : ( - // CMD: Authentication (obsolete) - new Buffer([ - 0xff, // Class - 0x88, // INS - 0x00, // P1 - blockNumber, // P2: Block Number - keyType, // P3: Key Type - keyNumber // Data In: Key Number - ]) - ); + // CMD: Authentication + new Buffer([ + 0xff, // Class + 0x86, // INS + 0x00, // P1 + 0x00, // P2 + 0x05, // Lc + // Data In: Authenticate Data Bytes (5 bytes) + 0x01, // Byte 1: Version + 0x00, // Byte 2 + blockNumber, // Byte 3: Block Number + keyType, // Byte 4: Key Type + keyNumber, // Byte 5: Key Number + ]) + ) : ( + // CMD: Authentication (obsolete) + new Buffer([ + 0xff, // Class + 0x88, // INS + 0x00, // P1 + blockNumber, // P2: Block Number + keyType, // P3: Key Type + keyNumber // Data In: Key Number + ]) + ); let response = null; @@ -430,7 +474,7 @@ class Reader extends EventEmitter { const statusCode = response.readUInt16BE(0); if (statusCode !== 0x9000) { - //console.log('[authentication operation failed][request packet]', packet); + this.logger.error('[authentication operation failed][request packet]', packet); throw new AuthenticationError(OPERATION_FAILED, `Authentication operation failed: Status code: 0x${statusCode.toString(16)}`); } @@ -612,7 +656,7 @@ class Reader extends EventEmitter { // TODO: improve error handling and debugging async handle_Iso_14443_3_Tag() { - if (!this.card || !this.card.protocol) { + if (!this.card || !this.connection) { return false; } @@ -675,7 +719,7 @@ class Reader extends EventEmitter { // TODO: improve error handling and debugging async handle_Iso_14443_4_Tag() { - if (!this.card || !this.card.protocol) { + if (!this.card || !this.connection) { return false; } diff --git a/src/errors.js b/src/errors.js index b663ab9..05e93ec 100755 --- a/src/errors.js +++ b/src/errors.js @@ -43,6 +43,18 @@ export class TransmitError extends BaseError { } +export class ControlError extends BaseError { + + constructor(code, message, previousError) { + + super(code, message, previousError); + + this.name = 'ControlError'; + + } + +} + export class ReadError extends BaseError { constructor(code, message, previousError) { diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..fd7c032 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1400 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@pokusew/pcsclite@^0.4.15": + version "0.4.15" + resolved "https://registry.yarnpkg.com/@pokusew/pcsclite/-/pcsclite-0.4.15.tgz#469768723afdc5dcc87b677caaa38e928117fe4a" + dependencies: + bindings "^1.2.1" + nan "^2.5.1" + +abbrev@1: + version "1.0.9" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +anymatch@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" + dependencies: + arrify "^1.0.0" + micromatch "^2.1.5" + +aproba@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.0.4.tgz#2713680775e7614c8ba186c065d4e2e52d1072c0" + +are-we-there-yet@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz#80e470e95a084794fe1899262c5667c6e88de1b3" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.0 || ^1.1.13" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +arrify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws4@^1.2.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.5.0.tgz#0a29ffb79c31c9e712eeb087e8e7a64b4a56d755" + +babel-cli@^6.22.2: + version "6.22.2" + resolved "https://registry.yarnpkg.com/babel-cli/-/babel-cli-6.22.2.tgz#3f814c8acf52759082b8fedd9627f938936ab559" + dependencies: + babel-core "^6.22.1" + babel-polyfill "^6.22.0" + babel-register "^6.22.0" + babel-runtime "^6.22.0" + commander "^2.8.1" + convert-source-map "^1.1.0" + fs-readdir-recursive "^1.0.0" + glob "^7.0.0" + lodash "^4.2.0" + output-file-sync "^1.1.0" + path-is-absolute "^1.0.0" + slash "^1.0.0" + source-map "^0.5.0" + v8flags "^2.0.10" + optionalDependencies: + chokidar "^1.6.1" + +babel-code-frame@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" + dependencies: + chalk "^1.1.0" + esutils "^2.0.2" + js-tokens "^3.0.0" + +babel-core@^6.22.0, babel-core@^6.22.1: + version "6.22.1" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.22.1.tgz#9c5fd658ba1772d28d721f6d25d968fc7ae21648" + dependencies: + babel-code-frame "^6.22.0" + babel-generator "^6.22.0" + babel-helpers "^6.22.0" + babel-messages "^6.22.0" + babel-register "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-traverse "^6.22.1" + babel-types "^6.22.0" + babylon "^6.11.0" + convert-source-map "^1.1.0" + debug "^2.1.1" + json5 "^0.5.0" + lodash "^4.2.0" + minimatch "^3.0.2" + path-is-absolute "^1.0.0" + private "^0.1.6" + slash "^1.0.0" + source-map "^0.5.0" + +babel-generator@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.22.0.tgz#d642bf4961911a8adc7c692b0c9297f325cda805" + dependencies: + babel-messages "^6.22.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.2.0" + source-map "^0.5.0" + +babel-helper-call-delegate@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.22.0.tgz#119921b56120f17e9dae3f74b4f5cc7bcc1b37ef" + dependencies: + babel-helper-hoist-variables "^6.22.0" + babel-runtime "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" + +babel-helper-function-name@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.22.0.tgz#51f1bdc4bb89b15f57a9b249f33d742816dcbefc" + dependencies: + babel-helper-get-function-arity "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" + +babel-helper-get-function-arity@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.22.0.tgz#0beb464ad69dc7347410ac6ade9f03a50634f5ce" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.22.0" + +babel-helper-hoist-variables@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.22.0.tgz#3eacbf731d80705845dd2e9718f600cfb9b4ba72" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.22.0" + +babel-helper-remap-async-to-generator@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.22.0.tgz#2186ae73278ed03b8b15ced089609da981053383" + dependencies: + babel-helper-function-name "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" + +babel-helpers@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.22.0.tgz#d275f55f2252b8101bff07bc0c556deda657392c" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.22.0" + +babel-messages@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.22.0.tgz#36066a214f1217e4ed4164867669ecb39e3ea575" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + +babel-plugin-syntax-class-properties@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" + +babel-plugin-syntax-object-rest-spread@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + +babel-plugin-transform-async-to-generator@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.22.0.tgz#194b6938ec195ad36efc4c33a971acf00d8cd35e" + dependencies: + babel-helper-remap-async-to-generator "^6.22.0" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-class-properties@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.22.0.tgz#aa78f8134495c7de06c097118ba061844e1dc1d8" + dependencies: + babel-helper-function-name "^6.22.0" + babel-plugin-syntax-class-properties "^6.8.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + +babel-plugin-transform-es2015-destructuring@^6.6.5: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.22.0.tgz#8e0af2f885a0b2cf999d47c4c1dd23ce88cfa4c6" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.5.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.22.0.tgz#f5fcc8b09093f9a23c76ac3d9e392c3ec4b77104" + dependencies: + babel-helper-function-name "^6.22.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" + +babel-plugin-transform-es2015-modules-commonjs@^6.7.4: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.22.0.tgz#6ca04e22b8e214fb50169730657e7a07dc941145" + dependencies: + babel-plugin-transform-strict-mode "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-types "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.8.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.22.0.tgz#57076069232019094f27da8c68bb7162fe208dbb" + dependencies: + babel-helper-call-delegate "^6.22.0" + babel-helper-get-function-arity "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" + +babel-plugin-transform-object-rest-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.22.0.tgz#1d419b55e68d2e4f64a5ff3373bd67d73c8e83bc" + dependencies: + babel-plugin-syntax-object-rest-spread "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-strict-mode@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.22.0.tgz#e008df01340fdc87e959da65991b7e05970c8c7c" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.22.0" + +babel-polyfill@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.22.0.tgz#1ac99ebdcc6ba4db1e2618c387b2084a82154a3b" + dependencies: + babel-runtime "^6.22.0" + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + +babel-preset-es2015-node6@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/babel-preset-es2015-node6/-/babel-preset-es2015-node6-0.4.0.tgz#f8893f81b6533747924c657348867bd63b4f9dc2" + dependencies: + babel-plugin-transform-es2015-destructuring "^6.6.5" + babel-plugin-transform-es2015-function-name "^6.5.0" + babel-plugin-transform-es2015-modules-commonjs "^6.7.4" + babel-plugin-transform-es2015-parameters "^6.8.0" + +babel-register@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.22.0.tgz#a61dd83975f9ca4a9e7d6eff3059494cd5ea4c63" + dependencies: + babel-core "^6.22.0" + babel-runtime "^6.22.0" + core-js "^2.4.0" + home-or-tmp "^2.0.0" + lodash "^4.2.0" + mkdirp "^0.5.1" + source-map-support "^0.4.2" + +babel-runtime@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.22.0.tgz#1cf8b4ac67c77a4ddb0db2ae1f74de52ac4ca611" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + +babel-template@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.22.0.tgz#403d110905a4626b317a2a1fcb8f3b73204b2edb" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" + babylon "^6.11.0" + lodash "^4.2.0" + +babel-traverse@^6.22.0, babel-traverse@^6.22.1: + version "6.22.1" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.22.1.tgz#3b95cd6b7427d6f1f757704908f2fc9748a5f59f" + dependencies: + babel-code-frame "^6.22.0" + babel-messages "^6.22.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" + babylon "^6.15.0" + debug "^2.2.0" + globals "^9.0.0" + invariant "^2.2.0" + lodash "^4.2.0" + +babel-types@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.22.0.tgz#2a447e8d0ea25d2512409e4175479fd78cc8b1db" + dependencies: + babel-runtime "^6.22.0" + esutils "^2.0.2" + lodash "^4.2.0" + to-fast-properties "^1.0.1" + +babylon@^6.11.0, babylon@^6.15.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.15.0.tgz#ba65cfa1a80e1759b0e89fb562e27dccae70348e" + +balanced-match@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + +bcrypt-pbkdf@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.0.tgz#3ca76b85241c7170bf7d9703e7b9aa74630040d4" + dependencies: + tweetnacl "^0.14.3" + +binary-extensions@^1.0.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" + +bindings@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +brace-expansion@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" + dependencies: + balanced-match "^0.4.1" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +buffer-shims@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" + +caseless@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + +chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chokidar@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +colors@1.0.x: + version "1.0.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + +colors@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +commander@^2.8.1, commander@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + dependencies: + graceful-readlink ">= 1.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +convert-source-map@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.3.0.tgz#e9f3e9c6e2728efc2676696a70eb382f73106a67" + +core-js@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +cycle@1.0.x: + version "1.0.3" + resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +debug@^2.1.1, debug@^2.2.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" + dependencies: + ms "0.7.2" + +debug@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + dependencies: + ms "0.7.1" + +deep-extend@~0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +escape-string-regexp@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +extend@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extsprintf@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" + +eyes@0.1.x: + version "0.1.8" + resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" + +filename-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" + +fill-range@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^1.1.3" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +for-in@^0.1.5: + version "0.1.6" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8" + +for-own@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.4.tgz#0149b41a39088c7515f51ebe1c1386d45f935072" + dependencies: + for-in "^0.1.5" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.2.tgz#89c3534008b97eada4cbb157d58f6f5df025eae4" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +fs-readdir-recursive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz#8cd1745c8b4f8a29c8caec392476921ba195f560" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.0.17" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.0.17.tgz#8537f3f12272678765b4fd6528c0f1f66f8f4558" + dependencies: + nan "^2.3.0" + node-pre-gyp "^0.6.29" + +fstream-ignore@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream@^1.0.0, fstream@^1.0.2, fstream@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.10.tgz#604e8a92fe26ffd9f6fae30399d4984e1ab22822" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +gauge@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.2.tgz#15cecc31b02d05345a5d6b0e171cdb3ad2307774" + dependencies: + 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" + supports-color "^0.2.0" + wide-align "^1.1.0" + +generate-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + dependencies: + is-property "^1.0.0" + +getpass@^0.1.1: + version "0.1.6" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob@^7.0.0, glob@^7.0.5: + version "7.1.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^9.0.0: + version "9.14.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.14.0.tgz#8859936af0038741263053b39d0e76ca241e4034" + +graceful-fs@^4.1.2, graceful-fs@^4.1.4: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + +har-validator@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + dependencies: + chalk "^1.1.1" + commander "^2.9.0" + is-my-json-valid "^2.12.4" + pinkie-promise "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +ini@~1.3.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" + +invariant@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + dependencies: + loose-envify "^1.0.0" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.0.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" + +is-dotfile@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-my-json-valid@^2.12.4: + version "2.15.0" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz#936edda3ca3c211fd98f3b2d3e08da43f7b2915b" + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + +is-number@^2.0.2, is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-property@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isstream@0.1.x, isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +jodid25519@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" + dependencies: + jsbn "~0.1.0" + +js-tokens@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.0.tgz#a2f2a969caae142fb3cd56228358c89366957bd1" + +jsbn@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json5@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +jsonpointer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + +jsprim@^1.2.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.3.1.tgz#2a7256f70412a29ee3670aaca625994c4dcff252" + dependencies: + extsprintf "1.0.2" + json-schema "0.2.3" + verror "1.3.6" + +kind-of@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" + dependencies: + is-buffer "^1.0.2" + +lodash@^4.2.0: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +loose-envify@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + +micromatch@^2.1.5: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +mime-db@~1.26.0: + version "1.26.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.26.0.tgz#eaffcd0e4fc6935cf8134da246e2e6c35305adff" + +mime-types@^2.1.12, mime-types@~2.1.7: + version "2.1.14" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.14.tgz#f7ef7d97583fcaf3b7d282b6f8b5679dab1e94ee" + dependencies: + mime-db "~1.26.0" + +minimatch@^3.0.0, minimatch@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" + dependencies: + brace-expansion "^1.0.0" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +"mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +ms@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + +ms@0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" + +nan@^2.3.0, nan@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2" + +node-pre-gyp@^0.6.29: + version "0.6.32" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.32.tgz#fc452b376e7319b3d255f5f34853ef6fd8fe1fd5" + dependencies: + mkdirp "~0.5.1" + nopt "~3.0.6" + npmlog "^4.0.1" + rc "~1.1.6" + request "^2.79.0" + rimraf "~2.5.4" + semver "~5.3.0" + tar "~2.2.1" + tar-pack "~3.3.0" + +nopt@~3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + +normalize-path@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" + +npmlog@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.1" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +oauth-sign@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +once@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" + dependencies: + wrappy "1" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-tmpdir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +output-file-sync@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/output-file-sync/-/output-file-sync-1.1.2.tgz#d0a33eefe61a205facb90092e826598d5245ce76" + dependencies: + graceful-fs "^4.1.4" + mkdirp "^0.5.1" + object-assign "^4.1.0" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +prettyjson@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prettyjson/-/prettyjson-1.2.1.tgz#fcffab41d19cab4dfae5e575e64246619b12d289" + dependencies: + colors "^1.1.2" + minimist "^1.2.0" + +private@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.6.tgz#55c6a976d0f9bafb9924851350fe47b9b5fbb7c1" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +qs@~6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" + +randomatic@^1.1.3: + version "1.1.6" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb" + dependencies: + is-number "^2.0.2" + kind-of "^3.0.2" + +rc@~1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.6.tgz#43651b76b6ae53b5c802f1151fa3fc3b059969c9" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~1.0.4" + +"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e" + dependencies: + buffer-shims "^1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + +readable-stream@~2.1.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" + dependencies: + buffer-shims "^1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +regenerator-runtime@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.1.tgz#257f41961ce44558b18f7814af48c17559f9faeb" + +regex-cache@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" + dependencies: + is-equal-shallow "^0.1.3" + is-primitive "^2.0.0" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +request@^2.79.0: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + qs "~6.3.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + uuid "^3.0.0" + +rimraf@2, rimraf@~2.5.1, rimraf@~2.5.4: + version "2.5.4" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" + dependencies: + glob "^7.0.5" + +semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +source-map-support@^0.4.2: + version "0.4.11" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.11.tgz#647f939978b38535909530885303daf23279f322" + dependencies: + source-map "^0.5.3" + +source-map@^0.5.0, source-map@^0.5.3: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + +sshpk@^1.7.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.10.2.tgz#d5a804ce22695515638e798dbe23273de070a5fa" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jodid25519 "^1.0.0" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +stack-trace@0.0.x: + version "0.0.9" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.9.tgz#a8f6eaeca90674c333e7c43953f275b451510695" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +stringstream@~0.0.4: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-json-comments@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" + +supports-color@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +tar-pack@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.3.0.tgz#30931816418f55afc4d21775afdd6720cee45dae" + dependencies: + debug "~2.2.0" + fstream "~1.0.10" + fstream-ignore "~1.0.5" + once "~1.3.3" + readable-stream "~2.1.4" + rimraf "~2.5.1" + tar "~2.2.1" + uid-number "~0.0.6" + +tar@~2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +to-fast-properties@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" + +tough-cookie@~2.3.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" + dependencies: + punycode "^1.4.1" + +tunnel-agent@~0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +uid-number@~0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + +user-home@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +uuid@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" + +v8flags@^2.0.10: + version "2.0.11" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.0.11.tgz#bca8f30f0d6d60612cc2c00641e6962d42ae6881" + dependencies: + user-home "^1.1.1" + +verror@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" + dependencies: + extsprintf "1.0.2" + +wide-align@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad" + dependencies: + string-width "^1.0.1" + +winston@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/winston/-/winston-2.3.1.tgz#0b48420d978c01804cf0230b648861598225a119" + dependencies: + async "~1.0.0" + colors "1.0.x" + cycle "1.0.x" + eyes "0.1.x" + isstream "0.1.x" + stack-trace "0.0.x" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +xtend@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"