├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── SECURITY.md ├── package.json ├── src ├── CachedResponse.ts ├── Callback.ts ├── FileCache.ts ├── Headers.ts ├── HttpVerb.ts ├── ICache.ts ├── MemoryCache.ts ├── Options.ts ├── cache-control-utils.ts ├── cache-utils.ts └── index.ts ├── test-fixture └── invalidation-failure-cache.js ├── test ├── cache-invalidation.js ├── cache.js ├── gzip.js └── index.js ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /cache 2 | lib-cov 3 | *.seed 4 | *.log 5 | *.csv 6 | *.dat 7 | *.out 8 | *.pid 9 | *.gz 10 | pids 11 | logs 12 | results 13 | npm-debug.log 14 | node_modules 15 | /lib 16 | package-lock.json 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | - "8" 5 | - "10" 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Forbes Lindesay 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # http-basic 2 | 3 | Simple wrapper arround http.request/https.request 4 | 5 | [![Build Status](https://img.shields.io/travis/ForbesLindesay/http-basic/master.svg)](https://travis-ci.org/ForbesLindesay/http-basic) 6 | [![Dependency Status](https://img.shields.io/david/ForbesLindesay/http-basic.svg)](https://david-dm.org/ForbesLindesay/http-basic) 7 | [![NPM version](https://img.shields.io/npm/v/http-basic.svg)](https://www.npmjs.org/package/http-basic) 8 | 9 | ## Installation 10 | 11 | npm install http-basic 12 | 13 | ## Usage 14 | 15 | ```js 16 | var request = require('http-basic'); 17 | 18 | var options = {followRedirects: true, gzip: true, cache: 'memory'}; 19 | 20 | var req = request('GET', 'http://example.com', options, function (err, res) { 21 | if (err) throw err; 22 | console.dir(res.statusCode); 23 | res.body.resume(); 24 | }); 25 | req.end(); 26 | ``` 27 | 28 | **method:** 29 | 30 | The http method (e.g. `GET`, `POST`, `PUT`, `DELETE` etc.) 31 | 32 | **url:** 33 | 34 | The url as a string (e.g. `http://example.com`). It must be fully qualified and either http or https. 35 | 36 | **options:** 37 | 38 | - `headers` - (default `{}`) http headers 39 | - `agent` - (default: `false`) controlls keep-alive (see http://nodejs.org/api/http.html#http_http_request_options_callback) 40 | - `duplex` - (default: `true` except for `GET`, `OPTIONS` and `HEAD` requests) allows you to explicitly set a body on a request that uses a method that normally would not have a body 41 | - `followRedirects` - (default: `false`) - if true, redirects are followed (note that this only affects the result in the callback) 42 | - `maxRedirects` - (default: `Infinity`) - limit the number of redirects allowed. 43 | - `allowRedirectHeaders` (default: `null`) - an array of headers allowed for redirects (none if `null`). 44 | - `gzip` (default: `false`) - automatically accept gzip and deflate encodings. This is kept completely transparent to the user. 45 | - `cache` - (default: `null`) - `'memory'` or `'file'` to use the default built in caches or you can pass your own cache implementation. 46 | - `timeout` (default: `false`) - times out if no response is returned within the given number of milliseconds. 47 | - `socketTimeout` (default: `false`) - calls `req.setTimeout` internally which causes the request to timeout if no new data is seen for the given number of milliseconds. 48 | - `retry` (default: `false`) - retry GET requests. Set this to `true` to retry when the request errors or returns a status code greater than or equal to 400 (can also be a function that takes `(err, req, attemptNo) => shouldRetry`) 49 | - `retryDelay` (default: `200`) - the delay between retries (can also be set to a function that takes `(err, res, attemptNo) => delay`) 50 | - `maxRetries` (default: `5`) - the number of times to retry before giving up. 51 | - `ignoreFailedInvalidation` (default: `false`) - whether the cache should swallow errors if there is a problem removing a cached response. Note that enabling this setting may result in incorrect, cached data being returned to the user. 52 | - `isMatch` - `(requestHeaders: Headers, cachedResponse: CachedResponse, defaultValue: boolean) => boolean` - override the default behaviour for testing whether a cached response matches a request. 53 | - `isExpired` - `(cachedResponse: CachedResponse, defaultValue: boolean) => boolean` - override the default behaviour for testing whether a cached response has expired 54 | - `canCache` - `(res: Response, defaultValue: boolean) => boolean` - override the default behaviour for testing whether a response can be cached 55 | 56 | **callback:** 57 | 58 | The callback is called with `err` as the first argument and `res` as the second argument. `res` is an [http-response-object](https://github.com/ForbesLindesay/http-response-object). It has the following properties: 59 | 60 | - `statusCode` - a number representing the HTTP Status Code 61 | - `headers` - an object representing the HTTP headers 62 | - `body` - a readable stream respresenting the request body. 63 | - `url` - the URL that was requested (in the case of redirects, this is the final url that was requested) 64 | 65 | **returns:** 66 | 67 | If the method is `GET`, `DELETE` or `HEAD`, it returns `undefined`. 68 | 69 | Otherwise, it returns a writable stream for the body of the request. 70 | 71 | ## Implementing a Cache 72 | 73 | A `Cache` is an object with three methods: 74 | 75 | - `getResponse(url, callback)` - retrieve a cached response object 76 | - `setResponse(url, response)` - cache a response object 77 | - `invalidateResponse(url, callback)` - remove a response which is no longer valid 78 | 79 | A cached response object is an object with the following properties: 80 | 81 | - `statusCode` - Number 82 | - `headers` - Object (key value pairs of strings) 83 | - `body` - Stream (a stream of binary data) 84 | - `requestHeaders` - Object (key value pairs of strings) 85 | - `requestTimestamp` - Number 86 | 87 | `getResponse` should call the callback with an optional error and either `null` or a cached response object, depending on whether the url can be found in the cache. Only `GET`s are cached. 88 | 89 | `setResponse` should just swallow any errors it has (or resport them using `console.warn`). 90 | 91 | `invalidateResponse` should call the callback with an optional error if it is unable to invalidate a response. 92 | 93 | A cache may also define any of the methods from `lib/cache-utils.js` to override behaviour for what gets cached. It is currently still only possible to cache "get" requests, although this could be changed. 94 | 95 | ## License 96 | 97 | MIT 98 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Security contact information 2 | 3 | To report a security vulnerability, please use the 4 | [Tidelift security contact](https://tidelift.com/security). 5 | Tidelift will coordinate the fix and disclosure. 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "http-basic", 3 | "version": "8.1.3", 4 | "main": "lib/index.js", 5 | "types": "lib/index.d.ts", 6 | "files": [ 7 | "lib" 8 | ], 9 | "description": "Very low level wrapper arround http.request/https.request", 10 | "keywords": [ 11 | "http", 12 | "https", 13 | "request", 14 | "fetch", 15 | "gzip", 16 | "deflate", 17 | "redirect", 18 | "cache", 19 | "etag", 20 | "cache-control" 21 | ], 22 | "dependencies": { 23 | "caseless": "^0.12.0", 24 | "concat-stream": "^1.6.2", 25 | "http-response-object": "^3.0.1", 26 | "parse-cache-control": "^1.0.1" 27 | }, 28 | "devDependencies": { 29 | "@types/concat-stream": "^1.6.0", 30 | "@types/node": "^11.9.0", 31 | "flowgen2": "^2.2.1", 32 | "rimraf": "^2.5.4", 33 | "serve-static": "^1.11.1", 34 | "typescript": "^2.3.4" 35 | }, 36 | "scripts": { 37 | "prepublishOnly": "npm run build", 38 | "build": "tsc && flowgen lib/**/*", 39 | "pretest": "npm run build", 40 | "test": "node test/index && node test/cache && node test/cache-invalidation && rimraf lib/cache" 41 | }, 42 | "engines": { 43 | "node": ">=6.0.0" 44 | }, 45 | "repository": { 46 | "type": "git", 47 | "url": "https://github.com/ForbesLindesay/http-basic.git" 48 | }, 49 | "author": "ForbesLindesay", 50 | "license": "MIT" 51 | } -------------------------------------------------------------------------------- /src/CachedResponse.ts: -------------------------------------------------------------------------------- 1 | import {Headers} from './Headers'; 2 | 3 | interface CachedResponse { 4 | statusCode: number; 5 | headers: Headers; 6 | body: NodeJS.ReadableStream; 7 | requestHeaders: Headers; 8 | requestTimestamp: number; 9 | } 10 | 11 | export {CachedResponse}; -------------------------------------------------------------------------------- /src/Callback.ts: -------------------------------------------------------------------------------- 1 | import Response = require('http-response-object'); 2 | 3 | type Callback = (err: NodeJS.ErrnoException | null, response?: Response) => void; 4 | export {Callback}; -------------------------------------------------------------------------------- /src/FileCache.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as fs from 'fs'; 4 | import {resolve} from 'path'; 5 | import {createHash} from 'crypto'; 6 | import Response = require('http-response-object'); 7 | import {ICache} from './ICache'; 8 | import {CachedResponse} from './CachedResponse'; 9 | 10 | function jsonParse(data: string, cb: (err: Error | null, result?: any) => void): void { 11 | let result = null; 12 | try { 13 | result = JSON.parse(data); 14 | } catch (ex) { 15 | return cb(ex); 16 | } 17 | cb(null, result); 18 | } 19 | 20 | function getCacheKey(url: string): string { 21 | const hash = createHash('sha512') 22 | hash.update(url) 23 | return hash.digest('hex') 24 | } 25 | 26 | export default class FileCache implements ICache { 27 | private readonly _location: string; 28 | constructor(location: string) { 29 | this._location = location; 30 | } 31 | 32 | getResponse(url: string, callback: (err: null | Error, response: null | CachedResponse) => void) { 33 | const key = resolve(this._location, getCacheKey(url)); 34 | 35 | fs.readFile(key + '.json', 'utf8', function (err, data) { 36 | if (err && err.code === 'ENOENT') return callback(null, null); 37 | else if (err) return callback(err, null); 38 | jsonParse(data, (err, response) => { 39 | if (err) { 40 | return callback(err, null); 41 | } 42 | const body = fs.createReadStream(key + '.body'); 43 | response.body = body; 44 | callback(null, response); 45 | }); 46 | }); 47 | } 48 | 49 | setResponse(url: string, response: CachedResponse): void { 50 | const key = resolve(this._location, getCacheKey(url)); 51 | let errored = false; 52 | 53 | fs.mkdir(this._location, function (err) { 54 | if (err && err.code !== 'EEXIST') { 55 | console.warn('Error creating cache: ' + err.message); 56 | return; 57 | } 58 | response.body.pipe(fs.createWriteStream(key + '.body')).on('error', (err: NodeJS.ErrnoException) => { 59 | errored = true; 60 | console.warn('Error writing to cache: ' + err.message); 61 | }).on('close', function () { 62 | if (!errored) { 63 | fs.writeFile(key + '.json', JSON.stringify({ 64 | statusCode: response.statusCode, 65 | headers: response.headers, 66 | requestHeaders: response.requestHeaders, 67 | requestTimestamp: response.requestTimestamp 68 | }, null, ' '), function (err) { 69 | if (err) { 70 | console.warn('Error writing to cache: ' + err.message); 71 | } 72 | }); 73 | } 74 | }); 75 | }); 76 | } 77 | 78 | updateResponseHeaders(url: string, response: Pick) { 79 | const key = resolve(this._location, getCacheKey(url)); 80 | fs.readFile(key + '.json', 'utf8', function (err, data) { 81 | if (err) { 82 | console.warn('Error writing to cache: ' + err.message); 83 | return; 84 | } 85 | let parsed = null; 86 | try { 87 | parsed = JSON.parse(data); 88 | } catch (ex) { 89 | console.warn('Error writing to cache: ' + ex.message); 90 | return; 91 | } 92 | fs.writeFile(key + '.json', JSON.stringify({ 93 | statusCode: parsed.statusCode, 94 | headers: response.headers, 95 | requestHeaders: parsed.requestHeaders, 96 | requestTimestamp: response.requestTimestamp 97 | }, null, ' '), function (err) { 98 | if (err) { 99 | console.warn('Error writing to cache: ' + err.message); 100 | } 101 | }); 102 | }); 103 | } 104 | 105 | invalidateResponse(url: string, callback: (err: NodeJS.ErrnoException | null) => void): void { 106 | const key = resolve(this._location, getCacheKey(url)); 107 | fs.unlink(key + '.json', (err?: NodeJS.ErrnoException | null) => { 108 | if (err && err.code === 'ENOENT') return callback(null); 109 | else callback(err || null); 110 | }); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/Headers.ts: -------------------------------------------------------------------------------- 1 | import { IncomingHttpHeaders } from 'http'; 2 | export type Headers = IncomingHttpHeaders; -------------------------------------------------------------------------------- /src/HttpVerb.ts: -------------------------------------------------------------------------------- 1 | type HttpVerb = ( 2 | | 'GET' 3 | | 'HEAD' 4 | | 'POST' 5 | | 'PUT' 6 | | 'DELETE' 7 | | 'CONNECT' 8 | | 'OPTIONS' 9 | | 'TRACE' 10 | | 'PATCH' 11 | ); 12 | export {HttpVerb}; -------------------------------------------------------------------------------- /src/ICache.ts: -------------------------------------------------------------------------------- 1 | import {CachedResponse} from './CachedResponse'; 2 | 3 | interface ICache { 4 | getResponse(url: string, cb: (err: Error | null, response: CachedResponse | null) => void): void; 5 | setResponse(url: string, response: CachedResponse | null): void; 6 | updateResponseHeaders?: (url: string, response: Pick) => void; 7 | invalidateResponse(url: string, cb: (err: Error | null) => void): void; 8 | } 9 | 10 | export {ICache}; 11 | -------------------------------------------------------------------------------- /src/MemoryCache.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import {PassThrough} from 'stream'; 4 | import Response = require('http-response-object'); 5 | import concat = require('concat-stream'); 6 | import {Headers} from './Headers'; 7 | import {ICache} from './ICache'; 8 | import {CachedResponse} from './CachedResponse'; 9 | 10 | interface StoredResponse { 11 | statusCode: number, 12 | headers: Headers, 13 | body: Buffer, 14 | requestHeaders: Headers, 15 | requestTimestamp: number 16 | } 17 | 18 | export default class MemoryCache { 19 | private readonly _cache: {[url: string]: StoredResponse} = {}; 20 | 21 | getResponse(url: string, callback: (err: null | Error, response: null | CachedResponse) => void): void { 22 | const cache = this._cache; 23 | if (cache[url]) { 24 | const body = new PassThrough(); 25 | body.end(cache[url].body); 26 | callback(null, { 27 | statusCode: cache[url].statusCode, 28 | headers: cache[url].headers, 29 | body: body, 30 | requestHeaders: cache[url].requestHeaders, 31 | requestTimestamp: cache[url].requestTimestamp 32 | }); 33 | } else { 34 | callback(null, null); 35 | } 36 | } 37 | 38 | updateResponseHeaders(url: string, response: Pick) { 39 | this._cache[url] = { 40 | ...this._cache[url], 41 | headers: response.headers, 42 | requestTimestamp: response.requestTimestamp 43 | }; 44 | } 45 | 46 | setResponse(url: string, response: CachedResponse): void { 47 | const cache = this._cache; 48 | response.body.pipe(concat((body) => { 49 | cache[url] = { 50 | statusCode: response.statusCode, 51 | headers: response.headers, 52 | body: body, 53 | requestHeaders: response.requestHeaders, 54 | requestTimestamp: response.requestTimestamp 55 | }; 56 | })); 57 | } 58 | 59 | invalidateResponse(url: string, callback: (err: NodeJS.ErrnoException | null) => void) { 60 | const cache = this._cache; 61 | delete cache[url]; 62 | callback(null); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Options.ts: -------------------------------------------------------------------------------- 1 | import { Agent } from 'http'; 2 | import { Headers } from './Headers'; 3 | import { ICache } from './ICache'; 4 | import Response = require('http-response-object'); 5 | import {CachedResponse} from './CachedResponse'; 6 | 7 | interface Options { 8 | agent?: Agent | boolean; 9 | allowRedirectHeaders?: string[]; 10 | cache?: 'file' | 'memory' | ICache; 11 | duplex?: boolean; 12 | followRedirects?: boolean; 13 | gzip?: boolean; 14 | headers?: Headers; 15 | ignoreFailedInvalidation?: boolean; 16 | maxRedirects?: number; 17 | maxRetries?: number; 18 | retry?: boolean | ((err: NodeJS.ErrnoException | null, res: Response | void, attemptNumber: number) => boolean); 19 | retryDelay?: number | ((err: NodeJS.ErrnoException | null, res: Response | void, attemptNumber: number) => number); 20 | socketTimeout?: number; 21 | timeout?: number; 22 | 23 | isMatch?: (requestHeaders: Headers, cachedResponse: CachedResponse, defaultValue: boolean) => boolean; 24 | isExpired?: (cachedResponse: CachedResponse, defaultValue: boolean) => boolean; 25 | canCache?: (res: Response, defaultValue: boolean) => boolean 26 | } 27 | export {Options}; -------------------------------------------------------------------------------- /src/cache-control-utils.ts: -------------------------------------------------------------------------------- 1 | import {CachedResponse} from './CachedResponse'; 2 | import Response = require('http-response-object'); 3 | const parseCacheControl = require('parse-cache-control'); 4 | 5 | type ParsedCacheControl = { 6 | [key: string]: string | number | boolean | null | void, 7 | 'max-age': number | null | void, 8 | } 9 | export type Policy = { 10 | maxage: number | null, 11 | } 12 | function parseCacheControlHeader(res: Response | CachedResponse): ParsedCacheControl | null { 13 | const cacheControl = res.headers['cache-control']; 14 | const normalisedCacheControl = typeof cacheControl === 'string' ? cacheControl.trim() : '' // must be normalised for parsing (e.g. parseCacheControl) 15 | if (!cacheControl) { 16 | return null; 17 | } 18 | return parseCacheControl(cacheControl); 19 | } 20 | 21 | // for the purposes of this library, we err on the side of caution and do not cache anything except public (or implicit public) 22 | const nonCaching = ['private','no-cache','no-store','no-transform','must-revalidate','proxy-revalidate']; 23 | 24 | function isCacheControlCacheable(parsedCacheControl: ParsedCacheControl | null): boolean { 25 | if (!parsedCacheControl) { 26 | return false; 27 | } 28 | if (parsedCacheControl.public) { 29 | return true; 30 | } 31 | // note that the library does not currently support s-maxage 32 | if (parsedCacheControl["max-age"]) { 33 | // https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3 34 | // The max-age directive on a response implies that the response is cacheable (i.e., "public") unless some other, more restrictive cache directive is also present. 35 | for (let i=0; i(res: Response | CachedResponse): boolean { 47 | return isCacheControlCacheable(parseCacheControlHeader(res)); 48 | } 49 | 50 | function buildPolicy(parsedCacheControl: ParsedCacheControl): Policy { 51 | // note that the library does not currently support s-maxage 52 | return {maxage: parsedCacheControl['max-age'] || null}; 53 | } 54 | 55 | /** 56 | * if the response is cacheable, returns an object detailing the maxage of the cache 57 | * otherwise returns null 58 | */ 59 | export function cachePolicy(res: Response | CachedResponse): Policy | null { 60 | const parsed = parseCacheControlHeader(res); 61 | return parsed && isCacheControlCacheable(parsed) ? buildPolicy(parsed) : null; 62 | } 63 | -------------------------------------------------------------------------------- /src/cache-utils.ts: -------------------------------------------------------------------------------- 1 | import Response = require('http-response-object'); 2 | import {cachePolicy, isCacheable} from './cache-control-utils'; 3 | import {Headers} from './Headers'; 4 | import {CachedResponse} from './CachedResponse'; 5 | 6 | export function isMatch(requestHeaders: Headers, cachedResponse: CachedResponse): boolean { 7 | let vary = cachedResponse.headers['vary']; 8 | if (vary && cachedResponse.requestHeaders) { 9 | vary = '' + vary; 10 | return vary.split(',').map(function (header) { return header.trim().toLowerCase(); }).every(function (header) { 11 | return requestHeaders[header] === cachedResponse.requestHeaders[header]; 12 | }); 13 | } else { 14 | return true; 15 | } 16 | }; 17 | export function isExpired(cachedResponse: CachedResponse): boolean { 18 | const policy = cachePolicy(cachedResponse); 19 | if (policy) { 20 | const time = (Date.now() - cachedResponse.requestTimestamp) / 1000; 21 | if (policy.maxage !== null && policy.maxage > time) { 22 | return false; 23 | } 24 | } 25 | if (cachedResponse.statusCode === 301 || cachedResponse.statusCode === 308) return false; 26 | return true; 27 | }; 28 | export function canCache(res: Response): boolean { 29 | if (res.headers['etag']) return true; 30 | if (res.headers['last-modified']) return true; 31 | if (isCacheable(res)) return true; 32 | if (res.statusCode === 301 || res.statusCode === 308) return true; 33 | return false; 34 | }; 35 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as cacheUtils from './cache-utils'; 2 | import FileCache from './FileCache'; 3 | import MemoryCache from './MemoryCache'; 4 | import { Callback } from './Callback'; 5 | import {CachedResponse} from './CachedResponse'; 6 | import { 7 | ClientRequest, 8 | IncomingMessage, 9 | request as requestHttp, 10 | RequestOptions 11 | } from 'http'; 12 | import { createGunzip, createInflate } from 'zlib'; 13 | import { HttpVerb } from './HttpVerb'; 14 | import { ICache } from './ICache'; 15 | import { Options } from './Options'; 16 | import { parse as parseUrl, resolve as resolveUrl } from 'url'; 17 | import { PassThrough } from 'stream'; 18 | import { request as requestHttps } from 'https'; 19 | import Response = require('http-response-object'); 20 | import {URL} from 'url'; 21 | 22 | const caseless = require('caseless'); 23 | const fileCache = new FileCache(__dirname + '/cache'); 24 | const memoryCache = new MemoryCache(); 25 | 26 | function requestProtocol(protocol: string, options: RequestOptions, callback?: (res: IncomingMessage) => void): ClientRequest { 27 | if (protocol === 'http') { 28 | return requestHttp(options, callback); 29 | } else if (protocol === 'https') { 30 | return requestHttps(options, callback); 31 | } 32 | throw new Error('Unsupported protocol ' + protocol); 33 | } 34 | 35 | function request(method: HttpVerb, url: string | URL, options: Options | null | void, callback: Callback): void | NodeJS.WritableStream; 36 | function request(method: HttpVerb, url: string | URL, callback: Callback): void | NodeJS.WritableStream; 37 | function request(method: HttpVerb, url: string | URL, options?: Options | Callback | null | void, callback?: Callback | null): void | NodeJS.WritableStream { 38 | if (typeof options === 'function') { 39 | callback = options; 40 | options = null; 41 | } 42 | if (options === null || options === undefined) { 43 | options = {}; 44 | } 45 | if (typeof options !== 'object') { 46 | throw new TypeError('options must be an object (or null)'); 47 | } 48 | if (typeof callback !== 'function') { 49 | throw new TypeError('callback must be a function'); 50 | } 51 | return _request(method, ( 52 | (url && typeof url === 'object') ? url.href : url 53 | ), options, callback); 54 | } 55 | function _request(method: HttpVerb, url: string, options: Options, callback: Callback): void | NodeJS.WritableStream { 56 | const start = Date.now(); 57 | if (typeof method !== 'string') { 58 | throw new TypeError('The method must be a string.'); 59 | } 60 | if (typeof url !== 'string') { 61 | throw new TypeError('The URL/path must be a string or a URL object.'); 62 | } 63 | 64 | method = (method.toUpperCase() as any); 65 | const urlObject = parseUrl(url); 66 | 67 | const protocol = (urlObject.protocol || '').replace(/\:$/, ''); 68 | if (protocol !== 'http' && protocol !== 'https') { 69 | throw new TypeError('The protocol "' + protocol + '" is not supported, cannot load "' + url + '"'); 70 | } 71 | 72 | const rawHeaders = options.headers || {}; 73 | const headers = caseless(rawHeaders); 74 | if (urlObject.auth) { 75 | headers.set('Authorization', 'Basic ' + (Buffer.from(urlObject.auth)).toString('base64')); 76 | } 77 | const agent = 'agent' in options ? options.agent : false; 78 | 79 | let cache = options.cache; 80 | if (typeof cache === 'string') { 81 | if (cache === 'file') { 82 | cache = fileCache 83 | } else if (cache === 'memory') { 84 | cache = memoryCache; 85 | } 86 | } 87 | if (cache && !(typeof cache === 'object' && typeof cache.getResponse === 'function' && typeof cache.setResponse === 'function' && typeof cache.invalidateResponse === 'function')) { 88 | throw new TypeError(cache + ' is not a valid cache, caches must have `getResponse`, `setResponse` and `invalidateResponse` methods.'); 89 | } 90 | 91 | const ignoreFailedInvalidation = options.ignoreFailedInvalidation; 92 | 93 | if (options.duplex !== undefined && typeof options.duplex !== 'boolean') { 94 | throw new Error('expected options.duplex to be a boolean if provided'); 95 | } 96 | const duplex = options.duplex !== undefined ? options.duplex : !(method === 'GET' || method === 'DELETE' || method === 'HEAD'); 97 | const unsafe = !(method === 'GET' || method === 'OPTIONS' || method === 'HEAD'); 98 | 99 | if (options.gzip) { 100 | headers.set('Accept-Encoding', headers.has('Accept-Encoding') ? headers.get('Accept-Encoding') + ',gzip,deflate' : 'gzip,deflate'); 101 | return _request(method, url, { 102 | allowRedirectHeaders: options.allowRedirectHeaders, 103 | duplex: duplex, 104 | headers: rawHeaders, 105 | agent: agent, 106 | followRedirects: options.followRedirects, 107 | retry: options.retry, 108 | retryDelay: options.retryDelay, 109 | maxRetries: options.maxRetries, 110 | cache: cache, 111 | timeout: options.timeout 112 | }, function (err, res) { 113 | if (err) return callback(err); 114 | if (!res) return callback(new Error('Response should not be undefined if there is no error.')); 115 | const newHeaders = ({...res.headers} as any); 116 | let newBody = res.body; 117 | switch (newHeaders['content-encoding']) { 118 | case 'gzip': 119 | delete newHeaders['content-encoding']; 120 | newBody = res.body.pipe(createGunzip()); 121 | break; 122 | case 'deflate': 123 | delete newHeaders['content-encoding']; 124 | newBody = res.body.pipe(createInflate()); 125 | break; 126 | } 127 | return callback(err, new Response(res.statusCode, newHeaders, newBody, res.url)); 128 | }); 129 | } 130 | if (options.followRedirects) { 131 | return _request(method, url, { 132 | allowRedirectHeaders: options.allowRedirectHeaders, 133 | duplex: duplex, 134 | headers: rawHeaders, 135 | agent: agent, 136 | retry: options.retry, 137 | retryDelay: options.retryDelay, 138 | maxRetries: options.maxRetries, 139 | cache: cache, 140 | timeout: options.timeout 141 | }, function (err, res) { 142 | if (err) return callback(err); 143 | if (!res) return callback(new Error('Response should not be undefined if there is no error.')); 144 | if (options.followRedirects && isRedirect(res.statusCode)) { 145 | // prevent leakage of file handles 146 | res.body.resume(); 147 | if (method === 'DELETE' && res.statusCode === 303) { 148 | // 303 See Other should convert to GET for duplex 149 | // requests and for DELETE 150 | method = 'GET'; 151 | } 152 | if (options.maxRedirects === 0) { 153 | const err = new Error('Maximum number of redirects exceeded'); 154 | (err as any).res = res; 155 | return callback(err, res); 156 | } 157 | options = { 158 | ...options, 159 | duplex: false, 160 | maxRedirects: options.maxRedirects && options.maxRedirects !== Infinity ? options.maxRedirects - 1 : options.maxRedirects, 161 | }; 162 | // don't maintain headers through redirects 163 | // This fixes a problem where a POST to http://example.com 164 | // might result in a GET to http://example.co.uk that includes "content-length" 165 | // as a header 166 | const headers = caseless(options.headers); 167 | const redirectHeaders: any = {}; 168 | if (options.allowRedirectHeaders) { 169 | for (let i = 0; i < options.allowRedirectHeaders.length; i++) { 170 | const headerName = options.allowRedirectHeaders[i]; 171 | const headerValue = headers.get(headerName); 172 | if (headerValue) { 173 | redirectHeaders[headerName] = headerValue; 174 | } 175 | } 176 | } 177 | options.headers = redirectHeaders; 178 | const location = res.headers.location; 179 | if (typeof location !== 'string') { 180 | return callback(new Error('Cannot redirect to non string location: ' + location)); 181 | } 182 | return request(duplex ? 'GET' : method, resolveUrl(url, location), options, callback); 183 | } else { 184 | return callback(null, res); 185 | } 186 | }); 187 | } 188 | if (cache && method === 'GET' && !duplex) { 189 | const timestamp = Date.now(); 190 | return cache.getResponse(url, function (err, cachedResponse) { 191 | if (err) { 192 | console.warn('Error reading from cache: ' + err.message); 193 | } 194 | const isMatch = !!(cachedResponse && cacheUtils.isMatch(rawHeaders, cachedResponse)); 195 | if (cachedResponse && (options.isMatch ? options.isMatch(rawHeaders, cachedResponse, isMatch) : isMatch)) { 196 | const isExpired = cacheUtils.isExpired(cachedResponse); 197 | if (!(options.isExpired ? options.isExpired(cachedResponse, isExpired) : isExpired)) { 198 | const res = new Response(cachedResponse.statusCode, cachedResponse.headers, cachedResponse.body, url); 199 | (res as any).fromCache = true; 200 | (res as any).fromNotModified = false; 201 | return callback(null, res); 202 | } else { 203 | if (cachedResponse.headers['etag']) { 204 | headers.set('If-None-Match', cachedResponse.headers['etag']); 205 | } 206 | if (cachedResponse.headers['last-modified']) { 207 | headers.set('If-Modified-Since', cachedResponse.headers['last-modified']); 208 | } 209 | } 210 | } 211 | request('GET', url, { 212 | allowRedirectHeaders: options.allowRedirectHeaders, 213 | headers: rawHeaders, 214 | retry: options.retry, 215 | retryDelay: options.retryDelay, 216 | maxRetries: options.maxRetries, 217 | agent: agent, 218 | timeout: options.timeout 219 | }, function (err, res) { 220 | if (err) return callback(err); 221 | if (!res) return callback(new Error('Response should not be undefined if there is no error.')); 222 | if (res.statusCode === 304 && cachedResponse) { // Not Modified 223 | // prevent leakage of file handles 224 | res.body.resume(); 225 | let resultBody = cachedResponse.body; 226 | const c = (cache as ICache); 227 | if (c.updateResponseHeaders) { 228 | c.updateResponseHeaders(url, { 229 | headers: res.headers, 230 | requestTimestamp: timestamp, 231 | }); 232 | } else { 233 | const cachedResponseBody = new PassThrough(); 234 | const newResultBody = new PassThrough(); 235 | resultBody.on('data', (data: Buffer) => { 236 | cachedResponseBody.write(data); 237 | newResultBody.write(data); 238 | }); 239 | resultBody.on('end', () => { 240 | cachedResponseBody.end(); 241 | newResultBody.end(); 242 | }); 243 | resultBody = newResultBody; 244 | (cache as ICache).setResponse(url, { 245 | statusCode: cachedResponse.statusCode, 246 | headers: res.headers, 247 | body: cachedResponseBody, 248 | requestHeaders: cachedResponse.requestHeaders, 249 | requestTimestamp: timestamp, 250 | }); 251 | } 252 | const response = new Response(cachedResponse.statusCode, cachedResponse.headers, resultBody, url); 253 | (response as any).fromCache = true; 254 | (response as any).fromNotModified = true; 255 | return callback(null, response); 256 | } 257 | // prevent leakage of file handles 258 | cachedResponse && cachedResponse.body.resume(); 259 | const canCache = cacheUtils.canCache(res); 260 | if (options.canCache ? options.canCache(res, canCache) : canCache) { 261 | const cachedResponseBody = new PassThrough(); 262 | const resultResponseBody = new PassThrough(); 263 | res.body.on('data', (data: Buffer) => { 264 | cachedResponseBody.write(data); 265 | resultResponseBody.write(data); 266 | }); 267 | res.body.on('end', function () { cachedResponseBody.end(); resultResponseBody.end(); }); 268 | const resultResponse = new Response(res.statusCode, res.headers, resultResponseBody, url); 269 | (cache as ICache).setResponse(url, { 270 | statusCode: res.statusCode, 271 | headers: res.headers, 272 | body: cachedResponseBody, 273 | requestHeaders: rawHeaders, 274 | requestTimestamp: timestamp, 275 | }); 276 | return callback(null, resultResponse); 277 | } else { 278 | return callback(null, res); 279 | } 280 | }); 281 | }); 282 | } 283 | 284 | function attempt(n: number) { 285 | return _request(method, url, { 286 | allowRedirectHeaders: options.allowRedirectHeaders, 287 | headers: rawHeaders, 288 | agent: agent, 289 | timeout: options.timeout 290 | }, function (err, res) { 291 | let retry = err || !res || res.statusCode >= 400; 292 | if (typeof options.retry === 'function') { 293 | retry = options.retry(err, res, n + 1); 294 | } 295 | if (n >= (options.maxRetries || 5)) { 296 | retry = false; 297 | } 298 | if (retry) { 299 | let delay = options.retryDelay; 300 | if (typeof delay === 'function') { 301 | delay = delay(err, res, n + 1); 302 | } 303 | delay = delay || 200; 304 | setTimeout(function () { 305 | attempt(n + 1); 306 | }, delay); 307 | } else { 308 | callback(err, res); 309 | } 310 | }); 311 | } 312 | if (options.retry && method === 'GET' && !duplex) { 313 | return attempt(0); 314 | } 315 | 316 | let responded = false; 317 | 318 | let timeout: NodeJS.Timer | null = null; 319 | 320 | const req = requestProtocol(protocol, { 321 | host: urlObject.hostname, 322 | port: urlObject.port == null ? undefined : +urlObject.port, 323 | path: urlObject.path, 324 | method: method, 325 | headers: rawHeaders, 326 | agent: agent 327 | }, function (res) { 328 | const end = Date.now(); 329 | if (responded) return res.resume(); 330 | responded = true; 331 | if (timeout !== null) clearTimeout(timeout); 332 | const result = new Response(res.statusCode || 0, res.headers, res, url); 333 | if (cache && unsafe && res.statusCode && res.statusCode < 400){ 334 | (cache as ICache).invalidateResponse(url, (err: Error | null) => { 335 | if (err && !ignoreFailedInvalidation) { 336 | callback(new Error('Error invalidating the cache for' + url + ': ' + err.message), result); 337 | } else { 338 | callback(null, result); 339 | } 340 | }); 341 | } else { 342 | callback(null, result); 343 | } 344 | }).on('error', function (err) { 345 | if (responded) return; 346 | responded = true; 347 | if (timeout !== null) clearTimeout(timeout); 348 | callback(err); 349 | }); 350 | 351 | function onTimeout() { 352 | if (responded) return; 353 | responded = true; 354 | if (timeout !== null) clearTimeout(timeout); 355 | req.abort(); 356 | const duration = Date.now() - start; 357 | const err: any = new Error('Request timed out after ' + duration + 'ms'); 358 | err.timeout = true; 359 | err.duration = duration; 360 | callback(err); 361 | } 362 | if (options.socketTimeout) { 363 | req.setTimeout(options.socketTimeout, onTimeout); 364 | } 365 | if (options.timeout) { 366 | timeout = setTimeout(onTimeout, options.timeout); 367 | } 368 | 369 | if (duplex) { 370 | return req; 371 | } else { 372 | req.end(); 373 | } 374 | return undefined; 375 | } 376 | 377 | function isRedirect(statusCode: number): boolean { 378 | return statusCode === 301 || statusCode === 302 || statusCode === 303 || statusCode === 307 || statusCode === 308; 379 | } 380 | 381 | export default request; 382 | export {HttpVerb}; 383 | export {Options}; 384 | export {Callback}; 385 | export {Response}; 386 | export {CachedResponse}; 387 | export {ICache}; 388 | 389 | module.exports = request; 390 | module.exports.default = request; 391 | module.exports.Response = Response; -------------------------------------------------------------------------------- /test-fixture/invalidation-failure-cache.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var MemoryCache = require('../lib/MemoryCache').default; 4 | 5 | module.exports = InvalidationFailCache; 6 | 7 | function InvalidationFailCache(){ 8 | MemoryCache.call(this); 9 | InvalidationFailCache.constructor = InvalidationFailCache; 10 | } 11 | InvalidationFailCache.prototype = Object.create(MemoryCache.prototype); 12 | InvalidationFailCache.prototype.constructor = InvalidationFailCache; 13 | 14 | InvalidationFailCache.prototype.invalidateResponse = function (url, callback) { 15 | callback(new Error('Invalidation failed')); 16 | }; 17 | -------------------------------------------------------------------------------- /test/cache-invalidation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var request = require('../'); 5 | var http = require('http'); 6 | var rimraf = require('rimraf'); 7 | var path = require('path'); 8 | var InvalidationFailureCache = require('../test-fixture/invalidation-failure-cache'); 9 | 10 | rimraf.sync(path.resolve(__dirname, '..', 'cache')); 11 | 12 | var PORT = 3294; 13 | 14 | var cacheControlServer = http.createServer(function(req, res){ 15 | 16 | if(req.headers["x-please-fail"]){ 17 | res.statusCode = 500; 18 | res.end('oops'); 19 | } else { 20 | res.statusCode = 200; 21 | if(req.method === "GET"){ 22 | res.setHeader('cache-control','public,max-age=60'); 23 | res.end('These are my favourite things'); 24 | } else { 25 | res.end(); 26 | } 27 | } 28 | 29 | }); 30 | 31 | function resourceUri(traceId) { 32 | 33 | return 'http://localhost:'+PORT+'/collection-'+traceId; 34 | 35 | } 36 | 37 | function shouldNotBeCached(traceId, cache) { 38 | request('GET', resourceUri(traceId), {cache: cache}, function (err, res) { 39 | if (err) throw err; 40 | 41 | console.log('response ' + traceId + '.2 (should no longer be cached)'); 42 | assert(res.statusCode === 200); 43 | assert(res.fromCache === undefined); 44 | res.body.resume(); 45 | }); 46 | 47 | } 48 | 49 | function shouldStillBeCached(traceId, cache) { 50 | request('GET', resourceUri(traceId), {cache: cache}, function (err, res) { 51 | if (err) throw err; 52 | 53 | console.log('response ' + traceId + '.2 (should still be cached)'); 54 | assert(res.statusCode === 200); 55 | assert(res.fromCache === true); 56 | res.body.resume(); 57 | }); 58 | 59 | } 60 | 61 | function arrange(traceId, description, cache, callback) { 62 | request('GET', resourceUri(traceId), {cache: cache}, function (err, res) { 63 | if (err) throw err; 64 | 65 | console.log('response ' + traceId + '.0 ' + description); 66 | assert(res.statusCode === 200); 67 | assert(res.fromCache === undefined); 68 | assert(res.fromNotModified === undefined); 69 | res.body.on('data', function () {}); 70 | res.body.on('end', function () { 71 | setTimeout(callback, 25); 72 | } ); 73 | }); 74 | return { 75 | 76 | assertIsNotCached: function() { shouldNotBeCached(traceId, cache); }, 77 | assertIsCached: function() { shouldStillBeCached(traceId, cache); } 78 | 79 | }; 80 | 81 | } 82 | 83 | function invalidationScenario(traceId, method, cacheType, isDuplex) { 84 | 85 | var scenario = arrange(traceId, '(populate memory cache prior to ' + method + ')', cacheType, function() { 86 | 87 | var options = { 88 | cache: cacheType, 89 | headers: { 'content-type': 'text/plain' } 90 | }; 91 | var req = request(method, resourceUri(traceId), options, function (err, res) { 92 | if (err) throw err; 93 | 94 | console.log('response '+traceId+'.1 ('+method+' invalidates ' + cacheType + ' cache)'); 95 | res.body.on('data', function() {}); 96 | res.body.on('end', function() { 97 | 98 | setTimeout(scenario.assertIsNotCached, 25); 99 | 100 | }); 101 | 102 | }); 103 | if(isDuplex){ 104 | req.end('hello world'); 105 | } 106 | 107 | }); 108 | 109 | } 110 | 111 | function invalidationScenario__failedUnsafeRequest(traceId, method, cacheType, isDuplex) { 112 | 113 | var scenario = arrange(traceId, '(populate memory cache prior to attempting ' + method + ')', cacheType, function() { 114 | 115 | var options = { 116 | cache: cacheType, 117 | headers: { 'content-type': 'text/plain', 'x-please-fail': 'true' } 118 | }; 119 | var req = request(method, resourceUri(traceId), options, function (err, res) { 120 | if (err) throw err; 121 | 122 | console.log('response '+traceId+'.1 ('+method+' fails, so does not invalidate ' + cacheType + ' cache)'); 123 | res.body.on('data', function() {}); 124 | res.body.on('end', function() { 125 | 126 | setTimeout(scenario.assertIsCached, 25); 127 | 128 | }); 129 | 130 | }); 131 | if(isDuplex){ 132 | req.end('hello world'); 133 | } 134 | 135 | }); 136 | 137 | } 138 | 139 | function invalidationScenario__failedCacheInvalidation(traceId, method, cacheObject, isDuplex, ignoreFailedInvalidation) { 140 | 141 | var scenario = arrange(traceId, '(populate memory cache prior to attempting ' + method + ')', cacheObject, function() { 142 | 143 | var options = { 144 | cache: cacheObject, 145 | headers: { 'content-type': 'text/plain' } 146 | }; 147 | if(typeof ignoreFailedInvalidation === "boolean"){ 148 | options.ignoreFailedInvalidation = ignoreFailedInvalidation; 149 | } 150 | var req = request(method, resourceUri(traceId), options, function (err, res) { 151 | if (ignoreFailedInvalidation && err) throw err; 152 | 153 | console.log('response '+traceId+'.1 (Cache invalidation fails after ' + method + ')'); 154 | res.body.on('data', function() {}); 155 | res.body.on('end', function() { 156 | 157 | setTimeout(scenario.assertIsCached, 25); 158 | 159 | }); 160 | 161 | }); 162 | if(isDuplex){ 163 | req.end('hello world'); 164 | } 165 | 166 | }); 167 | 168 | } 169 | 170 | 171 | cacheControlServer.listen(PORT, function onListen() { 172 | 173 | // unsafe methods should invalidate the cache 174 | invalidationScenario('P', 'POST', 'memory', true); 175 | invalidationScenario('Q', 'POST', 'file', true); 176 | invalidationScenario('R', 'PUT', 'memory', true); 177 | invalidationScenario('S', 'PUT', 'file', true); 178 | invalidationScenario('T', 'DELETE', 'memory', false); 179 | invalidationScenario('U', 'DELETE', 'file', false); 180 | // if the DELETE fails, the cache should not be invalidated 181 | invalidationScenario__failedUnsafeRequest('V', 'DELETE', 'memory', false); 182 | // if the cache fails to invalidate, should throw an error 183 | invalidationScenario__failedCacheInvalidation('W', 'DELETE', new InvalidationFailureCache(), false, false); 184 | // if the cache fails to invalidate, but the ignoreFailedInvalidation option is set, should still be cached 185 | invalidationScenario__failedCacheInvalidation('X', 'DELETE', new InvalidationFailureCache(), false, true); 186 | 187 | }); 188 | 189 | cacheControlServer.unref(); 190 | -------------------------------------------------------------------------------- /test/cache.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var request = require('../'); 5 | var http = require('http'); 6 | var serveStatic = require('serve-static'); 7 | var rimraf = require('rimraf'); 8 | var path = require('path'); 9 | var url = require('url'); 10 | var qs = require('querystring'); 11 | 12 | rimraf.sync(path.resolve(__dirname, '..', 'lib', 'cache')); 13 | 14 | 15 | var CACHED_BY_CACHE_CONTROL = 'http://localhost:3293/index.js'; 16 | var CACHED_BY_CACHE_CONTROL__MAX_AGE = CACHED_BY_CACHE_CONTROL +'?cache-control='+encodeURIComponent('max-age=60'); 17 | var CACHED_BY_CACHE_CONTROL__MAX_AGE_PUBLIC = CACHED_BY_CACHE_CONTROL +'?cache-control='+encodeURIComponent('max-age=60,public'); 18 | var CACHED_BY_CACHE_CONTROL__MAX_AGE_PRIVATE = CACHED_BY_CACHE_CONTROL +'?cache-control='+encodeURIComponent('max-age=60,private'); 19 | var CACHED_BY_CACHE_CONTROL__LWS = CACHED_BY_CACHE_CONTROL +'?cache-control='+encodeURIComponent(' public , max-age=60 '); 20 | 21 | 22 | var overrideableHeaders = ['cache-control']; 23 | function overrideHeaders(res, path, stat) { 24 | 25 | var query = qs.parse(url.parse(this.req.url).query); 26 | overrideableHeaders.forEach(function(header){ 27 | if(header in query) { 28 | res.setHeader(header,query[header]); 29 | } 30 | }); 31 | 32 | } 33 | 34 | var cacheControlServer = http.createServer(serveStatic(__dirname, { 35 | etag: false, 36 | lastModified: false, 37 | cacheControl: true, 38 | maxAge: 5000, 39 | fallthrough: false, 40 | setHeaders: overrideHeaders 41 | })); 42 | 43 | cacheControlServer.listen(3293, function onListen() { 44 | request('GET', CACHED_BY_CACHE_CONTROL, {cache: 'memory'}, function (err, res) { 45 | if (err) throw err; 46 | 47 | console.log('response E (populate memory cache)'); 48 | assert(res.statusCode === 200); 49 | assert(res.fromCache === undefined); 50 | assert(res.fromNotModified === undefined); 51 | res.body.on('data', function () {}); 52 | res.body.on('end', function () { 53 | setTimeout(function () { 54 | request('GET', CACHED_BY_CACHE_CONTROL, {cache: 'memory'}, function (err, res) { 55 | if (err) throw err; 56 | 57 | console.log('response F (from memory cache)'); 58 | assert(res.statusCode === 200); 59 | assert(res.fromCache === true); 60 | assert(res.fromNotModified === false); 61 | res.body.resume(); 62 | }); 63 | }, 25); 64 | }); 65 | }); 66 | 67 | request('GET', CACHED_BY_CACHE_CONTROL, {cache: 'file'}, function (err, res) { 68 | if (err) throw err; 69 | 70 | console.log('response G (populate file cache)'); 71 | assert(res.statusCode === 200); 72 | assert(res.fromCache === undefined); 73 | assert(res.fromNotModified === undefined); 74 | res.body.on('data', function () {}); 75 | res.body.on('end', function () { 76 | setTimeout(function () { 77 | request('GET', CACHED_BY_CACHE_CONTROL, {cache: 'file'}, function (err, res) { 78 | if (err) throw err; 79 | 80 | console.log('response H (from file cache)'); 81 | assert(res.statusCode === 200); 82 | assert(res.fromCache === true); 83 | assert(res.fromNotModified === false); 84 | res.body.resume(); 85 | }); 86 | }, 1000); 87 | }); 88 | }); 89 | 90 | request('GET', CACHED_BY_CACHE_CONTROL__MAX_AGE, {cache: 'memory'}, function(err, res){ 91 | if (err) throw err; 92 | 93 | console.log('response E.1 ("max-age=60")'); 94 | assert(res.statusCode === 200); 95 | assert(res.fromCache === undefined); 96 | assert(res.fromNotModified === undefined); 97 | res.body.on('data', function () {}); 98 | res.body.on('end', function () { 99 | setTimeout(function () { 100 | request('GET', CACHED_BY_CACHE_CONTROL__MAX_AGE, {cache: 'memory'}, function (err, res) { 101 | if (err) throw err; 102 | 103 | console.log('response F.1 ("max-age=60")'); 104 | assert(res.statusCode === 200); 105 | assert(res.fromCache === true); 106 | assert(res.fromNotModified === false); 107 | res.body.resume(); 108 | }); 109 | }, 25); 110 | }); 111 | }); 112 | 113 | request('GET', CACHED_BY_CACHE_CONTROL__MAX_AGE_PUBLIC, {cache: 'memory'}, function(err, res){ 114 | if (err) throw err; 115 | 116 | console.log('response E.2 ("max-age=60,public)'); 117 | assert(res.statusCode === 200); 118 | assert(res.fromCache === undefined); 119 | assert(res.fromNotModified === undefined); 120 | res.body.on('data', function () {}); 121 | res.body.on('end', function () { 122 | setTimeout(function () { 123 | request('GET', CACHED_BY_CACHE_CONTROL__MAX_AGE_PUBLIC, {cache: 'memory'}, function (err, res) { 124 | if (err) throw err; 125 | 126 | console.log('response F.2 ("max-age=60,public)'); 127 | assert(res.statusCode === 200); 128 | assert(res.fromCache === true); 129 | assert(res.fromNotModified === false); 130 | res.body.resume(); 131 | }); 132 | }, 25); 133 | }); 134 | }); 135 | 136 | request('GET', CACHED_BY_CACHE_CONTROL__MAX_AGE_PRIVATE, {cache: 'memory'}, function(err, res){ 137 | if (err) throw err; 138 | 139 | console.log('response E.3 ("max-age=60,private)'); 140 | assert(res.statusCode === 200); 141 | assert(res.fromCache === undefined); 142 | assert(res.fromNotModified === undefined); 143 | res.body.on('data', function () {}); 144 | res.body.on('end', function () { 145 | setTimeout(function () { 146 | request('GET', CACHED_BY_CACHE_CONTROL__MAX_AGE_PRIVATE, {cache: 'memory'}, function (err, res) { 147 | if (err) throw err; 148 | 149 | console.log('response F.3 ("max-age=60,private)'); 150 | assert(res.statusCode === 200); 151 | assert(!res.fromCache); 152 | res.body.resume(); 153 | }); 154 | }, 25); 155 | }); 156 | }); 157 | 158 | request('GET', CACHED_BY_CACHE_CONTROL__LWS, {cache: 'memory'}, function(err, res){ 159 | if (err) throw err; 160 | 161 | console.log('response E.4 (" public , max-age=60 ")'); 162 | assert(res.statusCode === 200); 163 | assert(res.fromCache === undefined); 164 | assert(res.fromNotModified === undefined); 165 | res.body.on('data', function () {}); 166 | res.body.on('end', function () { 167 | setTimeout(function () { 168 | request('GET', CACHED_BY_CACHE_CONTROL__MAX_AGE_PUBLIC, {cache: 'memory'}, function (err, res) { 169 | if (err) throw err; 170 | 171 | console.log('response F.4 (" public , max-age=60 ")'); 172 | assert(res.statusCode === 200); 173 | assert(res.fromCache === true); 174 | assert(res.fromNotModified === false); 175 | res.body.resume(); 176 | }); 177 | }, 25); 178 | }); 179 | }); 180 | }); 181 | 182 | cacheControlServer.unref(); 183 | 184 | 185 | var CACHED_BY_ETAGS = 'http://localhost:4294/index.js'; 186 | 187 | var etagsServer = http.createServer(serveStatic(__dirname, { 188 | etag: true, 189 | lastModified: false, 190 | cacheControl: false, 191 | fallthrough: false 192 | })); 193 | 194 | etagsServer.listen(4294, function onListen() { 195 | request('GET', CACHED_BY_ETAGS, {cache: 'memory'}, function (err, res) { 196 | if (err) throw err; 197 | 198 | console.log('response I (populate memory cache)'); 199 | assert(res.statusCode === 200); 200 | assert(res.fromCache === undefined); 201 | assert(res.fromNotModified === undefined); 202 | res.body.on('data', function () {}); 203 | res.body.on('end', function () { 204 | setTimeout(function () { 205 | request('GET', CACHED_BY_ETAGS, {cache: 'memory'}, function (err, res) { 206 | if (err) throw err; 207 | 208 | console.log('response J (from memory cache)'); 209 | assert(res.statusCode === 200); 210 | assert(res.fromCache === true); 211 | assert(res.fromNotModified === true); 212 | res.body.resume(); 213 | }); 214 | }, 25); 215 | }); 216 | }); 217 | 218 | request('GET', CACHED_BY_ETAGS, {cache: 'file'}, function (err, res) { 219 | if (err) throw err; 220 | 221 | console.log('response K (populate file cache)'); 222 | assert(res.statusCode === 200); 223 | assert(res.fromCache === undefined); 224 | assert(res.fromNotModified === undefined); 225 | res.body.on('data', function () {}); 226 | res.body.on('end', function () { 227 | setTimeout(function () { 228 | request('GET', CACHED_BY_ETAGS, {cache: 'file'}, function (err, res) { 229 | if (err) throw err; 230 | 231 | console.log('response L (from file cache)'); 232 | assert(res.statusCode === 200); 233 | assert(res.fromCache === true); 234 | assert(res.fromNotModified === true); 235 | res.body.resume(); 236 | }); 237 | }, 1000); 238 | }); 239 | }); 240 | }); 241 | 242 | etagsServer.unref(); 243 | 244 | 245 | var CACHED_BY_LAST_MODIFIED = 'http://localhost:5295/index.js'; 246 | 247 | var lastModifiedServer = http.createServer(serveStatic(__dirname, { 248 | etag: false, 249 | lastModified: true, 250 | cacheControl: false, 251 | fallthrough: false 252 | })); 253 | 254 | lastModifiedServer.listen(5295, function onListen() { 255 | request('GET', CACHED_BY_LAST_MODIFIED, {cache: 'memory'}, function (err, res) { 256 | if (err) throw err; 257 | 258 | console.log('response M (populate memory cache)'); 259 | assert(res.statusCode === 200); 260 | assert(res.fromCache === undefined); 261 | assert(res.fromNotModified === undefined); 262 | res.body.on('data', function () {}); 263 | res.body.on('end', function () { 264 | setTimeout(function () { 265 | request('GET', CACHED_BY_LAST_MODIFIED, {cache: 'memory'}, function (err, res) { 266 | if (err) throw err; 267 | 268 | console.log('response N (from memory cache)'); 269 | assert(res.statusCode === 200); 270 | assert(res.fromCache === true); 271 | assert(res.fromNotModified === true); 272 | res.body.resume(); 273 | }); 274 | }, 25); 275 | }); 276 | }); 277 | 278 | request('GET', CACHED_BY_LAST_MODIFIED, {cache: 'file'}, function (err, res) { 279 | if (err) throw err; 280 | 281 | console.log('response O (populate file cache)'); 282 | assert(res.statusCode === 200); 283 | assert(res.fromCache === undefined); 284 | assert(res.fromNotModified === undefined); 285 | res.body.on('data', function () {}); 286 | res.body.on('end', function () { 287 | setTimeout(function () { 288 | request('GET', CACHED_BY_LAST_MODIFIED, {cache: 'file'}, function (err, res) { 289 | if (err) throw err; 290 | 291 | console.log('response P (from file cache)'); 292 | assert(res.statusCode === 200); 293 | assert(res.fromCache === true); 294 | assert(res.fromNotModified === true); 295 | res.body.resume(); 296 | }); 297 | }, 1000); 298 | }); 299 | }); 300 | }); 301 | 302 | lastModifiedServer.unref(); 303 | -------------------------------------------------------------------------------- /test/gzip.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var request = require('../'); 5 | 6 | request('GET', 'https://www.yahoo.com', {cache: 'file', followRedirects: true, gzip: true}, function (err, res) { 7 | if (err) throw err; 8 | assert(res.statusCode === 200); 9 | res.body.pipe(process.stdout); 10 | }); 11 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var request = require('../'); 5 | 6 | request('GET', 'http://example.com', function (err, res) { 7 | if (err) throw err; 8 | 9 | console.log('response A'); 10 | assert(res.statusCode === 200); 11 | res.body.resume(); 12 | }); 13 | 14 | request('GET', 'http://example.com:80', function (err, res) { 15 | if (err) throw err; 16 | 17 | console.log('response A1'); 18 | assert(res.statusCode === 200); 19 | res.body.resume(); 20 | }); 21 | 22 | 23 | request('GET', 'https://www.promisejs.org', function (err, res) { 24 | if (err) throw err; 25 | 26 | console.log('response B'); 27 | assert(res.statusCode === 200); 28 | res.body.resume(); 29 | }); 30 | 31 | request('GET', 'https://promisejs.org', {followRedirects: true}, function (err, res) { 32 | if (err) throw err; 33 | 34 | console.log('response C'); 35 | assert(res.statusCode === 200); 36 | res.body.resume(); 37 | }); 38 | 39 | request('GET', 'https://api.github.com/repos/isaacs/npm', {allowRedirectHeaders: ['User-Agent'], followRedirects: true, headers: {'User-Agent': 'http-basic'}}, function (err, res) { 40 | if (err) throw err; 41 | 42 | console.log('response D'); 43 | assert(res.statusCode === 200); 44 | res.body.resume(); 45 | }); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "declaration": true, 5 | "outDir": "lib", 6 | "strict": true, 7 | "lib": [ 8 | "es2016" 9 | ] 10 | } 11 | } -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/concat-stream@^1.6.0": 6 | version "1.6.0" 7 | resolved "https://registry.yarnpkg.com/@types/concat-stream/-/concat-stream-1.6.0.tgz#394dbe0bb5fee46b38d896735e8b68ef2390d00d" 8 | integrity sha1-OU2+C7X+5Gs42JZzXoto7yOQ0A0= 9 | dependencies: 10 | "@types/node" "*" 11 | 12 | "@types/micromatch@^2.3.29": 13 | version "2.3.30" 14 | resolved "https://registry.yarnpkg.com/@types/micromatch/-/micromatch-2.3.30.tgz#c2a143675f200fbcebe57fb0dab0cbf58093d4b0" 15 | integrity sha512-6rW4NsUHaDudxJSuRlm1PdNu61CDXkgix7LBOBg7b3yWQ43XANYSPwkvX1cGiZvBVZW8c5rsCEfrfzbPkch8ag== 16 | dependencies: 17 | "@types/parse-glob" "*" 18 | 19 | "@types/node@*", "@types/node@^11.9.0": 20 | version "11.11.4" 21 | resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.4.tgz#8808bd5a82bbf6f5d412eff1c228d178e7c24bb3" 22 | integrity sha512-02tIL+QIi/RW4E5xILdoAMjeJ9kYq5t5S2vciUdFPXv/ikFTb0zK8q9vXkg4+WAJuYXGiVT1H28AkD2C+IkXVw== 23 | 24 | "@types/node@^10.0.3": 25 | version "10.14.1" 26 | resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.1.tgz#8701cd760acc20beba5ffe0b7a1b879f39cb8c41" 27 | integrity sha512-Rymt08vh1GaW4vYB6QP61/5m/CFLGnFZP++bJpWbiNxceNa6RBipDmb413jvtSf/R1gg5a/jQVl2jY4XVRscEA== 28 | 29 | "@types/node@^7.0.31": 30 | version "7.10.5" 31 | resolved "https://registry.yarnpkg.com/@types/node/-/node-7.10.5.tgz#6831006b2c87441d69e79b37ae9bc03246dfb379" 32 | integrity sha512-RYkagUUbxQBss46ElbEa+j4q4X3GR12QwB7a/PM5hmVuVkYoW1jENT1+taspKUv8ibwW8cw+kRFbOaTc/Key3w== 33 | 34 | "@types/parse-glob@*": 35 | version "3.0.29" 36 | resolved "https://registry.yarnpkg.com/@types/parse-glob/-/parse-glob-3.0.29.tgz#6a40ec7ebd2418ee69ee397e48e42169268a10bf" 37 | integrity sha1-akDsfr0kGO5p7jl+SOQhaSaKEL8= 38 | 39 | "@types/resolve@0.0.4": 40 | version "0.0.4" 41 | resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.4.tgz#9b586d65a947dea88c4bc24da0b905fe9520a0d5" 42 | integrity sha1-m1htZalH3qiMS8JNoLkF/pUgoNU= 43 | dependencies: 44 | "@types/node" "*" 45 | 46 | arr-diff@^4.0.0: 47 | version "4.0.0" 48 | resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" 49 | integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= 50 | 51 | arr-flatten@^1.1.0: 52 | version "1.1.0" 53 | resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" 54 | integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== 55 | 56 | arr-union@^3.1.0: 57 | version "3.1.0" 58 | resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" 59 | integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= 60 | 61 | array-unique@^0.3.2: 62 | version "0.3.2" 63 | resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" 64 | integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= 65 | 66 | asap@~1.0.0: 67 | version "1.0.0" 68 | resolved "https://registry.yarnpkg.com/asap/-/asap-1.0.0.tgz#b2a45da5fdfa20b0496fc3768cc27c12fa916a7d" 69 | integrity sha1-sqRdpf36ILBJb8N2jMJ8EvqRan0= 70 | 71 | asap@~2.0.3: 72 | version "2.0.6" 73 | resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" 74 | integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= 75 | 76 | assign-symbols@^1.0.0: 77 | version "1.0.0" 78 | resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" 79 | integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= 80 | 81 | atob@^2.1.1: 82 | version "2.1.2" 83 | resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" 84 | integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== 85 | 86 | babel-polyfill@^6.23.0: 87 | version "6.26.0" 88 | resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" 89 | integrity sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM= 90 | dependencies: 91 | babel-runtime "^6.26.0" 92 | core-js "^2.5.0" 93 | regenerator-runtime "^0.10.5" 94 | 95 | babel-runtime@^6.26.0: 96 | version "6.26.0" 97 | resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" 98 | integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= 99 | dependencies: 100 | core-js "^2.4.0" 101 | regenerator-runtime "^0.11.0" 102 | 103 | balanced-match@^1.0.0: 104 | version "1.0.0" 105 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 106 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 107 | 108 | barrage@^1.1.0: 109 | version "1.1.0" 110 | resolved "https://registry.yarnpkg.com/barrage/-/barrage-1.1.0.tgz#6f5384765c463f7718012f160dccf033fc6fc394" 111 | integrity sha1-b1OEdlxGP3cYAS8WDczwM/xvw5Q= 112 | dependencies: 113 | promise "^6.0.0" 114 | 115 | base@^0.11.1: 116 | version "0.11.2" 117 | resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" 118 | integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== 119 | dependencies: 120 | cache-base "^1.0.1" 121 | class-utils "^0.3.5" 122 | component-emitter "^1.2.1" 123 | define-property "^1.0.0" 124 | isobject "^3.0.1" 125 | mixin-deep "^1.2.0" 126 | pascalcase "^0.1.1" 127 | 128 | brace-expansion@^1.1.7: 129 | version "1.1.11" 130 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 131 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 132 | dependencies: 133 | balanced-match "^1.0.0" 134 | concat-map "0.0.1" 135 | 136 | braces@^2.3.1: 137 | version "2.3.2" 138 | resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" 139 | integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== 140 | dependencies: 141 | arr-flatten "^1.1.0" 142 | array-unique "^0.3.2" 143 | extend-shallow "^2.0.1" 144 | fill-range "^4.0.0" 145 | isobject "^3.0.1" 146 | repeat-element "^1.1.2" 147 | snapdragon "^0.8.1" 148 | snapdragon-node "^2.0.1" 149 | split-string "^3.0.2" 150 | to-regex "^3.0.1" 151 | 152 | buffer-from@^1.0.0: 153 | version "1.1.1" 154 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 155 | integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== 156 | 157 | cache-base@^1.0.1: 158 | version "1.0.1" 159 | resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" 160 | integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== 161 | dependencies: 162 | collection-visit "^1.0.0" 163 | component-emitter "^1.2.1" 164 | get-value "^2.0.6" 165 | has-value "^1.0.0" 166 | isobject "^3.0.1" 167 | set-value "^2.0.0" 168 | to-object-path "^0.3.0" 169 | union-value "^1.0.0" 170 | unset-value "^1.0.0" 171 | 172 | caseless@^0.12.0: 173 | version "0.12.0" 174 | resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" 175 | integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= 176 | 177 | class-utils@^0.3.5: 178 | version "0.3.6" 179 | resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" 180 | integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== 181 | dependencies: 182 | arr-union "^3.1.0" 183 | define-property "^0.2.5" 184 | isobject "^3.0.0" 185 | static-extend "^0.1.1" 186 | 187 | collection-visit@^1.0.0: 188 | version "1.0.0" 189 | resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" 190 | integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= 191 | dependencies: 192 | map-visit "^1.0.0" 193 | object-visit "^1.0.0" 194 | 195 | component-emitter@^1.2.1: 196 | version "1.2.1" 197 | resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" 198 | integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= 199 | 200 | concat-map@0.0.1: 201 | version "0.0.1" 202 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 203 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 204 | 205 | concat-stream@^1.6.2: 206 | version "1.6.2" 207 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" 208 | integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== 209 | dependencies: 210 | buffer-from "^1.0.0" 211 | inherits "^2.0.3" 212 | readable-stream "^2.2.2" 213 | typedarray "^0.0.6" 214 | 215 | copy-descriptor@^0.1.0: 216 | version "0.1.1" 217 | resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" 218 | integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= 219 | 220 | core-js@^2.4.0, core-js@^2.5.0: 221 | version "2.6.5" 222 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895" 223 | integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A== 224 | 225 | core-util-is@~1.0.0: 226 | version "1.0.2" 227 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 228 | integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= 229 | 230 | debug@2.6.9, debug@^2.2.0, debug@^2.3.3: 231 | version "2.6.9" 232 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 233 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 234 | dependencies: 235 | ms "2.0.0" 236 | 237 | decode-uri-component@^0.2.0: 238 | version "0.2.0" 239 | resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" 240 | integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= 241 | 242 | define-property@^0.2.5: 243 | version "0.2.5" 244 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" 245 | integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= 246 | dependencies: 247 | is-descriptor "^0.1.0" 248 | 249 | define-property@^1.0.0: 250 | version "1.0.0" 251 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" 252 | integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= 253 | dependencies: 254 | is-descriptor "^1.0.0" 255 | 256 | define-property@^2.0.2: 257 | version "2.0.2" 258 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" 259 | integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== 260 | dependencies: 261 | is-descriptor "^1.0.2" 262 | isobject "^3.0.1" 263 | 264 | depd@~1.1.2: 265 | version "1.1.2" 266 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 267 | integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= 268 | 269 | destroy@~1.0.4: 270 | version "1.0.4" 271 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 272 | integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= 273 | 274 | ee-first@1.1.1: 275 | version "1.1.1" 276 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 277 | integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= 278 | 279 | encodeurl@~1.0.2: 280 | version "1.0.2" 281 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" 282 | integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= 283 | 284 | escape-html@~1.0.3: 285 | version "1.0.3" 286 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 287 | integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= 288 | 289 | etag@~1.8.1: 290 | version "1.8.1" 291 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 292 | integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= 293 | 294 | expand-brackets@^2.1.4: 295 | version "2.1.4" 296 | resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" 297 | integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= 298 | dependencies: 299 | debug "^2.3.3" 300 | define-property "^0.2.5" 301 | extend-shallow "^2.0.1" 302 | posix-character-classes "^0.1.0" 303 | regex-not "^1.0.0" 304 | snapdragon "^0.8.1" 305 | to-regex "^3.0.1" 306 | 307 | extend-shallow@^2.0.1: 308 | version "2.0.1" 309 | resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" 310 | integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= 311 | dependencies: 312 | is-extendable "^0.1.0" 313 | 314 | extend-shallow@^3.0.0, extend-shallow@^3.0.2: 315 | version "3.0.2" 316 | resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" 317 | integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= 318 | dependencies: 319 | assign-symbols "^1.0.0" 320 | is-extendable "^1.0.1" 321 | 322 | extglob@^2.0.4: 323 | version "2.0.4" 324 | resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" 325 | integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== 326 | dependencies: 327 | array-unique "^0.3.2" 328 | define-property "^1.0.0" 329 | expand-brackets "^2.1.4" 330 | extend-shallow "^2.0.1" 331 | fragment-cache "^0.2.1" 332 | regex-not "^1.0.0" 333 | snapdragon "^0.8.1" 334 | to-regex "^3.0.1" 335 | 336 | fill-range@^4.0.0: 337 | version "4.0.0" 338 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" 339 | integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= 340 | dependencies: 341 | extend-shallow "^2.0.1" 342 | is-number "^3.0.0" 343 | repeat-string "^1.6.1" 344 | to-regex-range "^2.1.0" 345 | 346 | flowgen2@^2.2.1: 347 | version "2.2.1" 348 | resolved "https://registry.yarnpkg.com/flowgen2/-/flowgen2-2.2.1.tgz#67fbbf06a299e35ddf82c26e96359e1d251dcd32" 349 | integrity sha512-7LG9UuDpXL/g9taO044o/1MRk1guJ2nr/7wDcennzYZfz1YQyepKNzYLLot8kHLgtQhFzGwzLKItH6RJ4EZZTw== 350 | dependencies: 351 | "@types/micromatch" "^2.3.29" 352 | "@types/node" "^7.0.31" 353 | "@types/resolve" "0.0.4" 354 | babel-polyfill "^6.23.0" 355 | lsr "^2.0.0" 356 | micromatch "^3.0.3" 357 | prettier "^1.7.4" 358 | resolve "^1.3.3" 359 | typescript "^2.4.0" 360 | 361 | for-in@^1.0.2: 362 | version "1.0.2" 363 | resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" 364 | integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= 365 | 366 | fragment-cache@^0.2.1: 367 | version "0.2.1" 368 | resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" 369 | integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= 370 | dependencies: 371 | map-cache "^0.2.2" 372 | 373 | fresh@0.5.2: 374 | version "0.5.2" 375 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 376 | integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= 377 | 378 | fs.realpath@^1.0.0: 379 | version "1.0.0" 380 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 381 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 382 | 383 | get-value@^2.0.3, get-value@^2.0.6: 384 | version "2.0.6" 385 | resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" 386 | integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= 387 | 388 | glob@^7.1.3: 389 | version "7.1.3" 390 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" 391 | integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== 392 | dependencies: 393 | fs.realpath "^1.0.0" 394 | inflight "^1.0.4" 395 | inherits "2" 396 | minimatch "^3.0.4" 397 | once "^1.3.0" 398 | path-is-absolute "^1.0.0" 399 | 400 | has-value@^0.3.1: 401 | version "0.3.1" 402 | resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" 403 | integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= 404 | dependencies: 405 | get-value "^2.0.3" 406 | has-values "^0.1.4" 407 | isobject "^2.0.0" 408 | 409 | has-value@^1.0.0: 410 | version "1.0.0" 411 | resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" 412 | integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= 413 | dependencies: 414 | get-value "^2.0.6" 415 | has-values "^1.0.0" 416 | isobject "^3.0.0" 417 | 418 | has-values@^0.1.4: 419 | version "0.1.4" 420 | resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" 421 | integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= 422 | 423 | has-values@^1.0.0: 424 | version "1.0.0" 425 | resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" 426 | integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= 427 | dependencies: 428 | is-number "^3.0.0" 429 | kind-of "^4.0.0" 430 | 431 | http-errors@~1.6.2: 432 | version "1.6.3" 433 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" 434 | integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= 435 | dependencies: 436 | depd "~1.1.2" 437 | inherits "2.0.3" 438 | setprototypeof "1.1.0" 439 | statuses ">= 1.4.0 < 2" 440 | 441 | http-response-object@^3.0.1: 442 | version "3.0.2" 443 | resolved "https://registry.yarnpkg.com/http-response-object/-/http-response-object-3.0.2.tgz#7f435bb210454e4360d074ef1f989d5ea8aa9810" 444 | integrity sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA== 445 | dependencies: 446 | "@types/node" "^10.0.3" 447 | 448 | inflight@^1.0.4: 449 | version "1.0.6" 450 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 451 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 452 | dependencies: 453 | once "^1.3.0" 454 | wrappy "1" 455 | 456 | inherits@2, inherits@2.0.3, inherits@^2.0.3, inherits@~2.0.3: 457 | version "2.0.3" 458 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 459 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 460 | 461 | is-accessor-descriptor@^0.1.6: 462 | version "0.1.6" 463 | resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" 464 | integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= 465 | dependencies: 466 | kind-of "^3.0.2" 467 | 468 | is-accessor-descriptor@^1.0.0: 469 | version "1.0.0" 470 | resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" 471 | integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== 472 | dependencies: 473 | kind-of "^6.0.0" 474 | 475 | is-buffer@^1.1.5: 476 | version "1.1.6" 477 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" 478 | integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== 479 | 480 | is-data-descriptor@^0.1.4: 481 | version "0.1.4" 482 | resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" 483 | integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= 484 | dependencies: 485 | kind-of "^3.0.2" 486 | 487 | is-data-descriptor@^1.0.0: 488 | version "1.0.0" 489 | resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" 490 | integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== 491 | dependencies: 492 | kind-of "^6.0.0" 493 | 494 | is-descriptor@^0.1.0: 495 | version "0.1.6" 496 | resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" 497 | integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== 498 | dependencies: 499 | is-accessor-descriptor "^0.1.6" 500 | is-data-descriptor "^0.1.4" 501 | kind-of "^5.0.0" 502 | 503 | is-descriptor@^1.0.0, is-descriptor@^1.0.2: 504 | version "1.0.2" 505 | resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" 506 | integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== 507 | dependencies: 508 | is-accessor-descriptor "^1.0.0" 509 | is-data-descriptor "^1.0.0" 510 | kind-of "^6.0.2" 511 | 512 | is-extendable@^0.1.0, is-extendable@^0.1.1: 513 | version "0.1.1" 514 | resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" 515 | integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= 516 | 517 | is-extendable@^1.0.1: 518 | version "1.0.1" 519 | resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" 520 | integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== 521 | dependencies: 522 | is-plain-object "^2.0.4" 523 | 524 | is-number@^3.0.0: 525 | version "3.0.0" 526 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" 527 | integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= 528 | dependencies: 529 | kind-of "^3.0.2" 530 | 531 | is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: 532 | version "2.0.4" 533 | resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" 534 | integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== 535 | dependencies: 536 | isobject "^3.0.1" 537 | 538 | is-windows@^1.0.2: 539 | version "1.0.2" 540 | resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" 541 | integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== 542 | 543 | isarray@1.0.0, isarray@~1.0.0: 544 | version "1.0.0" 545 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 546 | integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= 547 | 548 | isobject@^2.0.0: 549 | version "2.1.0" 550 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" 551 | integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= 552 | dependencies: 553 | isarray "1.0.0" 554 | 555 | isobject@^3.0.0, isobject@^3.0.1: 556 | version "3.0.1" 557 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" 558 | integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= 559 | 560 | kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: 561 | version "3.2.2" 562 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" 563 | integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= 564 | dependencies: 565 | is-buffer "^1.1.5" 566 | 567 | kind-of@^4.0.0: 568 | version "4.0.0" 569 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" 570 | integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= 571 | dependencies: 572 | is-buffer "^1.1.5" 573 | 574 | kind-of@^5.0.0: 575 | version "5.1.0" 576 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" 577 | integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== 578 | 579 | kind-of@^6.0.0, kind-of@^6.0.2: 580 | version "6.0.2" 581 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" 582 | integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== 583 | 584 | lsr@^2.0.0: 585 | version "2.0.0" 586 | resolved "https://registry.yarnpkg.com/lsr/-/lsr-2.0.0.tgz#d2186b14c2a08f7617515d8125426bfb2821ce2b" 587 | integrity sha512-6hf3xSnx2vanmjgEXGXqEXSXWDV6DkhlcAX6EgmbL2UI3M6KnWfnyfnZYEMLhEhRUm3La5IPPe5v15fe/xKk1Q== 588 | dependencies: 589 | "@types/node" "^7.0.31" 590 | barrage "^1.1.0" 591 | promise "^7.2.0" 592 | 593 | map-cache@^0.2.2: 594 | version "0.2.2" 595 | resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" 596 | integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= 597 | 598 | map-visit@^1.0.0: 599 | version "1.0.0" 600 | resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" 601 | integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= 602 | dependencies: 603 | object-visit "^1.0.0" 604 | 605 | micromatch@^3.0.3: 606 | version "3.1.10" 607 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" 608 | integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== 609 | dependencies: 610 | arr-diff "^4.0.0" 611 | array-unique "^0.3.2" 612 | braces "^2.3.1" 613 | define-property "^2.0.2" 614 | extend-shallow "^3.0.2" 615 | extglob "^2.0.4" 616 | fragment-cache "^0.2.1" 617 | kind-of "^6.0.2" 618 | nanomatch "^1.2.9" 619 | object.pick "^1.3.0" 620 | regex-not "^1.0.0" 621 | snapdragon "^0.8.1" 622 | to-regex "^3.0.2" 623 | 624 | mime@1.4.1: 625 | version "1.4.1" 626 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" 627 | integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== 628 | 629 | minimatch@^3.0.4: 630 | version "3.0.4" 631 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 632 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 633 | dependencies: 634 | brace-expansion "^1.1.7" 635 | 636 | mixin-deep@^1.2.0: 637 | version "1.3.1" 638 | resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" 639 | integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ== 640 | dependencies: 641 | for-in "^1.0.2" 642 | is-extendable "^1.0.1" 643 | 644 | ms@2.0.0: 645 | version "2.0.0" 646 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 647 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 648 | 649 | nanomatch@^1.2.9: 650 | version "1.2.13" 651 | resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" 652 | integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== 653 | dependencies: 654 | arr-diff "^4.0.0" 655 | array-unique "^0.3.2" 656 | define-property "^2.0.2" 657 | extend-shallow "^3.0.2" 658 | fragment-cache "^0.2.1" 659 | is-windows "^1.0.2" 660 | kind-of "^6.0.2" 661 | object.pick "^1.3.0" 662 | regex-not "^1.0.0" 663 | snapdragon "^0.8.1" 664 | to-regex "^3.0.1" 665 | 666 | object-copy@^0.1.0: 667 | version "0.1.0" 668 | resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" 669 | integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= 670 | dependencies: 671 | copy-descriptor "^0.1.0" 672 | define-property "^0.2.5" 673 | kind-of "^3.0.3" 674 | 675 | object-visit@^1.0.0: 676 | version "1.0.1" 677 | resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" 678 | integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= 679 | dependencies: 680 | isobject "^3.0.0" 681 | 682 | object.pick@^1.3.0: 683 | version "1.3.0" 684 | resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" 685 | integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= 686 | dependencies: 687 | isobject "^3.0.1" 688 | 689 | on-finished@~2.3.0: 690 | version "2.3.0" 691 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 692 | integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= 693 | dependencies: 694 | ee-first "1.1.1" 695 | 696 | once@^1.3.0: 697 | version "1.4.0" 698 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 699 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 700 | dependencies: 701 | wrappy "1" 702 | 703 | parse-cache-control@^1.0.1: 704 | version "1.0.1" 705 | resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e" 706 | integrity sha1-juqz5U+laSD+Fro493+iGqzC104= 707 | 708 | parseurl@~1.3.2: 709 | version "1.3.2" 710 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" 711 | integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= 712 | 713 | pascalcase@^0.1.1: 714 | version "0.1.1" 715 | resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" 716 | integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= 717 | 718 | path-is-absolute@^1.0.0: 719 | version "1.0.1" 720 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 721 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 722 | 723 | path-parse@^1.0.6: 724 | version "1.0.6" 725 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" 726 | integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== 727 | 728 | posix-character-classes@^0.1.0: 729 | version "0.1.1" 730 | resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" 731 | integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= 732 | 733 | prettier@^1.7.4: 734 | version "1.16.4" 735 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717" 736 | integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g== 737 | 738 | process-nextick-args@~2.0.0: 739 | version "2.0.0" 740 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" 741 | integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== 742 | 743 | promise@^6.0.0: 744 | version "6.1.0" 745 | resolved "https://registry.yarnpkg.com/promise/-/promise-6.1.0.tgz#2ce729f6b94b45c26891ad0602c5c90e04c6eef6" 746 | integrity sha1-LOcp9rlLRcJoka0GAsXJDgTG7vY= 747 | dependencies: 748 | asap "~1.0.0" 749 | 750 | promise@^7.2.0: 751 | version "7.3.1" 752 | resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" 753 | integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== 754 | dependencies: 755 | asap "~2.0.3" 756 | 757 | range-parser@~1.2.0: 758 | version "1.2.0" 759 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" 760 | integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= 761 | 762 | readable-stream@^2.2.2: 763 | version "2.3.6" 764 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" 765 | integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== 766 | dependencies: 767 | core-util-is "~1.0.0" 768 | inherits "~2.0.3" 769 | isarray "~1.0.0" 770 | process-nextick-args "~2.0.0" 771 | safe-buffer "~5.1.1" 772 | string_decoder "~1.1.1" 773 | util-deprecate "~1.0.1" 774 | 775 | regenerator-runtime@^0.10.5: 776 | version "0.10.5" 777 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" 778 | integrity sha1-M2w+/BIgrc7dosn6tntaeVWjNlg= 779 | 780 | regenerator-runtime@^0.11.0: 781 | version "0.11.1" 782 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" 783 | integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== 784 | 785 | regex-not@^1.0.0, regex-not@^1.0.2: 786 | version "1.0.2" 787 | resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" 788 | integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== 789 | dependencies: 790 | extend-shallow "^3.0.2" 791 | safe-regex "^1.1.0" 792 | 793 | repeat-element@^1.1.2: 794 | version "1.1.3" 795 | resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" 796 | integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== 797 | 798 | repeat-string@^1.6.1: 799 | version "1.6.1" 800 | resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" 801 | integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= 802 | 803 | resolve-url@^0.2.1: 804 | version "0.2.1" 805 | resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" 806 | integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= 807 | 808 | resolve@^1.3.3: 809 | version "1.10.0" 810 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" 811 | integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg== 812 | dependencies: 813 | path-parse "^1.0.6" 814 | 815 | ret@~0.1.10: 816 | version "0.1.15" 817 | resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" 818 | integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== 819 | 820 | rimraf@^2.5.4: 821 | version "2.6.3" 822 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" 823 | integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== 824 | dependencies: 825 | glob "^7.1.3" 826 | 827 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 828 | version "5.1.2" 829 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 830 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 831 | 832 | safe-regex@^1.1.0: 833 | version "1.1.0" 834 | resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" 835 | integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= 836 | dependencies: 837 | ret "~0.1.10" 838 | 839 | send@0.16.2: 840 | version "0.16.2" 841 | resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" 842 | integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== 843 | dependencies: 844 | debug "2.6.9" 845 | depd "~1.1.2" 846 | destroy "~1.0.4" 847 | encodeurl "~1.0.2" 848 | escape-html "~1.0.3" 849 | etag "~1.8.1" 850 | fresh "0.5.2" 851 | http-errors "~1.6.2" 852 | mime "1.4.1" 853 | ms "2.0.0" 854 | on-finished "~2.3.0" 855 | range-parser "~1.2.0" 856 | statuses "~1.4.0" 857 | 858 | serve-static@^1.11.1: 859 | version "1.13.2" 860 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" 861 | integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== 862 | dependencies: 863 | encodeurl "~1.0.2" 864 | escape-html "~1.0.3" 865 | parseurl "~1.3.2" 866 | send "0.16.2" 867 | 868 | set-value@^0.4.3: 869 | version "0.4.3" 870 | resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" 871 | integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE= 872 | dependencies: 873 | extend-shallow "^2.0.1" 874 | is-extendable "^0.1.1" 875 | is-plain-object "^2.0.1" 876 | to-object-path "^0.3.0" 877 | 878 | set-value@^2.0.0: 879 | version "2.0.0" 880 | resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" 881 | integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg== 882 | dependencies: 883 | extend-shallow "^2.0.1" 884 | is-extendable "^0.1.1" 885 | is-plain-object "^2.0.3" 886 | split-string "^3.0.1" 887 | 888 | setprototypeof@1.1.0: 889 | version "1.1.0" 890 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" 891 | integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== 892 | 893 | snapdragon-node@^2.0.1: 894 | version "2.1.1" 895 | resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" 896 | integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== 897 | dependencies: 898 | define-property "^1.0.0" 899 | isobject "^3.0.0" 900 | snapdragon-util "^3.0.1" 901 | 902 | snapdragon-util@^3.0.1: 903 | version "3.0.1" 904 | resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" 905 | integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== 906 | dependencies: 907 | kind-of "^3.2.0" 908 | 909 | snapdragon@^0.8.1: 910 | version "0.8.2" 911 | resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" 912 | integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== 913 | dependencies: 914 | base "^0.11.1" 915 | debug "^2.2.0" 916 | define-property "^0.2.5" 917 | extend-shallow "^2.0.1" 918 | map-cache "^0.2.2" 919 | source-map "^0.5.6" 920 | source-map-resolve "^0.5.0" 921 | use "^3.1.0" 922 | 923 | source-map-resolve@^0.5.0: 924 | version "0.5.2" 925 | resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" 926 | integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== 927 | dependencies: 928 | atob "^2.1.1" 929 | decode-uri-component "^0.2.0" 930 | resolve-url "^0.2.1" 931 | source-map-url "^0.4.0" 932 | urix "^0.1.0" 933 | 934 | source-map-url@^0.4.0: 935 | version "0.4.0" 936 | resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" 937 | integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= 938 | 939 | source-map@^0.5.6: 940 | version "0.5.7" 941 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" 942 | integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= 943 | 944 | split-string@^3.0.1, split-string@^3.0.2: 945 | version "3.1.0" 946 | resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" 947 | integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== 948 | dependencies: 949 | extend-shallow "^3.0.0" 950 | 951 | static-extend@^0.1.1: 952 | version "0.1.2" 953 | resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" 954 | integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= 955 | dependencies: 956 | define-property "^0.2.5" 957 | object-copy "^0.1.0" 958 | 959 | "statuses@>= 1.4.0 < 2": 960 | version "1.5.0" 961 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" 962 | integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= 963 | 964 | statuses@~1.4.0: 965 | version "1.4.0" 966 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" 967 | integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== 968 | 969 | string_decoder@~1.1.1: 970 | version "1.1.1" 971 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 972 | integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== 973 | dependencies: 974 | safe-buffer "~5.1.0" 975 | 976 | to-object-path@^0.3.0: 977 | version "0.3.0" 978 | resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" 979 | integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= 980 | dependencies: 981 | kind-of "^3.0.2" 982 | 983 | to-regex-range@^2.1.0: 984 | version "2.1.1" 985 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" 986 | integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= 987 | dependencies: 988 | is-number "^3.0.0" 989 | repeat-string "^1.6.1" 990 | 991 | to-regex@^3.0.1, to-regex@^3.0.2: 992 | version "3.0.2" 993 | resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" 994 | integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== 995 | dependencies: 996 | define-property "^2.0.2" 997 | extend-shallow "^3.0.2" 998 | regex-not "^1.0.2" 999 | safe-regex "^1.1.0" 1000 | 1001 | typedarray@^0.0.6: 1002 | version "0.0.6" 1003 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 1004 | integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= 1005 | 1006 | typescript@^2.3.4, typescript@^2.4.0: 1007 | version "2.9.2" 1008 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" 1009 | integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w== 1010 | 1011 | union-value@^1.0.0: 1012 | version "1.0.0" 1013 | resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" 1014 | integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ= 1015 | dependencies: 1016 | arr-union "^3.1.0" 1017 | get-value "^2.0.6" 1018 | is-extendable "^0.1.1" 1019 | set-value "^0.4.3" 1020 | 1021 | unset-value@^1.0.0: 1022 | version "1.0.0" 1023 | resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" 1024 | integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= 1025 | dependencies: 1026 | has-value "^0.3.1" 1027 | isobject "^3.0.0" 1028 | 1029 | urix@^0.1.0: 1030 | version "0.1.0" 1031 | resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" 1032 | integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= 1033 | 1034 | use@^3.1.0: 1035 | version "3.1.1" 1036 | resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" 1037 | integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== 1038 | 1039 | util-deprecate@~1.0.1: 1040 | version "1.0.2" 1041 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1042 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 1043 | 1044 | wrappy@1: 1045 | version "1.0.2" 1046 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1047 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 1048 | --------------------------------------------------------------------------------