├── src ├── style │ └── main.css ├── icon_128.png ├── default.html ├── main.js ├── manifest.json ├── index.html └── index.js ├── .gitignore ├── assets ├── loaded.png └── screenshot.png ├── example ├── example.bin └── example.html ├── .travis.yml ├── vendor └── websocket-server │ ├── README.md │ ├── sha1.js │ └── http.js ├── gulpfile.js ├── package.json ├── LICENSE ├── README.md └── yarn.lock /src/style/main.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | chrome-raw-print/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /src/icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/receipt-print-hq/chrome-raw-print/master/src/icon_128.png -------------------------------------------------------------------------------- /assets/loaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/receipt-print-hq/chrome-raw-print/master/assets/loaded.png -------------------------------------------------------------------------------- /assets/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/receipt-print-hq/chrome-raw-print/master/assets/screenshot.png -------------------------------------------------------------------------------- /example/example.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/receipt-print-hq/chrome-raw-print/master/example/example.bin -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | 4 | node_js: 5 | - "7" 6 | 7 | cache: 8 | directories: 9 | - "node_modules" 10 | 11 | script: 12 | - gulp 13 | -------------------------------------------------------------------------------- /src/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

This end-point may not be accessed directly via HTTP.

5 |

Please connect via a websocket to send raw data to configured printers.

6 | 7 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | chrome.app.runtime.onLaunched.addListener(function(data) { 2 | // Open main window 3 | chrome.app.window.create('index.html', 4 | { id: 'main', 5 | innerBounds: {width: 600, height: 600} 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /vendor/websocket-server/README.md: -------------------------------------------------------------------------------- 1 | # websocker-server 2 | 3 | These files are sourced from the Chrome App '[websocket-server](https://github.com/GoogleChrome/chrome-app-samples/blob/master/samples/websocket-server/index.js)' example. 4 | 5 | 6 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | 3 | var dest = 'chrome-raw-print' 4 | 5 | gulp.task('src', function() { 6 | gulp.src(['src/**']) 7 | .pipe(gulp.dest(dest + '/')) 8 | }) 9 | 10 | gulp.task('vendor', function() { 11 | gulp.src(['vendor/**']) 12 | .pipe(gulp.dest(dest + '/vendor')) 13 | }) 14 | 15 | 16 | gulp.task('default', ['src', 'vendor']); 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chrome-raw-print", 3 | "version": "0.2", 4 | "description": "Chrome app to enable raw printing from a browser ", 5 | "main": "dest/index.js", 6 | "repository": "git@github.com:receipt-print-hq/chrome-raw-print.git", 7 | "author": "Michael Billington ", 8 | "license": "MIT", 9 | "devDependencies": { 10 | "gulp": "^3.9.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "background": { 4 | "scripts": ["main.js"] 5 | } 6 | }, 7 | "manifest_version": 2, 8 | "name": "Raw print connector", 9 | "version": "0.2", 10 | "description": "Pass raw data to USB receipt printers", 11 | "icons": { 12 | "128": "icon_128.png" 13 | }, 14 | "minimum_chrome_version": "25", 15 | "sockets": { 16 | "tcp": { 17 | "connect": "*" 18 | }, 19 | "tcpServer": { 20 | "listen": "*" 21 | } 22 | }, 23 | "permissions": ["usb"], 24 | "optional_permissions": [ 25 | {"usbDevices": [ 26 | {"vendorId": 1046, "productId": 20497}, 27 | {"vendorId": 1659, "productId": 8965}, 28 | {"vendorId": 1208, "productId": 3587}, 29 | {"vendorId": 1352, "productId": 2056}, 30 | {"vendorId": 5380, "productId": 26}] 31 | }] 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-18 receipt-print-hq 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 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

Raw print connector

11 | 12 |

This app allows you to send raw commands to your printer. It is designed to make receipt printers accessible from webpages for point-of-sale kiosks.

13 | 14 |

While this app is open, web pages can pass data to those printers via a websocket. Example code for accessing this websocket from Javascript can be found on the project page.

15 | 16 |

On supported printers, check that:

17 | 18 | 19 | 20 |
21 | 22 | 23 |
24 | 25 | 26 | 27 |
28 | 29 |
Status: Waiting for printer selection.
30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /example/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Web-based raw printing example 5 | 6 | 7 |

Web-based raw printing example

8 | 9 |

This snippet demonstrates how to send raw binary data to a printer from a browser-based Javascript application.

10 | 11 |

A running Chrome App will pick up the data via a local WebSocket, and deliver it to any configured printers.

12 | 13 |
14 | 15 | 16 |
17 | 18 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raw printing for Chrome 2 | 3 | **Chrome Apps are being discontinued by Google in 2018, so this project will no longer be actively developed. At the time of writing, neither WebUSB nor raw sockets can be used from regular web pages, so you will need to transition to a native app for this functionality once the runtime becomes unavailable.** 4 | 5 | **Thank you to everybody who has used or contributed to this app over the past year and a half!** 6 | 7 | ---- 8 | 9 | This is a Chrome app to allow browser-based Javascript code to pass raw data to a supported printer. It is aimed at developers of web-based point-of-sale systems where server-side printing is not feasable. 10 | 11 | This has been developed for use with a USB receipt printer, with an ESC/POS command generator ([escpos-php](https://github.com/mike42/escpos-php)) providing the binary data. 12 | 13 | ## The problem 14 | 15 | It can be difficult to add printing to a web-based point-of-sale system: 16 | 17 | - The local print system does not use raw output, so you get low-quality, rasterised output. 18 | - Allowing inbound connectivity to allow a server to print requires a static IP, and is challenging to secure and debug. 19 | 20 | This app solves the problem by exposing a websocket on localhost. A web-page can then retrieve binary print data from the server, and pass it to the app for printing. 21 | 22 | ## Compatibility 23 | 24 | ### Printer types 25 | 26 | Only USB receipt printers are currently supported. 27 | 28 | - **Networked printers** may be supported in future. 29 | - **Serial/Parallel printers** may be accessed via a USB adaptor. 30 | - **CUPS/IPP/Windows** shared printers are out of scope at this stage, please hook in directly via USB. 31 | 32 | ### Supported printers 33 | 34 | Raw printing for Chrome is loaded with vendor/product ID's for the following printers: 35 | 36 | - Epson TM-T20 37 | - PL2305 Parallel Port 38 | - Winbond Virtual Com Port. 39 | 40 | If your USB printer is not listed, please create a new issue to request support. Because of the way Chrome app permissions work, please include the USB vendor ID and product ID. 41 | 42 | ### Page Description Languages 43 | 44 | This app is simply a connector. In theory, any page description language is supported, provided that you are able to generate it, and your printer can understand it. 45 | 46 | ## Installation 47 | 48 | This application is not yet available in the Chrome store, so the installation steps are a bit unusual: 49 | 50 | - Download and extract the [most recent release](https://github.com/receipt-print-hq/chrome-raw-print/releases). 51 | - Under `chrome://extensions`, tick developer mode, and 'load unpacked extension', and locate the extracted folder. 52 | 53 | ![screenshot](https://github.com/receipt-print-hq/chrome-raw-print/raw/master/assets/loaded.png) 54 | 55 | - Launch the extension, select your printer 56 | - If not listed, find the USB product, vendor ID, add it to `manifest.md`, and restart. 57 | - Click print 58 | - If it doesn't print, check the error log and see the notes displayed on the app window for the basic things to check. 59 | - In particular, unload the `usblp` kernel module if applicable, as it will claim the USB interface to the printer. 60 | 61 | ## Demo 62 | 63 | A small example web-page available under `example/`, which will print a "Hello World" receipt in ESC/POS if the raw printing app is running, with an ESC/POS printer configured. Steps to try it out: 64 | 65 | - Run the app 66 | - Add a printer 67 | - Open the example web page 68 | - Click a few buttons and hope for output. 69 | 70 | This was tested on an Epson TM-T20. 71 | 72 | # Build 73 | 74 | ``` 75 | yarn 76 | gulp 77 | ``` 78 | 79 | The `chrome-raw-print` subfolder contains the compiled files. 80 | 81 | ## Contribute 82 | 83 | Pull requests are welcome and appreciated. This project is quite new, please see the issue tracker for ideas of things to work on. 84 | 85 | # Screenshot 86 | 87 | ![screenshot](https://github.com/receipt-print-hq/chrome-raw-print/raw/master/assets/screenshot.png) 88 | 89 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | var $ = function(id) { return document.getElementById(id); }; 2 | 3 | /** 4 | * Select and print to USB printers 5 | */ 6 | selectedPrinters = []; 7 | 8 | function selectPrinter() { 9 | // Dialog and printer selection 10 | selectedPrinters = []; 11 | var onDeviceFound = function(devices) { 12 | $('printerList').innerHTML = ''; 13 | if(devices.length == 0) { 14 | updateStatus('No devices selected'); 15 | return; 16 | } 17 | // Refresh the printer list 18 | $('printerList').innerHTML = 'ManufacturerProduct name'; 19 | selectedPrinters = []; 20 | devices.forEach(function(device) { 21 | // Add each printer to list 22 | idStr = device.device 23 | $('printerList').innerHTML += ''; 24 | $(idStr + '-manufacturerName').innerText = device.manufacturerName; 25 | $(idStr + '-productName').innerText = device.productName; 26 | selectedPrinters.push(device); 27 | }); 28 | updateStatus(selectedPrinters.length + ' printer(s) selected'); 29 | } 30 | // Allow the user to select any devices listed in the manifest 31 | filters = chrome.runtime.getManifest().optional_permissions[0].usbDevices; 32 | chrome.usb.getUserSelectedDevices({'multiple': true, 'filters': filters}, onDeviceFound) 33 | } 34 | 35 | function print() { 36 | // Print text and cut paper. This data contains 'Hello world LF GS V 65 3' 37 | hello = [0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0a, 0x1d, 0x56, 0x41, 0x03]; 38 | var totalDataSize = hello.length; 39 | var data = new ArrayBuffer(totalDataSize); 40 | var dataView = new Uint8Array(data, 0, totalDataSize); 41 | dataView.set(hello, 0) 42 | // Print this data to all devices 43 | printDataToDevices(data, selectedPrinters) 44 | } 45 | 46 | function printDataToDevices(data, devices) { 47 | // For each device, print data to the device 48 | if(devices.length == 0) { 49 | updateStatus("Cannot print: No printers selected."); 50 | return; 51 | } 52 | devices.forEach(function(device) { 53 | printDataToDevice(data, device); 54 | }); 55 | } 56 | 57 | function printDataToDevice(data, device) { 58 | // Open a device and print the data to the resulting handle 59 | chrome.usb.openDevice(device, function(handle) { 60 | printDataToHandle(data, device, handle) 61 | }); 62 | } 63 | 64 | function printDataToHandle(data, device, handle) { 65 | // Claim interface and print data to it 66 | chrome.usb.claimInterface(handle, 0, function() { 67 | if (chrome.runtime.lastError) { 68 | console.error(chrome.runtime.lastError, device); 69 | updateStatus("Failed to claim interface for device"); 70 | return; 71 | } 72 | printDataToInterface(data, device, handle); 73 | }); 74 | } 75 | 76 | function printDataToInterface(data, device, handle) { 77 | // Transfer data to a claimed interface on an open device 78 | var info = { 79 | "direction": "out", 80 | "endpoint": 1, 81 | "data": data 82 | }; 83 | chrome.usb.bulkTransfer(handle, info, function(transferResult) { 84 | chrome.usb.releaseInterface(handle, 0, function() { 85 | if (chrome.runtime.lastError) 86 | console.error(chrome.runtime.lastError); 87 | return; 88 | }); 89 | }); 90 | } 91 | 92 | function updateStatus(text) { 93 | // Change status line on page 94 | $('status').innerText = 'Status: ' + text; 95 | } 96 | 97 | window.addEventListener('DOMContentLoaded', function() { 98 | // Hook up buttons 99 | $('selectPrinter').addEventListener('click', selectPrinter); 100 | $('print').addEventListener('click', print); 101 | }); 102 | 103 | /** 104 | * Accept raw print data via WebSocket on port 9876 (serialised as byte array) 105 | */ 106 | port = 9876; 107 | var server = new http.Server(); 108 | var wsServer = new http.WebSocketServer(server); 109 | server.listen(port); 110 | server.addEventListener('request', function(req) { 111 | // Serve a deault page of this chrome application. 112 | url = '/default.html'; 113 | req.serveUrl(url); 114 | return true; 115 | }); 116 | 117 | wsServer.addEventListener('request', function(req) { 118 | console.log('Client connected'); 119 | var socket = req.accept(); 120 | socket.addEventListener('message', function(e) { 121 | console.log('Data received', e); 122 | var receivedData = JSON.parse(e.data); 123 | receivedData = new Uint8Array(receivedData).buffer; 124 | printDataToDevices(receivedData, selectedPrinters) 125 | }); 126 | socket.addEventListener('close', function() { 127 | console.log('Client disconnected'); 128 | }); 129 | return true; 130 | }); 131 | -------------------------------------------------------------------------------- /vendor/websocket-server/sha1.js: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License at 4 | // 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Unless required by applicable law or agreed to in writing, software 8 | // distributed under the License is distributed on an "AS IS" BASIS, 9 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | // See the License for the specific language governing permissions and 11 | // limitations under the License. 12 | 13 | // Copyright 2005 Google Inc. All Rights Reserved. 14 | 15 | /** 16 | * @fileoverview SHA-1 cryptographic hash. 17 | * Variable names follow the notation in FIPS PUB 180-3: 18 | * http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf. 19 | * 20 | * Usage: 21 | * var sha1 = new goog.crypt.sha1(); 22 | * sha1.update(bytes); 23 | * var hash = sha1.digest(); 24 | * 25 | */ 26 | 27 | /** 28 | * SHA-1 cryptographic hash constructor. 29 | * 30 | * The properties declared here are discussed in the above algorithm document. 31 | * @constructor 32 | */ 33 | var Sha1 = function() { 34 | /** 35 | * Holds the previous values of accumulated variables a-e in the compress_ 36 | * function. 37 | * @type {Array.} 38 | * @private 39 | */ 40 | this.chain_ = []; 41 | 42 | /** 43 | * A buffer holding the partially computed hash result. 44 | * @type {Array.} 45 | * @private 46 | */ 47 | this.buf_ = []; 48 | 49 | /** 50 | * An array of 80 bytes, each a part of the message to be hashed. Referred to 51 | * as the message schedule in the docs. 52 | * @type {Array.} 53 | * @private 54 | */ 55 | this.W_ = []; 56 | 57 | /** 58 | * Contains data needed to pad messages less than 64 bytes. 59 | * @type {Array.} 60 | * @private 61 | */ 62 | this.pad_ = []; 63 | 64 | this.pad_[0] = 128; 65 | for (var i = 1; i < 64; ++i) { 66 | this.pad_[i] = 0; 67 | } 68 | 69 | this.reset(); 70 | }; 71 | 72 | 73 | /** 74 | * Resets the internal accumulator. 75 | */ 76 | Sha1.prototype.reset = function() { 77 | this.chain_[0] = 0x67452301; 78 | this.chain_[1] = 0xefcdab89; 79 | this.chain_[2] = 0x98badcfe; 80 | this.chain_[3] = 0x10325476; 81 | this.chain_[4] = 0xc3d2e1f0; 82 | 83 | this.inbuf_ = 0; 84 | this.total_ = 0; 85 | }; 86 | 87 | 88 | /** 89 | * Internal helper performing 32 bit left rotate. 90 | * @return {number} w rotated left by r bits. 91 | * @private 92 | */ 93 | Sha1.prototype.rotl_ = function(w, r) { 94 | return ((w << r) | (w >>> (32 - r))) & 0xffffffff; 95 | }; 96 | 97 | 98 | /** 99 | * Internal compress helper function. 100 | * @param {Array} buf containing block to compress. 101 | * @private 102 | */ 103 | Sha1.prototype.compress_ = function(buf) { 104 | var W = this.W_; 105 | 106 | // get 16 big endian words 107 | for (var i = 0; i < 64; i += 4) { 108 | var w = (buf[i] << 24) | 109 | (buf[i + 1] << 16) | 110 | (buf[i + 2] << 8) | 111 | (buf[i + 3]); 112 | W[i / 4] = w; 113 | } 114 | 115 | // expand to 80 words 116 | for (var i = 16; i < 80; i++) { 117 | W[i] = this.rotl_(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1); 118 | } 119 | 120 | var a = this.chain_[0]; 121 | var b = this.chain_[1]; 122 | var c = this.chain_[2]; 123 | var d = this.chain_[3]; 124 | var e = this.chain_[4]; 125 | var f, k; 126 | 127 | for (var i = 0; i < 80; i++) { 128 | if (i < 40) { 129 | if (i < 20) { 130 | f = d ^ (b & (c ^ d)); 131 | k = 0x5a827999; 132 | } else { 133 | f = b ^ c ^ d; 134 | k = 0x6ed9eba1; 135 | } 136 | } else { 137 | if (i < 60) { 138 | f = (b & c) | (d & (b | c)); 139 | k = 0x8f1bbcdc; 140 | } else { 141 | f = b ^ c ^ d; 142 | k = 0xca62c1d6; 143 | } 144 | } 145 | 146 | var t = (this.rotl_(a, 5) + f + e + k + W[i]) & 0xffffffff; 147 | e = d; 148 | d = c; 149 | c = this.rotl_(b, 30); 150 | b = a; 151 | a = t; 152 | } 153 | 154 | this.chain_[0] = (this.chain_[0] + a) & 0xffffffff; 155 | this.chain_[1] = (this.chain_[1] + b) & 0xffffffff; 156 | this.chain_[2] = (this.chain_[2] + c) & 0xffffffff; 157 | this.chain_[3] = (this.chain_[3] + d) & 0xffffffff; 158 | this.chain_[4] = (this.chain_[4] + e) & 0xffffffff; 159 | }; 160 | 161 | 162 | /** 163 | * Adds a byte array to internal accumulator. 164 | * @param {Array.} bytes to add to digest. 165 | * @param {number} opt_length is # of bytes to compress. 166 | */ 167 | Sha1.prototype.update = function(bytes, opt_length) { 168 | if (!opt_length) { 169 | opt_length = bytes.length; 170 | } 171 | 172 | var n = 0; 173 | 174 | // Optimize for 64 byte chunks at 64 byte boundaries. 175 | if (this.inbuf_ == 0) { 176 | while (n + 64 < opt_length) { 177 | this.compress_(bytes.slice(n, n + 64)); 178 | n += 64; 179 | this.total_ += 64; 180 | } 181 | } 182 | 183 | while (n < opt_length) { 184 | this.buf_[this.inbuf_++] = bytes[n++]; 185 | this.total_++; 186 | 187 | if (this.inbuf_ == 64) { 188 | this.inbuf_ = 0; 189 | this.compress_(this.buf_); 190 | 191 | // Pick up 64 byte chunks. 192 | while (n + 64 < opt_length) { 193 | this.compress_(bytes.slice(n, n + 64)); 194 | n += 64; 195 | this.total_ += 64; 196 | } 197 | } 198 | } 199 | }; 200 | 201 | 202 | /** 203 | * @return {Array} byte[20] containing finalized hash. 204 | */ 205 | Sha1.prototype.digest = function() { 206 | var digest = []; 207 | var totalBits = this.total_ * 8; 208 | 209 | // Add pad 0x80 0x00*. 210 | if (this.inbuf_ < 56) { 211 | this.update(this.pad_, 56 - this.inbuf_); 212 | } else { 213 | this.update(this.pad_, 64 - (this.inbuf_ - 56)); 214 | } 215 | 216 | // Add # bits. 217 | for (var i = 63; i >= 56; i--) { 218 | this.buf_[i] = totalBits & 255; 219 | totalBits >>>= 8; 220 | } 221 | 222 | this.compress_(this.buf_); 223 | 224 | var n = 0; 225 | for (var i = 0; i < 5; i++) { 226 | for (var j = 24; j >= 0; j -= 8) { 227 | digest[n++] = (this.chain_[i] >> j) & 255; 228 | } 229 | } 230 | 231 | return digest; 232 | }; 233 | -------------------------------------------------------------------------------- /vendor/websocket-server/http.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013 The Chromium Authors. All rights reserved. 3 | * Use of this source code is governed by a BSD-style license that can be 4 | * found in the LICENSE file. 5 | **/ 6 | 7 | var http = function() { 8 | 9 | if (!chrome.sockets || !chrome.sockets.tcpServer) 10 | return {}; 11 | 12 | // Wrap chrome.sockets.tcp socketId with a Promise API. 13 | var PSocket = (function() { 14 | // chrome.sockets.tcp uses a global listener for incoming data so 15 | // use a map to dispatch to the proper instance. 16 | var socketMap = {}; 17 | chrome.sockets.tcp.onReceive.addListener(function(info) { 18 | var pSocket = socketMap[info.socketId]; 19 | if (pSocket) { 20 | if (pSocket.handlers) { 21 | // Fulfil the pending read. 22 | pSocket.handlers.resolve(info.data); 23 | delete pSocket.handlers; 24 | } 25 | else { 26 | // No pending read so put data on the queue. 27 | pSocket.readQueue.push(info); 28 | } 29 | } 30 | }); 31 | 32 | // Read errors also use a global listener. 33 | chrome.sockets.tcp.onReceiveError.addListener(function(info) { 34 | var pSocket = socketMap[info.socketId]; 35 | if (pSocket) { 36 | if (pSocket.handlers) { 37 | // Reject the pending read. 38 | pSocket.handlers.reject(new Error('chrome.sockets.tcp error ' + info.resultCode)); 39 | delete pSocket.handlers; 40 | } 41 | else { 42 | // No pending read so put data on the queue. 43 | pSocket.readQueue.push(info); 44 | } 45 | } 46 | }); 47 | 48 | // PSocket constructor. 49 | return function(socketId) { 50 | this.socketId = socketId; 51 | this.readQueue = []; 52 | 53 | // Register this instance for incoming data processing. 54 | socketMap[socketId] = this; 55 | chrome.sockets.tcp.setPaused(socketId, false); 56 | }; 57 | })(); 58 | 59 | // Returns a Promise with read data. 60 | PSocket.prototype.read = function() { 61 | var that = this; 62 | if (this.readQueue.length) { 63 | // Return data from the queue. 64 | var info = this.readQueue.shift(); 65 | if (!info.resultCode) 66 | return Promise.resolve(info.data); 67 | else 68 | return Promise.reject(new Error('chrome.sockets.tcp error ' + info.resultCode)); 69 | } 70 | else { 71 | // The queue is empty so install handlers. 72 | return new Promise(function(resolve, reject) { 73 | that.handlers = { resolve: resolve, reject: reject }; 74 | }); 75 | } 76 | }; 77 | 78 | // Returns a Promise with the number of bytes written. 79 | PSocket.prototype.write = function(data) { 80 | var that = this; 81 | return new Promise(function(resolve, reject) { 82 | chrome.sockets.tcp.send(that.socketId, data, function(info) { 83 | if (info && info.resultCode >= 0) 84 | resolve(info.bytesSent); 85 | else 86 | reject(new Error('chrome sockets.tcp error ' + (info && info.resultCode))); 87 | }); 88 | }); 89 | }; 90 | 91 | // Returns a Promise. 92 | PSocket.prototype.close = function() { 93 | var that = this; 94 | return new Promise(function(resolve, reject) { 95 | chrome.sockets.tcp.disconnect(that.socketId, function() { 96 | chrome.sockets.tcp.close(that.socketId, resolve); 97 | }); 98 | }); 99 | }; 100 | 101 | // Http response code strings. 102 | var responseMap = { 103 | 200: 'OK', 104 | 301: 'Moved Permanently', 105 | 304: 'Not Modified', 106 | 400: 'Bad Request', 107 | 401: 'Unauthorized', 108 | 403: 'Forbidden', 109 | 404: 'Not Found', 110 | 413: 'Request Entity Too Large', 111 | 414: 'Request-URI Too Long', 112 | 500: 'Internal Server Error'}; 113 | 114 | /** 115 | * Convert from an ArrayBuffer to a string. 116 | * @param {ArrayBuffer} buffer The array buffer to convert. 117 | * @return {string} The textual representation of the array. 118 | */ 119 | var arrayBufferToString = function(buffer) { 120 | var array = new Uint8Array(buffer); 121 | var str = ''; 122 | for (var i = 0; i < array.length; ++i) { 123 | str += String.fromCharCode(array[i]); 124 | } 125 | return str; 126 | }; 127 | 128 | /** 129 | * Convert from an UTF-8 array to UTF-8 string. 130 | * @param {array} UTF-8 array 131 | * @return {string} UTF-8 string 132 | */ 133 | var ary2utf8 = (function() { 134 | 135 | var patterns = [ 136 | {pattern: '0xxxxxxx', bytes: 1}, 137 | {pattern: '110xxxxx', bytes: 2}, 138 | {pattern: '1110xxxx', bytes: 3}, 139 | {pattern: '11110xxx', bytes: 4}, 140 | {pattern: '111110xx', bytes: 5}, 141 | {pattern: '1111110x', bytes: 6} 142 | ]; 143 | patterns.forEach(function(item) { 144 | item.header = item.pattern.replace(/[^10]/g, ''); 145 | item.pattern01 = item.pattern.replace(/[^10]/g, '0'); 146 | item.pattern01 = parseInt(item.pattern01, 2); 147 | item.mask_length = item.header.length; 148 | item.data_length = 8 - item.header.length; 149 | var mask = ''; 150 | for (var i = 0, len = item.mask_length; i < len; i++) { 151 | mask += '1'; 152 | } 153 | for (var i = 0, len = item.data_length; i < len; i++) { 154 | mask += '0'; 155 | } 156 | item.mask = mask; 157 | item.mask = parseInt(item.mask, 2); 158 | }); 159 | 160 | return function(ary) { 161 | var codes = []; 162 | var cur = 0; 163 | while(cur < ary.length) { 164 | var first = ary[cur]; 165 | var pattern = null; 166 | for (var i = 0, len = patterns.length; i < len; i++) { 167 | if ((first & patterns[i].mask) == patterns[i].pattern01) { 168 | pattern = patterns[i]; 169 | break; 170 | } 171 | } 172 | if (pattern == null) { 173 | throw 'utf-8 decode error'; 174 | } 175 | var rest = ary.slice(cur + 1, cur + pattern.bytes); 176 | cur += pattern.bytes; 177 | var code = ''; 178 | code += ('00000000' + (first & (255 ^ pattern.mask)).toString(2)).slice(-pattern.data_length); 179 | for (var i = 0, len = rest.length; i < len; i++) { 180 | code += ('00000000' + (rest[i] & parseInt('111111', 2)).toString(2)).slice(-6); 181 | } 182 | codes.push(parseInt(code, 2)); 183 | } 184 | return String.fromCharCode.apply(null, codes); 185 | }; 186 | 187 | })(); 188 | 189 | /** 190 | * Convert from an UTF-8 string to UTF-8 array. 191 | * @param {string} UTF-8 string 192 | * @return {array} UTF-8 array 193 | */ 194 | var utf82ary = (function() { 195 | 196 | var patterns = [ 197 | {pattern: '0xxxxxxx', bytes: 1}, 198 | {pattern: '110xxxxx', bytes: 2}, 199 | {pattern: '1110xxxx', bytes: 3}, 200 | {pattern: '11110xxx', bytes: 4}, 201 | {pattern: '111110xx', bytes: 5}, 202 | {pattern: '1111110x', bytes: 6} 203 | ]; 204 | patterns.forEach(function(item) { 205 | item.header = item.pattern.replace(/[^10]/g, ''); 206 | item.mask_length = item.header.length; 207 | item.data_length = 8 - item.header.length; 208 | item.max_bit_length = (item.bytes - 1) * 6 + item.data_length; 209 | }); 210 | 211 | var code2utf8array = function(code) { 212 | var pattern = null; 213 | var code01 = code.toString(2); 214 | for (var i = 0, len = patterns.length; i < len; i++) { 215 | if (code01.length <= patterns[i].max_bit_length) { 216 | pattern = patterns[i]; 217 | break; 218 | } 219 | } 220 | if (pattern == null) { 221 | throw 'utf-8 encode error'; 222 | } 223 | var ary = []; 224 | for (var i = 0, len = pattern.bytes - 1; i < len; i++) { 225 | ary.unshift(parseInt('10' + ('000000' + code01.slice(-6)).slice(-6), 2)); 226 | code01 = code01.slice(0, -6); 227 | } 228 | ary.unshift(parseInt(pattern.header + ('00000000' + code01).slice(-pattern.data_length), 2)); 229 | return ary; 230 | }; 231 | 232 | return function(str) { 233 | var codes = []; 234 | for (var i = 0, len = str.length; i < len; i++) { 235 | var code = str.charCodeAt(i); 236 | Array.prototype.push.apply(codes, code2utf8array(code)); 237 | } 238 | return codes; 239 | }; 240 | 241 | })(); 242 | 243 | /** 244 | * Convert a string to an ArrayBuffer. 245 | * @param {string} string The string to convert. 246 | * @return {ArrayBuffer} An array buffer whose bytes correspond to the string. 247 | */ 248 | var stringToArrayBuffer = function(string) { 249 | var buffer = new ArrayBuffer(string.length); 250 | var bufferView = new Uint8Array(buffer); 251 | for (var i = 0; i < string.length; i++) { 252 | bufferView[i] = string.charCodeAt(i); 253 | } 254 | return buffer; 255 | }; 256 | 257 | /** 258 | * An event source can dispatch events. These are dispatched to all of the 259 | * functions listening for that event type with arguments. 260 | * @constructor 261 | */ 262 | function EventSource() { 263 | this.listeners_ = {}; 264 | }; 265 | 266 | EventSource.prototype = { 267 | /** 268 | * Add |callback| as a listener for |type| events. 269 | * @param {string} type The type of the event. 270 | * @param {function(Object|undefined): boolean} callback The function to call 271 | * when this event type is dispatched. Arguments depend on the event 272 | * source and type. The function returns whether the event was "handled" 273 | * which will prevent delivery to the rest of the listeners. 274 | */ 275 | addEventListener: function(type, callback) { 276 | if (!this.listeners_[type]) 277 | this.listeners_[type] = []; 278 | this.listeners_[type].push(callback); 279 | }, 280 | 281 | /** 282 | * Remove |callback| as a listener for |type| events. 283 | * @param {string} type The type of the event. 284 | * @param {function(Object|undefined): boolean} callback The callback 285 | * function to remove from the event listeners for events having type 286 | * |type|. 287 | */ 288 | removeEventListener: function(type, callback) { 289 | if (!this.listeners_[type]) 290 | return; 291 | for (var i = this.listeners_[type].length - 1; i >= 0; i--) { 292 | if (this.listeners_[type][i] == callback) { 293 | this.listeners_[type].splice(i, 1); 294 | } 295 | } 296 | }, 297 | 298 | /** 299 | * Dispatch an event to all listeners for events of type |type|. 300 | * @param {type} type The type of the event being dispatched. 301 | * @param {...Object} var_args The arguments to pass when calling the 302 | * callback function. 303 | * @return {boolean} Returns true if the event was handled. 304 | */ 305 | dispatchEvent: function(type, var_args) { 306 | if (!this.listeners_[type]) 307 | return false; 308 | for (var i = 0; i < this.listeners_[type].length; i++) { 309 | if (this.listeners_[type][i].apply( 310 | /* this */ null, 311 | /* var_args */ Array.prototype.slice.call(arguments, 1))) { 312 | return true; 313 | } 314 | } 315 | } 316 | }; 317 | 318 | /** 319 | * HttpServer provides a lightweight Http web server. Currently it only 320 | * supports GET requests and upgrading to other protocols (i.e. WebSockets). 321 | * @constructor 322 | */ 323 | function HttpServer() { 324 | EventSource.apply(this); 325 | this.readyState_ = 0; 326 | } 327 | 328 | HttpServer.prototype = { 329 | __proto__: EventSource.prototype, 330 | 331 | /** 332 | * Listen for connections on |port| using the interface |host|. 333 | * @param {number} port The port to listen for incoming connections on. 334 | * @param {string=} opt_host The host interface to listen for connections on. 335 | * This will default to 0.0.0.0 if not specified which will listen on 336 | * all interfaces. 337 | */ 338 | listen: function(port, opt_host) { 339 | var t = this; 340 | chrome.sockets.tcpServer.create(function(socketInfo) { 341 | chrome.sockets.tcpServer.onAccept.addListener(function(acceptInfo) { 342 | if (acceptInfo.socketId === socketInfo.socketId) 343 | t.readRequestFromSocket_(new PSocket(acceptInfo.clientSocketId)); 344 | }); 345 | 346 | chrome.sockets.tcpServer.listen( 347 | socketInfo.socketId, 348 | opt_host || '0.0.0.0', 349 | port, 350 | 50, 351 | function(result) { 352 | if (!result) { 353 | t.readyState_ = 1; 354 | } 355 | else { 356 | console.log( 357 | 'listen error ' + 358 | chrome.runtime.lastError.message + 359 | ' (normal if another instance is already serving requests)'); 360 | } 361 | }); 362 | }); 363 | }, 364 | 365 | readRequestFromSocket_: function(pSocket) { 366 | var t = this; 367 | var requestData = ''; 368 | var endIndex = 0; 369 | var onDataRead = function(data) { 370 | requestData += arrayBufferToString(data).replace(/\r\n/g, '\n'); 371 | // Check for end of request. 372 | endIndex = requestData.indexOf('\n\n', endIndex); 373 | if (endIndex == -1) { 374 | endIndex = requestData.length - 1; 375 | return pSocket.read().then(onDataRead); 376 | } 377 | 378 | var headers = requestData.substring(0, endIndex).split('\n'); 379 | var headerMap = {}; 380 | // headers[0] should be the Request-Line 381 | var requestLine = headers[0].split(' '); 382 | headerMap['method'] = requestLine[0]; 383 | headerMap['url'] = requestLine[1]; 384 | headerMap['Http-Version'] = requestLine[2]; 385 | for (var i = 1; i < headers.length; i++) { 386 | requestLine = headers[i].split(':', 2); 387 | if (requestLine.length == 2) 388 | headerMap[requestLine[0]] = requestLine[1].trim(); 389 | } 390 | var request = new HttpRequest(headerMap, pSocket); 391 | t.onRequest_(request); 392 | }; 393 | 394 | pSocket.read().then(onDataRead).catch(function(e) { 395 | pSocket.close(); 396 | }); 397 | }, 398 | 399 | onRequest_: function(request) { 400 | var type = request.headers['Upgrade'] ? 'upgrade' : 'request'; 401 | var keepAlive = request.headers['Connection'] == 'keep-alive'; 402 | if (!this.dispatchEvent(type, request)) 403 | request.close(); 404 | else if (keepAlive) 405 | this.readRequestFromSocket_(request.pSocket_); 406 | }, 407 | }; 408 | 409 | // MIME types for common extensions. 410 | var extensionTypes = { 411 | 'css': 'text/css', 412 | 'html': 'text/html', 413 | 'htm': 'text/html', 414 | 'jpg': 'image/jpeg', 415 | 'jpeg': 'image/jpeg', 416 | 'js': 'text/javascript', 417 | 'png': 'image/png', 418 | 'svg': 'image/svg+xml', 419 | 'txt': 'text/plain'}; 420 | 421 | /** 422 | * Constructs an HttpRequest object which tracks all of the request headers and 423 | * socket for an active Http request. 424 | * @param {Object} headers The HTTP request headers. 425 | * @param {Object} pSocket The socket to use for the response. 426 | * @constructor 427 | */ 428 | function HttpRequest(headers, pSocket) { 429 | this.version = 'HTTP/1.1'; 430 | this.headers = headers; 431 | this.responseHeaders_ = {}; 432 | this.headersSent = false; 433 | this.pSocket_ = pSocket; 434 | this.writes_ = 0; 435 | this.bytesRemaining = 0; 436 | this.finished_ = false; 437 | this.readyState = 1; 438 | } 439 | 440 | HttpRequest.prototype = { 441 | __proto__: EventSource.prototype, 442 | 443 | /** 444 | * Closes the Http request. 445 | */ 446 | close: function() { 447 | // The socket for keep alive connections will be re-used by the server. 448 | // Just stop referencing or using the socket in this HttpRequest. 449 | if (this.headers['Connection'] != 'keep-alive') 450 | pSocket.close(); 451 | 452 | this.pSocket_ = null; 453 | this.readyState = 3; 454 | }, 455 | 456 | /** 457 | * Write the provided headers as a response to the request. 458 | * @param {int} responseCode The HTTP status code to respond with. 459 | * @param {Object} responseHeaders The response headers describing the 460 | * response. 461 | */ 462 | writeHead: function(responseCode, responseHeaders) { 463 | var headerString = this.version + ' ' + responseCode + ' ' + 464 | (responseMap[responseCode] || 'Unknown'); 465 | this.responseHeaders_ = responseHeaders; 466 | if (this.headers['Connection'] == 'keep-alive') 467 | responseHeaders['Connection'] = 'keep-alive'; 468 | if (!responseHeaders['Content-Length'] && responseHeaders['Connection'] == 'keep-alive') 469 | responseHeaders['Transfer-Encoding'] = 'chunked'; 470 | for (var i in responseHeaders) { 471 | headerString += '\r\n' + i + ': ' + responseHeaders[i]; 472 | } 473 | headerString += '\r\n\r\n'; 474 | this.write_(stringToArrayBuffer(headerString)); 475 | }, 476 | 477 | /** 478 | * Writes data to the response stream. 479 | * @param {string|ArrayBuffer} data The data to write to the stream. 480 | */ 481 | write: function(data) { 482 | if (this.responseHeaders_['Transfer-Encoding'] == 'chunked') { 483 | var newline = '\r\n'; 484 | var byteLength = (data instanceof ArrayBuffer) ? data.byteLength : data.length; 485 | var chunkLength = byteLength.toString(16).toUpperCase() + newline; 486 | var buffer = new ArrayBuffer(chunkLength.length + byteLength + newline.length); 487 | var bufferView = new Uint8Array(buffer); 488 | for (var i = 0; i < chunkLength.length; i++) 489 | bufferView[i] = chunkLength.charCodeAt(i); 490 | if (data instanceof ArrayBuffer) { 491 | bufferView.set(new Uint8Array(data), chunkLength.length); 492 | } else { 493 | for (var i = 0; i < data.length; i++) 494 | bufferView[chunkLength.length + i] = data.charCodeAt(i); 495 | } 496 | for (var i = 0; i < newline.length; i++) 497 | bufferView[chunkLength.length + byteLength + i] = newline.charCodeAt(i); 498 | data = buffer; 499 | } else if (!(data instanceof ArrayBuffer)) { 500 | data = stringToArrayBuffer(data); 501 | } 502 | this.write_(data); 503 | }, 504 | 505 | /** 506 | * Finishes the HTTP response writing |data| before closing. 507 | * @param {string|ArrayBuffer=} opt_data Optional data to write to the stream 508 | * before closing it. 509 | */ 510 | end: function(opt_data) { 511 | if (opt_data) 512 | this.write(opt_data); 513 | if (this.responseHeaders_['Transfer-Encoding'] == 'chunked') 514 | this.write(''); 515 | this.finished_ = true; 516 | this.checkFinished_(); 517 | }, 518 | 519 | /** 520 | * Automatically serve the given |url| request. 521 | * @param {string} url The URL to fetch the file to be served from. This is 522 | * retrieved via an XmlHttpRequest and served as the response to the 523 | * request. 524 | */ 525 | serveUrl: function(url) { 526 | var t = this; 527 | var xhr = new XMLHttpRequest(); 528 | xhr.onloadend = function() { 529 | var type = 'text/plain'; 530 | if (this.getResponseHeader('Content-Type')) { 531 | type = this.getResponseHeader('Content-Type'); 532 | } else if (url.indexOf('.') != -1) { 533 | var extension = url.substr(url.indexOf('.') + 1); 534 | type = extensionTypes[extension] || type; 535 | } 536 | console.log('Served ' + url); 537 | var contentLength = this.getResponseHeader('Content-Length'); 538 | if (xhr.status == 200) 539 | contentLength = (this.response && this.response.byteLength) || 0; 540 | t.writeHead(this.status, { 541 | 'Content-Type': type, 542 | 'Content-Length': contentLength}); 543 | t.end(this.response); 544 | }; 545 | xhr.open('GET', url, true); 546 | xhr.responseType = 'arraybuffer'; 547 | xhr.send(); 548 | }, 549 | 550 | write_: function(array) { 551 | var t = this; 552 | this.bytesRemaining += array.byteLength; 553 | this.pSocket_.write(array).then(function(bytesWritten) { 554 | t.bytesRemaining -= bytesWritten; 555 | t.checkFinished_(); 556 | }).catch(function(e) { 557 | console.error(e.message); 558 | return; 559 | }); 560 | }, 561 | 562 | checkFinished_: function() { 563 | if (!this.finished_ || this.bytesRemaining > 0) 564 | return; 565 | this.close(); 566 | } 567 | }; 568 | 569 | /** 570 | * Constructs a server which is capable of accepting WebSocket connections. 571 | * @param {HttpServer} httpServer The Http Server to listen and handle 572 | * WebSocket upgrade requests on. 573 | * @constructor 574 | */ 575 | function WebSocketServer(httpServer) { 576 | EventSource.apply(this); 577 | httpServer.addEventListener('upgrade', this.upgradeToWebSocket_.bind(this)); 578 | } 579 | 580 | WebSocketServer.prototype = { 581 | __proto__: EventSource.prototype, 582 | 583 | upgradeToWebSocket_: function(request) { 584 | if (request.headers['Upgrade'] != 'websocket' || 585 | !request.headers['Sec-WebSocket-Key']) { 586 | return false; 587 | } 588 | 589 | if (this.dispatchEvent('request', new WebSocketRequest(request))) { 590 | if (request.pSocket_) 591 | request.reject(); 592 | return true; 593 | } 594 | 595 | return false; 596 | } 597 | }; 598 | 599 | /** 600 | * Constructs a WebSocket request object from an Http request. This invalidates 601 | * the Http request's socket and offers accept and reject methods for accepting 602 | * and rejecting the WebSocket upgrade request. 603 | * @param {HttpRequest} httpRequest The HTTP request to upgrade. 604 | */ 605 | function WebSocketRequest(httpRequest) { 606 | // We'll assume control of the socket for this request. 607 | HttpRequest.apply(this, [httpRequest.headers, httpRequest.pSocket_]); 608 | httpRequest.pSocket_ = null; 609 | } 610 | 611 | WebSocketRequest.prototype = { 612 | __proto__: HttpRequest.prototype, 613 | 614 | /** 615 | * Accepts the WebSocket request. 616 | * @return {WebSocketServerSocket} The websocket for the accepted request. 617 | */ 618 | accept: function() { 619 | // Construct WebSocket response key. 620 | var clientKey = this.headers['Sec-WebSocket-Key']; 621 | var toArray = function(str) { 622 | var a = []; 623 | for (var i = 0; i < str.length; i++) { 624 | a.push(str.charCodeAt(i)); 625 | } 626 | return a; 627 | } 628 | var toString = function(a) { 629 | var str = ''; 630 | for (var i = 0; i < a.length; i++) { 631 | str += String.fromCharCode(a[i]); 632 | } 633 | return str; 634 | } 635 | 636 | // Magic string used for websocket connection key hashing: 637 | // http://en.wikipedia.org/wiki/WebSocket 638 | var magicStr = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; 639 | 640 | // clientKey is base64 encoded key. 641 | clientKey += magicStr; 642 | var sha1 = new Sha1(); 643 | sha1.reset(); 644 | sha1.update(toArray(clientKey)); 645 | var responseKey = btoa(toString(sha1.digest())); 646 | var responseHeader = { 647 | 'Upgrade': 'websocket', 648 | 'Connection': 'Upgrade', 649 | 'Sec-WebSocket-Accept': responseKey}; 650 | if (this.headers['Sec-WebSocket-Protocol']) 651 | responseHeader['Sec-WebSocket-Protocol'] = this.headers['Sec-WebSocket-Protocol']; 652 | this.writeHead(101, responseHeader); 653 | var socket = new WebSocketServerSocket(this.pSocket_); 654 | // Detach the socket so that we don't use it anymore. 655 | this.pSocket_ = 0; 656 | return socket; 657 | }, 658 | 659 | /** 660 | * Rejects the WebSocket request, closing the connection. 661 | */ 662 | reject: function() { 663 | this.close(); 664 | } 665 | } 666 | 667 | /** 668 | * Constructs a WebSocketServerSocket using the given socketId. This should be 669 | * a socket which has already been upgraded from an Http request. 670 | * @param {number} socketId The socket id with an active websocket connection. 671 | */ 672 | function WebSocketServerSocket(pSocket) { 673 | this.pSocket_ = pSocket; 674 | this.readyState = 1; 675 | EventSource.apply(this); 676 | this.readFromSocket_(); 677 | } 678 | 679 | WebSocketServerSocket.prototype = { 680 | __proto__: EventSource.prototype, 681 | 682 | /** 683 | * Send |data| on the WebSocket. 684 | * @param {string|Array.|ArrayBuffer} data The data to send over the WebSocket. 685 | */ 686 | send: function(data) { 687 | // WebSocket must specify opcode when send frame. 688 | // The opcode for data frame is 1(text) or 2(binary). 689 | if (typeof data == 'string' || data instanceof String) { 690 | this.sendFrame_(1, data); 691 | } else { 692 | this.sendFrame_(2, data); 693 | } 694 | }, 695 | 696 | /** 697 | * Begin closing the WebSocket. Note that the WebSocket protocol uses a 698 | * handshake to close the connection, so this call will begin the closing 699 | * process. 700 | */ 701 | close: function() { 702 | if (this.readyState === 1) { 703 | this.sendFrame_(8); 704 | this.readyState = 2; 705 | } 706 | }, 707 | 708 | readFromSocket_: function() { 709 | var t = this; 710 | var data = []; 711 | var message = ''; 712 | var fragmentedOp = 0; 713 | var fragmentedMessages = []; 714 | 715 | var onDataRead = function(dataBuffer) { 716 | var a = new Uint8Array(dataBuffer); 717 | for (var i = 0; i < a.length; i++) 718 | data.push(a[i]); 719 | 720 | while (data.length) { 721 | var length_code = -1; 722 | var data_start = 6; 723 | var mask; 724 | var fin = (data[0] & 128) >> 7; 725 | var op = data[0] & 15; 726 | 727 | if (data.length > 1) 728 | length_code = data[1] & 127; 729 | if (length_code > 125) { 730 | if ((length_code == 126 && data.length > 7) || 731 | (length_code == 127 && data.length > 14)) { 732 | if (length_code == 126) { 733 | length_code = data[2] * 256 + data[3]; 734 | mask = data.slice(4, 8); 735 | data_start = 8; 736 | } else if (length_code == 127) { 737 | length_code = 0; 738 | for (var i = 0; i < 8; i++) { 739 | length_code = length_code * 256 + data[2 + i]; 740 | } 741 | mask = data.slice(10, 14); 742 | data_start = 14; 743 | } 744 | } else { 745 | length_code = -1; // Insufficient data to compute length 746 | } 747 | } else { 748 | if (data.length > 5) 749 | mask = data.slice(2, 6); 750 | } 751 | 752 | if (length_code > -1 && data.length >= data_start + length_code) { 753 | var decoded = data.slice(data_start, data_start + length_code).map(function(byte, index) { 754 | return byte ^ mask[index % 4]; 755 | }); 756 | if (op == 1) { 757 | decoded = ary2utf8(decoded); 758 | } 759 | data = data.slice(data_start + length_code); 760 | if (fin && op > 0) { 761 | // Unfragmented message. 762 | if (!t.onFrame_(op, decoded)) 763 | return; 764 | } else { 765 | // Fragmented message. 766 | fragmentedOp = fragmentedOp || op; 767 | fragmentedMessages.push(decoded); 768 | if (fin) { 769 | var joinMessage = null; 770 | if (op == 1) { 771 | joinMessage = fragmentedMessagess.join(''); 772 | } else { 773 | joinMessage = fragmentedMessages.reduce(function(pre, cur) { 774 | return Array.prototype.push.apply(pre, cur); 775 | }, []); 776 | } 777 | if (!t.onFrame_(fragmentedOp, joinMessage)) 778 | return; 779 | fragmentedOp = 0; 780 | fragmentedMessages = []; 781 | } 782 | } 783 | } else { 784 | break; // Insufficient data, wait for more. 785 | } 786 | } 787 | 788 | return t.pSocket_.read().then(onDataRead); 789 | }; 790 | 791 | this.pSocket_.read().then(function(data) { 792 | return onDataRead(data); 793 | }).catch(function(e) { 794 | t.close_(); 795 | }); 796 | }, 797 | 798 | onFrame_: function(op, data) { 799 | if (op == 1 || op == 2) { 800 | if (typeof data == 'string' || data instanceof String) { 801 | // Don't do anything. 802 | } else if (Array.isArray(data)) { 803 | data = new Uint8Array(data).buffer; 804 | } else if (data instanceof ArrayBuffer) { 805 | // Don't do anything. 806 | } else { 807 | data = data.buffer; 808 | } 809 | this.dispatchEvent('message', {'data': data}); 810 | } else if (op == 8) { 811 | // A close message must be confirmed before the websocket is closed. 812 | if (this.readyState === 1) { 813 | this.sendFrame_(8); 814 | this.readyState = 2; 815 | } else { 816 | this.close_(); 817 | return false; 818 | } 819 | } 820 | return true; 821 | }, 822 | 823 | sendFrame_: function(op, data) { 824 | var t = this; 825 | var WebsocketFrameData = function(op, data) { 826 | var ary = data; 827 | if (typeof data == 'string' || data instanceof String) { 828 | ary = utf82ary(data); 829 | } 830 | if (Array.isArray(ary)) { 831 | ary = new Uint8Array(ary); 832 | } 833 | if (ary instanceof ArrayBuffer) { 834 | ary = new Uint8Array(ary); 835 | } 836 | ary = new Uint8Array(ary.buffer); 837 | var length = ary.length; 838 | if (ary.length > 65535) 839 | length += 10; 840 | else if (ary.length > 125) 841 | length += 4; 842 | else 843 | length += 2; 844 | var lengthBytes = 0; 845 | var buffer = new ArrayBuffer(length); 846 | var bv = new Uint8Array(buffer); 847 | bv[0] = 128 | (op & 15); // Fin and type text. 848 | bv[1] = ary.length > 65535 ? 127 : 849 | (ary.length > 125 ? 126 : ary.length); 850 | if (ary.length > 65535) 851 | lengthBytes = 8; 852 | else if (ary.length > 125) 853 | lengthBytes = 2; 854 | var len = ary.length; 855 | for (var i = lengthBytes - 1; i >= 0; i--) { 856 | bv[2 + i] = len & 255; 857 | len = len >> 8; 858 | } 859 | var dataStart = lengthBytes + 2; 860 | for (var i = 0; i < ary.length; i++) { 861 | bv[dataStart + i] = ary[i]; 862 | } 863 | return buffer; 864 | } 865 | var array = WebsocketFrameData(op, data || ''); 866 | this.pSocket_.write(array).then(function(bytesWritten) { 867 | if (bytesWritten !== array.byteLength) 868 | throw new Error('insufficient write'); 869 | }).catch(function(e) { 870 | t.close_(); 871 | }); 872 | }, 873 | 874 | close_: function() { 875 | if (this.readyState !== 3) { 876 | this.pSocket_.close(); 877 | this.readyState = 3; 878 | this.dispatchEvent('close'); 879 | } 880 | } 881 | }; 882 | 883 | return { 884 | 'Server': HttpServer, 885 | 'WebSocketServer': WebSocketServer, 886 | }; 887 | }(); 888 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | ansi-gray@^0.1.1: 6 | version "0.1.1" 7 | resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" 8 | dependencies: 9 | ansi-wrap "0.1.0" 10 | 11 | ansi-regex@^2.0.0: 12 | version "2.1.1" 13 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 14 | 15 | ansi-styles@^2.2.1: 16 | version "2.2.1" 17 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 18 | 19 | ansi-wrap@0.1.0: 20 | version "0.1.0" 21 | resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" 22 | 23 | archy@^1.0.0: 24 | version "1.0.0" 25 | resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" 26 | 27 | arr-diff@^4.0.0: 28 | version "4.0.0" 29 | resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" 30 | 31 | arr-flatten@^1.1.0: 32 | version "1.1.0" 33 | resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" 34 | 35 | arr-union@^3.1.0: 36 | version "3.1.0" 37 | resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" 38 | 39 | array-differ@^1.0.0: 40 | version "1.0.0" 41 | resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" 42 | 43 | array-each@^1.0.1: 44 | version "1.0.1" 45 | resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" 46 | 47 | array-slice@^1.0.0: 48 | version "1.1.0" 49 | resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" 50 | 51 | array-uniq@^1.0.2: 52 | version "1.0.3" 53 | resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" 54 | 55 | array-unique@^0.3.2: 56 | version "0.3.2" 57 | resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" 58 | 59 | assign-symbols@^1.0.0: 60 | version "1.0.0" 61 | resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" 62 | 63 | atob@^2.0.0: 64 | version "2.0.3" 65 | resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d" 66 | 67 | balanced-match@^1.0.0: 68 | version "1.0.0" 69 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 70 | 71 | base@^0.11.1: 72 | version "0.11.2" 73 | resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" 74 | dependencies: 75 | cache-base "^1.0.1" 76 | class-utils "^0.3.5" 77 | component-emitter "^1.2.1" 78 | define-property "^1.0.0" 79 | isobject "^3.0.1" 80 | mixin-deep "^1.2.0" 81 | pascalcase "^0.1.1" 82 | 83 | beeper@^1.0.0: 84 | version "1.1.1" 85 | resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" 86 | 87 | brace-expansion@^1.0.0: 88 | version "1.1.8" 89 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" 90 | dependencies: 91 | balanced-match "^1.0.0" 92 | concat-map "0.0.1" 93 | 94 | braces@^2.3.0: 95 | version "2.3.0" 96 | resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.0.tgz#a46941cb5fb492156b3d6a656e06c35364e3e66e" 97 | dependencies: 98 | arr-flatten "^1.1.0" 99 | array-unique "^0.3.2" 100 | define-property "^1.0.0" 101 | extend-shallow "^2.0.1" 102 | fill-range "^4.0.0" 103 | isobject "^3.0.1" 104 | repeat-element "^1.1.2" 105 | snapdragon "^0.8.1" 106 | snapdragon-node "^2.0.1" 107 | split-string "^3.0.2" 108 | to-regex "^3.0.1" 109 | 110 | cache-base@^1.0.1: 111 | version "1.0.1" 112 | resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" 113 | dependencies: 114 | collection-visit "^1.0.0" 115 | component-emitter "^1.2.1" 116 | get-value "^2.0.6" 117 | has-value "^1.0.0" 118 | isobject "^3.0.1" 119 | set-value "^2.0.0" 120 | to-object-path "^0.3.0" 121 | union-value "^1.0.0" 122 | unset-value "^1.0.0" 123 | 124 | chalk@^1.0.0: 125 | version "1.1.3" 126 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 127 | dependencies: 128 | ansi-styles "^2.2.1" 129 | escape-string-regexp "^1.0.2" 130 | has-ansi "^2.0.0" 131 | strip-ansi "^3.0.0" 132 | supports-color "^2.0.0" 133 | 134 | class-utils@^0.3.5: 135 | version "0.3.6" 136 | resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" 137 | dependencies: 138 | arr-union "^3.1.0" 139 | define-property "^0.2.5" 140 | isobject "^3.0.0" 141 | static-extend "^0.1.1" 142 | 143 | clone-stats@^0.0.1: 144 | version "0.0.1" 145 | resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" 146 | 147 | clone@^0.2.0: 148 | version "0.2.0" 149 | resolved "https://registry.yarnpkg.com/clone/-/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" 150 | 151 | clone@^1.0.0, clone@^1.0.2: 152 | version "1.0.3" 153 | resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f" 154 | 155 | collection-visit@^1.0.0: 156 | version "1.0.0" 157 | resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" 158 | dependencies: 159 | map-visit "^1.0.0" 160 | object-visit "^1.0.0" 161 | 162 | color-support@^1.1.3: 163 | version "1.1.3" 164 | resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" 165 | 166 | component-emitter@^1.2.1: 167 | version "1.2.1" 168 | resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" 169 | 170 | concat-map@0.0.1: 171 | version "0.0.1" 172 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 173 | 174 | copy-descriptor@^0.1.0: 175 | version "0.1.1" 176 | resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" 177 | 178 | core-util-is@~1.0.0: 179 | version "1.0.2" 180 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 181 | 182 | dateformat@^2.0.0: 183 | version "2.2.0" 184 | resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" 185 | 186 | debug@^2.2.0, debug@^2.3.3: 187 | version "2.6.9" 188 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 189 | dependencies: 190 | ms "2.0.0" 191 | 192 | decode-uri-component@^0.2.0: 193 | version "0.2.0" 194 | resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" 195 | 196 | defaults@^1.0.0: 197 | version "1.0.3" 198 | resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" 199 | dependencies: 200 | clone "^1.0.2" 201 | 202 | define-property@^0.2.5: 203 | version "0.2.5" 204 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" 205 | dependencies: 206 | is-descriptor "^0.1.0" 207 | 208 | define-property@^1.0.0: 209 | version "1.0.0" 210 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" 211 | dependencies: 212 | is-descriptor "^1.0.0" 213 | 214 | deprecated@^0.0.1: 215 | version "0.0.1" 216 | resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19" 217 | 218 | detect-file@^1.0.0: 219 | version "1.0.0" 220 | resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" 221 | 222 | duplexer2@0.0.2: 223 | version "0.0.2" 224 | resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" 225 | dependencies: 226 | readable-stream "~1.1.9" 227 | 228 | end-of-stream@~0.1.5: 229 | version "0.1.5" 230 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-0.1.5.tgz#8e177206c3c80837d85632e8b9359dfe8b2f6eaf" 231 | dependencies: 232 | once "~1.3.0" 233 | 234 | escape-string-regexp@^1.0.2: 235 | version "1.0.5" 236 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 237 | 238 | expand-brackets@^2.1.4: 239 | version "2.1.4" 240 | resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" 241 | dependencies: 242 | debug "^2.3.3" 243 | define-property "^0.2.5" 244 | extend-shallow "^2.0.1" 245 | posix-character-classes "^0.1.0" 246 | regex-not "^1.0.0" 247 | snapdragon "^0.8.1" 248 | to-regex "^3.0.1" 249 | 250 | expand-tilde@^2.0.0, expand-tilde@^2.0.2: 251 | version "2.0.2" 252 | resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" 253 | dependencies: 254 | homedir-polyfill "^1.0.1" 255 | 256 | extend-shallow@^2.0.1: 257 | version "2.0.1" 258 | resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" 259 | dependencies: 260 | is-extendable "^0.1.0" 261 | 262 | extend-shallow@^3.0.0: 263 | version "3.0.2" 264 | resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" 265 | dependencies: 266 | assign-symbols "^1.0.0" 267 | is-extendable "^1.0.1" 268 | 269 | extend@^3.0.0: 270 | version "3.0.1" 271 | resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" 272 | 273 | extglob@^2.0.2: 274 | version "2.0.4" 275 | resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" 276 | dependencies: 277 | array-unique "^0.3.2" 278 | define-property "^1.0.0" 279 | expand-brackets "^2.1.4" 280 | extend-shallow "^2.0.1" 281 | fragment-cache "^0.2.1" 282 | regex-not "^1.0.0" 283 | snapdragon "^0.8.1" 284 | to-regex "^3.0.1" 285 | 286 | fancy-log@^1.1.0: 287 | version "1.3.2" 288 | resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.2.tgz#f41125e3d84f2e7d89a43d06d958c8f78be16be1" 289 | dependencies: 290 | ansi-gray "^0.1.1" 291 | color-support "^1.1.3" 292 | time-stamp "^1.0.0" 293 | 294 | fill-range@^4.0.0: 295 | version "4.0.0" 296 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" 297 | dependencies: 298 | extend-shallow "^2.0.1" 299 | is-number "^3.0.0" 300 | repeat-string "^1.6.1" 301 | to-regex-range "^2.1.0" 302 | 303 | find-index@^0.1.1: 304 | version "0.1.1" 305 | resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" 306 | 307 | findup-sync@^2.0.0: 308 | version "2.0.0" 309 | resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" 310 | dependencies: 311 | detect-file "^1.0.0" 312 | is-glob "^3.1.0" 313 | micromatch "^3.0.4" 314 | resolve-dir "^1.0.1" 315 | 316 | fined@^1.0.1: 317 | version "1.1.0" 318 | resolved "https://registry.yarnpkg.com/fined/-/fined-1.1.0.tgz#b37dc844b76a2f5e7081e884f7c0ae344f153476" 319 | dependencies: 320 | expand-tilde "^2.0.2" 321 | is-plain-object "^2.0.3" 322 | object.defaults "^1.1.0" 323 | object.pick "^1.2.0" 324 | parse-filepath "^1.0.1" 325 | 326 | first-chunk-stream@^1.0.0: 327 | version "1.0.0" 328 | resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" 329 | 330 | flagged-respawn@^1.0.0: 331 | version "1.0.0" 332 | resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.0.tgz#4e79ae9b2eb38bf86b3bb56bf3e0a56aa5fcabd7" 333 | 334 | for-in@^1.0.1, for-in@^1.0.2: 335 | version "1.0.2" 336 | resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" 337 | 338 | for-own@^1.0.0: 339 | version "1.0.0" 340 | resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" 341 | dependencies: 342 | for-in "^1.0.1" 343 | 344 | fragment-cache@^0.2.1: 345 | version "0.2.1" 346 | resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" 347 | dependencies: 348 | map-cache "^0.2.2" 349 | 350 | gaze@^0.5.1: 351 | version "0.5.2" 352 | resolved "https://registry.yarnpkg.com/gaze/-/gaze-0.5.2.tgz#40b709537d24d1d45767db5a908689dfe69ac44f" 353 | dependencies: 354 | globule "~0.1.0" 355 | 356 | get-value@^2.0.3, get-value@^2.0.6: 357 | version "2.0.6" 358 | resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" 359 | 360 | glob-stream@^3.1.5: 361 | version "3.1.18" 362 | resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-3.1.18.tgz#9170a5f12b790306fdfe598f313f8f7954fd143b" 363 | dependencies: 364 | glob "^4.3.1" 365 | glob2base "^0.0.12" 366 | minimatch "^2.0.1" 367 | ordered-read-streams "^0.1.0" 368 | through2 "^0.6.1" 369 | unique-stream "^1.0.0" 370 | 371 | glob-watcher@^0.0.6: 372 | version "0.0.6" 373 | resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-0.0.6.tgz#b95b4a8df74b39c83298b0c05c978b4d9a3b710b" 374 | dependencies: 375 | gaze "^0.5.1" 376 | 377 | glob2base@^0.0.12: 378 | version "0.0.12" 379 | resolved "https://registry.yarnpkg.com/glob2base/-/glob2base-0.0.12.tgz#9d419b3e28f12e83a362164a277055922c9c0d56" 380 | dependencies: 381 | find-index "^0.1.1" 382 | 383 | glob@^4.3.1: 384 | version "4.5.3" 385 | resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f" 386 | dependencies: 387 | inflight "^1.0.4" 388 | inherits "2" 389 | minimatch "^2.0.1" 390 | once "^1.3.0" 391 | 392 | glob@~3.1.21: 393 | version "3.1.21" 394 | resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd" 395 | dependencies: 396 | graceful-fs "~1.2.0" 397 | inherits "1" 398 | minimatch "~0.2.11" 399 | 400 | global-modules@^1.0.0: 401 | version "1.0.0" 402 | resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" 403 | dependencies: 404 | global-prefix "^1.0.1" 405 | is-windows "^1.0.1" 406 | resolve-dir "^1.0.0" 407 | 408 | global-prefix@^1.0.1: 409 | version "1.0.2" 410 | resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" 411 | dependencies: 412 | expand-tilde "^2.0.2" 413 | homedir-polyfill "^1.0.1" 414 | ini "^1.3.4" 415 | is-windows "^1.0.1" 416 | which "^1.2.14" 417 | 418 | globule@~0.1.0: 419 | version "0.1.0" 420 | resolved "https://registry.yarnpkg.com/globule/-/globule-0.1.0.tgz#d9c8edde1da79d125a151b79533b978676346ae5" 421 | dependencies: 422 | glob "~3.1.21" 423 | lodash "~1.0.1" 424 | minimatch "~0.2.11" 425 | 426 | glogg@^1.0.0: 427 | version "1.0.1" 428 | resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.1.tgz#dcf758e44789cc3f3d32c1f3562a3676e6a34810" 429 | dependencies: 430 | sparkles "^1.0.0" 431 | 432 | graceful-fs@^3.0.0: 433 | version "3.0.11" 434 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-3.0.11.tgz#7613c778a1afea62f25c630a086d7f3acbbdd818" 435 | dependencies: 436 | natives "^1.1.0" 437 | 438 | graceful-fs@~1.2.0: 439 | version "1.2.3" 440 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364" 441 | 442 | gulp-util@^3.0.0: 443 | version "3.0.8" 444 | resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" 445 | dependencies: 446 | array-differ "^1.0.0" 447 | array-uniq "^1.0.2" 448 | beeper "^1.0.0" 449 | chalk "^1.0.0" 450 | dateformat "^2.0.0" 451 | fancy-log "^1.1.0" 452 | gulplog "^1.0.0" 453 | has-gulplog "^0.1.0" 454 | lodash._reescape "^3.0.0" 455 | lodash._reevaluate "^3.0.0" 456 | lodash._reinterpolate "^3.0.0" 457 | lodash.template "^3.0.0" 458 | minimist "^1.1.0" 459 | multipipe "^0.1.2" 460 | object-assign "^3.0.0" 461 | replace-ext "0.0.1" 462 | through2 "^2.0.0" 463 | vinyl "^0.5.0" 464 | 465 | gulp@^3.9.1: 466 | version "3.9.1" 467 | resolved "https://registry.yarnpkg.com/gulp/-/gulp-3.9.1.tgz#571ce45928dd40af6514fc4011866016c13845b4" 468 | dependencies: 469 | archy "^1.0.0" 470 | chalk "^1.0.0" 471 | deprecated "^0.0.1" 472 | gulp-util "^3.0.0" 473 | interpret "^1.0.0" 474 | liftoff "^2.1.0" 475 | minimist "^1.1.0" 476 | orchestrator "^0.3.0" 477 | pretty-hrtime "^1.0.0" 478 | semver "^4.1.0" 479 | tildify "^1.0.0" 480 | v8flags "^2.0.2" 481 | vinyl-fs "^0.3.0" 482 | 483 | gulplog@^1.0.0: 484 | version "1.0.0" 485 | resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" 486 | dependencies: 487 | glogg "^1.0.0" 488 | 489 | has-ansi@^2.0.0: 490 | version "2.0.0" 491 | resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 492 | dependencies: 493 | ansi-regex "^2.0.0" 494 | 495 | has-gulplog@^0.1.0: 496 | version "0.1.0" 497 | resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" 498 | dependencies: 499 | sparkles "^1.0.0" 500 | 501 | has-value@^0.3.1: 502 | version "0.3.1" 503 | resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" 504 | dependencies: 505 | get-value "^2.0.3" 506 | has-values "^0.1.4" 507 | isobject "^2.0.0" 508 | 509 | has-value@^1.0.0: 510 | version "1.0.0" 511 | resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" 512 | dependencies: 513 | get-value "^2.0.6" 514 | has-values "^1.0.0" 515 | isobject "^3.0.0" 516 | 517 | has-values@^0.1.4: 518 | version "0.1.4" 519 | resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" 520 | 521 | has-values@^1.0.0: 522 | version "1.0.0" 523 | resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" 524 | dependencies: 525 | is-number "^3.0.0" 526 | kind-of "^4.0.0" 527 | 528 | homedir-polyfill@^1.0.1: 529 | version "1.0.1" 530 | resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" 531 | dependencies: 532 | parse-passwd "^1.0.0" 533 | 534 | inflight@^1.0.4: 535 | version "1.0.6" 536 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 537 | dependencies: 538 | once "^1.3.0" 539 | wrappy "1" 540 | 541 | inherits@1: 542 | version "1.0.2" 543 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" 544 | 545 | inherits@2, inherits@~2.0.1, inherits@~2.0.3: 546 | version "2.0.3" 547 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 548 | 549 | ini@^1.3.4: 550 | version "1.3.5" 551 | resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" 552 | 553 | interpret@^1.0.0: 554 | version "1.1.0" 555 | resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" 556 | 557 | is-absolute@^1.0.0: 558 | version "1.0.0" 559 | resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" 560 | dependencies: 561 | is-relative "^1.0.0" 562 | is-windows "^1.0.1" 563 | 564 | is-accessor-descriptor@^0.1.6: 565 | version "0.1.6" 566 | resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" 567 | dependencies: 568 | kind-of "^3.0.2" 569 | 570 | is-accessor-descriptor@^1.0.0: 571 | version "1.0.0" 572 | resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" 573 | dependencies: 574 | kind-of "^6.0.0" 575 | 576 | is-buffer@^1.1.5: 577 | version "1.1.6" 578 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" 579 | 580 | is-data-descriptor@^0.1.4: 581 | version "0.1.4" 582 | resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" 583 | dependencies: 584 | kind-of "^3.0.2" 585 | 586 | is-data-descriptor@^1.0.0: 587 | version "1.0.0" 588 | resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" 589 | dependencies: 590 | kind-of "^6.0.0" 591 | 592 | is-descriptor@^0.1.0: 593 | version "0.1.6" 594 | resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" 595 | dependencies: 596 | is-accessor-descriptor "^0.1.6" 597 | is-data-descriptor "^0.1.4" 598 | kind-of "^5.0.0" 599 | 600 | is-descriptor@^1.0.0: 601 | version "1.0.2" 602 | resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" 603 | dependencies: 604 | is-accessor-descriptor "^1.0.0" 605 | is-data-descriptor "^1.0.0" 606 | kind-of "^6.0.2" 607 | 608 | is-extendable@^0.1.0, is-extendable@^0.1.1: 609 | version "0.1.1" 610 | resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" 611 | 612 | is-extendable@^1.0.1: 613 | version "1.0.1" 614 | resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" 615 | dependencies: 616 | is-plain-object "^2.0.4" 617 | 618 | is-extglob@^2.1.0: 619 | version "2.1.1" 620 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 621 | 622 | is-glob@^3.1.0: 623 | version "3.1.0" 624 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" 625 | dependencies: 626 | is-extglob "^2.1.0" 627 | 628 | is-number@^3.0.0: 629 | version "3.0.0" 630 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" 631 | dependencies: 632 | kind-of "^3.0.2" 633 | 634 | is-odd@^1.0.0: 635 | version "1.0.0" 636 | resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-1.0.0.tgz#3b8a932eb028b3775c39bb09e91767accdb69088" 637 | dependencies: 638 | is-number "^3.0.0" 639 | 640 | is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: 641 | version "2.0.4" 642 | resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" 643 | dependencies: 644 | isobject "^3.0.1" 645 | 646 | is-relative@^1.0.0: 647 | version "1.0.0" 648 | resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" 649 | dependencies: 650 | is-unc-path "^1.0.0" 651 | 652 | is-unc-path@^1.0.0: 653 | version "1.0.0" 654 | resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" 655 | dependencies: 656 | unc-path-regex "^0.1.2" 657 | 658 | is-utf8@^0.2.0: 659 | version "0.2.1" 660 | resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" 661 | 662 | is-windows@^1.0.1: 663 | version "1.0.1" 664 | resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.1.tgz#310db70f742d259a16a369202b51af84233310d9" 665 | 666 | isarray@0.0.1: 667 | version "0.0.1" 668 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" 669 | 670 | isarray@1.0.0, isarray@~1.0.0: 671 | version "1.0.0" 672 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 673 | 674 | isexe@^2.0.0: 675 | version "2.0.0" 676 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 677 | 678 | isobject@^2.0.0: 679 | version "2.1.0" 680 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" 681 | dependencies: 682 | isarray "1.0.0" 683 | 684 | isobject@^3.0.0, isobject@^3.0.1: 685 | version "3.0.1" 686 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" 687 | 688 | kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.1.0, kind-of@^3.2.0: 689 | version "3.2.2" 690 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" 691 | dependencies: 692 | is-buffer "^1.1.5" 693 | 694 | kind-of@^4.0.0: 695 | version "4.0.0" 696 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" 697 | dependencies: 698 | is-buffer "^1.1.5" 699 | 700 | kind-of@^5.0.0, kind-of@^5.0.2: 701 | version "5.1.0" 702 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" 703 | 704 | kind-of@^6.0.0, kind-of@^6.0.2: 705 | version "6.0.2" 706 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" 707 | 708 | lazy-cache@^2.0.2: 709 | version "2.0.2" 710 | resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-2.0.2.tgz#b9190a4f913354694840859f8a8f7084d8822264" 711 | dependencies: 712 | set-getter "^0.1.0" 713 | 714 | liftoff@^2.1.0: 715 | version "2.5.0" 716 | resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.5.0.tgz#2009291bb31cea861bbf10a7c15a28caf75c31ec" 717 | dependencies: 718 | extend "^3.0.0" 719 | findup-sync "^2.0.0" 720 | fined "^1.0.1" 721 | flagged-respawn "^1.0.0" 722 | is-plain-object "^2.0.4" 723 | object.map "^1.0.0" 724 | rechoir "^0.6.2" 725 | resolve "^1.1.7" 726 | 727 | lodash._basecopy@^3.0.0: 728 | version "3.0.1" 729 | resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" 730 | 731 | lodash._basetostring@^3.0.0: 732 | version "3.0.1" 733 | resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" 734 | 735 | lodash._basevalues@^3.0.0: 736 | version "3.0.0" 737 | resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" 738 | 739 | lodash._getnative@^3.0.0: 740 | version "3.9.1" 741 | resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" 742 | 743 | lodash._isiterateecall@^3.0.0: 744 | version "3.0.9" 745 | resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" 746 | 747 | lodash._reescape@^3.0.0: 748 | version "3.0.0" 749 | resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" 750 | 751 | lodash._reevaluate@^3.0.0: 752 | version "3.0.0" 753 | resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" 754 | 755 | lodash._reinterpolate@^3.0.0: 756 | version "3.0.0" 757 | resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" 758 | 759 | lodash._root@^3.0.0: 760 | version "3.0.1" 761 | resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" 762 | 763 | lodash.escape@^3.0.0: 764 | version "3.2.0" 765 | resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" 766 | dependencies: 767 | lodash._root "^3.0.0" 768 | 769 | lodash.isarguments@^3.0.0: 770 | version "3.1.0" 771 | resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 772 | 773 | lodash.isarray@^3.0.0: 774 | version "3.0.4" 775 | resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" 776 | 777 | lodash.keys@^3.0.0: 778 | version "3.1.2" 779 | resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" 780 | dependencies: 781 | lodash._getnative "^3.0.0" 782 | lodash.isarguments "^3.0.0" 783 | lodash.isarray "^3.0.0" 784 | 785 | lodash.restparam@^3.0.0: 786 | version "3.6.1" 787 | resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" 788 | 789 | lodash.template@^3.0.0: 790 | version "3.6.2" 791 | resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" 792 | dependencies: 793 | lodash._basecopy "^3.0.0" 794 | lodash._basetostring "^3.0.0" 795 | lodash._basevalues "^3.0.0" 796 | lodash._isiterateecall "^3.0.0" 797 | lodash._reinterpolate "^3.0.0" 798 | lodash.escape "^3.0.0" 799 | lodash.keys "^3.0.0" 800 | lodash.restparam "^3.0.0" 801 | lodash.templatesettings "^3.0.0" 802 | 803 | lodash.templatesettings@^3.0.0: 804 | version "3.1.1" 805 | resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" 806 | dependencies: 807 | lodash._reinterpolate "^3.0.0" 808 | lodash.escape "^3.0.0" 809 | 810 | lodash@~1.0.1: 811 | version "1.0.2" 812 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" 813 | 814 | lru-cache@2: 815 | version "2.7.3" 816 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" 817 | 818 | make-iterator@^1.0.0: 819 | version "1.0.0" 820 | resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.0.tgz#57bef5dc85d23923ba23767324d8e8f8f3d9694b" 821 | dependencies: 822 | kind-of "^3.1.0" 823 | 824 | map-cache@^0.2.0, map-cache@^0.2.2: 825 | version "0.2.2" 826 | resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" 827 | 828 | map-visit@^1.0.0: 829 | version "1.0.0" 830 | resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" 831 | dependencies: 832 | object-visit "^1.0.0" 833 | 834 | micromatch@^3.0.4: 835 | version "3.1.5" 836 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.5.tgz#d05e168c206472dfbca985bfef4f57797b4cd4ba" 837 | dependencies: 838 | arr-diff "^4.0.0" 839 | array-unique "^0.3.2" 840 | braces "^2.3.0" 841 | define-property "^1.0.0" 842 | extend-shallow "^2.0.1" 843 | extglob "^2.0.2" 844 | fragment-cache "^0.2.1" 845 | kind-of "^6.0.0" 846 | nanomatch "^1.2.5" 847 | object.pick "^1.3.0" 848 | regex-not "^1.0.0" 849 | snapdragon "^0.8.1" 850 | to-regex "^3.0.1" 851 | 852 | minimatch@^2.0.1: 853 | version "2.0.10" 854 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" 855 | dependencies: 856 | brace-expansion "^1.0.0" 857 | 858 | minimatch@~0.2.11: 859 | version "0.2.14" 860 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a" 861 | dependencies: 862 | lru-cache "2" 863 | sigmund "~1.0.0" 864 | 865 | minimist@0.0.8: 866 | version "0.0.8" 867 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 868 | 869 | minimist@^1.1.0: 870 | version "1.2.0" 871 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 872 | 873 | mixin-deep@^1.2.0: 874 | version "1.3.0" 875 | resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.0.tgz#47a8732ba97799457c8c1eca28f95132d7e8150a" 876 | dependencies: 877 | for-in "^1.0.2" 878 | is-extendable "^1.0.1" 879 | 880 | mkdirp@^0.5.0: 881 | version "0.5.1" 882 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 883 | dependencies: 884 | minimist "0.0.8" 885 | 886 | ms@2.0.0: 887 | version "2.0.0" 888 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 889 | 890 | multipipe@^0.1.2: 891 | version "0.1.2" 892 | resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" 893 | dependencies: 894 | duplexer2 "0.0.2" 895 | 896 | nanomatch@^1.2.5: 897 | version "1.2.7" 898 | resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.7.tgz#53cd4aa109ff68b7f869591fdc9d10daeeea3e79" 899 | dependencies: 900 | arr-diff "^4.0.0" 901 | array-unique "^0.3.2" 902 | define-property "^1.0.0" 903 | extend-shallow "^2.0.1" 904 | fragment-cache "^0.2.1" 905 | is-odd "^1.0.0" 906 | kind-of "^5.0.2" 907 | object.pick "^1.3.0" 908 | regex-not "^1.0.0" 909 | snapdragon "^0.8.1" 910 | to-regex "^3.0.1" 911 | 912 | natives@^1.1.0: 913 | version "1.1.1" 914 | resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.1.tgz#011acce1f7cbd87f7ba6b3093d6cd9392be1c574" 915 | 916 | object-assign@^3.0.0: 917 | version "3.0.0" 918 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" 919 | 920 | object-copy@^0.1.0: 921 | version "0.1.0" 922 | resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" 923 | dependencies: 924 | copy-descriptor "^0.1.0" 925 | define-property "^0.2.5" 926 | kind-of "^3.0.3" 927 | 928 | object-visit@^1.0.0: 929 | version "1.0.1" 930 | resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" 931 | dependencies: 932 | isobject "^3.0.0" 933 | 934 | object.defaults@^1.1.0: 935 | version "1.1.0" 936 | resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" 937 | dependencies: 938 | array-each "^1.0.1" 939 | array-slice "^1.0.0" 940 | for-own "^1.0.0" 941 | isobject "^3.0.0" 942 | 943 | object.map@^1.0.0: 944 | version "1.0.1" 945 | resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" 946 | dependencies: 947 | for-own "^1.0.0" 948 | make-iterator "^1.0.0" 949 | 950 | object.pick@^1.2.0, object.pick@^1.3.0: 951 | version "1.3.0" 952 | resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" 953 | dependencies: 954 | isobject "^3.0.1" 955 | 956 | once@^1.3.0: 957 | version "1.4.0" 958 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 959 | dependencies: 960 | wrappy "1" 961 | 962 | once@~1.3.0: 963 | version "1.3.3" 964 | resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" 965 | dependencies: 966 | wrappy "1" 967 | 968 | orchestrator@^0.3.0: 969 | version "0.3.8" 970 | resolved "https://registry.yarnpkg.com/orchestrator/-/orchestrator-0.3.8.tgz#14e7e9e2764f7315fbac184e506c7aa6df94ad7e" 971 | dependencies: 972 | end-of-stream "~0.1.5" 973 | sequencify "~0.0.7" 974 | stream-consume "~0.1.0" 975 | 976 | ordered-read-streams@^0.1.0: 977 | version "0.1.0" 978 | resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz#fd565a9af8eb4473ba69b6ed8a34352cb552f126" 979 | 980 | os-homedir@^1.0.0: 981 | version "1.0.2" 982 | resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" 983 | 984 | parse-filepath@^1.0.1: 985 | version "1.0.2" 986 | resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" 987 | dependencies: 988 | is-absolute "^1.0.0" 989 | map-cache "^0.2.0" 990 | path-root "^0.1.1" 991 | 992 | parse-passwd@^1.0.0: 993 | version "1.0.0" 994 | resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" 995 | 996 | pascalcase@^0.1.1: 997 | version "0.1.1" 998 | resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" 999 | 1000 | path-parse@^1.0.5: 1001 | version "1.0.5" 1002 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" 1003 | 1004 | path-root-regex@^0.1.0: 1005 | version "0.1.2" 1006 | resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" 1007 | 1008 | path-root@^0.1.1: 1009 | version "0.1.1" 1010 | resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" 1011 | dependencies: 1012 | path-root-regex "^0.1.0" 1013 | 1014 | posix-character-classes@^0.1.0: 1015 | version "0.1.1" 1016 | resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" 1017 | 1018 | pretty-hrtime@^1.0.0: 1019 | version "1.0.3" 1020 | resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" 1021 | 1022 | process-nextick-args@~1.0.6: 1023 | version "1.0.7" 1024 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" 1025 | 1026 | "readable-stream@>=1.0.33-1 <1.1.0-0": 1027 | version "1.0.34" 1028 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" 1029 | dependencies: 1030 | core-util-is "~1.0.0" 1031 | inherits "~2.0.1" 1032 | isarray "0.0.1" 1033 | string_decoder "~0.10.x" 1034 | 1035 | readable-stream@^2.1.5: 1036 | version "2.3.3" 1037 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" 1038 | dependencies: 1039 | core-util-is "~1.0.0" 1040 | inherits "~2.0.3" 1041 | isarray "~1.0.0" 1042 | process-nextick-args "~1.0.6" 1043 | safe-buffer "~5.1.1" 1044 | string_decoder "~1.0.3" 1045 | util-deprecate "~1.0.1" 1046 | 1047 | readable-stream@~1.1.9: 1048 | version "1.1.14" 1049 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" 1050 | dependencies: 1051 | core-util-is "~1.0.0" 1052 | inherits "~2.0.1" 1053 | isarray "0.0.1" 1054 | string_decoder "~0.10.x" 1055 | 1056 | rechoir@^0.6.2: 1057 | version "0.6.2" 1058 | resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" 1059 | dependencies: 1060 | resolve "^1.1.6" 1061 | 1062 | regex-not@^1.0.0: 1063 | version "1.0.0" 1064 | resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.0.tgz#42f83e39771622df826b02af176525d6a5f157f9" 1065 | dependencies: 1066 | extend-shallow "^2.0.1" 1067 | 1068 | repeat-element@^1.1.2: 1069 | version "1.1.2" 1070 | resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" 1071 | 1072 | repeat-string@^1.6.1: 1073 | version "1.6.1" 1074 | resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" 1075 | 1076 | replace-ext@0.0.1: 1077 | version "0.0.1" 1078 | resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" 1079 | 1080 | resolve-dir@^1.0.0, resolve-dir@^1.0.1: 1081 | version "1.0.1" 1082 | resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" 1083 | dependencies: 1084 | expand-tilde "^2.0.0" 1085 | global-modules "^1.0.0" 1086 | 1087 | resolve-url@^0.2.1: 1088 | version "0.2.1" 1089 | resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" 1090 | 1091 | resolve@^1.1.6, resolve@^1.1.7: 1092 | version "1.5.0" 1093 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" 1094 | dependencies: 1095 | path-parse "^1.0.5" 1096 | 1097 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 1098 | version "5.1.1" 1099 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" 1100 | 1101 | semver@^4.1.0: 1102 | version "4.3.6" 1103 | resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" 1104 | 1105 | sequencify@~0.0.7: 1106 | version "0.0.7" 1107 | resolved "https://registry.yarnpkg.com/sequencify/-/sequencify-0.0.7.tgz#90cff19d02e07027fd767f5ead3e7b95d1e7380c" 1108 | 1109 | set-getter@^0.1.0: 1110 | version "0.1.0" 1111 | resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376" 1112 | dependencies: 1113 | to-object-path "^0.3.0" 1114 | 1115 | set-value@^0.4.3: 1116 | version "0.4.3" 1117 | resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" 1118 | dependencies: 1119 | extend-shallow "^2.0.1" 1120 | is-extendable "^0.1.1" 1121 | is-plain-object "^2.0.1" 1122 | to-object-path "^0.3.0" 1123 | 1124 | set-value@^2.0.0: 1125 | version "2.0.0" 1126 | resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" 1127 | dependencies: 1128 | extend-shallow "^2.0.1" 1129 | is-extendable "^0.1.1" 1130 | is-plain-object "^2.0.3" 1131 | split-string "^3.0.1" 1132 | 1133 | sigmund@~1.0.0: 1134 | version "1.0.1" 1135 | resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" 1136 | 1137 | snapdragon-node@^2.0.1: 1138 | version "2.1.1" 1139 | resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" 1140 | dependencies: 1141 | define-property "^1.0.0" 1142 | isobject "^3.0.0" 1143 | snapdragon-util "^3.0.1" 1144 | 1145 | snapdragon-util@^3.0.1: 1146 | version "3.0.1" 1147 | resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" 1148 | dependencies: 1149 | kind-of "^3.2.0" 1150 | 1151 | snapdragon@^0.8.1: 1152 | version "0.8.1" 1153 | resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.1.tgz#e12b5487faded3e3dea0ac91e9400bf75b401370" 1154 | dependencies: 1155 | base "^0.11.1" 1156 | debug "^2.2.0" 1157 | define-property "^0.2.5" 1158 | extend-shallow "^2.0.1" 1159 | map-cache "^0.2.2" 1160 | source-map "^0.5.6" 1161 | source-map-resolve "^0.5.0" 1162 | use "^2.0.0" 1163 | 1164 | source-map-resolve@^0.5.0: 1165 | version "0.5.1" 1166 | resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a" 1167 | dependencies: 1168 | atob "^2.0.0" 1169 | decode-uri-component "^0.2.0" 1170 | resolve-url "^0.2.1" 1171 | source-map-url "^0.4.0" 1172 | urix "^0.1.0" 1173 | 1174 | source-map-url@^0.4.0: 1175 | version "0.4.0" 1176 | resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" 1177 | 1178 | source-map@^0.5.6: 1179 | version "0.5.7" 1180 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" 1181 | 1182 | sparkles@^1.0.0: 1183 | version "1.0.0" 1184 | resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" 1185 | 1186 | split-string@^3.0.1, split-string@^3.0.2: 1187 | version "3.1.0" 1188 | resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" 1189 | dependencies: 1190 | extend-shallow "^3.0.0" 1191 | 1192 | static-extend@^0.1.1: 1193 | version "0.1.2" 1194 | resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" 1195 | dependencies: 1196 | define-property "^0.2.5" 1197 | object-copy "^0.1.0" 1198 | 1199 | stream-consume@~0.1.0: 1200 | version "0.1.0" 1201 | resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.0.tgz#a41ead1a6d6081ceb79f65b061901b6d8f3d1d0f" 1202 | 1203 | string_decoder@~0.10.x: 1204 | version "0.10.31" 1205 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" 1206 | 1207 | string_decoder@~1.0.3: 1208 | version "1.0.3" 1209 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" 1210 | dependencies: 1211 | safe-buffer "~5.1.0" 1212 | 1213 | strip-ansi@^3.0.0: 1214 | version "3.0.1" 1215 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 1216 | dependencies: 1217 | ansi-regex "^2.0.0" 1218 | 1219 | strip-bom@^1.0.0: 1220 | version "1.0.0" 1221 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-1.0.0.tgz#85b8862f3844b5a6d5ec8467a93598173a36f794" 1222 | dependencies: 1223 | first-chunk-stream "^1.0.0" 1224 | is-utf8 "^0.2.0" 1225 | 1226 | supports-color@^2.0.0: 1227 | version "2.0.0" 1228 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 1229 | 1230 | through2@^0.6.1: 1231 | version "0.6.5" 1232 | resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" 1233 | dependencies: 1234 | readable-stream ">=1.0.33-1 <1.1.0-0" 1235 | xtend ">=4.0.0 <4.1.0-0" 1236 | 1237 | through2@^2.0.0: 1238 | version "2.0.3" 1239 | resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" 1240 | dependencies: 1241 | readable-stream "^2.1.5" 1242 | xtend "~4.0.1" 1243 | 1244 | tildify@^1.0.0: 1245 | version "1.2.0" 1246 | resolved "https://registry.yarnpkg.com/tildify/-/tildify-1.2.0.tgz#dcec03f55dca9b7aa3e5b04f21817eb56e63588a" 1247 | dependencies: 1248 | os-homedir "^1.0.0" 1249 | 1250 | time-stamp@^1.0.0: 1251 | version "1.1.0" 1252 | resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" 1253 | 1254 | to-object-path@^0.3.0: 1255 | version "0.3.0" 1256 | resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" 1257 | dependencies: 1258 | kind-of "^3.0.2" 1259 | 1260 | to-regex-range@^2.1.0: 1261 | version "2.1.1" 1262 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" 1263 | dependencies: 1264 | is-number "^3.0.0" 1265 | repeat-string "^1.6.1" 1266 | 1267 | to-regex@^3.0.1: 1268 | version "3.0.1" 1269 | resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.1.tgz#15358bee4a2c83bd76377ba1dc049d0f18837aae" 1270 | dependencies: 1271 | define-property "^0.2.5" 1272 | extend-shallow "^2.0.1" 1273 | regex-not "^1.0.0" 1274 | 1275 | unc-path-regex@^0.1.2: 1276 | version "0.1.2" 1277 | resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" 1278 | 1279 | union-value@^1.0.0: 1280 | version "1.0.0" 1281 | resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" 1282 | dependencies: 1283 | arr-union "^3.1.0" 1284 | get-value "^2.0.6" 1285 | is-extendable "^0.1.1" 1286 | set-value "^0.4.3" 1287 | 1288 | unique-stream@^1.0.0: 1289 | version "1.0.0" 1290 | resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-1.0.0.tgz#d59a4a75427447d9aa6c91e70263f8d26a4b104b" 1291 | 1292 | unset-value@^1.0.0: 1293 | version "1.0.0" 1294 | resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" 1295 | dependencies: 1296 | has-value "^0.3.1" 1297 | isobject "^3.0.0" 1298 | 1299 | urix@^0.1.0: 1300 | version "0.1.0" 1301 | resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" 1302 | 1303 | use@^2.0.0: 1304 | version "2.0.2" 1305 | resolved "https://registry.yarnpkg.com/use/-/use-2.0.2.tgz#ae28a0d72f93bf22422a18a2e379993112dec8e8" 1306 | dependencies: 1307 | define-property "^0.2.5" 1308 | isobject "^3.0.0" 1309 | lazy-cache "^2.0.2" 1310 | 1311 | user-home@^1.1.1: 1312 | version "1.1.1" 1313 | resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" 1314 | 1315 | util-deprecate@~1.0.1: 1316 | version "1.0.2" 1317 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1318 | 1319 | v8flags@^2.0.2: 1320 | version "2.1.1" 1321 | resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" 1322 | dependencies: 1323 | user-home "^1.1.1" 1324 | 1325 | vinyl-fs@^0.3.0: 1326 | version "0.3.14" 1327 | resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-0.3.14.tgz#9a6851ce1cac1c1cea5fe86c0931d620c2cfa9e6" 1328 | dependencies: 1329 | defaults "^1.0.0" 1330 | glob-stream "^3.1.5" 1331 | glob-watcher "^0.0.6" 1332 | graceful-fs "^3.0.0" 1333 | mkdirp "^0.5.0" 1334 | strip-bom "^1.0.0" 1335 | through2 "^0.6.1" 1336 | vinyl "^0.4.0" 1337 | 1338 | vinyl@^0.4.0: 1339 | version "0.4.6" 1340 | resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.4.6.tgz#2f356c87a550a255461f36bbeb2a5ba8bf784847" 1341 | dependencies: 1342 | clone "^0.2.0" 1343 | clone-stats "^0.0.1" 1344 | 1345 | vinyl@^0.5.0: 1346 | version "0.5.3" 1347 | resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" 1348 | dependencies: 1349 | clone "^1.0.0" 1350 | clone-stats "^0.0.1" 1351 | replace-ext "0.0.1" 1352 | 1353 | which@^1.2.14: 1354 | version "1.3.0" 1355 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" 1356 | dependencies: 1357 | isexe "^2.0.0" 1358 | 1359 | wrappy@1: 1360 | version "1.0.2" 1361 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1362 | 1363 | "xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.1: 1364 | version "4.0.1" 1365 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" 1366 | --------------------------------------------------------------------------------