├── .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 |
--------------------------------------------------------------------------------