├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── scratch-link-1.png ├── scratch-link-2.png ├── src ├── index.js └── session │ ├── ble.js │ └── session.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Micircle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scratch-node-link 2 | 使用 nodejs 模拟 scratch-link 实现硬件连接通讯 3 | 4 | ### 使用方法 5 | ```bash 6 | git clone https://github.com/Micircle/scratch-node-link.git 7 | npm i 8 | npm start 9 | ``` 10 | 将 ```scratch-gui/node_modules/scratch-vm/src/util/scratch-link-websocket.js``` 中 11 | 12 | ```javascript 13 | this._ws = new WebSocket('wss://device-manager.scratch.mit.edu:20110/scratch/ble'); 14 | ``` 15 | 改为 16 | ```javascript 17 | this._ws = new WebSocket('ws://127.0.0.1:20110/scratch/ble'); 18 | ``` 19 | 就可以连接 microbit 了 20 | 21 | Windows 如果 npm 安装依赖失败,请查看:https://github.com/noble/noble 的说明 22 | 23 | ### 以 microbit 扩展为例,了解 Scratch 硬件通讯流程 24 | 想知道 Scratch 3.0 怎么使用 micro:bit 扩展,请查看 microbit 扩展的帮助页 https://scratch.mit.edu/microbit 。本文是对 Scratch 硬件连接通讯过程的分析。 25 | 26 | 在浏览器中运行的 Scratch,无法直接与硬件设备通讯(除非使用实验中的 Bluetooth 等功能),需要一个本地服务来帮助它来实现,这就是 Scratch Link 的作用。Scratch Link 的主要功能就是与 Scratch 通讯、与硬件设备通讯。 27 | 28 | 29 | 30 | Scratch Link 会开启一个本地 WebSocket 服务器,Scratch 通过 WebSocket 与 Scratch Link 进行连接通讯,使用 WebScoket 服务端和客户端都可以主动推送消息,想要详细了解的可以查看阮一峰老师的 《WebSocket 教程》[http://www.ruanyifeng.com/blog/2017/05/websocket.html]。 31 | 32 | Scratch Link 会监听 Scratch 发来的连接请求,根据请求的路由来与不同的设备进行通讯,目前支持两个路由 scratch/ble 和 scrattch/bt,分别对应低功耗蓝牙和蓝牙。 33 | Scratch Link 在接收到 Scratch 的连接请求后,双方就会建立起 socket 通道开始通讯,Scratch 会向 Scratch Link 发送命令(command) 或请求(request)如:检测设备、连接设备、向设备写入数据、读取设备数据、订阅设备信息、取消订阅、断开设备等。Scratch 也会响应(response)Scratch Link 发来的命令请求如:发现设备、设备数据更新、设备断开等。 34 | 35 | 以 microbit 为例,流程如下: 36 | 1. Scratch 会使用路由 scratch/ble 向 Scratch Link 发起连接请求。两者握手成功后建立起 WebSocket 连接。 37 | 2. Scratch Link 根据路由选择开启与低功耗设备通讯的会话 BLE Session。 38 | 3. Scratch 发起检测 microbit 设备的命令。 39 | 4. Scratch Link 接收到命令,BLE Session 开始检测设备。 40 | 5. BLE Session 发现设备,通过 WebSocket 向 Scratch 发送检测到设备的信息。 41 | 6. Scratch 接收到发现的设备,开始发送连接请求。 42 | 7. Scratch Link 接收到请求,BLE Session 开始连接设备。 43 | 8. BLE Session 连接成功,并告知 Scratch。 44 | 9. Scratch 得知连接成功,发起读取或写入请求。 45 | 10. Scratch Link 接收到请求,BLE Session 开始读取或写入数据到设备,并订阅设备的信息,在接收到信息时发送给 Scratch。 46 | 11. 断开设备时,关闭 BLE Session,关闭 WebSocket 47 | 48 | 49 | 50 | 详细实现可以查看使用 nodejs 的代码实现。 51 | 52 | 除了 ble 连接外,也可以用同样的方式实现串口 serialport 的连接通讯。 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scratch-node-link", 3 | "version": "0.1.0", 4 | "description": "使用 nodejs 模拟 scratch-link 实现硬件连接", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "start": "node src/index.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Micircle/scratch-node-link.git" 12 | }, 13 | "keywords": [ 14 | "scratch", 15 | "link", 16 | "microbit", 17 | "nodejs", 18 | "scratch3.0", 19 | "ble" 20 | ], 21 | "author": "KaneShao", 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/Micircle/scratch-node-link/issues" 25 | }, 26 | "homepage": "https://github.com/Micircle/scratch-node-link#readme", 27 | "dependencies": { 28 | "noble-mac": "https://github.com/Timeular/noble-mac.git", 29 | "ws": "^7.1.1" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /scratch-link-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Micircle/scratch-node-link/4f837d45faebbf5e97f61d78d00d27ebc5f5c035/scratch-link-1.png -------------------------------------------------------------------------------- /scratch-link-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Micircle/scratch-node-link/4f837d45faebbf5e97f61d78d00d27ebc5f5c035/scratch-link-2.png -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const url = require('url'); 3 | const {Server} = require('ws'); 4 | 5 | const SocketPort = 20110; 6 | const ROUTERS = { 7 | '/scratch/ble': require('./session/ble') 8 | }; 9 | 10 | const httpServer = http.createServer() 11 | .listen(SocketPort, '127.0.0.1', () => { 12 | console.log('socket server listend: ', `http://127.0.0.1:${SocketPort}`); 13 | }); 14 | const socketServer = new Server({server: httpServer}); 15 | 16 | socketServer.on('connection', (socket, request) => { 17 | const {pathname} = url.parse(request.url); 18 | const Session = ROUTERS[pathname]; 19 | let session; 20 | if (Session) { 21 | session = new Session(socket); 22 | } else { 23 | return socket.close(); 24 | } 25 | const dispose = () => { 26 | if (session) { 27 | session.dispose(); 28 | session = null; 29 | } 30 | }; 31 | socket.on('close', dispose); 32 | socket.on('error', dispose); 33 | }); -------------------------------------------------------------------------------- /src/session/ble.js: -------------------------------------------------------------------------------- 1 | const noble = require('noble-mac'); 2 | const Session = require('./session'); 3 | 4 | const getUUID = id => { 5 | if (typeof id === 'number') return id.toString(16); 6 | if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id)) { 7 | return id.split('-').join(''); 8 | } 9 | return id; 10 | }; 11 | 12 | class BLESession extends Session { 13 | constructor (socket) { 14 | super(socket); 15 | this._type = 'ble'; 16 | this.peripheral = null; 17 | this.services = null; 18 | this.characteristics = {}; 19 | this.notifyCharacteristics = {}; 20 | this.scanningTimeId = null; 21 | this.reportedPeripherals = {}; 22 | this.discoverListener = null; 23 | } 24 | 25 | async didReceiveCall (method, params, completion) { 26 | switch (method) { 27 | case 'discover': 28 | this.discover(params); 29 | completion(null, null); 30 | break; 31 | case 'connect': 32 | await this.connect(params); 33 | completion(null, null); 34 | break; 35 | case 'disconnect': 36 | await this.disconnect(params); 37 | completion(null, null); 38 | break; 39 | case 'write': 40 | completion(await this.write(params), null); 41 | this.repairNotifyAfterWrite(); 42 | break; 43 | case 'read': 44 | completion(await this.read(params), null); 45 | case 'startNotifications': 46 | await this.startNotifications(params); 47 | completion(null, null); 48 | break; 49 | case 'stopNotifications': 50 | await this.stopNotifications(params); 51 | completion(null, null); 52 | break; 53 | case 'getServices': 54 | completion((this.services || []).map(service => service.uuid), null); 55 | break; 56 | case 'pingMe': 57 | completion('willPing', null); 58 | this.sendRemoteRequest('ping', null, (result, error) => { 59 | console.log(`Got result from ping: ${result}`); 60 | }); 61 | break; 62 | default: 63 | throw new Error(`Method not found`); 64 | } 65 | } 66 | 67 | discover (params) { 68 | if (this.services) { 69 | throw new Error('cannot discover when connected'); 70 | } 71 | const {filters} = params; 72 | // if (!Array.isArray(filters) || filters.length < 1) { 73 | // throw new Error('discovery request must include filters'); 74 | // } 75 | // filters.forEach(item => { 76 | // const {services} = item; 77 | // if (!Array.isArray(services) || services.length < 1) { 78 | // throw new Error(`filter contains empty or invalid services list: ${item}`); 79 | // } 80 | // }); 81 | if (this.scanningTimeId) { 82 | clearTimeout(this.scanningTimeId); 83 | } 84 | this.reportedPeripherals = {}; 85 | noble.startScanning([], true); 86 | this.discoverListener = peripheral => { 87 | this.onAdvertisementReceived(peripheral, filters); 88 | }; 89 | noble.on('discover', this.discoverListener); 90 | } 91 | 92 | onAdvertisementReceived (peripheral, filters) { 93 | const {advertisement} = peripheral; 94 | if (advertisement) { 95 | const finded = (filters || []).find(filter => { 96 | const {name, namePrefix, services, manufacturerData} = filter; 97 | if (name && name !== advertisement.localName) return false; 98 | if (namePrefix && advertisement.localName.indexOf(namePrefix) !== 0) return false; 99 | if (services && !services.every(service => { 100 | if (!advertisement.serviceUuids) return false; 101 | return advertisement.serviceUuids.indexOf(getUUID(service)) !== -1; 102 | })) return false; 103 | if (manufacturerData && advertisement.manufacturerData) { 104 | if (manufacturerData.length !== advertisement.manufacturerData.length) { 105 | return false 106 | } 107 | if (!manufacturerData.every((data, i) => dvertisement.manufacturerData[i] === data)) { 108 | return false; 109 | } 110 | } 111 | return !!advertisement.localName; 112 | }); 113 | if (finded) { 114 | this.reportedPeripherals[peripheral.id] = peripheral; 115 | this.sendRemoteRequest('didDiscoverPeripheral', { 116 | peripheralId: peripheral.id, 117 | name: advertisement.localName, 118 | rssi: peripheral.rssi 119 | }); 120 | if (!this.scanningTimeId) { 121 | this.scanningTimeId = setTimeout(() => { 122 | this.scanningTimeId = null; 123 | noble.stopScanning(); 124 | if (this.discoverListener) { 125 | noble.removeListener('discover', this.discoverListener); 126 | } 127 | }, 1000); 128 | } 129 | } 130 | } 131 | } 132 | 133 | connect (params) { 134 | return new Promise((resolve, reject) => { 135 | if (this.peripheral && this.peripheral.state === 'connected') { 136 | return reject(new Error('already connected to peripheral')); 137 | } 138 | const {peripheralId} = params; 139 | const peripheral = this.reportedPeripherals[peripheralId]; 140 | if (!peripheral) { 141 | return reject(new Error(`invalid peripheral ID: ${peripheralId}`)); 142 | } 143 | if (this.scanningTimeId) { 144 | clearTimeout(this.scanningTimeId); 145 | this.scanningTimeId = null; 146 | noble.stopScanning(); 147 | } 148 | try { 149 | peripheral.connect(error => { 150 | if (error) { 151 | return reject(new Error(error)); 152 | } 153 | peripheral.discoverAllServicesAndCharacteristics((err, services) => { 154 | if (err) { 155 | return reject(new Error(error)); 156 | } 157 | this.services = services; 158 | this.peripheral = peripheral; 159 | resolve(); 160 | }); 161 | }); 162 | peripheral.on('disconnect', (err) => { 163 | this.disconnect(); 164 | }); 165 | } catch (err) { 166 | reject(err); 167 | } 168 | }) 169 | } 170 | 171 | bleWriteData (characteristic, withResponse, data) { 172 | return new Promise((resolve, reject) => { 173 | characteristic.write(data, !withResponse, (err) => { 174 | if (err) return reject(err); 175 | resolve(); 176 | }); 177 | }) 178 | } 179 | 180 | async write (params) { 181 | try { 182 | const {message, encoding, withResponse} = params; 183 | const buffer = new Buffer(message, encoding); 184 | const characteristic = await this.getEndpoint('write request', params, 'write'); 185 | for (let i = 0; i < buffer.length; i += 20) { 186 | await this.bleWriteData(characteristic, withResponse, buffer.slice(i, 20)); 187 | } 188 | return buffer.length; 189 | } catch (err) { 190 | return new Error(`Error while attempting to write: ${err.message}`); 191 | } 192 | } 193 | 194 | bleReadData (characteristic, encoding = 'base64') { 195 | return new Promise((resolve, reject) => { 196 | characteristic.read((err, data) => { 197 | if (err) { 198 | return reject(err); 199 | } 200 | resolve(data.toString(encoding)); 201 | }); 202 | }); 203 | } 204 | 205 | async read (params) { 206 | try { 207 | const characteristic = await this.getEndpoint('read request', params, 'read'); 208 | const readedData = await this.bleReadData(characteristic); 209 | const {startNotifications} = params; 210 | if (startNotifications) { 211 | await this.startNotifications(params, characteristic); 212 | } 213 | return readedData; 214 | } catch (err) { 215 | console.log('Error while attempting to read: ', err); 216 | return new Error(`Error while attempting to read: ${err.message}`); 217 | } 218 | } 219 | 220 | async startNotifications (params, characteristic) { 221 | let uuid; 222 | if (!characteristic || characteristic.properties.indexOf('notify') === -1) { 223 | characteristic = await this.getEndpoint('startNotifications request', params, 'notify'); 224 | } 225 | uuid = getUUID(characteristic.uuid); 226 | if (!this.notifyCharacteristics[uuid]) { 227 | this.notifyCharacteristics[uuid] = characteristic; 228 | characteristic.subscribe(); 229 | } 230 | if (!characteristic._events || !characteristic._events['data']) { 231 | characteristic.on('data', (data) => { 232 | this.onValueChanged(characteristic, data); 233 | }); 234 | } 235 | } 236 | 237 | async stopNotifications (params) { 238 | console.log('stopNotifications !!!') 239 | const characteristic = await this.getEndpoint('stopNotifications request', params, 'notify'); 240 | characteristic.unsubscribe(); 241 | characteristic.removeAllListeners('data'); 242 | delete this.notifyCharacteristics[getUUID(characteristic.uuid)]; 243 | } 244 | 245 | notify (characteristic, notify) { 246 | return new Promise((resolve, reject) => { 247 | characteristic.notify(notify, err => { 248 | if (err) return reject(err); 249 | resolve(); 250 | }) 251 | }) 252 | } 253 | 254 | // noble bug: 当 write 之后, characteristic 对象会发生变化 255 | repairNotifyAfterWrite () { 256 | for (const id in this.notifyCharacteristics) { 257 | const characteristic = this.notifyCharacteristics[id]; 258 | const {_peripheralId, _serviceUuid, uuid} = characteristic; 259 | const currentCharacteristic = noble._characteristics[_peripheralId][_serviceUuid][uuid]; 260 | if (characteristic !== currentCharacteristic) { 261 | currentCharacteristic._events = characteristic._events; 262 | this.notifyCharacteristics[id] = currentCharacteristic; 263 | } 264 | } 265 | } 266 | 267 | async stopAllNotifications () { 268 | for (const id in this.notifyCharacteristics) { 269 | await this.notify(this.notifyCharacteristics[id], false); 270 | this.notifyCharacteristics[id].removeAllListeners('data'); 271 | } 272 | } 273 | 274 | onValueChanged (characteristic, data) { 275 | const params = { 276 | serviceId: characteristic._serviceUuid, 277 | characteristicId: characteristic.uuid, 278 | encoding: 'base64', 279 | message: data.toString('base64') 280 | }; 281 | this.sendRemoteRequest('characteristicDidChange', params); 282 | } 283 | 284 | getEndpoint (errorText, params, type) { 285 | return new Promise((resolve, reject) => { 286 | if (!this.peripheral || this.peripheral.state !== 'connected') { 287 | return reject(`Peripheral is not connected for ${errorText}`); 288 | } 289 | let service; 290 | let {serviceId, characteristicId} = params; 291 | characteristicId = getUUID(characteristicId); 292 | if (this.characteristics[characteristicId]) { 293 | return resolve(this.characteristics[characteristicId]); 294 | } 295 | if (serviceId) { 296 | serviceId = getUUID(serviceId); 297 | service = this.services.find(item => item.uuid === serviceId); 298 | } else { 299 | service = this.services[0]; 300 | serviceUuid = service.uuid; 301 | } 302 | if (!service) { 303 | reject(`Could not determine service UUID for ${errorText}`); 304 | } 305 | service.discoverCharacteristics([characteristicId], (err, characteristics) => { 306 | if (err) { 307 | console.warn(err); 308 | return reject(`could not find characteristic ${characteristicId} on service ${serviceUuid}`); 309 | } 310 | const characteristic = characteristics.find(item => item.properties.includes(type)); 311 | if (characteristic) { 312 | this.characteristics[characteristicId] = characteristic; 313 | resolve(characteristic); 314 | } else { 315 | reject(`failed to collect ${type} characteristic from service`); 316 | } 317 | }); 318 | }); 319 | } 320 | 321 | disconnect () { 322 | if (this.peripheral && this.peripheral.state === 'connected') { 323 | this.peripheral.disconnect(); 324 | } 325 | } 326 | 327 | dispose () { 328 | this.disconnect(); 329 | super.dispose(); 330 | this.stopAllNotifications(); 331 | this.socket = null; 332 | this.peripheral = null; 333 | this.services = null; 334 | this.characteristics = null; 335 | this.scanningTimeId = null; 336 | this.reportedPeripherals = null; 337 | this.notifyCharacteristics = null; 338 | if (this.discoverListener) { 339 | noble.removeListener('discover', this.discoverListener); 340 | this.discoverListener = null; 341 | } 342 | } 343 | } 344 | 345 | module.exports = BLESession; -------------------------------------------------------------------------------- /src/session/session.js: -------------------------------------------------------------------------------- 1 | class Session { 2 | constructor (socket) { 3 | this._socket = socket; 4 | this._nextId = 0; 5 | this._type = ''; 6 | this._completionHandlers = {}; 7 | this.onMessage = this.onMessage.bind(this); 8 | this.dispose = this.dispose.bind(this); 9 | this._socket.addListener('message', this.onMessage); 10 | } 11 | 12 | dispose() { 13 | if (this._socket) { 14 | this._socket.removeListener('message', this.onMessage); 15 | if (this._socket.readyState === this._socket.OPEN) { 16 | this._socket.close(); 17 | } 18 | } 19 | this._socket = null; 20 | this._completionHandlers = null; 21 | } 22 | 23 | getNextId () { 24 | return this._nextId ++; 25 | } 26 | 27 | makeResponse (id, result, error) { 28 | const response = { 29 | id, 30 | jsonrpc: '2.0', 31 | }; 32 | if (error) { 33 | response.error = error; 34 | } else { 35 | response.result = result; 36 | } 37 | return response; 38 | } 39 | 40 | onMessage (message) { 41 | this.didReceiveMessage(message, response => { 42 | if (this._socket) { 43 | this._socket.send(response); 44 | } 45 | }); 46 | } 47 | 48 | onBinary (messageBytes) { 49 | 50 | } 51 | 52 | didReceiveMessage (message, sendResponseText) { 53 | const json = JSON.parse(message); 54 | const sendResponseInternal = (result, error) => { 55 | const response = this.makeResponse(json.id, result, error); 56 | sendResponseText(JSON.stringify(response)); 57 | }; 58 | const sendResponse = (result, error) => { 59 | try { 60 | sendResponseInternal(result, error); 61 | } catch (err1) { 62 | sendResponseInternal(null, 'Could not encode response'); 63 | } 64 | } 65 | try { 66 | if (json.jsonrpc !== '2.0') { 67 | throw new Error('unrecognized JSON-RPC version string'); 68 | } 69 | if (json.method) { 70 | this.didReceiveRequest(json, (result, err) => sendResponse(result, err)); 71 | } else if (json.result || json.error) { 72 | this.didReceiveResponse(json); 73 | } else { 74 | throw new Error('message is neither request nor response'); 75 | } 76 | } catch (err) { 77 | sendResponse(null, err); 78 | } 79 | } 80 | 81 | didReceiveRequest (request, sendResult) { 82 | const {method, params} = request; 83 | if (typeof method !== 'string') { 84 | throw new Error('methon value missing or not a string'); 85 | } 86 | this.didReceiveCall(method, params || {}, sendResult); 87 | } 88 | 89 | didReceiveResponse (response) { 90 | const {id, error, result} = response; 91 | if (!id) { 92 | throw new Error('esponse ID value missing or wrong type'); 93 | } 94 | const completionHandler = this._completionHandlers[id]; 95 | if (!completionHandler) { 96 | throw new Error('response ID does not correspond to any open request'); 97 | } 98 | try { 99 | if (error) { 100 | completionHandler(null, error); 101 | } else { 102 | completionHandler(result, null); 103 | } 104 | } catch (err) { 105 | throw new Error(`exception encountered while handling response ${id}`); 106 | } 107 | } 108 | 109 | didReceiveCall (method, params, resultHandler) { 110 | // 被复写 111 | } 112 | 113 | sendRemoteRequest (method, params, completion) { 114 | const request = { 115 | jsonrpc: '2.0', 116 | method 117 | }; 118 | if (params) { 119 | request.params = params 120 | } 121 | if (completion) { 122 | const requestId = this.getNextId(); 123 | request.id = requestId; 124 | this._completionHandlers[requestId] = completion; 125 | } 126 | try { 127 | this._socket.send(JSON.stringify(request)); 128 | } catch (err) { 129 | console.log(`Error serializing or sending request: ${err}`); 130 | console.log(`Request was: ${request}`); 131 | } 132 | } 133 | } 134 | 135 | module.exports = Session; -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | abbrev@1: 6 | version "1.1.1" 7 | resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" 8 | 9 | ansi-regex@^2.0.0: 10 | version "2.1.1" 11 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 12 | 13 | ansi-regex@^3.0.0: 14 | version "3.0.0" 15 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 16 | 17 | aproba@^1.0.3: 18 | version "1.2.0" 19 | resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" 20 | 21 | are-we-there-yet@~1.1.2: 22 | version "1.1.5" 23 | resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" 24 | dependencies: 25 | delegates "^1.0.0" 26 | readable-stream "^2.0.6" 27 | 28 | async-limiter@^1.0.0: 29 | version "1.0.1" 30 | resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" 31 | 32 | balanced-match@^1.0.0: 33 | version "1.0.0" 34 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 35 | 36 | bindings@^1.4.0: 37 | version "1.5.0" 38 | resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" 39 | dependencies: 40 | file-uri-to-path "1.0.0" 41 | 42 | bl@^1.0.0: 43 | version "1.2.2" 44 | resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" 45 | dependencies: 46 | readable-stream "^2.3.5" 47 | safe-buffer "^5.1.1" 48 | 49 | bluetooth-hci-socket@^0.5.1: 50 | version "0.5.1" 51 | resolved "https://registry.yarnpkg.com/bluetooth-hci-socket/-/bluetooth-hci-socket-0.5.1.tgz#efbe21524fc1cf5d3fae5d51365d561d4abbed0b" 52 | dependencies: 53 | debug "^2.2.0" 54 | nan "^2.0.5" 55 | optionalDependencies: 56 | usb "^1.1.0" 57 | 58 | bplist-parser@0.0.6: 59 | version "0.0.6" 60 | resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.0.6.tgz#38da3471817df9d44ab3892e27707bbbd75a11b9" 61 | 62 | brace-expansion@^1.1.7: 63 | version "1.1.11" 64 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 65 | dependencies: 66 | balanced-match "^1.0.0" 67 | concat-map "0.0.1" 68 | 69 | buffer-alloc-unsafe@^1.1.0: 70 | version "1.1.0" 71 | resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" 72 | 73 | buffer-alloc@^1.2.0: 74 | version "1.2.0" 75 | resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" 76 | dependencies: 77 | buffer-alloc-unsafe "^1.1.0" 78 | buffer-fill "^1.0.0" 79 | 80 | buffer-fill@^1.0.0: 81 | version "1.0.0" 82 | resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" 83 | 84 | chownr@^1.0.1, chownr@^1.1.1: 85 | version "1.1.2" 86 | resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6" 87 | 88 | code-point-at@^1.0.0: 89 | version "1.1.0" 90 | resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" 91 | 92 | concat-map@0.0.1: 93 | version "0.0.1" 94 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 95 | 96 | console-control-strings@^1.0.0, console-control-strings@~1.1.0: 97 | version "1.1.0" 98 | resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" 99 | 100 | core-util-is@~1.0.0: 101 | version "1.0.2" 102 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 103 | 104 | cross-spawn@^6.0.5: 105 | version "6.0.5" 106 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" 107 | dependencies: 108 | nice-try "^1.0.4" 109 | path-key "^2.0.1" 110 | semver "^5.5.0" 111 | shebang-command "^1.2.0" 112 | which "^1.2.9" 113 | 114 | debug@^2.2.0: 115 | version "2.6.9" 116 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 117 | dependencies: 118 | ms "2.0.0" 119 | 120 | debug@^3.2.6: 121 | version "3.2.6" 122 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" 123 | dependencies: 124 | ms "^2.1.1" 125 | 126 | debug@~2.2.0: 127 | version "2.2.0" 128 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" 129 | dependencies: 130 | ms "0.7.1" 131 | 132 | decompress-response@^3.3.0: 133 | version "3.3.0" 134 | resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" 135 | dependencies: 136 | mimic-response "^1.0.0" 137 | 138 | deep-extend@^0.6.0: 139 | version "0.6.0" 140 | resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" 141 | 142 | delegates@^1.0.0: 143 | version "1.0.0" 144 | resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" 145 | 146 | detect-libc@^1.0.2, detect-libc@^1.0.3: 147 | version "1.0.3" 148 | resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" 149 | 150 | end-of-stream@^1.0.0, end-of-stream@^1.1.0: 151 | version "1.4.1" 152 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" 153 | dependencies: 154 | once "^1.4.0" 155 | 156 | expand-template@^2.0.3: 157 | version "2.0.3" 158 | resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" 159 | 160 | file-uri-to-path@1.0.0: 161 | version "1.0.0" 162 | resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" 163 | 164 | fs-constants@^1.0.0: 165 | version "1.0.0" 166 | resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" 167 | 168 | fs-minipass@^1.2.5: 169 | version "1.2.6" 170 | resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" 171 | dependencies: 172 | minipass "^2.2.1" 173 | 174 | fs.realpath@^1.0.0: 175 | version "1.0.0" 176 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 177 | 178 | gauge@~2.7.3: 179 | version "2.7.4" 180 | resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" 181 | dependencies: 182 | aproba "^1.0.3" 183 | console-control-strings "^1.0.0" 184 | has-unicode "^2.0.0" 185 | object-assign "^4.1.0" 186 | signal-exit "^3.0.0" 187 | string-width "^1.0.1" 188 | strip-ansi "^3.0.1" 189 | wide-align "^1.1.0" 190 | 191 | github-from-package@0.0.0: 192 | version "0.0.0" 193 | resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" 194 | 195 | glob@^7.1.3: 196 | version "7.1.4" 197 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" 198 | dependencies: 199 | fs.realpath "^1.0.0" 200 | inflight "^1.0.4" 201 | inherits "2" 202 | minimatch "^3.0.4" 203 | once "^1.3.0" 204 | path-is-absolute "^1.0.0" 205 | 206 | has-unicode@^2.0.0: 207 | version "2.0.1" 208 | resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" 209 | 210 | iconv-lite@^0.4.4: 211 | version "0.4.24" 212 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 213 | dependencies: 214 | safer-buffer ">= 2.1.2 < 3" 215 | 216 | ignore-walk@^3.0.1: 217 | version "3.0.1" 218 | resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" 219 | dependencies: 220 | minimatch "^3.0.4" 221 | 222 | inflight@^1.0.4: 223 | version "1.0.6" 224 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 225 | dependencies: 226 | once "^1.3.0" 227 | wrappy "1" 228 | 229 | inherits@2, inherits@~2.0.3: 230 | version "2.0.4" 231 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 232 | 233 | ini@~1.3.0: 234 | version "1.3.5" 235 | resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" 236 | 237 | is-fullwidth-code-point@^1.0.0: 238 | version "1.0.0" 239 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" 240 | dependencies: 241 | number-is-nan "^1.0.0" 242 | 243 | is-fullwidth-code-point@^2.0.0: 244 | version "2.0.0" 245 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 246 | 247 | isarray@~1.0.0: 248 | version "1.0.0" 249 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 250 | 251 | isexe@^2.0.0: 252 | version "2.0.0" 253 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 254 | 255 | mimic-response@^1.0.0: 256 | version "1.0.1" 257 | resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" 258 | 259 | minimatch@^3.0.4: 260 | version "3.0.4" 261 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 262 | dependencies: 263 | brace-expansion "^1.1.7" 264 | 265 | minimist@0.0.8: 266 | version "0.0.8" 267 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 268 | 269 | minimist@^1.2.0: 270 | version "1.2.0" 271 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 272 | 273 | minipass@^2.2.1, minipass@^2.3.5: 274 | version "2.3.5" 275 | resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" 276 | dependencies: 277 | safe-buffer "^5.1.2" 278 | yallist "^3.0.0" 279 | 280 | minizlib@^1.2.1: 281 | version "1.2.1" 282 | resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" 283 | dependencies: 284 | minipass "^2.2.1" 285 | 286 | mkdirp@^0.5.0, mkdirp@^0.5.1: 287 | version "0.5.1" 288 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 289 | dependencies: 290 | minimist "0.0.8" 291 | 292 | ms@0.7.1: 293 | version "0.7.1" 294 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" 295 | 296 | ms@2.0.0: 297 | version "2.0.0" 298 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 299 | 300 | ms@^2.1.1: 301 | version "2.1.2" 302 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 303 | 304 | nan@2.13.2: 305 | version "2.13.2" 306 | resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7" 307 | 308 | nan@^2.0.5: 309 | version "2.14.0" 310 | resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" 311 | 312 | napi-build-utils@^1.0.1: 313 | version "1.0.1" 314 | resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.1.tgz#1381a0f92c39d66bf19852e7873432fc2123e508" 315 | 316 | napi-thread-safe-callback@0.0.6: 317 | version "0.0.6" 318 | resolved "https://registry.yarnpkg.com/napi-thread-safe-callback/-/napi-thread-safe-callback-0.0.6.tgz#ef86a149b5312e480f74e89a614e6d9e3b17b456" 319 | 320 | needle@^2.2.1: 321 | version "2.4.0" 322 | resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" 323 | dependencies: 324 | debug "^3.2.6" 325 | iconv-lite "^0.4.4" 326 | sax "^1.2.4" 327 | 328 | nice-try@^1.0.4: 329 | version "1.0.5" 330 | resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" 331 | 332 | "noble-mac@https://github.com/Timeular/noble-mac.git": 333 | version "0.0.4" 334 | resolved "https://github.com/Timeular/noble-mac.git#363a2811867cdef48633988925767e45d38405bf" 335 | dependencies: 336 | cross-spawn "^6.0.5" 337 | napi-thread-safe-callback "0.0.6" 338 | noble "^1.9.1" 339 | node-addon-api "^1.1.0" 340 | node-pre-gyp "^0.10.0" 341 | 342 | noble@^1.9.1: 343 | version "1.9.1" 344 | resolved "https://registry.yarnpkg.com/noble/-/noble-1.9.1.tgz#2ccd31ead8ec92dbff6f19a42e420b258bcdcdd0" 345 | dependencies: 346 | debug "~2.2.0" 347 | optionalDependencies: 348 | bluetooth-hci-socket "^0.5.1" 349 | bplist-parser "0.0.6" 350 | xpc-connection "~0.1.4" 351 | 352 | node-abi@^2.7.0: 353 | version "2.11.0" 354 | resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.11.0.tgz#b7dce18815057544a049be5ae75cd1fdc2e9ea59" 355 | dependencies: 356 | semver "^5.4.1" 357 | 358 | node-addon-api@^1.1.0: 359 | version "1.7.1" 360 | resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.1.tgz#cf813cd69bb8d9100f6bdca6755fc268f54ac492" 361 | 362 | node-pre-gyp@^0.10.0: 363 | version "0.10.3" 364 | resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" 365 | dependencies: 366 | detect-libc "^1.0.2" 367 | mkdirp "^0.5.1" 368 | needle "^2.2.1" 369 | nopt "^4.0.1" 370 | npm-packlist "^1.1.6" 371 | npmlog "^4.0.2" 372 | rc "^1.2.7" 373 | rimraf "^2.6.1" 374 | semver "^5.3.0" 375 | tar "^4" 376 | 377 | noop-logger@^0.1.1: 378 | version "0.1.1" 379 | resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" 380 | 381 | nopt@^4.0.1: 382 | version "4.0.1" 383 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" 384 | dependencies: 385 | abbrev "1" 386 | osenv "^0.1.4" 387 | 388 | npm-bundled@^1.0.1: 389 | version "1.0.6" 390 | resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" 391 | 392 | npm-packlist@^1.1.6: 393 | version "1.4.4" 394 | resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44" 395 | dependencies: 396 | ignore-walk "^3.0.1" 397 | npm-bundled "^1.0.1" 398 | 399 | npmlog@^4.0.1, npmlog@^4.0.2: 400 | version "4.1.2" 401 | resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" 402 | dependencies: 403 | are-we-there-yet "~1.1.2" 404 | console-control-strings "~1.1.0" 405 | gauge "~2.7.3" 406 | set-blocking "~2.0.0" 407 | 408 | number-is-nan@^1.0.0: 409 | version "1.0.1" 410 | resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" 411 | 412 | object-assign@^4.1.0: 413 | version "4.1.1" 414 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 415 | 416 | once@^1.3.0, once@^1.3.1, once@^1.4.0: 417 | version "1.4.0" 418 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 419 | dependencies: 420 | wrappy "1" 421 | 422 | os-homedir@^1.0.0, os-homedir@^1.0.1: 423 | version "1.0.2" 424 | resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" 425 | 426 | os-tmpdir@^1.0.0: 427 | version "1.0.2" 428 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 429 | 430 | osenv@^0.1.4: 431 | version "0.1.5" 432 | resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" 433 | dependencies: 434 | os-homedir "^1.0.0" 435 | os-tmpdir "^1.0.0" 436 | 437 | path-is-absolute@^1.0.0: 438 | version "1.0.1" 439 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 440 | 441 | path-key@^2.0.1: 442 | version "2.0.1" 443 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" 444 | 445 | prebuild-install@^5.2.4: 446 | version "5.3.0" 447 | resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.0.tgz#58b4d8344e03590990931ee088dd5401b03004c8" 448 | dependencies: 449 | detect-libc "^1.0.3" 450 | expand-template "^2.0.3" 451 | github-from-package "0.0.0" 452 | minimist "^1.2.0" 453 | mkdirp "^0.5.1" 454 | napi-build-utils "^1.0.1" 455 | node-abi "^2.7.0" 456 | noop-logger "^0.1.1" 457 | npmlog "^4.0.1" 458 | os-homedir "^1.0.1" 459 | pump "^2.0.1" 460 | rc "^1.2.7" 461 | simple-get "^2.7.0" 462 | tar-fs "^1.13.0" 463 | tunnel-agent "^0.6.0" 464 | which-pm-runs "^1.0.0" 465 | 466 | process-nextick-args@~2.0.0: 467 | version "2.0.1" 468 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" 469 | 470 | pump@^1.0.0: 471 | version "1.0.3" 472 | resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954" 473 | dependencies: 474 | end-of-stream "^1.1.0" 475 | once "^1.3.1" 476 | 477 | pump@^2.0.1: 478 | version "2.0.1" 479 | resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" 480 | dependencies: 481 | end-of-stream "^1.1.0" 482 | once "^1.3.1" 483 | 484 | rc@^1.2.7: 485 | version "1.2.8" 486 | resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" 487 | dependencies: 488 | deep-extend "^0.6.0" 489 | ini "~1.3.0" 490 | minimist "^1.2.0" 491 | strip-json-comments "~2.0.1" 492 | 493 | readable-stream@^2.0.6, readable-stream@^2.3.0, readable-stream@^2.3.5: 494 | version "2.3.6" 495 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" 496 | dependencies: 497 | core-util-is "~1.0.0" 498 | inherits "~2.0.3" 499 | isarray "~1.0.0" 500 | process-nextick-args "~2.0.0" 501 | safe-buffer "~5.1.1" 502 | string_decoder "~1.1.1" 503 | util-deprecate "~1.0.1" 504 | 505 | rimraf@^2.6.1: 506 | version "2.7.1" 507 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" 508 | dependencies: 509 | glob "^7.1.3" 510 | 511 | safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2: 512 | version "5.2.0" 513 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" 514 | 515 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 516 | version "5.1.2" 517 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 518 | 519 | "safer-buffer@>= 2.1.2 < 3": 520 | version "2.1.2" 521 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 522 | 523 | sax@^1.2.4: 524 | version "1.2.4" 525 | resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" 526 | 527 | semver@^5.3.0, semver@^5.4.1, semver@^5.5.0: 528 | version "5.7.1" 529 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 530 | 531 | set-blocking@~2.0.0: 532 | version "2.0.0" 533 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" 534 | 535 | shebang-command@^1.2.0: 536 | version "1.2.0" 537 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 538 | dependencies: 539 | shebang-regex "^1.0.0" 540 | 541 | shebang-regex@^1.0.0: 542 | version "1.0.0" 543 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 544 | 545 | signal-exit@^3.0.0: 546 | version "3.0.2" 547 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 548 | 549 | simple-concat@^1.0.0: 550 | version "1.0.0" 551 | resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" 552 | 553 | simple-get@^2.7.0: 554 | version "2.8.1" 555 | resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" 556 | dependencies: 557 | decompress-response "^3.3.0" 558 | once "^1.3.1" 559 | simple-concat "^1.0.0" 560 | 561 | string-width@^1.0.1: 562 | version "1.0.2" 563 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" 564 | dependencies: 565 | code-point-at "^1.0.0" 566 | is-fullwidth-code-point "^1.0.0" 567 | strip-ansi "^3.0.0" 568 | 569 | "string-width@^1.0.2 || 2": 570 | version "2.1.1" 571 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" 572 | dependencies: 573 | is-fullwidth-code-point "^2.0.0" 574 | strip-ansi "^4.0.0" 575 | 576 | string_decoder@~1.1.1: 577 | version "1.1.1" 578 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 579 | dependencies: 580 | safe-buffer "~5.1.0" 581 | 582 | strip-ansi@^3.0.0, strip-ansi@^3.0.1: 583 | version "3.0.1" 584 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 585 | dependencies: 586 | ansi-regex "^2.0.0" 587 | 588 | strip-ansi@^4.0.0: 589 | version "4.0.0" 590 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 591 | dependencies: 592 | ansi-regex "^3.0.0" 593 | 594 | strip-json-comments@~2.0.1: 595 | version "2.0.1" 596 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 597 | 598 | tar-fs@^1.13.0: 599 | version "1.16.3" 600 | resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509" 601 | dependencies: 602 | chownr "^1.0.1" 603 | mkdirp "^0.5.1" 604 | pump "^1.0.0" 605 | tar-stream "^1.1.2" 606 | 607 | tar-stream@^1.1.2: 608 | version "1.6.2" 609 | resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" 610 | dependencies: 611 | bl "^1.0.0" 612 | buffer-alloc "^1.2.0" 613 | end-of-stream "^1.0.0" 614 | fs-constants "^1.0.0" 615 | readable-stream "^2.3.0" 616 | to-buffer "^1.1.1" 617 | xtend "^4.0.0" 618 | 619 | tar@^4: 620 | version "4.4.10" 621 | resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" 622 | dependencies: 623 | chownr "^1.1.1" 624 | fs-minipass "^1.2.5" 625 | minipass "^2.3.5" 626 | minizlib "^1.2.1" 627 | mkdirp "^0.5.0" 628 | safe-buffer "^5.1.2" 629 | yallist "^3.0.3" 630 | 631 | to-buffer@^1.1.1: 632 | version "1.1.1" 633 | resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" 634 | 635 | tunnel-agent@^0.6.0: 636 | version "0.6.0" 637 | resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" 638 | dependencies: 639 | safe-buffer "^5.0.1" 640 | 641 | usb@^1.1.0: 642 | version "1.6.0" 643 | resolved "https://registry.yarnpkg.com/usb/-/usb-1.6.0.tgz#bc5d4decf4ffca32d1136717edcf73366137a789" 644 | dependencies: 645 | bindings "^1.4.0" 646 | nan "2.13.2" 647 | prebuild-install "^5.2.4" 648 | 649 | util-deprecate@~1.0.1: 650 | version "1.0.2" 651 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 652 | 653 | which-pm-runs@^1.0.0: 654 | version "1.0.0" 655 | resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" 656 | 657 | which@^1.2.9: 658 | version "1.3.1" 659 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 660 | dependencies: 661 | isexe "^2.0.0" 662 | 663 | wide-align@^1.1.0: 664 | version "1.1.3" 665 | resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" 666 | dependencies: 667 | string-width "^1.0.2 || 2" 668 | 669 | wrappy@1: 670 | version "1.0.2" 671 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 672 | 673 | ws@^7.1.1: 674 | version "7.1.2" 675 | resolved "https://registry.yarnpkg.com/ws/-/ws-7.1.2.tgz#c672d1629de8bb27a9699eb599be47aeeedd8f73" 676 | dependencies: 677 | async-limiter "^1.0.0" 678 | 679 | xpc-connection@~0.1.4: 680 | version "0.1.4" 681 | resolved "https://registry.yarnpkg.com/xpc-connection/-/xpc-connection-0.1.4.tgz#dcd7faa2aec6b7a6e18cc5ddad042f7a34c77156" 682 | dependencies: 683 | nan "^2.0.5" 684 | 685 | xtend@^4.0.0: 686 | version "4.0.2" 687 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" 688 | 689 | yallist@^3.0.0, yallist@^3.0.3: 690 | version "3.0.3" 691 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" 692 | --------------------------------------------------------------------------------