diff --git a/package.json b/package.json index 38dc4c5..bd979ce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "mhysa", - "version": "1.0.2", + "name": "@jogogo/mhysa", + "version": "0.0.1-alpha.1", "description": "Streams and event emitter utils for Node.js", "keywords": [ "promise", @@ -11,18 +11,33 @@ "author": { "name": "Wenzil" }, + "contributors": [ + { + "name": "jerry", + "email": "jerry@jogogo.co" + }, + { + "name": "lewis", + "email": "lewis@jogogo.co" + } + ], "license": "MIT", "main": "dist/index.js", "types": "dist/index.d.ts", "files": [ "dist" ], + "publishConfig": { + "registry": "https://npm.dev.jogogo.co/" + }, "repository": { - "url": "git@github.com:Wenzil/Mhysa.git", + "url": "git@github.com:Jogogoplay/mhysa.git", "type": "git" }, "scripts": { - "test": "ava", + "test": "NODE_PATH=src node node_modules/.bin/ava 'tests/*.spec.ts' -e", + "test:debug": "NODE_PATH=src node inspect node_modules/ava/profile.js", + "test:all": "NODE_PATH=src node node_modules/.bin/ava", "lint": "tslint -p tsconfig.json", "validate:tslint": "tslint-config-prettier-check ./tslint.json", "prepublishOnly": "yarn lint && yarn test && yarn tsc" @@ -30,20 +45,22 @@ "dependencies": {}, "devDependencies": { "@types/chai": "^4.1.7", - "@types/node": "^10.12.10", + "@types/node": "^12.7.2", + "@types/sinon": "^7.0.13", "ava": "^1.0.0-rc.2", "chai": "^4.2.0", "mhysa": "./", "prettier": "^1.14.3", - "ts-node": "^7.0.1", + "sinon": "^7.4.2", + "ts-node": "^8.3.0", "tslint": "^5.11.0", "tslint-config-prettier": "^1.16.0", "tslint-plugin-prettier": "^2.0.1", - "typescript": "^3.1.6" + "typescript": "^3.5.3" }, "ava": { "files": [ - "src/**/*.spec.ts" + "tests/*.spec.ts" ], "sources": [ "src/**/*.ts" diff --git a/src/functions/accumulator.ts b/src/functions/accumulator.ts new file mode 100644 index 0000000..82ca9ae --- /dev/null +++ b/src/functions/accumulator.ts @@ -0,0 +1,181 @@ +import { Transform, TransformOptions } from "stream"; +import { batch } from "."; + +export enum FlushStrategy { + rolling = "rolling", + sliding = "sliding", +} + +export type AccumulatorByIteratee = (event: T, bufferChunk: T) => boolean; + +function _accumulator( + accumulateBy: (data: T, buffer: T[], stream: Transform) => void, + shouldFlush: boolean = true, + options: TransformOptions = {}, +) { + const buffer: T[] = []; + return new Transform({ + ...options, + transform(data: T, encoding, callback) { + accumulateBy(data, buffer, this); + callback(); + }, + flush(callback) { + if (shouldFlush) { + this.push(buffer); + } + callback(); + }, + }); +} + +function _sliding( + windowLength: number, + key?: string, +): (event: T, buffer: T[], stream: Transform) => void { + return (event: T, buffer: T[], stream: Transform) => { + if (key) { + let index = 0; + if (event[key] === undefined) { + stream.emit( + "error", + new Error( + `Key is missing in event: (${key}, ${JSON.stringify( + event, + )})`, + ), + ); + stream.resume(); + return; + } + while ( + index < buffer.length && + buffer[index][key] + windowLength <= event[key] + ) { + index++; + } + buffer.splice(0, index); + } else if (buffer.length === windowLength) { + buffer.shift(); + } + buffer.push(event); + stream.push(buffer); + }; +} + +function _slidingByFunction( + iteratee: AccumulatorByIteratee, +): (event: T, buffer: T[], stream: Transform) => void { + return (event: T, buffer: T[], stream: Transform) => { + let index = 0; + while (index < buffer.length && iteratee(event, buffer[index])) { + index++; + } + buffer.splice(0, index); + buffer.push(event); + stream.push(buffer); + }; +} + +function _rollingByFunction( + iteratee: AccumulatorByIteratee, +): (event: T, buffer: T[], stream: Transform) => void { + return (event: T, buffer: T[], stream: Transform) => { + if (iteratee) { + if (buffer.length > 0 && iteratee(event, buffer[0])) { + stream.push(buffer.slice(0)); + buffer.length = 0; + } + } + buffer.push(event); + }; +} + +function _rolling( + windowLength: number, + key?: string, +): (event: T, buffer: T[], stream: Transform) => void { + return (event: T, buffer: T[], stream: Transform) => { + if (key) { + if (event[key] === undefined) { + stream.emit( + "error", + new Error( + `Key is missing in event: (${key}, ${JSON.stringify( + event, + )})`, + ), + ); + stream.resume(); + return; + } else if ( + buffer.length > 0 && + buffer[0][key] + windowLength <= event[key] + ) { + stream.push(buffer.slice(0)); + buffer.length = 0; + } + } else if (buffer.length === windowLength) { + stream.push(buffer.slice(0)); + buffer.length = 0; + } + buffer.push(event); + }; +} + +export function accumulator( + flushStrategy: FlushStrategy, + batchSize: number, + keyBy?: string, + options?: TransformOptions, +): Transform { + if (flushStrategy === FlushStrategy.sliding) { + return sliding(batchSize, keyBy, options); + } else if (flushStrategy === FlushStrategy.rolling) { + return rolling(batchSize, keyBy, options); + } else { + return batch(batchSize); + } +} + +export function accumulatorBy( + flushStrategy: S, + iteratee: AccumulatorByIteratee, + options?: TransformOptions, +): Transform { + if (flushStrategy === FlushStrategy.sliding) { + return slidingBy(iteratee, options); + } else { + return rollingBy(iteratee, options); + } +} + +function sliding( + windowLength: number, + key?: string, + options?: TransformOptions, +): Transform { + return _accumulator(_sliding(windowLength, key), false, options); +} + +function slidingBy( + iteratee: AccumulatorByIteratee, + options?: TransformOptions, +): Transform { + return _accumulator(_slidingByFunction(iteratee), false, options); +} + +function rolling( + windowLength: number, + key?: string, + options?: TransformOptions, +): Transform { + return _accumulator(_rolling(windowLength, key), true, options); +} + +function rollingBy( + iteratee: AccumulatorByIteratee, + options?: TransformOptions, +): Transform { + return _accumulator(_rollingByFunction(iteratee), true, options); +} diff --git a/src/functions/baseDefinitions.ts b/src/functions/baseDefinitions.ts new file mode 100644 index 0000000..b02dd10 --- /dev/null +++ b/src/functions/baseDefinitions.ts @@ -0,0 +1,14 @@ +export interface WithEncoding { + encoding: string; +} + +export enum SerializationFormats { + utf8 = "utf8", +} + +type JsonPrimitive = string | number | object; +export type JsonValue = JsonPrimitive | JsonPrimitive[]; + +export interface JsonParseOptions { + pretty: boolean; +} diff --git a/src/functions/baseFunctions.ts b/src/functions/baseFunctions.ts new file mode 100644 index 0000000..6ff480a --- /dev/null +++ b/src/functions/baseFunctions.ts @@ -0,0 +1,23 @@ +export { accumulator, accumulatorBy } from "./accumulator"; +export { batch } from "./batch"; +export { child } from "./child"; +export { collect } from "./collect"; +export { concat } from "./concat"; +export { duplex } from "./duplex"; +export { filter } from "./filter"; +export { flatMap } from "./flatMap"; +export { fromArray } from "./fromArray"; +export { join } from "./join"; +export { last } from "./last"; +export { map } from "./map"; +export { merge } from "./merge"; +export { parallelMap } from "./parallelMap"; +export { parse } from "./parse"; +export { rate } from "./rate"; +export { reduce } from "./reduce"; +export { replace } from "./replace"; +export { split } from "./split"; +export { stringify } from "./stringify"; +export { unbatch } from "./unbatch"; +export { compose } from "./compose"; +export { demux } from "./demux"; diff --git a/src/functions/batch.ts b/src/functions/batch.ts new file mode 100644 index 0000000..e9f8915 --- /dev/null +++ b/src/functions/batch.ts @@ -0,0 +1,40 @@ +import { Transform, TransformOptions } from "stream"; + +export function batch( + batchSize: number = 1000, + maxBatchAge: number = 500, + options: TransformOptions = { + objectMode: true, + }, +): Transform { + let buffer: any[] = []; + let timer: NodeJS.Timer | null = null; + const sendChunk = (self: Transform) => { + if (timer) { + clearTimeout(timer); + } + timer = null; + self.push(buffer); + buffer = []; + }; + return new Transform({ + ...options, + transform(chunk, encoding, callback) { + buffer.push(chunk); + if (buffer.length === batchSize) { + sendChunk(this); + } else { + if (timer === null) { + timer = setInterval(() => { + sendChunk(this); + }, maxBatchAge); + } + } + callback(); + }, + flush(callback) { + sendChunk(this); + callback(); + }, + }); +} diff --git a/src/functions/child.ts b/src/functions/child.ts new file mode 100644 index 0000000..73bdbef --- /dev/null +++ b/src/functions/child.ts @@ -0,0 +1,11 @@ +import { ChildProcess } from "child_process"; +import { duplex } from "./baseFunctions"; + +export function child(childProcess: ChildProcess) { + if (childProcess.stdin === null) { + throw new Error("childProcess.stdin is null"); + } else if (childProcess.stdout === null) { + throw new Error("childProcess.stdout is null"); + } + return duplex(childProcess.stdin, childProcess.stdout); +} diff --git a/src/functions/collect.ts b/src/functions/collect.ts new file mode 100644 index 0000000..3c081bb --- /dev/null +++ b/src/functions/collect.ts @@ -0,0 +1,18 @@ +import { Transform, TransformOptions } from "stream"; + +export function collect(options: TransformOptions = {}): Transform { + const collected: any[] = []; + return new Transform({ + ...options, + transform(data, encoding, callback) { + collected.push(data); + callback(); + }, + flush(callback) { + this.push( + options.objectMode ? collected : Buffer.concat(collected), + ); + callback(); + }, + }); +} diff --git a/src/functions/compose.ts b/src/functions/compose.ts new file mode 100644 index 0000000..195598e --- /dev/null +++ b/src/functions/compose.ts @@ -0,0 +1,106 @@ +import { pipeline, Duplex, DuplexOptions } from "stream"; + +export function compose( + streams: Array< + NodeJS.ReadableStream | NodeJS.ReadWriteStream | NodeJS.WritableStream + >, + options?: DuplexOptions, +): Compose { + if (streams.length < 2) { + throw new Error("At least two streams are required to compose"); + } + + return new Compose(streams, options); +} + +enum EventSubscription { + Last = 0, + First, + All, + Self, +} + +const eventsTarget = { + close: EventSubscription.Last, + data: EventSubscription.Last, + drain: EventSubscription.Self, + end: EventSubscription.Last, + error: EventSubscription.Self, + finish: EventSubscription.Last, + pause: EventSubscription.Last, + pipe: EventSubscription.First, + readable: EventSubscription.Last, + resume: EventSubscription.Last, + unpipe: EventSubscription.First, +}; + +type AllStreams = + | NodeJS.ReadableStream + | NodeJS.ReadWriteStream + | NodeJS.WritableStream; + +export class Compose extends Duplex { + private first: AllStreams; + private last: AllStreams; + private streams: AllStreams[]; + + constructor(streams: AllStreams[], options?: DuplexOptions) { + super(options); + this.first = streams[0]; + this.last = streams[streams.length - 1]; + this.streams = streams; + pipeline(streams, (err: any) => { + this.emit("error", err); + }); + } + + public pipe(dest: T) { + return (this.last as NodeJS.ReadableStream).pipe(dest); + } + + public _write(chunk: any, encoding: string, cb: any) { + (this.first as NodeJS.WritableStream).write(chunk, encoding, cb); + } + + public bubble(...events: string[]) { + this.streams.forEach(s => { + events.forEach(e => { + s.on(e, (...args) => super.emit(e, ...args)); + }); + }); + } + + public on(event: string, cb: any) { + switch (eventsTarget[event]) { + case EventSubscription.First: + this.first.on(event, cb); + break; + case EventSubscription.Last: + this.last.on(event, cb); + break; + case EventSubscription.All: + this.streams.forEach(s => s.on(event, cb)); + break; + default: + super.on(event, cb); + } + return this; + } + + public once(event: string, cb: any) { + switch (eventsTarget[event]) { + case EventSubscription.First: + this.first.once(event, cb); + break; + case EventSubscription.Last: + this.last.once(event, cb); + break; + case EventSubscription.All: + this.streams.forEach(s => s.once(event, cb)); + break; + default: + super.once(event, cb); + } + return this; + } +} diff --git a/src/functions/concat.ts b/src/functions/concat.ts new file mode 100644 index 0000000..d15f936 --- /dev/null +++ b/src/functions/concat.ts @@ -0,0 +1,37 @@ +import { Readable } from "stream"; + +export function concat(...streams: NodeJS.ReadableStream[]): Readable { + let isStarted = false; + let currentStreamIndex = 0; + const startCurrentStream = () => { + if (currentStreamIndex >= streams.length) { + wrapper.push(null); + } else { + streams[currentStreamIndex] + .on("data", chunk => { + if (!wrapper.push(chunk)) { + streams[currentStreamIndex].pause(); + } + }) + .on("error", err => wrapper.emit("error", err)) + .on("end", () => { + currentStreamIndex++; + startCurrentStream(); + }); + } + }; + + const wrapper = new Readable({ + objectMode: true, + read() { + if (!isStarted) { + isStarted = true; + startCurrentStream(); + } + if (currentStreamIndex < streams.length) { + streams[currentStreamIndex].resume(); + } + }, + }); + return wrapper; +} diff --git a/src/functions/demux.ts b/src/functions/demux.ts new file mode 100644 index 0000000..66ee487 --- /dev/null +++ b/src/functions/demux.ts @@ -0,0 +1,99 @@ +import { WritableOptions, Writable } from "stream"; + +enum EventSubscription { + Last = 0, + First, + All, + Self, + Unhandled, +} + +const eventsTarget = { + close: EventSubscription.Self, + data: EventSubscription.All, + drain: EventSubscription.Self, + end: EventSubscription.Self, + error: EventSubscription.Self, + finish: EventSubscription.Self, + pause: EventSubscription.Self, + pipe: EventSubscription.Self, + readable: EventSubscription.Self, + resume: EventSubscription.Self, + unpipe: EventSubscription.Self, +}; + +type DemuxStreams = NodeJS.WritableStream | NodeJS.ReadWriteStream; + +export function demux( + construct: () => DemuxStreams, + demuxBy: string | ((chunk: any) => string), + options?: WritableOptions, +): Writable { + return new Demux(construct, demuxBy, options); +} + +// @TODO handle pipe event ie) Multiplex +class Demux extends Writable { + private streamsByKey: { + [key: string]: DemuxStreams; + }; + private demuxer: (chunk: any) => string; + private construct: (destKey?: string) => DemuxStreams; + constructor( + construct: (destKey?: string) => DemuxStreams, + demuxBy: string | ((chunk: any) => string), + options: WritableOptions = {}, + ) { + super(options); + this.demuxer = + typeof demuxBy === "string" ? chunk => chunk[demuxBy] : demuxBy; + this.construct = construct; + this.streamsByKey = {}; + } + + public _write(chunk: any, encoding: any, cb: any) { + const destKey = this.demuxer(chunk); + if (this.streamsByKey[destKey] === undefined) { + this.streamsByKey[destKey] = this.construct(destKey); + } + if (!this.streamsByKey[destKey].write(chunk, encoding)) { + this.streamsByKey[destKey].once("drain", () => { + cb(); + }); + } else { + cb(); + } + } + + public on(event: string, cb: any) { + switch (eventsTarget[event]) { + case EventSubscription.Self: + super.on(event, cb); + break; + case EventSubscription.All: + Object.keys(this.streamsByKey).forEach(key => + this.streamsByKey[key].on(event, cb), + ); + break; + default: + super.on(event, cb); + } + return this; + } + + public once(event: string, cb: any) { + switch (eventsTarget[event]) { + case EventSubscription.Self: + super.once(event, cb); + break; + case EventSubscription.All: + Object.keys(this.streamsByKey).forEach(key => + this.streamsByKey[key].once(event, cb), + ); + break; + default: + super.once(event, cb); + } + return this; + } +} diff --git a/src/functions/duplex.ts b/src/functions/duplex.ts new file mode 100644 index 0000000..b72fd0d --- /dev/null +++ b/src/functions/duplex.ts @@ -0,0 +1,31 @@ +import { Duplex } from "stream"; + +export function duplex( + writable: NodeJS.WritableStream, + readable: NodeJS.ReadableStream, +) { + const wrapper = new Duplex({ + readableObjectMode: true, + writableObjectMode: true, + read() { + readable.resume(); + }, + write(chunk, encoding, callback) { + return writable.write(chunk, encoding, callback); + }, + final(callback) { + writable.end(callback); + }, + }); + readable + .on("data", chunk => { + if (!wrapper.push(chunk)) { + readable.pause(); + } + }) + .on("error", err => wrapper.emit("error", err)) + .on("end", () => wrapper.push(null)); + writable.on("drain", () => wrapper.emit("drain")); + writable.on("error", err => wrapper.emit("error", err)); + return wrapper; +} diff --git a/src/functions/filter.ts b/src/functions/filter.ts new file mode 100644 index 0000000..cd89864 --- /dev/null +++ b/src/functions/filter.ts @@ -0,0 +1,20 @@ +import { Transform, TransformOptions } from "stream"; + +export function filter( + predicate: + | ((chunk: T, encoding: string) => boolean) + | ((chunk: T, encoding: string) => Promise), + options?: TransformOptions, +) { + return new Transform({ + ...options, + async transform(chunk: T, encoding?: any, callback?: any) { + const result = await predicate(chunk, encoding); + if (result === true) { + callback(null, chunk); + } else { + callback(); + } + }, + }); +} diff --git a/src/functions/flatMap.ts b/src/functions/flatMap.ts new file mode 100644 index 0000000..dd7820d --- /dev/null +++ b/src/functions/flatMap.ts @@ -0,0 +1,18 @@ +import { Transform, TransformOptions } from "stream"; + +export function flatMap( + mapper: + | ((chunk: T, encoding: string) => R[]) + | ((chunk: T, encoding: string) => Promise), + options: TransformOptions = { + objectMode: true, + }, +): Transform { + return new Transform({ + ...options, + async transform(chunk: T, encoding, callback) { + (await mapper(chunk, encoding)).forEach(c => this.push(c)); + callback(); + }, + }); +} diff --git a/src/functions/fromArray.ts b/src/functions/fromArray.ts new file mode 100644 index 0000000..a757354 --- /dev/null +++ b/src/functions/fromArray.ts @@ -0,0 +1,16 @@ +import { Readable } from "stream"; + +export function fromArray(array: any[]): Readable { + let cursor = 0; + return new Readable({ + objectMode: true, + read() { + if (cursor < array.length) { + this.push(array[cursor]); + cursor++; + } else { + this.push(null); + } + }, + }); +} diff --git a/src/functions/index.ts b/src/functions/index.ts new file mode 100644 index 0000000..778b1b9 --- /dev/null +++ b/src/functions/index.ts @@ -0,0 +1,214 @@ +import { Transform } from "stream"; +import * as baseFunctions from "./baseFunctions"; + +/** + * Convert an array into a Readable stream of its elements + * @param array Array of elements to stream + */ +export const fromArray = baseFunctions.fromArray; + +/** + * Return a ReadWrite stream that maps streamed chunks + * @param mapper Mapper function, mapping each (chunk, encoding) to a new chunk (or a promise of such) + * @param options? + * @param options.readableObjectMode? Whether this stream should behave as a readable stream of objects + * @param options.writableObjectMode? Whether this stream should behave as a writable stream of objects + */ +export const map = baseFunctions.map; + +/** + * Return a ReadWrite stream that flat maps streamed chunks + * @param mapper Mapper function, mapping each (chunk, encoding) to an array of new chunks (or a promise of such) + * @param options? + * @param options.readableObjectMode? Whether this stream should behave as a readable stream of objects + * @param options.writableObjectMode? Whether this stream should behave as a writable stream of objects + */ +export const flatMap = baseFunctions.flatMap; + +/** + * Return a ReadWrite stream that filters out streamed chunks for which the predicate does not hold + * @param predicate Predicate with which to filter scream chunks + * @param options? + * @param options.objectMode? Whether this stream should behave as a stream of objects. + */ +export const filter = baseFunctions.filter; + +/** + * Return a ReadWrite stream that reduces streamed chunks down to a single value and yield that + * value + * @param iteratee Reducer function to apply on each streamed chunk + * @param initialValue Initial value + * @param options? + * @param options.readableObjectMode? Whether this stream should behave as a readable stream of objects + * @param options.writableObjectMode? Whether this stream should behave as a writable stream of objects + */ +export const reduce = baseFunctions.reduce; + +/** + * Return a ReadWrite stream that splits streamed chunks using the given separator + * @param separator? Separator to split by, defaulting to "\n" + * @param options? Defaults to encoding: utf8 + * @param options.encoding? Encoding written chunks are assumed to use + */ +export const split = baseFunctions.split; + +/** + * Return a ReadWrite stream that joins streamed chunks using the given separator + * @param separator Separator to join with + * @param options? Defaults to encoding: utf8 + * @param options.encoding? Encoding written chunks are assumed to use + */ +export const join = baseFunctions.join; + +/** + * Return a ReadWrite stream that replaces occurrences of the given string or regular expression in + * the streamed chunks with the specified replacement string + * @param searchValue Search string to use + * @param replaceValue Replacement string to use + * @param options? Defaults to encoding: utf8 + * @param options.encoding Encoding written chunks are assumed to use + */ +export const replace = baseFunctions.replace; + +/** + * Return a ReadWrite stream that parses the streamed chunks as JSON. Each streamed chunk + * must be a fully defined JSON string in utf8. + */ +export const parse = baseFunctions.parse; + +/** + * Return a ReadWrite stream that stringifies the streamed chunks to JSON + * @param options? + * @param options.pretty If true, whitespace is inserted into the stringified chunks. + * + */ +export const stringify = baseFunctions.stringify; + +/** + * Return a ReadWrite stream that collects streamed chunks into an array or buffer + * @param options? + * @param options.objectMode? Whether this stream should behave as a stream of objects + */ +export const collect = baseFunctions.collect; + +/** + * Return a Readable stream of readable streams concatenated together + * @param streams Readable streams to concatenate + */ +export const concat = baseFunctions.concat; + +/** + * Return a Readable stream of readable streams concatenated together + * @param streams Readable streams to merge + */ +export const merge = baseFunctions.merge; + +/** + * Return a Duplex stream from a writable stream that is assumed to somehow, when written to, + * cause the given readable stream to yield chunks + * @param writable Writable stream assumed to cause the readable stream to yield chunks when written to + * @param readable Readable stream assumed to yield chunks when the writable stream is written to + */ +export const duplex = baseFunctions.duplex; + +/** + * Return a Duplex stream from a child process' stdin and stdout + * @param childProcess Child process from which to create duplex stream + */ +export const child = baseFunctions.child; + +/** + * Return a Promise resolving to the last streamed chunk of the given readable stream, after it has + * ended + * @param readable Readable stream to wait on + */ +export const last = baseFunctions.last; + +/** + * Stores chunks of data internally in array and batches when batchSize is reached. + * @param batchSize Size of the batches, defaults to 1000. + * @param maxBatchAge? Max lifetime of a batch, defaults to 500 + */ +export function batch(batchSize?: number, maxBatchAge?: number): Transform { + return baseFunctions.batch(batchSize, maxBatchAge); +} + +/** + * Unbatches and sends individual chunks of data. + */ +export const unbatch = baseFunctions.unbatch; + +/** + * Limits rate of data transferred into stream. + * @param options? + * @param targetRate? Desired rate in ms. + * @param period? Period to sleep for when rate is above or equal to targetRate. + */ +export function rate(targetRate?: number, period?: number): Transform { + return baseFunctions.rate(targetRate, period); +} + +/** + * Limits number of parallel processes in flight. + * @param parallel Max number of parallel processes. + * @param func Function to execute on each data chunk. + * @param pause Amount of time to pause processing when max number of parallel processes are executing. + */ +export const parallelMap = baseFunctions.parallelMap; + +/** + * Accummulates and sends batches of data. Each chunk that flows into the stream is checked against items + * in the buffer. How the buffer is mutated is based on 1 of 2 possible buffering strategies: + * 1. Sliding + * - If the buffer is larger than the batchSize, the front of the buffer is popped to maintain + * the batchSize. When no key is provided, the batchSize is effectively the buffer length. When + * a key is provided, the batchSize is based on the value at that key. For example, given a key + * of `timestamp` and a batchSize of 3000, each item in the buffer will be guaranteed to be + * within 3000 timestamp units from the first element. This means that with a key, multiple elements + * may be spliced off the front of the buffer. The buffer is then pushed into the stream. + * 2. Rolling + * - If the buffer is larger than the batchSize, the buffer is cleared and pushed into the stream. + * When no key is provided, the batchSize is the buffer length. When a key is provided, the batchSize + * is based on the value at that key. For example, given a key of `timestamp` and a batchSize of 3000, + * each item in the buffer will be guaranteed to be within 3000 timestamp units from the first element. + * @param flushStrategy Buffering strategy to use. + * @param batchSize Size of the batch (in units of buffer length or value at key). + * @param batchRate Desired rate of data transfer to next stream. + * @param keyBy Key to determine if element fits into buffer or items need to be cleared from buffer. + * @param options Transform stream options + */ +export const accumulator = baseFunctions.accumulator; + +/** + * Accummulates and sends batches of data. Each chunk that flows into the stream is checked against items + * in the buffer. How the buffer is mutated is based on 1 of 2 possible buffering strategies: + * 1. Sliding + * - If the iteratee returns false, the front of the buffer is popped until iteratee returns true. The + * item is pushed into the buffer and buffer is pushed into stream. + * 2. Rolling + * - If the iteratee returns false, the buffer is cleared and pushed into stream. The item is + * then pushed into the buffer. + * @param flushStrategy Buffering strategy to use. + * @param iteratee Function applied to buffer when a chunk of data enters stream to determine if element fits into + * or items need to be cleared from buffer. + * @param batchRate Desired rate of data transfer to next stream. + * @param options Transform stream options + */ +export const accumulatorBy = baseFunctions.accumulatorBy; + +/** + * Composes multiple streams together. Writing occurs on first stream, piping occurs from last stream. + * @param streams Array of streams to compose. Minimum of two. + * @param options Transform stream options + */ +export const compose = baseFunctions.compose; + +/** + * Composes multiple streams together. Writing occurs on first stream, piping occurs from last stream. + * @param construct Constructor for new output source. Should return a Writable or ReadWrite stream. + * @param demuxBy + * @param demuxBy.key? Key to fetch value from source chunks to demultiplex source. + * @param demuxBy.keyBy? Function to fetch value from source chunks to demultiplex source. + * @param options Writable stream options + */ +export const demux = baseFunctions.demux; diff --git a/src/functions/join.ts b/src/functions/join.ts new file mode 100644 index 0000000..b49022b --- /dev/null +++ b/src/functions/join.ts @@ -0,0 +1,26 @@ +import { Transform } from "stream"; +import { StringDecoder } from "string_decoder"; +import { WithEncoding } from "./baseDefinitions"; + +export function join( + separator: string, + options: WithEncoding = { encoding: "utf8" }, +): Transform { + let isFirstChunk = true; + const decoder = new StringDecoder(options.encoding); + return new Transform({ + readableObjectMode: true, + async transform(chunk: Buffer, encoding, callback) { + const asString = decoder.write(chunk); + // Take care not to break up multi-byte characters spanning multiple chunks + if (asString !== "" || chunk.length === 0) { + if (!isFirstChunk) { + this.push(separator); + } + this.push(asString); + isFirstChunk = false; + } + callback(); + }, + }); +} diff --git a/src/functions/last.ts b/src/functions/last.ts new file mode 100644 index 0000000..412a34c --- /dev/null +++ b/src/functions/last.ts @@ -0,0 +1,8 @@ +export function last(readable: NodeJS.ReadableStream): Promise { + let lastChunk: T | null = null; + return new Promise((resolve, _) => { + readable + .on("data", chunk => (lastChunk = chunk)) + .on("end", () => resolve(lastChunk)); + }); +} diff --git a/src/functions/map.ts b/src/functions/map.ts new file mode 100644 index 0000000..38d6a59 --- /dev/null +++ b/src/functions/map.ts @@ -0,0 +1,13 @@ +import { Transform, TransformOptions } from "stream"; + +export function map( + mapper: (chunk: T, encoding: string) => R, + options: TransformOptions = { objectMode: true }, +): Transform { + return new Transform({ + ...options, + async transform(chunk: T, encoding, callback) { + callback(null, await mapper(chunk, encoding)); + }, + }); +} diff --git a/src/functions/merge.ts b/src/functions/merge.ts new file mode 100644 index 0000000..ff4d5f6 --- /dev/null +++ b/src/functions/merge.ts @@ -0,0 +1,33 @@ +import { Readable } from "stream"; + +export function merge(...streams: Readable[]): Readable { + let isStarted = false; + let streamEndedCount = 0; + return new Readable({ + objectMode: true, + read() { + if (streamEndedCount >= streams.length) { + this.push(null); + } else if (!isStarted) { + isStarted = true; + streams.forEach(stream => + stream + .on("data", chunk => { + if (!this.push(chunk)) { + streams.forEach(s => s.pause()); + } + }) + .on("error", err => this.emit("error", err)) + .on("end", () => { + streamEndedCount++; + if (streamEndedCount === streams.length) { + this.push(null); + } + }), + ); + } else { + streams.forEach(s => s.resume()); + } + }, + }); +} diff --git a/src/functions/parallelMap.ts b/src/functions/parallelMap.ts new file mode 100644 index 0000000..8cb3e80 --- /dev/null +++ b/src/functions/parallelMap.ts @@ -0,0 +1,32 @@ +import { Transform, TransformOptions } from "stream"; +import { sleep } from "../helpers"; + +export function parallelMap( + mapper: (data: T) => R, + parallel: number = 10, + sleepTime: number = 5, + options: TransformOptions = { + objectMode: true, + }, +) { + let inflight = 0; + return new Transform({ + ...options, + async transform(data, encoding, callback) { + while (parallel <= inflight) { + await sleep(sleepTime); + } + inflight += 1; + callback(); + const res = await mapper(data); + this.push(res); + inflight -= 1; + }, + async flush(callback) { + while (inflight > 0) { + await sleep(sleepTime); + } + callback(); + }, + }); +} diff --git a/src/functions/parse.ts b/src/functions/parse.ts new file mode 100644 index 0000000..451e86c --- /dev/null +++ b/src/functions/parse.ts @@ -0,0 +1,22 @@ +import { Transform } from "stream"; +import { StringDecoder } from "string_decoder"; +import { SerializationFormats } from "./baseDefinitions"; + +export function parse( + format: SerializationFormats = SerializationFormats.utf8, +): Transform { + const decoder = new StringDecoder(format); + return new Transform({ + readableObjectMode: true, + writableObjectMode: true, + async transform(chunk: Buffer, encoding, callback) { + try { + const asString = decoder.write(chunk); + // Using await causes parsing errors to be emitted + callback(undefined, await JSON.parse(asString)); + } catch (err) { + callback(err); + } + }, + }); +} diff --git a/src/functions/rate.ts b/src/functions/rate.ts new file mode 100644 index 0000000..083d854 --- /dev/null +++ b/src/functions/rate.ts @@ -0,0 +1,26 @@ +import { Transform, TransformOptions } from "stream"; +import { performance } from "perf_hooks"; +import { sleep } from "../helpers"; + +export function rate( + targetRate: number = 50, + period: number = 1, + options: TransformOptions = { + objectMode: true, + }, +): Transform { + const deltaMS = ((1 / targetRate) * 1000) / period; // Skip a full period + let total = 0; + const start = performance.now(); + return new Transform({ + ...options, + async transform(data, encoding, callback) { + const currentRate = (total / (performance.now() - start)) * 1000; + if (targetRate && currentRate > targetRate) { + await sleep(deltaMS); + } + total += 1; + callback(undefined, data); + }, + }); +} diff --git a/src/functions/reduce.ts b/src/functions/reduce.ts new file mode 100644 index 0000000..9f19ca4 --- /dev/null +++ b/src/functions/reduce.ts @@ -0,0 +1,33 @@ +import { Transform, TransformOptions } from "stream"; + +export function reduce( + iteratee: + | ((previousValue: R, chunk: T, encoding: string) => R) + | ((previousValue: R, chunk: T, encoding: string) => Promise), + initialValue: R, + options: TransformOptions = { + objectMode: true, + }, +) { + let value = initialValue; + return new Transform({ + ...options, + async transform(chunk: T, encoding, callback) { + value = await iteratee(value, chunk, encoding); + callback(); + }, + flush(callback) { + // Best effort attempt at yielding the final value (will throw if e.g. yielding an object and + // downstream doesn't expect objects) + try { + callback(undefined, value); + } catch (err) { + try { + this.emit("error", err); + } catch { + // Best effort was made + } + } + }, + }); +} diff --git a/src/functions/replace.ts b/src/functions/replace.ts new file mode 100644 index 0000000..dc5a05e --- /dev/null +++ b/src/functions/replace.ts @@ -0,0 +1,26 @@ +import { Transform } from "stream"; +import { StringDecoder } from "string_decoder"; +import { WithEncoding } from "./baseDefinitions"; + +export function replace( + searchValue: string | RegExp, + replaceValue: string, + options: WithEncoding = { encoding: "utf8" }, +): Transform { + const decoder = new StringDecoder(options.encoding); + return new Transform({ + readableObjectMode: true, + transform(chunk: Buffer, encoding, callback) { + const asString = decoder.write(chunk); + // Take care not to break up multi-byte characters spanning multiple chunks + if (asString !== "" || chunk.length === 0) { + callback( + undefined, + asString.replace(searchValue, replaceValue), + ); + } else { + callback(); + } + }, + }); +} diff --git a/src/functions/split.ts b/src/functions/split.ts new file mode 100644 index 0000000..8e517ed --- /dev/null +++ b/src/functions/split.ts @@ -0,0 +1,29 @@ +import { Transform } from "stream"; +import { StringDecoder } from "string_decoder"; +import { WithEncoding } from "./baseDefinitions"; + +export function split( + separator: string | RegExp = "\n", + options: WithEncoding = { encoding: "utf8" }, +): Transform { + let buffered = ""; + const decoder = new StringDecoder(options.encoding); + + return new Transform({ + readableObjectMode: true, + transform(chunk: Buffer, encoding, callback) { + const asString = decoder.write(chunk); + const splitted = asString.split(separator); + if (splitted.length > 1) { + splitted[0] = buffered.concat(splitted[0]); + buffered = ""; + } + buffered += splitted[splitted.length - 1]; + splitted.slice(0, -1).forEach((part: string) => this.push(part)); + callback(); + }, + flush(callback) { + callback(undefined, buffered + decoder.end()); + }, + }); +} diff --git a/src/functions/stringify.ts b/src/functions/stringify.ts new file mode 100644 index 0000000..34eb302 --- /dev/null +++ b/src/functions/stringify.ts @@ -0,0 +1,19 @@ +import { Transform } from "stream"; +import { JsonValue, JsonParseOptions } from "./baseDefinitions"; + +export function stringify( + options: JsonParseOptions = { pretty: false }, +): Transform { + return new Transform({ + readableObjectMode: true, + writableObjectMode: true, + transform(chunk: JsonValue, encoding, callback) { + callback( + undefined, + options.pretty + ? JSON.stringify(chunk, null, 2) + : JSON.stringify(chunk), + ); + }, + }); +} diff --git a/src/functions/unbatch.ts b/src/functions/unbatch.ts new file mode 100644 index 0000000..93d6bfe --- /dev/null +++ b/src/functions/unbatch.ts @@ -0,0 +1,17 @@ +import { Transform, TransformOptions } from "stream"; + +export function unbatch( + options: TransformOptions = { + objectMode: true, + }, +) { + return new Transform({ + ...options, + transform(data, encoding, callback) { + for (const d of data) { + this.push(d); + } + callback(); + }, + }); +} diff --git a/src/helpers.ts b/src/helpers.ts new file mode 100644 index 0000000..242d264 --- /dev/null +++ b/src/helpers.ts @@ -0,0 +1,3 @@ +export async function sleep(time: number): Promise<{} | null> { + return time > 0 ? new Promise(resolve => setTimeout(resolve, time)) : null; +} diff --git a/src/index.spec.ts b/src/index.spec.ts deleted file mode 100644 index a15050d..0000000 --- a/src/index.spec.ts +++ /dev/null @@ -1,1182 +0,0 @@ -import * as cp from "child_process"; -import test from "ava"; -import { expect } from "chai"; -import { Readable } from "stream"; -import { - fromArray, - map, - flatMap, - filter, - split, - join, - replace, - parse, - stringify, - collect, - concat, - merge, - duplex, - child, - reduce, - last, -} from "."; - -test.cb("fromArray() streams array elements in flowing mode", t => { - t.plan(3); - const elements = ["a", "b", "c"]; - const stream = fromArray(elements); - let i = 0; - stream - .on("data", (element: string) => { - expect(element).to.equal(elements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); -}); - -test.cb("fromArray() streams array elements in paused mode", t => { - t.plan(3); - const elements = ["a", "b", "c"]; - const stream = fromArray(elements); - let i = 0; - stream - .on("readable", () => { - let element = stream.read(); - while (element !== null) { - expect(element).to.equal(elements[i]); - t.pass(); - i++; - element = stream.read(); - } - }) - .on("error", t.end) - .on("end", t.end); -}); - -test.cb("fromArray() ends immediately if there are no array elements", t => { - t.plan(0); - fromArray([]) - .on("data", () => t.fail()) - .on("error", t.end) - .on("end", t.end); -}); - -test.cb("map() maps elements synchronously", t => { - t.plan(3); - const source = new Readable({ objectMode: true }); - const expectedElements = ["A", "B", "C"]; - let i = 0; - source - .pipe(map((element: string) => element.toUpperCase())) - .on("data", (element: string) => { - expect(element).to.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); -}); - -test.cb("map() maps elements asynchronously", t => { - t.plan(3); - const source = new Readable({ objectMode: true }); - const expectedElements = ["A", "B", "C"]; - let i = 0; - source - .pipe( - map(async (element: string) => { - await Promise.resolve(); - return element.toUpperCase(); - }), - ) - .on("data", (element: string) => { - expect(element).to.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); -}); - -test.cb("map() emits errors during synchronous mapping", t => { - t.plan(2); - const source = new Readable({ objectMode: true }); - source - .pipe( - map((element: string) => { - if (element !== "a") { - throw new Error("Failed mapping"); - } - return element.toUpperCase(); - }), - ) - .resume() - .on("error", err => { - expect(err.message).to.equal("Failed mapping"); - t.pass(); - }) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); -}); - -test.cb("map() emits errors during asynchronous mapping", t => { - t.plan(2); - const source = new Readable({ objectMode: true }); - source - .pipe( - map(async (element: string) => { - await Promise.resolve(); - if (element !== "a") { - throw new Error("Failed mapping"); - } - return element.toUpperCase(); - }), - ) - .resume() - .on("error", err => { - expect(err.message).to.equal("Failed mapping"); - t.pass(); - }) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); -}); - -test.cb("flatMap() maps elements synchronously", t => { - t.plan(6); - const source = new Readable({ objectMode: true }); - const expectedElements = ["a", "A", "b", "B", "c", "C"]; - let i = 0; - source - .pipe(flatMap((element: string) => [element, element.toUpperCase()])) - .on("data", (element: string) => { - expect(element).to.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); -}); - -test.cb("flatMap() maps elements asynchronously", t => { - t.plan(6); - const source = new Readable({ objectMode: true }); - const expectedElements = ["a", "A", "b", "B", "c", "C"]; - let i = 0; - source - .pipe( - flatMap(async (element: string) => { - await Promise.resolve(); - return [element, element.toUpperCase()]; - }), - ) - .on("data", (element: string) => { - expect(element).to.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); -}); - -test.cb("flatMap() emits errors during synchronous mapping", t => { - t.plan(2); - const source = new Readable({ objectMode: true }); - source - .pipe( - flatMap((element: string) => { - if (element !== "a") { - throw new Error("Failed mapping"); - } - return [element, element.toUpperCase()]; - }), - ) - .resume() - .on("error", err => { - expect(err.message).to.equal("Failed mapping"); - t.pass(); - }) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); -}); - -test.cb("flatMap() emits errors during asynchronous mapping", t => { - t.plan(2); - const source = new Readable({ objectMode: true }); - source - .pipe( - flatMap(async (element: string) => { - await Promise.resolve(); - if (element !== "a") { - throw new Error("Failed mapping"); - } - return [element, element.toUpperCase()]; - }), - ) - .resume() - .on("error", err => { - expect(err.message).to.equal("Failed mapping"); - t.pass(); - }) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); -}); - -test.cb("filter() filters elements synchronously", t => { - t.plan(2); - const source = new Readable({ objectMode: true }); - const expectedElements = ["a", "c"]; - let i = 0; - source - .pipe(filter((element: string) => element !== "b")) - .on("data", (element: string) => { - expect(element).to.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); -}); - -test.cb("filter() filters elements asynchronously", t => { - t.plan(2); - const source = new Readable({ objectMode: true }); - const expectedElements = ["a", "c"]; - let i = 0; - source - .pipe( - filter(async (element: string) => { - await Promise.resolve(); - return element !== "b"; - }), - ) - .on("data", (element: string) => { - expect(element).to.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); -}); - -test.cb("filter() emits errors during synchronous filtering", t => { - t.plan(2); - const source = new Readable({ objectMode: true }); - source - .pipe( - filter((element: string) => { - if (element !== "a") { - throw new Error("Failed filtering"); - } - return true; - }), - ) - .resume() - .on("error", err => { - expect(err.message).to.equal("Failed filtering"); - t.pass(); - }) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); -}); - -test.cb("filter() emits errors during asynchronous filtering", t => { - t.plan(2); - const source = new Readable({ objectMode: true }); - source - .pipe( - filter(async (element: string) => { - await Promise.resolve(); - if (element !== "a") { - throw new Error("Failed filtering"); - } - return true; - }), - ) - .resume() - .on("error", err => { - expect(err.message).to.equal("Failed filtering"); - t.pass(); - }) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); -}); - -test.cb("reduce() reduces elements synchronously", t => { - t.plan(1); - const source = new Readable({ objectMode: true }); - const expectedValue = 6; - source - .pipe(reduce((acc: number, element: string) => acc + element.length, 0)) - .on("data", (element: string) => { - expect(element).to.equal(expectedValue); - t.pass(); - }) - .on("error", t.end) - .on("end", t.end); - - source.push("ab"); - source.push("cd"); - source.push("ef"); - source.push(null); -}); - -test.cb("reduce() reduces elements asynchronously", t => { - t.plan(1); - const source = new Readable({ objectMode: true }); - const expectedValue = 6; - source - .pipe( - reduce(async (acc: number, element: string) => { - await Promise.resolve(); - return acc + element.length; - }, 0), - ) - .on("data", (element: string) => { - expect(element).to.equal(expectedValue); - t.pass(); - }) - .on("error", t.end) - .on("end", t.end); - - source.push("ab"); - source.push("cd"); - source.push("ef"); - source.push(null); -}); - -test.cb("reduce() emits errors during synchronous reduce", t => { - t.plan(2); - const source = new Readable({ objectMode: true }); - source - .pipe( - reduce((acc: number, element: string) => { - if (element !== "ab") { - throw new Error("Failed reduce"); - } - return acc + element.length; - }, 0), - ) - .resume() - .on("error", err => { - expect(err.message).to.equal("Failed reduce"); - t.pass(); - }) - .on("end", t.end); - - source.push("ab"); - source.push("cd"); - source.push("ef"); - source.push(null); -}); - -test.cb("reduce() emits errors during asynchronous reduce", t => { - t.plan(2); - const source = new Readable({ objectMode: true }); - source - .pipe( - reduce(async (acc: number, element: string) => { - await Promise.resolve(); - if (element !== "ab") { - throw new Error("Failed mapping"); - } - return acc + element.length; - }, 0), - ) - .resume() - .on("error", err => { - expect(err.message).to.equal("Failed mapping"); - t.pass(); - }) - .on("end", t.end); - - source.push("ab"); - source.push("cd"); - source.push("ef"); - source.push(null); -}); - -test.cb("split() splits chunks using the default separator (\\n)", t => { - t.plan(5); - const source = new Readable({ objectMode: true }); - const expectedParts = ["ab", "c", "d", "ef", ""]; - let i = 0; - source - .pipe(split()) - .on("data", part => { - expect(part).to.equal(expectedParts[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push("ab\n"); - source.push("c"); - source.push("\n"); - source.push("d"); - source.push("\nef\n"); - source.push(null); -}); - -test.cb("split() splits chunks using the specified separator", t => { - t.plan(6); - const source = new Readable({ objectMode: true }); - const expectedParts = ["ab", "c", "d", "e", "f", ""]; - let i = 0; - source - .pipe(split("|")) - .on("data", (part: string) => { - expect(part).to.equal(expectedParts[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push("ab|"); - source.push("c|d"); - source.push("|"); - source.push("e"); - source.push("|f|"); - source.push(null); -}); - -test.cb( - "split() splits utf8 encoded buffers using the specified separator", - t => { - t.plan(3); - const expectedElements = ["a", "b", "c"]; - let i = 0; - const through = split(","); - const buf = Buffer.from("a,b,c"); - through - .on("data", element => { - expect(element).to.equal(expectedElements[i]); - i++; - t.pass(); - }) - .on("error", t.end) - .on("end", t.end); - - for (let j = 0; j < buf.length; ++j) { - through.write(buf.slice(j, j + 1)); - } - through.end(); - }, -); - -test.cb( - "split() splits utf8 encoded buffers with multi-byte characters using the specified separator", - t => { - t.plan(3); - const expectedElements = ["一", "一", "一"]; - let i = 0; - const through = split(","); - const buf = Buffer.from("一,一,一"); // Those spaces are multi-byte utf8 characters (code: 4E00) - through - .on("data", element => { - expect(element).to.equal(expectedElements[i]); - i++; - t.pass(); - }) - .on("error", t.end) - .on("end", t.end); - - for (let j = 0; j < buf.length; ++j) { - through.write(buf.slice(j, j + 1)); - } - through.end(); - }, -); - -test.cb("join() joins chunks using the specified separator", t => { - t.plan(9); - const source = new Readable({ objectMode: true }); - const expectedParts = ["ab|", "|", "c|d", "|", "|", "|", "e", "|", "|f|"]; - let i = 0; - source - .pipe(join("|")) - .on("data", part => { - expect(part).to.equal(expectedParts[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push("ab|"); - source.push("c|d"); - source.push("|"); - source.push("e"); - source.push("|f|"); - source.push(null); -}); - -test.cb( - "join() joins chunks using the specified separator without breaking up multi-byte characters " + - "spanning multiple chunks", - t => { - t.plan(5); - const source = new Readable({ objectMode: true }); - const expectedParts = ["ø", "|", "ö", "|", "一"]; - let i = 0; - source - .pipe(join("|")) - .on("data", part => { - expect(part).to.equal(expectedParts[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push(Buffer.from("ø").slice(0, 1)); // 2-byte character spanning two chunks - source.push(Buffer.from("ø").slice(1, 2)); - source.push(Buffer.from("ö").slice(0, 1)); // 2-byte character spanning two chunks - source.push(Buffer.from("ö").slice(1, 2)); - source.push(Buffer.from("一").slice(0, 1)); // 3-byte character spanning three chunks - source.push(Buffer.from("一").slice(1, 2)); - source.push(Buffer.from("一").slice(2, 3)); - source.push(null); - }, -); - -test.cb( - "replace() replaces occurrences of the given string in the streamed elements with the specified " + - "replacement string", - t => { - t.plan(3); - const source = new Readable({ objectMode: true }); - const expectedElements = ["abc", "xyf", "ghi"]; - let i = 0; - source - .pipe(replace("de", "xy")) - .on("data", part => { - expect(part).to.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push("abc"); - source.push("def"); - source.push("ghi"); - source.push(null); - }, -); - -test.cb( - "replace() replaces occurrences of the given regular expression in the streamed elements with " + - "the specified replacement string", - t => { - t.plan(3); - const source = new Readable({ objectMode: true }); - const expectedElements = ["abc", "xyz", "ghi"]; - let i = 0; - source - .pipe(replace(/^def$/, "xyz")) - .on("data", part => { - expect(part).to.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push("abc"); - source.push("def"); - source.push("ghi"); - source.push(null); - }, -); - -test.cb( - "replace() replaces occurrences of the given multi-byte character even if it spans multiple chunks", - t => { - t.plan(3); - const source = new Readable({ objectMode: true }); - const expectedElements = ["ø", "O", "a"]; - let i = 0; - source - .pipe(replace("ö", "O")) - .on("data", part => { - expect(part).to.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push(Buffer.from("ø").slice(0, 1)); // 2-byte character spanning two chunks - source.push(Buffer.from("ø").slice(1, 2)); - source.push(Buffer.from("ö").slice(0, 1)); // 2-byte character spanning two chunks - source.push(Buffer.from("ö").slice(1, 2)); - source.push("a"); - source.push(null); - }, -); - -test.cb("parse() parses the streamed elements as JSON", t => { - t.plan(3); - const source = new Readable({ objectMode: true }); - const expectedElements = ["abc", {}, []]; - let i = 0; - source - .pipe(parse()) - .on("data", part => { - expect(part).to.deep.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push('"abc"'); - source.push("{}"); - source.push("[]"); - source.push(null); -}); - -test.cb("parse() emits errors on invalid JSON", t => { - t.plan(2); - const source = new Readable({ objectMode: true }); - source - .pipe(parse()) - .resume() - .on("error", () => t.pass()) - .on("end", t.end); - - source.push("{}"); - source.push({}); - source.push([]); - source.push(null); -}); - -test.cb("stringify() stringifies the streamed elements as JSON", t => { - t.plan(4); - const source = new Readable({ objectMode: true }); - const expectedElements = [ - '"abc"', - "0", - '{"a":"a","b":"b","c":"c"}', - '["a","b","c"]', - ]; - let i = 0; - source - .pipe(stringify()) - .on("data", part => { - expect(part).to.deep.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push("abc"); - source.push(0); - source.push({ a: "a", b: "b", c: "c" }); - source.push(["a", "b", "c"]); - source.push(null); -}); - -test.cb( - "stringify() stringifies the streamed elements as pretty-printed JSON", - t => { - t.plan(4); - const source = new Readable({ objectMode: true }); - const expectedElements = [ - '"abc"', - "0", - '{\n "a": "a",\n "b": "b",\n "c": "c"\n}', - '[\n "a",\n "b",\n "c"\n]', - ]; - let i = 0; - source - .pipe(stringify({ pretty: true })) - .on("data", part => { - expect(part).to.deep.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push("abc"); - source.push(0); - source.push({ a: "a", b: "b", c: "c" }); - source.push(["a", "b", "c"]); - source.push(null); - }, -); - -test.cb( - "collect() collects streamed elements into an array (object, flowing mode)", - t => { - t.plan(1); - const source = new Readable({ objectMode: true }); - - source - .pipe(collect({ objectMode: true })) - .on("data", collected => { - expect(collected).to.deep.equal(["a", "b", "c"]); - t.pass(); - }) - .on("error", t.end) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); - }, -); - -test.cb( - "collect() collects streamed elements into an array (object, paused mode)", - t => { - t.plan(1); - const source = new Readable({ objectMode: true }); - const collector = source.pipe(collect({ objectMode: true })); - - collector - .on("readable", () => { - let collected = collector.read(); - while (collected !== null) { - expect(collected).to.deep.equal(["a", "b", "c"]); - t.pass(); - collected = collector.read(); - } - }) - .on("error", t.end) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); - }, -); - -test.cb( - "collect() collects streamed bytes into a buffer (non-object, flowing mode)", - t => { - t.plan(1); - const source = new Readable({ objectMode: false }); - - source - .pipe(collect()) - .on("data", collected => { - expect(collected).to.deep.equal(Buffer.from("abc")); - t.pass(); - }) - .on("error", t.end) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); - }, -); - -test.cb( - "collect() collects streamed bytes into a buffer (non-object, paused mode)", - t => { - t.plan(1); - const source = new Readable({ objectMode: false }); - const collector = source.pipe(collect({ objectMode: false })); - collector - .on("readable", () => { - let collected = collector.read(); - while (collected !== null) { - expect(collected).to.deep.equal(Buffer.from("abc")); - t.pass(); - collected = collector.read(); - } - }) - .on("error", t.end) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); - }, -); - -test.cb( - "collect() emits an empty array if the source was empty (object mode)", - t => { - t.plan(1); - const source = new Readable({ objectMode: true }); - const collector = source.pipe(collect({ objectMode: true })); - collector - .on("data", collected => { - expect(collected).to.deep.equal([]); - t.pass(); - }) - .on("error", t.end) - .on("end", t.end); - - source.push(null); - }, -); - -test.cb( - "collect() emits nothing if the source was empty (non-object mode)", - t => { - t.plan(0); - const source = new Readable({ objectMode: false }); - const collector = source.pipe(collect({ objectMode: false })); - collector - .on("data", () => t.fail()) - .on("error", t.end) - .on("end", t.end); - - source.push(null); - }, -); - -test.cb( - "concat() concatenates multiple readable streams (object, flowing mode)", - t => { - t.plan(6); - const source1 = new Readable({ objectMode: true }); - const source2 = new Readable({ objectMode: true }); - const expectedElements = ["a", "b", "c", "d", "e", "f"]; - let i = 0; - concat(source1, source2) - .on("data", (element: string) => { - expect(element).to.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source1.push("a"); - source2.push("d"); - source1.push("b"); - source2.push("e"); - source1.push("c"); - source2.push("f"); - source2.push(null); - source1.push(null); - }, -); - -test.cb( - "concat() concatenates multiple readable streams (object, paused mode)", - t => { - t.plan(6); - const source1 = new Readable({ objectMode: true }); - const source2 = new Readable({ objectMode: true }); - const expectedElements = ["a", "b", "c", "d", "e", "f"]; - let i = 0; - const concatenation = concat(source1, source2) - .on("readable", () => { - let element = concatenation.read(); - while (element !== null) { - expect(element).to.equal(expectedElements[i]); - t.pass(); - i++; - element = concatenation.read(); - } - }) - .on("error", t.end) - .on("end", t.end); - - source1.push("a"); - source2.push("d"); - source1.push("b"); - source2.push("e"); - source1.push("c"); - source2.push("f"); - source2.push(null); - source1.push(null); - }, -); - -test.cb( - "concat() concatenates multiple readable streams (non-object, flowing mode)", - t => { - t.plan(6); - const source1 = new Readable({ objectMode: false }); - const source2 = new Readable({ objectMode: false }); - const expectedElements = ["a", "b", "c", "d", "e", "f"]; - let i = 0; - concat(source1, source2) - .on("data", (element: string) => { - expect(element).to.deep.equal(Buffer.from(expectedElements[i])); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source1.push("a"); - source2.push("d"); - source1.push("b"); - source2.push("e"); - source1.push("c"); - source2.push("f"); - source2.push(null); - source1.push(null); - }, -); - -test.cb( - "concat() concatenates multiple readable streams (non-object, paused mode)", - t => { - t.plan(6); - const source1 = new Readable({ objectMode: false, read: () => ({}) }); - const source2 = new Readable({ objectMode: false, read: () => ({}) }); - const expectedElements = ["a", "b", "c", "d", "e", "f"]; - let i = 0; - const concatenation = concat(source1, source2) - .on("readable", () => { - let element = concatenation.read(); - while (element !== null) { - expect(element).to.deep.equal( - Buffer.from(expectedElements[i]), - ); - t.pass(); - i++; - element = concatenation.read(); - } - }) - .on("error", t.end) - .on("end", t.end); - - source1.push("a"); - setTimeout(() => source2.push("d"), 10); - setTimeout(() => source1.push("b"), 20); - setTimeout(() => source2.push("e"), 30); - setTimeout(() => source1.push("c"), 40); - setTimeout(() => source2.push("f"), 50); - setTimeout(() => source2.push(null), 60); - setTimeout(() => source1.push(null), 70); - }, -); - -test.cb("concat() concatenates a single readable stream (object mode)", t => { - t.plan(3); - const source = new Readable({ objectMode: true }); - const expectedElements = ["a", "b", "c", "d", "e", "f"]; - let i = 0; - concat(source) - .on("data", (element: string) => { - expect(element).to.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); -}); - -test.cb( - "concat() concatenates a single readable stream (non-object mode)", - t => { - t.plan(3); - const source = new Readable({ objectMode: false }); - const expectedElements = ["a", "b", "c", "d", "e", "f"]; - let i = 0; - concat(source) - .on("data", (element: string) => { - expect(element).to.deep.equal(Buffer.from(expectedElements[i])); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); - }, -); - -test.cb("concat() concatenates empty list of readable streams", t => { - t.plan(0); - concat() - .pipe(collect()) - .on("data", _ => { - t.fail(); - }) - .on("error", t.end) - .on("end", t.end); -}); - -test.cb( - "merge() merges multiple readable streams in chunk arrival order", - t => { - t.plan(6); - const source1 = new Readable({ objectMode: true, read: () => ({}) }); - const source2 = new Readable({ objectMode: true, read: () => ({}) }); - const expectedElements = ["a", "d", "b", "e", "c", "f"]; - let i = 0; - merge(source1, source2) - .on("data", (element: string) => { - expect(element).to.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source1.push("a"); - setTimeout(() => source2.push("d"), 10); - setTimeout(() => source1.push("b"), 20); - setTimeout(() => source2.push("e"), 30); - setTimeout(() => source1.push("c"), 40); - setTimeout(() => source2.push("f"), 50); - setTimeout(() => source2.push(null), 60); - setTimeout(() => source1.push(null), 70); - }, -); - -test.cb("merge() merges a readable stream", t => { - t.plan(3); - const source = new Readable({ objectMode: true, read: () => ({}) }); - const expectedElements = ["a", "b", "c"]; - let i = 0; - merge(source) - .on("data", (element: string) => { - expect(element).to.equal(expectedElements[i]); - t.pass(); - i++; - }) - .on("error", t.end) - .on("end", t.end); - - source.push("a"); - source.push("b"); - source.push("c"); - source.push(null); -}); - -test.cb("merge() merges an empty list of readable streams", t => { - t.plan(0); - merge() - .on("data", () => t.pass()) - .on("error", t.end) - .on("end", t.end); -}); - -test.cb( - "duplex() combines a writable and readable stream into a ReadWrite stream", - t => { - t.plan(1); - const source = new Readable(); - const catProcess = cp.exec("cat"); - let out = ""; - source - .pipe(duplex(catProcess.stdin, catProcess.stdout)) - .on("data", chunk => (out += chunk)) - .on("error", t.end) - .on("end", () => { - expect(out).to.equal("abcdef"); - t.pass(); - t.end(); - }); - source.push("ab"); - source.push("cd"); - source.push("ef"); - source.push(null); - }, -); - -test.cb( - "child() allows easily writing to child process stdin and reading from its stdout", - t => { - t.plan(1); - const source = new Readable(); - const catProcess = cp.exec("cat"); - let out = ""; - source - .pipe(child(catProcess)) - .on("data", chunk => (out += chunk)) - .on("error", t.end) - .on("end", () => { - expect(out).to.equal("abcdef"); - t.pass(); - t.end(); - }); - source.push("ab"); - source.push("cd"); - source.push("ef"); - source.push(null); - }, -); - -test("last() resolves to the last chunk streamed by the given readable stream", async t => { - const source = new Readable({ objectMode: true }); - const lastPromise = last(source); - source.push("ab"); - source.push("cd"); - source.push("ef"); - source.push(null); - const lastChunk = await lastPromise; - expect(lastChunk).to.equal("ef"); -}); diff --git a/src/index.ts b/src/index.ts index 6da9256..924b246 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,501 +1,26 @@ -import { Transform, Readable, Writable, Duplex } from "stream"; -import { ChildProcess } from "child_process"; -import { StringDecoder } from "string_decoder"; - -export interface ThroughOptions { - objectMode?: boolean; -} -export interface TransformOptions { - readableObjectMode?: boolean; - writableObjectMode?: boolean; -} -export interface WithEncoding { - encoding: string; -} - -/** - * Convert an array into a Readable stream of its elements - * @param array Array of elements to stream - */ -export function fromArray(array: any[]): NodeJS.ReadableStream { - let cursor = 0; - return new Readable({ - objectMode: true, - read() { - if (cursor < array.length) { - this.push(array[cursor]); - cursor++; - } else { - this.push(null); - } - }, - }); -} - -/** - * Return a ReadWrite stream that maps streamed chunks - * @param mapper Mapper function, mapping each (chunk, encoding) to a new chunk (or a promise of such) - * @param options - * @param options.readableObjectMode Whether this stream should behave as a readable stream of objects - * @param options.writableObjectMode Whether this stream should behave as a writable stream of objects - */ -export function map( - mapper: (chunk: T, encoding: string) => R, - options: TransformOptions = { - readableObjectMode: true, - writableObjectMode: true, - }, -): NodeJS.ReadWriteStream { - return new Transform({ - ...options, - async transform(chunk: T, encoding, callback) { - let isPromise = false; - try { - const mapped = mapper(chunk, encoding); - isPromise = mapped instanceof Promise; - callback(undefined, await mapped); - } catch (err) { - if (isPromise) { - // Calling the callback asynchronously with an error wouldn't emit the error, so emit directly - this.emit("error", err); - callback(); - } else { - callback(err); - } - } - }, - }); -} - -/** - * Return a ReadWrite stream that flat maps streamed chunks - * @param mapper Mapper function, mapping each (chunk, encoding) to an array of new chunks (or a promise of such) - * @param options - * @param options.readableObjectMode Whether this stream should behave as a readable stream of objects - * @param options.writableObjectMode Whether this stream should behave as a writable stream of objects - */ -export function flatMap( - mapper: - | ((chunk: T, encoding: string) => R[]) - | ((chunk: T, encoding: string) => Promise), - options: TransformOptions = { - readableObjectMode: true, - writableObjectMode: true, - }, -): NodeJS.ReadWriteStream { - return new Transform({ - ...options, - async transform(chunk: T, encoding, callback) { - let isPromise = false; - try { - const mapped = mapper(chunk, encoding); - isPromise = mapped instanceof Promise; - (await mapped).forEach(c => this.push(c)); - callback(); - } catch (err) { - if (isPromise) { - // Calling the callback asynchronously with an error wouldn't emit the error, so emit directly - this.emit("error", err); - callback(); - } else { - callback(err); - } - } - }, - }); -} - -/** - * Return a ReadWrite stream that filters out streamed chunks for which the predicate does not hold - * @param predicate Predicate with which to filter scream chunks - * @param options - * @param options.objectMode Whether this stream should behave as a stream of objects - */ -export function filter( - predicate: - | ((chunk: T, encoding: string) => boolean) - | ((chunk: T, encoding: string) => Promise), - options: ThroughOptions = { - objectMode: true, - }, -) { - return new Transform({ - readableObjectMode: options.objectMode, - writableObjectMode: options.objectMode, - async transform(chunk: T, encoding, callback) { - let isPromise = false; - try { - const result = predicate(chunk, encoding); - isPromise = result instanceof Promise; - if (!!(await result)) { - callback(undefined, chunk); - } else { - callback(); - } - } catch (err) { - if (isPromise) { - // Calling the callback asynchronously with an error wouldn't emit the error, so emit directly - this.emit("error", err); - callback(); - } else { - callback(err); - } - } - }, - }); -} - -/** - * Return a ReadWrite stream that reduces streamed chunks down to a single value and yield that - * value - * @param iteratee Reducer function to apply on each streamed chunk - * @param initialValue Initial value - * @param options - * @param options.readableObjectMode Whether this stream should behave as a readable stream of objects - * @param options.writableObjectMode Whether this stream should behave as a writable stream of objects - */ -export function reduce( - iteratee: - | ((previousValue: R, chunk: T, encoding: string) => R) - | ((previousValue: R, chunk: T, encoding: string) => Promise), - initialValue: R, - options: TransformOptions = { - readableObjectMode: true, - writableObjectMode: true, - }, -) { - let value = initialValue; - return new Transform({ - readableObjectMode: options.readableObjectMode, - writableObjectMode: options.writableObjectMode, - async transform(chunk: T, encoding, callback) { - let isPromise = false; - try { - const result = iteratee(value, chunk, encoding); - isPromise = result instanceof Promise; - value = await result; - callback(); - } catch (err) { - if (isPromise) { - // Calling the callback asynchronously with an error wouldn't emit the error, so emit directly - this.emit("error", err); - callback(); - } else { - callback(err); - } - } - }, - flush(callback) { - // Best effort attempt at yielding the final value (will throw if e.g. yielding an object and - // downstream doesn't expect objects) - try { - callback(undefined, value); - } catch (err) { - try { - this.emit("error", err); - } catch { - // Best effort was made - } - } - }, - }); -} - -/** - * Return a ReadWrite stream that splits streamed chunks using the given separator - * @param separator Separator to split by, defaulting to "\n" - * @param options - * @param options.encoding Encoding written chunks are assumed to use - */ -export function split( - separator: string | RegExp = "\n", - options: WithEncoding = { encoding: "utf8" }, -): NodeJS.ReadWriteStream { - let buffered = ""; - const decoder = new StringDecoder(options.encoding); - - return new Transform({ - readableObjectMode: true, - transform(chunk: Buffer, encoding, callback) { - const asString = decoder.write(chunk); - const splitted = asString.split(separator); - if (splitted.length > 1) { - splitted[0] = buffered.concat(splitted[0]); - buffered = ""; - } - buffered += splitted[splitted.length - 1]; - splitted.slice(0, -1).forEach((part: string) => this.push(part)); - callback(); - }, - flush(callback) { - callback(undefined, buffered + decoder.end()); - }, - }); -} - -/** - * Return a ReadWrite stream that joins streamed chunks using the given separator - * @param separator Separator to join with - * @param options - * @param options.encoding Encoding written chunks are assumed to use - */ -export function join( - separator: string, - options: WithEncoding = { encoding: "utf8" }, -): NodeJS.ReadWriteStream { - let isFirstChunk = true; - const decoder = new StringDecoder(options.encoding); - return new Transform({ - readableObjectMode: true, - async transform(chunk: Buffer, encoding, callback) { - const asString = decoder.write(chunk); - // Take care not to break up multi-byte characters spanning multiple chunks - if (asString !== "" || chunk.length === 0) { - if (!isFirstChunk) { - this.push(separator); - } - this.push(asString); - isFirstChunk = false; - } - callback(); - }, - }); -} - -/** - * Return a ReadWrite stream that replaces occurrences of the given string or regular expression in - * the streamed chunks with the specified replacement string - * @param searchValue Search string to use - * @param replaceValue Replacement string to use - * @param options - * @param options.encoding Encoding written chunks are assumed to use - */ -export function replace( - searchValue: string | RegExp, - replaceValue: string, - options: WithEncoding = { encoding: "utf8" }, -): NodeJS.ReadWriteStream { - const decoder = new StringDecoder(options.encoding); - return new Transform({ - readableObjectMode: true, - transform(chunk: Buffer, encoding, callback) { - const asString = decoder.write(chunk); - // Take care not to break up multi-byte characters spanning multiple chunks - if (asString !== "" || chunk.length === 0) { - callback( - undefined, - asString.replace(searchValue, replaceValue), - ); - } else { - callback(); - } - }, - }); -} - -/** - * Return a ReadWrite stream that parses the streamed chunks as JSON. Each streamed chunk - * must be a fully defined JSON string. - */ -export function parse(): NodeJS.ReadWriteStream { - const decoder = new StringDecoder("utf8"); // JSON must be utf8 - return new Transform({ - readableObjectMode: true, - writableObjectMode: true, - async transform(chunk: Buffer, encoding, callback) { - try { - const asString = decoder.write(chunk); - // Using await causes parsing errors to be emitted - callback(undefined, await JSON.parse(asString)); - } catch (err) { - callback(err); - } - }, - }); -} - -type JsonPrimitive = string | number | object; -type JsonValue = JsonPrimitive | JsonPrimitive[]; -interface JsonParseOptions { - pretty: boolean; -} - -/** - * Return a ReadWrite stream that stringifies the streamed chunks to JSON - */ -export function stringify( - options: JsonParseOptions = { pretty: false }, -): NodeJS.ReadWriteStream { - return new Transform({ - readableObjectMode: true, - writableObjectMode: true, - transform(chunk: JsonValue, encoding, callback) { - callback( - undefined, - options.pretty - ? JSON.stringify(chunk, null, 2) - : JSON.stringify(chunk), - ); - }, - }); -} - -/** - * Return a ReadWrite stream that collects streamed chunks into an array or buffer - * @param options - * @param options.objectMode Whether this stream should behave as a stream of objects - */ -export function collect( - options: ThroughOptions = { objectMode: false }, -): NodeJS.ReadWriteStream { - const collected: any[] = []; - return new Transform({ - readableObjectMode: options.objectMode, - writableObjectMode: options.objectMode, - transform(data, encoding, callback) { - collected.push(data); - callback(); - }, - flush(callback) { - this.push( - options.objectMode ? collected : Buffer.concat(collected), - ); - callback(); - }, - }); -} - -/** - * Return a Readable stream of readable streams concatenated together - * @param streams Readable streams to concatenate - */ -export function concat( - ...streams: NodeJS.ReadableStream[] -): NodeJS.ReadableStream { - let isStarted = false; - let currentStreamIndex = 0; - const startCurrentStream = () => { - if (currentStreamIndex >= streams.length) { - wrapper.push(null); - } else { - streams[currentStreamIndex] - .on("data", chunk => { - if (!wrapper.push(chunk)) { - streams[currentStreamIndex].pause(); - } - }) - .on("error", err => wrapper.emit("error", err)) - .on("end", () => { - currentStreamIndex++; - startCurrentStream(); - }); - } - }; - - const wrapper = new Readable({ - objectMode: true, - read() { - if (!isStarted) { - isStarted = true; - startCurrentStream(); - } - if (currentStreamIndex < streams.length) { - streams[currentStreamIndex].resume(); - } - }, - }); - return wrapper; -} - -/** - * Return a Readable stream of readable streams merged together in chunk arrival order - * @param streams Readable streams to merge - */ -export function merge( - ...streams: NodeJS.ReadableStream[] -): NodeJS.ReadableStream { - let isStarted = false; - let streamEndedCount = 0; - return new Readable({ - objectMode: true, - read() { - if (streamEndedCount >= streams.length) { - this.push(null); - } else if (!isStarted) { - isStarted = true; - streams.forEach(stream => - stream - .on("data", chunk => { - if (!this.push(chunk)) { - streams.forEach(s => s.pause()); - } - }) - .on("error", err => this.emit("error", err)) - .on("end", () => { - streamEndedCount++; - if (streamEndedCount === streams.length) { - this.push(null); - } - }), - ); - } else { - streams.forEach(s => s.resume()); - } - }, - }); -} - -/** - * Return a Duplex stream from a writable stream that is assumed to somehow, when written to, - * cause the given readable stream to yield chunks - * @param writable Writable stream assumed to cause the readable stream to yield chunks when written to - * @param readable Readable stream assumed to yield chunks when the writable stream is written to - */ -export function duplex(writable: Writable, readable: Readable) { - const wrapper = new Duplex({ - readableObjectMode: true, - writableObjectMode: true, - read() { - readable.resume(); - }, - write(chunk, encoding, callback) { - return writable.write(chunk, encoding, callback); - }, - final(callback) { - writable.end(callback); - }, - }); - readable - .on("data", chunk => { - if (!wrapper.push(chunk)) { - readable.pause(); - } - }) - .on("error", err => wrapper.emit("error", err)) - .on("end", () => wrapper.push(null)); - writable.on("drain", () => wrapper.emit("drain")); - writable.on("error", err => wrapper.emit("error", err)); - return wrapper; -} - -/** - * Return a Duplex stream from a child process' stdin and stdout - * @param childProcess Child process from which to create duplex stream - */ -export function child(childProcess: ChildProcess) { - return duplex(childProcess.stdin, childProcess.stdout); -} - -/** - * Return a Promise resolving to the last streamed chunk of the given readable stream, after it has - * ended - * @param readable Readable stream to wait on - */ -export function last(readable: Readable): Promise { - let lastChunk: T | null = null; - return new Promise((resolve, reject) => { - readable - .on("data", chunk => (lastChunk = chunk)) - .on("end", () => resolve(lastChunk)); - }); -} +export { + fromArray, + map, + flatMap, + filter, + reduce, + split, + join, + replace, + parse, + stringify, + collect, + concat, + merge, + duplex, + child, + last, + batch, + unbatch, + rate, + parallelMap, + accumulator, + accumulatorBy, + compose, + demux, +} from "./functions"; diff --git a/tests/accumulator.spec.ts b/tests/accumulator.spec.ts new file mode 100644 index 0000000..51d6098 --- /dev/null +++ b/tests/accumulator.spec.ts @@ -0,0 +1,556 @@ +import test from "ava"; +import { expect } from "chai"; +import { Readable } from "stream"; +import { accumulator, accumulatorBy } from "../src"; +import { FlushStrategy } from "../src/functions/accumulator"; +import { performance } from "perf_hooks"; + +test.cb("accumulator() rolling", t => { + t.plan(3); + let chunkIndex = 0; + interface TestObject { + ts: number; + key: string; + } + const source = new Readable({ objectMode: true }); + const firstFlush = [{ ts: 0, key: "a" }, { ts: 1, key: "b" }]; + const secondFlush = [{ ts: 2, key: "d" }, { ts: 3, key: "e" }]; + const thirdFlush = [{ ts: 4, key: "f" }]; + const flushes = [firstFlush, secondFlush, thirdFlush]; + + source + .pipe( + accumulator(FlushStrategy.rolling, 2, undefined, { + objectMode: true, + }), + ) + .on("data", (flush: TestObject[]) => { + t.deepEqual(flush, flushes[chunkIndex]); + chunkIndex++; + }) + .on("error", (e: any) => { + t.end(e); + }) + .on("end", t.end); + [...firstFlush, ...secondFlush, ...thirdFlush].forEach(item => { + source.push(item); + }); + source.push(null); +}); + +test.cb("accumulator() rolling with key", t => { + t.plan(2); + let chunkIndex = 0; + interface TestObject { + ts: number; + key: string; + } + const source = new Readable({ objectMode: true }); + const firstFlush = [ + { ts: 0, key: "a" }, + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + { ts: 2, key: "d" }, + ]; + const secondFlush = [{ ts: 3, key: "e" }]; + const flushes = [firstFlush, secondFlush]; + + source + .pipe(accumulator(FlushStrategy.rolling, 3, "ts", { objectMode: true })) + .on("data", (flush: TestObject[]) => { + t.deepEqual(flush, flushes[chunkIndex]); + chunkIndex++; + }) + .on("error", (e: any) => { + t.end(e); + }) + .on("end", t.end); + [...firstFlush, ...secondFlush].forEach(item => { + source.push(item); + }); + source.push(null); +}); + +test.cb( + "accumulator() rolling should emit error and ignore chunk when its missing key", + t => { + t.plan(2); + let index = 0; + interface TestObject { + ts: number; + key: string; + } + const source = new Readable({ objectMode: true }); + const accumulatorStream = accumulator( + FlushStrategy.rolling, + 3, + "nonExistingKey", + { objectMode: true }, + ); + const input = [{ ts: 0, key: "a" }, { ts: 1, key: "b" }]; + + source + .pipe(accumulatorStream) + .on("data", (flush: TestObject[]) => { + // No valid data output + expect(flush).to.deep.equal([]); + }) + .on("error", (err: any) => { + source.pipe(accumulatorStream); + accumulatorStream.resume(); + expect(err.message).to.equal( + `Key is missing in event: (nonExistingKey, ${JSON.stringify( + input[index], + )})`, + ); + index++; + t.pass(); + }) + .on("end", t.end); + input.forEach(item => { + source.push(item); + }); + source.push(null); + }, +); + +test.cb( + "accumulator() rolling should emit error, ignore chunk when key is missing and continue processing chunks correctly", + t => { + t.plan(3); + let chunkIndex = 0; + interface TestObject { + ts: number; + key: string; + } + const source = new Readable({ objectMode: true }); + const accumulatorStream = accumulator(FlushStrategy.rolling, 3, "ts", { + objectMode: true, + }); + const input = [ + { ts: 0, key: "a" }, + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + { key: "d" }, + { ts: 3, key: "e" }, + ]; + const firstFlush = [ + { ts: 0, key: "a" }, + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + ]; + const secondFlush = [{ ts: 3, key: "e" }]; + const flushes = [firstFlush, secondFlush]; + + source + .pipe(accumulatorStream) + .on("data", (flush: TestObject[]) => { + t.deepEqual(flush, flushes[chunkIndex]); + chunkIndex++; + }) + .on("error", (err: any) => { + source.pipe(accumulatorStream); + accumulatorStream.resume(); + expect(err.message).to.equal( + `Key is missing in event: (ts, ${JSON.stringify( + input[3], + )})`, + ); + t.pass(); + }) + .on("end", t.end); + input.forEach(item => { + source.push(item); + }); + source.push(null); + }, +); + +test.cb("accumulator() sliding", t => { + t.plan(4); + let chunkIndex = 0; + interface TestObject { + ts: number; + key: string; + } + const source = new Readable({ objectMode: true }); + const input = [ + { ts: 0, key: "a" }, + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + { ts: 4, key: "d" }, + ]; + const firstFlush = [{ ts: 0, key: "a" }]; + const secondFlush = [{ ts: 0, key: "a" }, { ts: 1, key: "b" }]; + const thirdFlush = [ + { ts: 0, key: "a" }, + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + ]; + const fourthFlush = [ + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + { ts: 4, key: "d" }, + ]; + + const flushes = [firstFlush, secondFlush, thirdFlush, fourthFlush]; + source + .pipe( + accumulator(FlushStrategy.sliding, 3, undefined, { + objectMode: true, + }), + ) + .on("data", (flush: TestObject[]) => { + t.deepEqual(flush, flushes[chunkIndex]); + chunkIndex++; + }) + .on("error", (e: any) => { + t.end(e); + }) + .on("end", t.end); + input.forEach(item => { + source.push(item); + }); + source.push(null); +}); + +test.cb("accumulator() sliding with key", t => { + t.plan(6); + let chunkIndex = 0; + interface TestObject { + ts: number; + key: string; + } + const source = new Readable({ objectMode: true }); + const input = [ + { ts: 0, key: "a" }, + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + { ts: 3, key: "d" }, + { ts: 5, key: "f" }, + { ts: 6, key: "g" }, + ]; + const firstFlush = [{ ts: 0, key: "a" }]; + const secondFlush = [{ ts: 0, key: "a" }, { ts: 1, key: "b" }]; + const thirdFlush = [ + { ts: 0, key: "a" }, + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + ]; + const fourthFlush = [ + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + { ts: 3, key: "d" }, + ]; + const fifthFlush = [{ ts: 3, key: "d" }, { ts: 5, key: "f" }]; + const sixthFlush = [{ ts: 5, key: "f" }, { ts: 6, key: "g" }]; + + const flushes = [ + firstFlush, + secondFlush, + thirdFlush, + fourthFlush, + fifthFlush, + sixthFlush, + ]; + source + .pipe(accumulator(FlushStrategy.sliding, 3, "ts", { objectMode: true })) + .on("data", (flush: TestObject[]) => { + t.deepEqual(flush, flushes[chunkIndex]); + chunkIndex++; + }) + .on("error", (e: any) => { + t.end(e); + }) + .on("end", t.end); + input.forEach(item => { + source.push(item); + }); + source.push(null); +}); + +test.cb( + "accumulator() sliding should emit error and ignore chunk when key is missing", + t => { + t.plan(2); + let index = 0; + interface TestObject { + ts: number; + key: string; + } + const source = new Readable({ objectMode: true }); + const accumulatorStream = accumulator( + FlushStrategy.sliding, + 3, + "nonExistingKey", + { objectMode: true }, + ); + const input = [{ ts: 0, key: "a" }, { ts: 1, key: "b" }]; + + source + .pipe(accumulatorStream) + .on("data", (flush: TestObject[]) => { + expect(flush).to.deep.equal([]); + }) + .on("error", (err: any) => { + source.pipe(accumulatorStream); + accumulatorStream.resume(); + expect(err.message).to.equal( + `Key is missing in event: (nonExistingKey, ${JSON.stringify( + input[index], + )})`, + ); + index++; + t.pass(); + }) + .on("end", t.end); + input.forEach(item => { + source.push(item); + }); + source.push(null); + }, +); + +test.cb( + "accumulator() sliding should emit error, ignore chunk when key is missing and continue processing chunks correctly", + t => { + t.plan(6); + let chunkIndex = 0; + interface TestObject { + ts: number; + key: string; + } + const source = new Readable({ objectMode: true }); + const accumulatorStream = accumulator(FlushStrategy.sliding, 3, "ts", { + objectMode: true, + }); + const input = [ + { ts: 0, key: "a" }, + { key: "b" }, + { ts: 2, key: "c" }, + { ts: 3, key: "d" }, + { ts: 5, key: "f" }, + { ts: 6, key: "g" }, + ]; + const firstFlush = [{ ts: 0, key: "a" }]; + const secondFlush = [{ ts: 0, key: "a" }, { ts: 2, key: "c" }]; + const thirdFlush = [{ ts: 2, key: "c" }, { ts: 3, key: "d" }]; + const fourthFlush = [{ ts: 3, key: "d" }, { ts: 5, key: "f" }]; + const fifthFlush = [{ ts: 5, key: "f" }, { ts: 6, key: "g" }]; + + const flushes = [ + firstFlush, + secondFlush, + thirdFlush, + fourthFlush, + fifthFlush, + ]; + source + .pipe(accumulatorStream) + .on("data", (flush: TestObject[]) => { + t.deepEqual(flush, flushes[chunkIndex]); + chunkIndex++; + }) + .on("error", (err: any) => { + source.pipe(accumulatorStream); + accumulatorStream.resume(); + expect(err.message).to.equal( + `Key is missing in event: (ts, ${JSON.stringify( + input[1], + )})`, + ); + t.pass(); + }) + .on("end", t.end); + input.forEach(item => { + source.push(item); + }); + source.push(null); + }, +); + +test.cb("accumulatorBy() rolling", t => { + t.plan(2); + let chunkIndex = 0; + interface TestObject { + ts: number; + key: string; + } + const source = new Readable({ objectMode: true }); + const firstFlush = [ + { ts: 0, key: "a" }, + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + { ts: 2, key: "d" }, + ]; + const secondFlush = [{ ts: 3, key: "e" }]; + const flushes = [firstFlush, secondFlush]; + + source + .pipe( + accumulatorBy( + FlushStrategy.rolling, + (event: TestObject, bufferChunk: TestObject) => { + return bufferChunk.ts + 3 <= event.ts; + }, + { objectMode: true }, + ), + ) + .on("data", (flush: TestObject[]) => { + t.deepEqual(flush, flushes[chunkIndex]); + chunkIndex++; + }) + .on("error", (e: any) => { + t.end(e); + }) + .on("end", t.end); + [...firstFlush, ...secondFlush].forEach(item => { + source.push(item); + }); + source.push(null); +}); + +test.cb.skip( + "accumulatorBy() rolling should emit error when key iteratee throws", + t => { + t.plan(2); + interface TestObject { + ts: number; + key: string; + } + const source = new Readable({ objectMode: true }); + const input = [ + { ts: 0, key: "a" }, + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + ]; + const accumulaterStream = accumulatorBy( + FlushStrategy.rolling, + (event: TestObject, bufferChunk: TestObject) => { + if (event.key !== "a") { + throw new Error("Failed mapping"); + } + return bufferChunk.ts + 3 <= event.ts; + }, + { objectMode: true }, + ); + source + .pipe(accumulaterStream) + .on("error", (err: any) => { + source.pipe(accumulaterStream); + accumulaterStream.resume(); + expect(err.message).to.equal("Failed mapping"); + t.pass(); + }) + .on("end", t.end); + + input.forEach(item => { + source.push(item); + }); + source.push(null); + }, +); + +test.cb("accumulatorBy() sliding", t => { + t.plan(6); + let chunkIndex = 0; + interface TestObject { + ts: number; + key: string; + } + const source = new Readable({ objectMode: true }); + const input = [ + { ts: 0, key: "a" }, + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + { ts: 3, key: "d" }, + { ts: 5, key: "f" }, + { ts: 6, key: "g" }, + ]; + const firstFlush = [{ ts: 0, key: "a" }]; + const secondFlush = [{ ts: 0, key: "a" }, { ts: 1, key: "b" }]; + const thirdFlush = [ + { ts: 0, key: "a" }, + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + ]; + const fourthFlush = [ + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + { ts: 3, key: "d" }, + ]; + const fifthFlush = [{ ts: 3, key: "d" }, { ts: 5, key: "f" }]; + const sixthFlush = [{ ts: 5, key: "f" }, { ts: 6, key: "g" }]; + + const flushes = [ + firstFlush, + secondFlush, + thirdFlush, + fourthFlush, + fifthFlush, + sixthFlush, + ]; + source + .pipe( + accumulatorBy( + FlushStrategy.sliding, + (event: TestObject, bufferChunk: TestObject) => { + return bufferChunk.ts + 3 <= event.ts ? true : false; + }, + { objectMode: true }, + ), + ) + .on("data", (flush: TestObject[]) => { + t.deepEqual(flush, flushes[chunkIndex]); + chunkIndex++; + }) + .on("error", (e: any) => { + t.end(e); + }) + .on("end", t.end); + input.forEach(item => { + source.push(item); + }); + source.push(null); +}); + +test.cb.skip( + "accumulatorBy() sliding should emit error when key iteratee throws", + t => { + t.plan(2); + interface TestObject { + ts: number; + key: string; + } + const source = new Readable({ objectMode: true }); + const input = [ + { ts: 0, key: "a" }, + { ts: 1, key: "b" }, + { ts: 2, key: "c" }, + ]; + const accumulaterStream = accumulatorBy( + FlushStrategy.sliding, + (event: TestObject, bufferChunk: TestObject) => { + if (event.key !== "a") { + throw new Error("Failed mapping"); + } + return bufferChunk.ts + 3 <= event.ts ? true : false; + }, + { objectMode: true }, + ); + source + .pipe(accumulaterStream) + .on("error", (err: any) => { + source.pipe(accumulaterStream); + accumulaterStream.resume(); + expect(err.message).to.equal("Failed mapping"); + t.pass(); + }) + .on("end", t.end); + + input.forEach(item => { + source.push(item); + }); + source.push(null); + }, +); diff --git a/tests/batch.spec.ts b/tests/batch.spec.ts new file mode 100644 index 0000000..0c2cd3a --- /dev/null +++ b/tests/batch.spec.ts @@ -0,0 +1,56 @@ +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { batch } from "../src"; + +test.cb("batch() batches chunks together", t => { + t.plan(3); + const source = new Readable({ objectMode: true }); + const expectedElements = [["a", "b", "c"], ["d", "e", "f"], ["g"]]; + let i = 0; + source + .pipe(batch(3)) + .on("data", (element: string[]) => { + t.deepEqual(element, expectedElements[i]); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push("d"); + source.push("e"); + source.push("f"); + source.push("g"); + source.push(null); +}); + +test.cb("batch() yields a batch after the timeout", t => { + t.plan(3); + const source = new Readable({ + objectMode: true, + read(size: number) {}, + }); + const expectedElements = [["a", "b"], ["c"], ["d"]]; + let i = 0; + source + .pipe(batch(3)) + .on("data", (element: string[]) => { + t.deepEqual(element, expectedElements[i]); + i++; + }) + .on("error", t.fail) + .on("end", t.end); + + source.push("a"); + source.push("b"); + setTimeout(() => { + source.push("c"); + }, 600); + setTimeout(() => { + source.push("d"); + source.push(null); + }, 600 * 2); +}); diff --git a/tests/child.spec.ts b/tests/child.spec.ts new file mode 100644 index 0000000..7730790 --- /dev/null +++ b/tests/child.spec.ts @@ -0,0 +1,28 @@ +import * as cp from "child_process"; +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { child } from "../src"; + +test.cb( + "child() allows easily writing to child process stdin and reading from its stdout", + t => { + t.plan(1); + const source = new Readable(); + const catProcess = cp.exec("cat"); + let out = ""; + source + .pipe(child(catProcess)) + .on("data", chunk => (out += chunk)) + .on("error", t.end) + .on("end", () => { + expect(out).to.equal("abcdef"); + t.pass(); + t.end(); + }); + source.push("ab"); + source.push("cd"); + source.push("ef"); + source.push(null); + }, +); diff --git a/tests/collect.spec.ts b/tests/collect.spec.ts new file mode 100644 index 0000000..1e4cd03 --- /dev/null +++ b/tests/collect.spec.ts @@ -0,0 +1,132 @@ +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { collect } from "../src"; + +test.cb( + "collect() collects streamed elements into an array (object, flowing mode)", + t => { + t.plan(1); + const source = new Readable({ objectMode: true }); + + source + .pipe(collect({ objectMode: true })) + .on("data", collected => { + expect(collected).to.deep.equal(["a", "b", "c"]); + t.pass(); + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); + }, +); + +test.cb( + "collect() collects streamed elements into an array (object, paused mode)", + t => { + t.plan(1); + const source = new Readable({ objectMode: true }); + const collector = source.pipe(collect({ objectMode: true })); + + collector + .on("readable", () => { + let collected = collector.read(); + while (collected !== null) { + expect(collected).to.deep.equal(["a", "b", "c"]); + t.pass(); + collected = collector.read(); + } + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); + }, +); + +test.cb( + "collect() collects streamed bytes into a buffer (non-object, flowing mode)", + t => { + t.plan(1); + const source = new Readable({ objectMode: false }); + + source + .pipe(collect()) + .on("data", collected => { + expect(collected).to.deep.equal(Buffer.from("abc")); + t.pass(); + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); + }, +); + +test.cb( + "collect() collects streamed bytes into a buffer (non-object, paused mode)", + t => { + t.plan(1); + const source = new Readable({ objectMode: false }); + const collector = source.pipe(collect({ objectMode: false })); + collector + .on("readable", () => { + let collected = collector.read(); + while (collected !== null) { + expect(collected).to.deep.equal(Buffer.from("abc")); + t.pass(); + collected = collector.read(); + } + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); + }, +); + +test.cb( + "collect() emits an empty array if the source was empty (object mode)", + t => { + t.plan(1); + const source = new Readable({ objectMode: true }); + const collector = source.pipe(collect({ objectMode: true })); + collector + .on("data", collected => { + expect(collected).to.deep.equal([]); + t.pass(); + }) + .on("error", t.end) + .on("end", t.end); + + source.push(null); + }, +); + +test.cb( + "collect() emits nothing if the source was empty (non-object mode)", + t => { + t.plan(0); + const source = new Readable({ objectMode: false }); + const collector = source.pipe(collect({ objectMode: false })); + collector + .on("data", () => t.fail()) + .on("error", t.end) + .on("end", t.end); + + source.push(null); + }, +); diff --git a/tests/compose.spec.ts b/tests/compose.spec.ts new file mode 100644 index 0000000..bae5825 --- /dev/null +++ b/tests/compose.spec.ts @@ -0,0 +1,511 @@ +const test = require("ava"); +const { expect } = require("chai"); +const { compose, map } = require("../src"); +const { sleep } = require("../src/helpers"); +import { performance } from "perf_hooks"; + +test.cb("compose() chains two streams together in the correct order", t => { + t.plan(3); + interface Chunk { + visited: number[]; + key: string; + } + + let i = 0; + const first = map((chunk: Chunk) => { + chunk.visited.push(1); + return chunk; + }); + const second = map((chunk: Chunk) => { + chunk.visited.push(2); + return chunk; + }); + + const composed = compose( + [first, second], + { objectMode: true }, + ); + + composed.on("data", data => { + expect(data).to.deep.equal(result[i]); + t.pass(); + i++; + if (i === 3) { + t.end(); + } + }); + composed.on("error", err => { + t.end(err); + }); + composed.on("end", () => { + t.end(); + }); + + const input = [ + { key: "a", visited: [] }, + { key: "b", visited: [] }, + { key: "c", visited: [] }, + ]; + const result = [ + { key: "a", visited: [1, 2] }, + { key: "b", visited: [1, 2] }, + { key: "c", visited: [1, 2] }, + ]; + + input.forEach(item => composed.write(item)); +}); + +test.cb("piping compose() maintains correct order", t => { + t.plan(3); + interface Chunk { + visited: number[]; + key: string; + } + let i = 0; + const first = map((chunk: Chunk) => { + chunk.visited.push(1); + return chunk; + }); + const second = map((chunk: Chunk) => { + chunk.visited.push(2); + return chunk; + }); + + const composed = compose( + [first, second], + { objectMode: true }, + ); + const third = map((chunk: Chunk) => { + chunk.visited.push(3); + return chunk; + }); + + composed.pipe(third).on("data", data => { + expect(data).to.deep.equal(result[i]); + t.pass(); + i++; + if (i === 3) { + t.end(); + } + }); + + composed.on("error", err => { + t.end(err); + }); + + const input = [ + { key: "a", visited: [] }, + { key: "b", visited: [] }, + { key: "c", visited: [] }, + ]; + const result = [ + { key: "a", visited: [1, 2, 3] }, + { key: "b", visited: [1, 2, 3] }, + { key: "c", visited: [1, 2, 3] }, + ]; + + input.forEach(item => composed.write(item)); +}); + +test("compose() writable length should be less than highWaterMark when handing writes", async t => { + t.plan(7); + return new Promise(async (resolve, reject) => { + interface Chunk { + key: string; + mapped: number[]; + } + const first = map( + async (chunk: Chunk) => { + chunk.mapped.push(1); + return chunk; + }, + { + objectMode: true, + }, + ); + + const second = map( + async (chunk: Chunk) => { + chunk.mapped.push(2); + return chunk; + }, + { objectMode: true }, + ); + + const composed = compose( + [first, second], + { objectMode: true, highWaterMark: 2 }, + ); + composed.on("error", err => { + reject(); + }); + + composed.on("drain", () => { + t.pass(); + expect(composed._writableState.length).to.be.equal(0); + }); + + composed.on("data", (chunk: Chunk) => { + if (chunk.key === "e") { + resolve(); + } + }); + + const input = [ + { key: "a", mapped: [] }, + { key: "b", mapped: [] }, + { key: "c", mapped: [] }, + { key: "d", mapped: [] }, + { key: "e", mapped: [] }, + ]; + + for (const item of input) { + const res = composed.write(item); + expect(composed._writableState.length).to.be.at.most(2); + t.pass(); + if (!res) { + await sleep(10); + } + } + }); +}); + +test("compose() should emit drain event ~rate * highWaterMark ms for every write that causes backpressure", async t => { + t.plan(7); + const _rate = 100; + const highWaterMark = 2; + return new Promise(async (resolve, reject) => { + interface Chunk { + key: string; + mapped: number[]; + } + const first = map( + async (chunk: Chunk) => { + await sleep(_rate); + chunk.mapped.push(1); + return chunk; + }, + { + objectMode: true, + }, + ); + + const second = map( + async (chunk: Chunk) => { + chunk.mapped.push(2); + return chunk; + }, + { objectMode: true }, + ); + + const composed = compose( + [first, second], + { objectMode: true, highWaterMark }, + ); + composed.on("error", err => { + reject(); + }); + + composed.on("drain", () => { + t.pass(); + expect(composed._writableState.length).to.be.equal(0); + expect(performance.now() - start).to.be.closeTo( + _rate * highWaterMark, + 10, + ); + }); + + composed.on("data", (chunk: Chunk) => { + pendingReads--; + if (pendingReads === 0) { + resolve(); + } + }); + + const input = [ + { key: "a", mapped: [] }, + { key: "b", mapped: [] }, + { key: "c", mapped: [] }, + { key: "d", mapped: [] }, + { key: "e", mapped: [] }, + ]; + + let start = performance.now(); + let pendingReads = input.length; + start = performance.now(); + for (const item of input) { + const res = composed.write(item); + expect(composed._writableState.length).to.be.at.most(highWaterMark); + t.pass(); + if (!res) { + await sleep(_rate * highWaterMark * 2); + start = performance.now(); + } + } + }); +}); + +test.cb( + "compose() should emit drain event after 500 ms when writing 5 items that take 100ms to process with a highWaterMark of 5 ", + t => { + t.plan(6); + const _rate = 100; + interface Chunk { + key: string; + mapped: number[]; + } + const first = map( + async (chunk: Chunk) => { + await sleep(_rate); + chunk.mapped.push(1); + return chunk; + }, + { + objectMode: true, + }, + ); + + const second = map( + async (chunk: Chunk) => { + chunk.mapped.push(2); + return chunk; + }, + { objectMode: true }, + ); + + const composed = compose( + [first, second], + { objectMode: true, highWaterMark: 5 }, + ); + + composed.on("error", err => { + t.end(err); + }); + + composed.on("drain", () => { + expect(composed._writableState.length).to.be.equal(0); + expect(performance.now() - start).to.be.closeTo( + _rate * input.length, + 25, + ); + t.pass(); + }); + + composed.on("data", (chunk: Chunk) => { + t.pass(); + if (chunk.key === "e") { + t.end(); + } + }); + + const input = [ + { key: "a", mapped: [] }, + { key: "b", mapped: [] }, + { key: "c", mapped: [] }, + { key: "d", mapped: [] }, + { key: "e", mapped: [] }, + ]; + input.forEach(item => { + composed.write(item); + }); + const start = performance.now(); + }, +); + +test.cb( + "compose() should emit drain event immediately when second stream is bottleneck", + t => { + t.plan(6); + const _rate = 200; + interface Chunk { + key: string; + mapped: number[]; + } + const first = map( + (chunk: Chunk) => { + chunk.mapped.push(1); + return chunk; + }, + { + objectMode: true, + }, + ); + + const second = map( + async (chunk: Chunk) => { + pendingReads--; + await sleep(_rate); + expect(second._writableState.length).to.be.equal(1); + expect(first._readableState.length).to.equal(pendingReads); + chunk.mapped.push(2); + return chunk; + }, + { objectMode: true, highWaterMark: 1 }, + ); + + const composed = compose( + [first, second], + { objectMode: true, highWaterMark: 5 }, + ); + composed.on("error", err => { + t.end(err); + }); + + composed.on("drain", () => { + expect(composed._writableState.length).to.be.equal(0); + expect(performance.now() - start).to.be.lessThan(_rate); + t.pass(); + }); + + composed.on("data", (chunk: Chunk) => { + expect(composed._writableState.length).to.be.equal(0); + t.pass(); + if (chunk.key === "e") { + t.end(); + } + }); + + const input = [ + { key: "a", mapped: [] }, + { key: "b", mapped: [] }, + { key: "c", mapped: [] }, + { key: "d", mapped: [] }, + { key: "e", mapped: [] }, + ]; + let pendingReads = input.length; + + input.forEach(item => { + composed.write(item); + }); + + const start = performance.now(); + }, +); + +test.cb( + "compose() should emit drain event and first should contain up to highWaterMark items in readable state when second is bottleneck", + t => { + t.plan(6); + interface Chunk { + index: number; + mapped: string[]; + } + const first = map( + async (chunk: Chunk) => { + expect(first._readableState.length).to.be.at.most(2); + chunk.mapped.push("first"); + return chunk; + }, + { + objectMode: true, + highWaterMark: 2, + }, + ); + + const second = map( + async (chunk: Chunk) => { + expect(second._writableState.length).to.be.equal(1); + await sleep(100); + chunk.mapped.push("second"); + return chunk; + }, + { objectMode: true, highWaterMark: 2 }, + ); + + const composed = compose( + [first, second], + { objectMode: true, highWaterMark: 5 }, + ); + composed.on("error", err => { + t.end(err); + }); + + composed.on("data", (chunk: Chunk) => { + expect(chunk.mapped.length).to.equal(2); + expect(chunk.mapped).to.deep.equal(["first", "second"]); + t.pass(); + if (chunk.index === 5) { + t.end(); + } + }); + + composed.on("drain", () => { + expect(composed._writableState.length).to.be.equal(0); + t.pass(); + }); + + const input = [ + { index: 1, mapped: [] }, + { index: 2, mapped: [] }, + { index: 3, mapped: [] }, + { index: 4, mapped: [] }, + { index: 5, mapped: [] }, + ]; + + input.forEach(item => { + composed.write(item); + }); + }, +); + +test.cb( + "compose() should not emit drain event writing 5 items to compose with a highWaterMark of 6", + t => { + t.plan(5); + const _rate = 100; + interface Chunk { + key: string; + mapped: number[]; + } + const first = map( + async (chunk: Chunk) => { + await sleep(_rate); + chunk.mapped.push(1); + return chunk; + }, + { + objectMode: true, + }, + ); + + const second = map( + async (chunk: Chunk) => { + chunk.mapped.push(2); + return chunk; + }, + { objectMode: true }, + ); + + const composed = compose( + [first, second], + { objectMode: true, highWaterMark: 6 }, + ); + + composed.on("error", err => { + t.end(err); + }); + + composed.on("drain", () => { + t.end(new Error("Drain should not be emitted")); + }); + + composed.on("data", (chunk: Chunk) => { + t.pass(); + if (chunk.key === "e") { + t.end(); + } + }); + + const input = [ + { key: "a", mapped: [] }, + { key: "b", mapped: [] }, + { key: "c", mapped: [] }, + { key: "d", mapped: [] }, + { key: "e", mapped: [] }, + ]; + + input.forEach(item => { + composed.write(item); + }); + }, +); diff --git a/tests/concat.spec.ts b/tests/concat.spec.ts new file mode 100644 index 0000000..596fbad --- /dev/null +++ b/tests/concat.spec.ts @@ -0,0 +1,180 @@ +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { concat, collect } from "../src"; + +test.cb( + "concat() concatenates multiple readable streams (object, flowing mode)", + t => { + t.plan(6); + const source1 = new Readable({ objectMode: true }); + const source2 = new Readable({ objectMode: true }); + const expectedElements = ["a", "b", "c", "d", "e", "f"]; + let i = 0; + concat(source1, source2) + .on("data", (element: string) => { + expect(element).to.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source1.push("a"); + source2.push("d"); + source1.push("b"); + source2.push("e"); + source1.push("c"); + source2.push("f"); + source2.push(null); + source1.push(null); + }, +); + +test.cb( + "concat() concatenates multiple readable streams (object, paused mode)", + t => { + t.plan(6); + const source1 = new Readable({ objectMode: true }); + const source2 = new Readable({ objectMode: true }); + const expectedElements = ["a", "b", "c", "d", "e", "f"]; + let i = 0; + const concatenation = concat(source1, source2) + .on("readable", () => { + let element = concatenation.read(); + while (element !== null) { + expect(element).to.equal(expectedElements[i]); + t.pass(); + i++; + element = concatenation.read(); + } + }) + .on("error", t.end) + .on("end", t.end); + + source1.push("a"); + source2.push("d"); + source1.push("b"); + source2.push("e"); + source1.push("c"); + source2.push("f"); + source2.push(null); + source1.push(null); + }, +); + +test.cb( + "concat() concatenates multiple readable streams (non-object, flowing mode)", + t => { + t.plan(6); + const source1 = new Readable({ objectMode: false }); + const source2 = new Readable({ objectMode: false }); + const expectedElements = ["a", "b", "c", "d", "e", "f"]; + let i = 0; + concat(source1, source2) + .on("data", (element: string) => { + expect(element).to.deep.equal(Buffer.from(expectedElements[i])); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source1.push("a"); + source2.push("d"); + source1.push("b"); + source2.push("e"); + source1.push("c"); + source2.push("f"); + source2.push(null); + source1.push(null); + }, +); + +test.cb( + "concat() concatenates multiple readable streams (non-object, paused mode)", + t => { + t.plan(6); + const source1 = new Readable({ objectMode: false, read: () => ({}) }); + const source2 = new Readable({ objectMode: false, read: () => ({}) }); + const expectedElements = ["a", "b", "c", "d", "e", "f"]; + let i = 0; + const concatenation = concat(source1, source2) + .on("readable", () => { + let element = concatenation.read(); + while (element !== null) { + expect(element).to.deep.equal( + Buffer.from(expectedElements[i]), + ); + t.pass(); + i++; + element = concatenation.read(); + } + }) + .on("error", t.end) + .on("end", t.end); + + source1.push("a"); + setTimeout(() => source2.push("d"), 10); + setTimeout(() => source1.push("b"), 20); + setTimeout(() => source2.push("e"), 30); + setTimeout(() => source1.push("c"), 40); + setTimeout(() => source2.push("f"), 50); + setTimeout(() => source2.push(null), 60); + setTimeout(() => source1.push(null), 70); + }, +); + +test.cb("concat() concatenates a single readable stream (object mode)", t => { + t.plan(3); + const source = new Readable({ objectMode: true }); + const expectedElements = ["a", "b", "c", "d", "e", "f"]; + let i = 0; + concat(source) + .on("data", (element: string) => { + expect(element).to.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); +}); + +test.cb( + "concat() concatenates a single readable stream (non-object mode)", + t => { + t.plan(3); + const source = new Readable({ objectMode: false }); + const expectedElements = ["a", "b", "c", "d", "e", "f"]; + let i = 0; + concat(source) + .on("data", (element: string) => { + expect(element).to.deep.equal(Buffer.from(expectedElements[i])); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); + }, +); + +test.cb("concat() concatenates empty list of readable streams", t => { + t.plan(0); + concat() + .pipe(collect()) + .on("data", _ => { + t.fail(); + }) + .on("error", t.end) + .on("end", t.end); +}); diff --git a/tests/demux.spec.ts b/tests/demux.spec.ts new file mode 100644 index 0000000..2d43ec1 --- /dev/null +++ b/tests/demux.spec.ts @@ -0,0 +1,656 @@ +import test from "ava"; +import { expect } from "chai"; +const { demux, map } = require("../src"); +import { Writable } from "stream"; +const sinon = require("sinon"); +const { sleep } = require("../src/helpers"); +import { performance } from "perf_hooks"; + +interface Test { + key: string; + visited: number[]; +} + +test.cb("demux() constructor should be called once per key", t => { + t.plan(1); + const input = [ + { key: "a", visited: [] }, + { key: "b", visited: [] }, + { key: "a", visited: [] }, + { key: "c", visited: [] }, + { key: "a", visited: [] }, + { key: "b", visited: [] }, + ]; + const construct = sinon.spy((destKey: string) => { + const dest = map((chunk: Test) => { + chunk.visited.push(1); + return chunk; + }); + + return dest; + }); + + const demuxed = demux(construct, "key", { objectMode: true }); + + demuxed.on("finish", () => { + expect(construct.withArgs("a").callCount).to.equal(1); + expect(construct.withArgs("b").callCount).to.equal(1); + expect(construct.withArgs("c").callCount).to.equal(1); + t.pass(); + t.end(); + }); + + input.forEach(event => demuxed.write(event)); + demuxed.end(); +}); + +test.cb("demux() should send input through correct pipeline", t => { + t.plan(6); + const input = [ + { key: "a", visited: [] }, + { key: "b", visited: [] }, + { key: "a", visited: [] }, + { key: "c", visited: [] }, + { key: "a", visited: [] }, + { key: "b", visited: [] }, + ]; + const pipelineSpies = {}; + const construct = (destKey: string) => { + const mapper = sinon.spy((chunk: Test) => { + return { ...chunk, visited: [1] }; + }); + const dest = map(mapper); + pipelineSpies[destKey] = mapper; + + return dest; + }; + + const demuxed = demux(construct, "key", { objectMode: true }); + + demuxed.on("finish", () => { + pipelineSpies["a"].getCalls().forEach(call => { + expect(call.args[0].key).to.equal("a"); + t.pass(); + }); + pipelineSpies["b"].getCalls().forEach(call => { + expect(call.args[0].key).to.equal("b"); + t.pass(); + }); + pipelineSpies["c"].getCalls().forEach(call => { + expect(call.args[0].key).to.equal("c"); + t.pass(); + }); + t.end(); + }); + + input.forEach(event => demuxed.write(event)); + demuxed.end(); +}); + +test.cb("demux() constructor should be called once per key using keyBy", t => { + t.plan(1); + const input = [ + { key: "a", visited: [] }, + { key: "b", visited: [] }, + { key: "a", visited: [] }, + { key: "c", visited: [] }, + { key: "a", visited: [] }, + { key: "b", visited: [] }, + ]; + + const construct = sinon.spy((destKey: string) => { + const dest = map((chunk: Test) => { + chunk.visited.push(1); + return chunk; + }); + + return dest; + }); + + const demuxed = demux(construct, item => item.key, { objectMode: true }); + + demuxed.on("finish", () => { + expect(construct.withArgs("a").callCount).to.equal(1); + expect(construct.withArgs("b").callCount).to.equal(1); + expect(construct.withArgs("c").callCount).to.equal(1); + t.pass(); + t.end(); + }); + + input.forEach(event => demuxed.write(event)); + demuxed.end(); +}); + +test.cb("demux() should send input through correct pipeline using keyBy", t => { + t.plan(6); + const input = [ + { key: "a", visited: [] }, + { key: "b", visited: [] }, + { key: "a", visited: [] }, + { key: "c", visited: [] }, + { key: "a", visited: [] }, + { key: "b", visited: [] }, + ]; + const pipelineSpies = {}; + const construct = (destKey: string) => { + const mapper = sinon.spy((chunk: Test) => { + return { ...chunk, visited: [1] }; + }); + const dest = map(mapper); + pipelineSpies[destKey] = mapper; + + return dest; + }; + + const demuxed = demux(construct, item => item.key, { objectMode: true }); + + demuxed.on("finish", () => { + pipelineSpies["a"].getCalls().forEach(call => { + expect(call.args[0].key).to.equal("a"); + t.pass(); + }); + pipelineSpies["b"].getCalls().forEach(call => { + expect(call.args[0].key).to.equal("b"); + t.pass(); + }); + pipelineSpies["c"].getCalls().forEach(call => { + expect(call.args[0].key).to.equal("c"); + t.pass(); + }); + t.end(); + }); + + input.forEach(event => demuxed.write(event)); + demuxed.end(); +}); + +test("demux() write should return false after if it has >= highWaterMark items buffered and drain should be emitted", t => { + return new Promise(async (resolve, reject) => { + t.plan(7); + interface Chunk { + key: string; + mapped: number[]; + } + const input: Chunk[] = [ + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + ]; + let pendingReads = input.length; + const highWaterMark = 5; + const slowProcessorSpeed = 25; + const construct = (destKey: string) => { + const first = map( + async (chunk: Chunk) => { + await sleep(slowProcessorSpeed); + return { ...chunk, mapped: [1] }; + }, + { highWaterMark: 1, objectMode: true }, + ); + + first.on("data", chunk => { + expect(chunk.mapped).to.deep.equal([1]); + pendingReads--; + if (pendingReads === 0) { + resolve(); + } + t.pass(); + }); + + return first; + }; + + const _demux = demux(construct, "key", { + objectMode: true, + highWaterMark, + }); + + _demux.on("error", err => { + reject(); + }); + + for (const item of input) { + const res = _demux.write(item); + expect(_demux._writableState.length).to.be.at.most(highWaterMark); + if (!res) { + await new Promise((resolv, rej) => { + _demux.once("drain", () => { + expect(_demux._writableState.length).to.be.equal(0); + t.pass(); + resolv(); + }); + }); + } + } + }); +}); + +test("demux() should emit one drain event after slowProcessorSpeed * highWaterMark ms", t => { + return new Promise(async (resolve, reject) => { + t.plan(7); + interface Chunk { + key: string; + mapped: number[]; + } + const input: Chunk[] = [ + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + ]; + let pendingReads = input.length; + const highWaterMark = 5; + const slowProcessorSpeed = 25; + + const construct = (destKey: string) => { + const first = map( + async (chunk: Chunk) => { + await sleep(slowProcessorSpeed); + chunk.mapped.push(1); + return chunk; + }, + { highWaterMark: 1, objectMode: true }, + ); + + first.on("data", () => { + t.pass(); + pendingReads--; + if (pendingReads === 0) { + resolve(); + } + }); + return first; + }; + const _demux = demux(construct, "key", { + objectMode: true, + highWaterMark, + }); + _demux.on("error", err => { + reject(); + }); + + const start = performance.now(); + for (const item of input) { + const res = _demux.write(item); + if (!res) { + await new Promise((resolv, rej) => { + // This event should be received after all items in demux are processed + _demux.once("drain", () => { + expect(performance.now() - start).to.be.greaterThan( + slowProcessorSpeed * highWaterMark, + ); + t.pass(); + resolv(); + }); + }); + } + } + }); +}); + +test("demux() should emit one drain event when writing 6 items with highWaterMark of 5", t => { + return new Promise(async (resolve, reject) => { + t.plan(7); + interface Chunk { + key: string; + mapped: number[]; + } + const highWaterMark = 5; + const input = [ + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + ]; + let pendingReads = input.length; + const construct = (destKey: string) => { + const first = map( + async (chunk: Chunk) => { + await sleep(50); + chunk.mapped.push(2); + return chunk; + }, + { highWaterMark: 1, objectMode: true }, + ); + + first.on("data", () => { + pendingReads--; + t.pass(); + if (pendingReads === 0) { + resolve(); + } + }); + return first; + }; + const _demux = demux(construct, "key", { + objectMode: true, + highWaterMark: 5, + }); + + _demux.on("error", err => { + reject(); + }); + + for (const item of input) { + const res = _demux.write(item); + expect(_demux._writableState.length).to.be.at.most(highWaterMark); + if (!res) { + await new Promise(_resolve => { + _demux.once("drain", () => { + _resolve(); + expect(_demux._writableState.length).to.be.equal(0); + t.pass(); + }); + }); + } + } + }); +}); + +test.cb.only( + "demux() should emit drain event when third stream is bottleneck", + t => { + t.plan(8); + const slowProcessorSpeed = 100; + const highWaterMark = 5; + interface Chunk { + key: string; + mapped: number[]; + } + const sink = new Writable({ + objectMode: true, + write(chunk, encoding, cb) { + expect(chunk.mapped).to.deep.equal([1, 2]); + t.pass(); + pendingReads--; + if (pendingReads === 0) { + t.end(); + } + cb(); + }, + }); + const construct = (destKey: string) => { + const first = map( + (chunk: Chunk) => { + chunk.mapped.push(1); + return chunk; + }, + { objectMode: true, highWaterMark: 1 }, + ); + + const second = map( + async (chunk: Chunk) => { + await sleep(slowProcessorSpeed); + chunk.mapped.push(2); + return chunk; + }, + { objectMode: true, highWaterMark: 1 }, + ); + + first.pipe(second).pipe(sink); + return first; + }; + const _demux = demux(construct, () => "a", { + objectMode: true, + highWaterMark, + }); + _demux.on("error", err => { + t.end(err); + }); + + // This event should be received after at least 5 * slowProcessorSpeed (two are read immediately by first and second, 5 remaining in demux before drain event) + _demux.on("drain", () => { + expect(_demux._writableState.length).to.be.equal(0); + expect(performance.now() - start).to.be.greaterThan( + slowProcessorSpeed * (input.length - 2), + ); + t.pass(); + }); + + const input = [ + { key: "a", mapped: [] }, + { key: "b", mapped: [] }, + { key: "c", mapped: [] }, + { key: "d", mapped: [] }, + { key: "e", mapped: [] }, + { key: "f", mapped: [] }, + { key: "g", mapped: [] }, + ]; + let pendingReads = input.length; + + const start = performance.now(); + input.forEach(item => { + _demux.write(item); + }); + }, +); + +test.cb( + "demux() should emit drain event when second stream is bottleneck", + t => { + t.plan(8); + const slowProcessorSpeed = 100; + const highWaterMark = 5; + interface Chunk { + key: string; + mapped: number[]; + } + const sink = new Writable({ + objectMode: true, + write(chunk, encoding, cb) { + expect(chunk.mapped).to.deep.equal([1, 2]); + t.pass(); + pendingReads--; + if (pendingReads === 0) { + t.end(); + } + cb(); + }, + }); + const construct = (destKey: string) => { + const first = map( + (chunk: Chunk) => { + chunk.mapped.push(1); + return chunk; + }, + { objectMode: true, highWaterMark: 1 }, + ); + const second = map( + (chunk: Chunk) => { + chunk.mapped.push(2); + return chunk; + }, + { objectMode: true, highWaterMark: 1 }, + ); + + const third = map( + async (chunk: Chunk) => { + await sleep(slowProcessorSpeed); + chunk.mapped.push(3); + return chunk; + }, + { objectMode: true, highWaterMark: 1 }, + ); + + first + .pipe(second) + .pipe(third) + .pipe(sink); + return first; + }; + const _demux = demux(construct, () => "a", { + objectMode: true, + highWaterMark, + }); + _demux.on("error", err => { + t.end(err); + }); + + // This event should be received after at least 3 * slowProcessorSpeed (two are read immediately by first and second, 3 remaining in demux before drain event) + _demux.on("drain", () => { + expect(_demux._writableState.length).to.be.equal(0); + expect(performance.now() - start).to.be.greaterThan( + slowProcessorSpeed * (input.length - 4), + ); + t.pass(); + }); + + const input = [ + { key: "a", mapped: [] }, + { key: "b", mapped: [] }, + { key: "c", mapped: [] }, + { key: "d", mapped: [] }, + { key: "e", mapped: [] }, + { key: "f", mapped: [] }, + { key: "g", mapped: [] }, + ]; + let pendingReads = input.length; + + const start = performance.now(); + input.forEach(item => { + _demux.write(item); + }); + }, +); + +test("demux() should be blocked by slowest pipeline", t => { + t.plan(1); + const slowProcessorSpeed = 100; + interface Chunk { + key: string; + mapped: number[]; + } + return new Promise(async (resolve, reject) => { + const construct = (destKey: string) => { + const first = map( + async (chunk: Chunk) => { + await sleep(slowProcessorSpeed); + chunk.mapped.push(1); + return chunk; + }, + { objectMode: true, highWaterMark: 1 }, + ); + + first.on("data", chunk => { + pendingReads--; + if (chunk.key === "b") { + expect(performance.now() - start).to.be.greaterThan( + slowProcessorSpeed * totalItems, + ); + t.pass(); + expect(pendingReads).to.equal(0); + resolve(); + } + }); + return first; + }; + const _demux = demux(construct, "key", { + objectMode: true, + highWaterMark: 1, + }); + _demux.on("error", err => { + reject(err); + }); + + const input = [ + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "c", mapped: [] }, + { key: "c", mapped: [] }, + { key: "c", mapped: [] }, + { key: "b", mapped: [] }, + ]; + + let pendingReads = input.length; + const totalItems = input.length; + const start = performance.now(); + for (const item of input) { + if (!_demux.write(item)) { + await new Promise(_resolve => { + _demux.once("drain", () => { + _resolve(); + }); + }); + } + } + }); +}); + +test("demux() should emit drain event when second stream in pipeline is bottleneck", t => { + t.plan(5); + const highWaterMark = 3; + return new Promise(async (resolve, reject) => { + interface Chunk { + key: string; + mapped: number[]; + } + const sink = new Writable({ + objectMode: true, + write(chunk, encoding, cb) { + expect(chunk.mapped).to.deep.equal([1, 2]); + t.pass(); + cb(); + if (pendingReads === 0) { + resolve(); + } + }, + }); + + const construct = (destKey: string) => { + const first = map( + (chunk: Chunk) => { + expect(first._readableState.length).to.be.at.most(2); + chunk.mapped.push(1); + return chunk; + }, + { objectMode: true, highWaterMark: 2 }, + ); + + const second = map( + async (chunk: Chunk) => { + await sleep(100); + chunk.mapped.push(2); + expect(second._writableState.length).to.be.equal(1); + pendingReads--; + return chunk; + }, + { objectMode: true, highWaterMark: 1 }, + ); + + first.pipe(second).pipe(sink); + return first; + }; + + const _demux = demux(construct, "key", { + objectMode: true, + highWaterMark, + }); + _demux.on("error", err => { + reject(); + }); + + _demux.on("drain", () => { + expect(_demux._writableState.length).to.be.equal(0); + t.pass(); + }); + + const input = [ + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + { key: "a", mapped: [] }, + ]; + let pendingReads = input.length; + + input.forEach(item => { + _demux.write(item); + }); + }); +}); diff --git a/tests/duplex.spec.ts b/tests/duplex.spec.ts new file mode 100644 index 0000000..e5fafd7 --- /dev/null +++ b/tests/duplex.spec.ts @@ -0,0 +1,28 @@ +import * as cp from "child_process"; +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { duplex } from "../src"; + +test.cb( + "duplex() combines a writable and readable stream into a ReadWrite stream", + t => { + t.plan(1); + const source = new Readable(); + const catProcess = cp.exec("cat"); + let out = ""; + source + .pipe(duplex(catProcess.stdin!, catProcess.stdout!)) + .on("data", chunk => (out += chunk)) + .on("error", t.end) + .on("end", () => { + expect(out).to.equal("abcdef"); + t.pass(); + t.end(); + }); + source.push("ab"); + source.push("cd"); + source.push("ef"); + source.push(null); + }, +); diff --git a/tests/filter.spec.ts b/tests/filter.spec.ts new file mode 100644 index 0000000..badfda7 --- /dev/null +++ b/tests/filter.spec.ts @@ -0,0 +1,116 @@ +import test from "ava"; +import { expect } from "chai"; +import { Readable } from "stream"; +import { filter } from "../src"; + +test.cb("filter() filters elements synchronously", t => { + t.plan(2); + const source = new Readable({ objectMode: true }); + const expectedElements = ["a", "c"]; + let i = 0; + source + .pipe( + filter((element: string) => element !== "b", { + readableObjectMode: true, + writableObjectMode: true, + }), + ) + .on("data", (element: string) => { + expect(element).to.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); +}); + +test.cb("filter() filters elements asynchronously", t => { + t.plan(2); + const source = new Readable({ objectMode: true }); + const expectedElements = ["a", "c"]; + let i = 0; + source + .pipe( + filter( + async (element: string) => { + await Promise.resolve(); + return element !== "b"; + }, + { readableObjectMode: true, writableObjectMode: true }, + ), + ) + .on("data", (element: string) => { + expect(element).to.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); +}); + +test.cb.skip("filter() emits errors during synchronous filtering", t => { + t.plan(2); + const source = new Readable({ objectMode: true }); + source + .pipe( + filter( + (element: string) => { + if (element !== "a") { + throw new Error("Failed filtering"); + } + return true; + }, + { readableObjectMode: true, writableObjectMode: true }, + ), + ) + .resume() + .on("error", err => { + expect(err.message).to.equal("Failed filtering"); + t.pass(); + }) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); +}); + +test.cb.skip("filter() emits errors during asynchronous filtering", t => { + t.plan(2); + const source = new Readable({ objectMode: true }); + source + .pipe( + filter( + async (element: string) => { + await Promise.resolve(); + if (element !== "a") { + throw new Error("Failed filtering"); + } + return true; + }, + { readableObjectMode: true, writableObjectMode: true }, + ), + ) + .resume() + .on("error", err => { + expect(err.message).to.equal("Failed filtering"); + t.pass(); + }) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); +}); diff --git a/tests/flatMap.spec.ts b/tests/flatMap.spec.ts new file mode 100644 index 0000000..84cebfb --- /dev/null +++ b/tests/flatMap.spec.ts @@ -0,0 +1,100 @@ +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { flatMap } from "../src"; + +test.cb("flatMap() maps elements synchronously", t => { + t.plan(6); + const source = new Readable({ objectMode: true }); + const expectedElements = ["a", "A", "b", "B", "c", "C"]; + let i = 0; + source + .pipe(flatMap((element: string) => [element, element.toUpperCase()])) + .on("data", (element: string) => { + expect(element).to.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); +}); + +test.cb("flatMap() maps elements asynchronously", t => { + t.plan(6); + const source = new Readable({ objectMode: true }); + const expectedElements = ["a", "A", "b", "B", "c", "C"]; + let i = 0; + source + .pipe( + flatMap(async (element: string) => { + await Promise.resolve(); + return [element, element.toUpperCase()]; + }), + ) + .on("data", (element: string) => { + expect(element).to.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); +}); + +test.cb.skip("flatMap() emits errors during synchronous mapping", t => { + t.plan(2); + const source = new Readable({ objectMode: true }); + source + .pipe( + flatMap((element: string) => { + if (element !== "a") { + throw new Error("Failed mapping"); + } + return [element, element.toUpperCase()]; + }), + ) + .resume() + .on("error", err => { + expect(err.message).to.equal("Failed mapping"); + t.pass(); + }) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); +}); + +test.cb.skip("flatMap() emits errors during asynchronous mapping", t => { + t.plan(2); + const source = new Readable({ objectMode: true }); + source + .pipe( + flatMap(async (element: string) => { + await Promise.resolve(); + if (element !== "a") { + throw new Error("Failed mapping"); + } + return [element, element.toUpperCase()]; + }), + ) + .resume() + .on("error", err => { + expect(err.message).to.equal("Failed mapping"); + t.pass(); + }) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); +}); diff --git a/tests/fromArray.spec.ts b/tests/fromArray.spec.ts new file mode 100644 index 0000000..3e4c93e --- /dev/null +++ b/tests/fromArray.spec.ts @@ -0,0 +1,45 @@ +import test from "ava"; +import { expect } from "chai"; +import { fromArray } from "../src"; + +test.cb("fromArray() streams array elements in flowing mode", t => { + t.plan(3); + const elements = ["a", "b", "c"]; + const stream = fromArray(elements); + let i = 0; + stream + .on("data", (element: string) => { + expect(element).to.equal(elements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); +}); + +test.cb("fromArray() ends immediately if there are no array elements", t => { + t.plan(0); + fromArray([]) + .on("data", () => t.fail()) + .on("error", t.end) + .on("end", t.end); +}); + +test.cb("fromArray() streams array elements in paused mode", t => { + t.plan(3); + const elements = ["a", "b", "c"]; + const stream = fromArray(elements); + let i = 0; + stream + .on("readable", () => { + let element = stream.read(); + while (element !== null) { + expect(element).to.equal(elements[i]); + t.pass(); + i++; + element = stream.read(); + } + }) + .on("error", t.end) + .on("end", t.end); +}); diff --git a/tests/join.spec.ts b/tests/join.spec.ts new file mode 100644 index 0000000..6b0be52 --- /dev/null +++ b/tests/join.spec.ts @@ -0,0 +1,56 @@ +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { join } from "../src"; + +test.cb("join() joins chunks using the specified separator", t => { + t.plan(9); + const source = new Readable({ objectMode: true }); + const expectedParts = ["ab|", "|", "c|d", "|", "|", "|", "e", "|", "|f|"]; + let i = 0; + source + .pipe(join("|")) + .on("data", part => { + expect(part).to.equal(expectedParts[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("ab|"); + source.push("c|d"); + source.push("|"); + source.push("e"); + source.push("|f|"); + source.push(null); +}); + +test.cb( + "join() joins chunks using the specified separator without breaking up multi-byte characters " + + "spanning multiple chunks", + t => { + t.plan(5); + const source = new Readable({ objectMode: true }); + const expectedParts = ["ø", "|", "ö", "|", "一"]; + let i = 0; + source + .pipe(join("|")) + .on("data", part => { + expect(part).to.equal(expectedParts[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push(Buffer.from("ø").slice(0, 1)); // 2-byte character spanning two chunks + source.push(Buffer.from("ø").slice(1, 2)); + source.push(Buffer.from("ö").slice(0, 1)); // 2-byte character spanning two chunks + source.push(Buffer.from("ö").slice(1, 2)); + source.push(Buffer.from("一").slice(0, 1)); // 3-byte character spanning three chunks + source.push(Buffer.from("一").slice(1, 2)); + source.push(Buffer.from("一").slice(2, 3)); + source.push(null); + }, +); diff --git a/tests/last.spec.ts b/tests/last.spec.ts new file mode 100644 index 0000000..033c9d8 --- /dev/null +++ b/tests/last.spec.ts @@ -0,0 +1,15 @@ +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { last } from "../src"; + +test("last() resolves to the last chunk streamed by the given readable stream", async t => { + const source = new Readable({ objectMode: true }); + const lastPromise = last(source); + source.push("ab"); + source.push("cd"); + source.push("ef"); + source.push(null); + const lastChunk = await lastPromise; + expect(lastChunk).to.equal("ef"); +}); diff --git a/tests/map.spec.ts b/tests/map.spec.ts new file mode 100644 index 0000000..35c8e84 --- /dev/null +++ b/tests/map.spec.ts @@ -0,0 +1,51 @@ +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { map } from "../src"; + +test.cb("map() maps elements synchronously", t => { + t.plan(3); + const source = new Readable({ objectMode: true }); + const mapStream = map((element: string) => element.toUpperCase()); + const expectedElements = ["A", "B", "C"]; + let i = 0; + source + .pipe(mapStream) + .on("data", (element: string) => { + expect(element).to.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); +}); + +test.cb("map() maps elements asynchronously", t => { + t.plan(3); + const source = new Readable({ objectMode: true }); + const mapStream = map(async (element: string) => { + await Promise.resolve(); + return element.toUpperCase(); + }); + const expectedElements = ["A", "B", "C"]; + let i = 0; + source + .pipe(mapStream) + .on("data", (element: string) => { + expect(element).to.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); +}); diff --git a/tests/merge.spec.ts b/tests/merge.spec.ts new file mode 100644 index 0000000..dbbfd79 --- /dev/null +++ b/tests/merge.spec.ts @@ -0,0 +1,60 @@ +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { merge } from "../src"; + +test.cb( + "merge() merges multiple readable streams in chunk arrival order", + t => { + t.plan(6); + const source1 = new Readable({ objectMode: true, read: () => ({}) }); + const source2 = new Readable({ objectMode: true, read: () => ({}) }); + const expectedElements = ["a", "d", "b", "e", "c", "f"]; + let i = 0; + merge(source1, source2) + .on("data", (element: string) => { + expect(element).to.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source1.push("a"); + setTimeout(() => source2.push("d"), 10); + setTimeout(() => source1.push("b"), 20); + setTimeout(() => source2.push("e"), 30); + setTimeout(() => source1.push("c"), 40); + setTimeout(() => source2.push("f"), 50); + setTimeout(() => source2.push(null), 60); + setTimeout(() => source1.push(null), 70); + }, +); + +test.cb("merge() merges a readable stream", t => { + t.plan(3); + const source = new Readable({ objectMode: true, read: () => ({}) }); + const expectedElements = ["a", "b", "c"]; + let i = 0; + merge(source) + .on("data", (element: string) => { + expect(element).to.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); +}); + +test.cb("merge() merges an empty list of readable streams", t => { + t.plan(0); + merge() + .on("data", () => t.pass()) + .on("error", t.end) + .on("end", t.end); +}); diff --git a/tests/parallelMap.spec.ts b/tests/parallelMap.spec.ts new file mode 100644 index 0000000..dff719a --- /dev/null +++ b/tests/parallelMap.spec.ts @@ -0,0 +1,77 @@ +import { Readable } from "stream"; +import { performance } from "perf_hooks"; +import test from "ava"; +import { expect } from "chai"; +import { parallelMap } from "../src"; +import { sleep } from "../src/helpers"; + +test.cb("parallelMap() parallel mapping", t => { + t.plan(6); + const offset = 50; + const source = new Readable({ objectMode: true }); + const expectedElements = [ + "a_processed", + "b_processed", + "c_processed", + "d_processed", + "e_processed", + "f_processed", + ]; + interface IPerfData { + start: number; + output?: string; + finish?: number; + } + const orderedResults: IPerfData[] = []; + source + .pipe( + parallelMap(async (data: any) => { + const perfData: IPerfData = { start: performance.now() }; + const c = data + "_processed"; + perfData.output = c; + await sleep(offset); + perfData.finish = performance.now(); + orderedResults.push(perfData); + return c; + }, 2), + ) + .on("data", (element: string) => { + t.true(expectedElements.includes(element)); + }) + .on("error", t.end) + .on("end", async () => { + expect(orderedResults[0].finish).to.be.lessThan( + orderedResults[2].start, + ); + expect(orderedResults[1].finish).to.be.lessThan( + orderedResults[3].start, + ); + expect(orderedResults[2].finish).to.be.lessThan( + orderedResults[4].start, + ); + expect(orderedResults[3].finish).to.be.lessThan( + orderedResults[5].start, + ); + expect(orderedResults[0].start).to.be.lessThan( + orderedResults[2].start + offset, + ); + expect(orderedResults[1].start).to.be.lessThan( + orderedResults[3].start + offset, + ); + expect(orderedResults[2].start).to.be.lessThan( + orderedResults[4].start + offset, + ); + expect(orderedResults[3].start).to.be.lessThan( + orderedResults[5].start + offset, + ); + t.end(); + }); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push("d"); + source.push("e"); + source.push("f"); + source.push(null); +}); diff --git a/tests/parse.spec.ts b/tests/parse.spec.ts new file mode 100644 index 0000000..d9aebbb --- /dev/null +++ b/tests/parse.spec.ts @@ -0,0 +1,40 @@ +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { parse } from "../src"; + +test.cb("parse() parses the streamed elements as JSON", t => { + t.plan(3); + const source = new Readable({ objectMode: true }); + const expectedElements = ["abc", {}, []]; + let i = 0; + source + .pipe(parse()) + .on("data", part => { + expect(part).to.deep.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push('"abc"'); + source.push("{}"); + source.push("[]"); + source.push(null); +}); + +test.cb("parse() emits errors on invalid JSON", t => { + t.plan(2); + const source = new Readable({ objectMode: true }); + source + .pipe(parse()) + .resume() + .on("error", () => t.pass()) + .on("end", t.end); + + source.push("{}"); + source.push({}); + source.push([]); + source.push(null); +}); diff --git a/tests/rate.spec.ts b/tests/rate.spec.ts new file mode 100644 index 0000000..1c26554 --- /dev/null +++ b/tests/rate.spec.ts @@ -0,0 +1,89 @@ +import { Readable } from "stream"; +import { performance } from "perf_hooks"; +import test from "ava"; +import { expect } from "chai"; +import { rate } from "../src"; + +test.cb("rate() sends data at a rate of 150", t => { + t.plan(5); + const targetRate = 150; + const source = new Readable({ objectMode: true }); + const expectedElements = ["a", "b", "c", "d", "e"]; + const start = performance.now(); + let i = 0; + + source + .pipe(rate(targetRate)) + .on("data", (element: string[]) => { + const currentRate = (i / (performance.now() - start)) * 1000; + expect(element).to.deep.equal(expectedElements[i]); + expect(currentRate).lessThan(targetRate); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push("d"); + source.push("e"); + source.push(null); +}); + +test.cb("rate() sends data at a rate of 50", t => { + t.plan(5); + const targetRate = 50; + const source = new Readable({ objectMode: true }); + const expectedElements = ["a", "b", "c", "d", "e"]; + const start = performance.now(); + let i = 0; + + source + .pipe(rate(targetRate)) + .on("data", (element: string[]) => { + const currentRate = (i / (performance.now() - start)) * 1000; + expect(element).to.deep.equal(expectedElements[i]); + expect(currentRate).lessThan(targetRate); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push("d"); + source.push("e"); + source.push(null); +}); + +test.cb("rate() sends data at a rate of 1", t => { + t.plan(5); + const targetRate = 1; + const source = new Readable({ objectMode: true }); + const expectedElements = ["a", "b", "c", "d", "e"]; + const start = performance.now(); + let i = 0; + + source + .pipe(rate(targetRate)) + .on("data", (element: string[]) => { + const currentRate = (i / (performance.now() - start)) * 1000; + expect(element).to.deep.equal(expectedElements[i]); + expect(currentRate).lessThan(targetRate); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push("d"); + source.push("e"); + source.push(null); +}); diff --git a/tests/reduce.spec.ts b/tests/reduce.spec.ts new file mode 100644 index 0000000..8d504db --- /dev/null +++ b/tests/reduce.spec.ts @@ -0,0 +1,98 @@ +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { reduce } from "../src"; + +test.cb("reduce() reduces elements synchronously", t => { + t.plan(1); + const source = new Readable({ objectMode: true }); + const expectedValue = 6; + source + .pipe(reduce((acc: number, element: string) => acc + element.length, 0)) + .on("data", (element: string) => { + expect(element).to.equal(expectedValue); + t.pass(); + }) + .on("error", t.end) + .on("end", t.end); + + source.push("ab"); + source.push("cd"); + source.push("ef"); + source.push(null); +}); + +test.cb("reduce() reduces elements asynchronously", t => { + t.plan(1); + const source = new Readable({ objectMode: true }); + const expectedValue = 6; + source + .pipe( + reduce(async (acc: number, element: string) => { + await Promise.resolve(); + return acc + element.length; + }, 0), + ) + .on("data", (element: string) => { + expect(element).to.equal(expectedValue); + t.pass(); + }) + .on("error", t.end) + .on("end", t.end); + + source.push("ab"); + source.push("cd"); + source.push("ef"); + source.push(null); +}); + +test.cb.skip("reduce() emits errors during synchronous reduce", t => { + t.plan(2); + const source = new Readable({ objectMode: true }); + source + .pipe( + reduce((acc: number, element: string) => { + if (element !== "ab") { + throw new Error("Failed reduce"); + } + return acc + element.length; + }, 0), + ) + .resume() + .on("error", err => { + expect(err.message).to.equal("Failed reduce"); + t.pass(); + }) + .on("end", t.end); + + source.push("ab"); + source.push("cd"); + source.push("ef"); + source.push(null); +}); + +test.cb.skip("reduce() emits errors during asynchronous reduce", t => { + t.plan(2); + const source = new Readable({ objectMode: true }); + source + .pipe( + reduce(async (acc: number, element: string) => { + await Promise.resolve(); + if (element !== "ab") { + throw new Error("Failed mapping"); + } + return acc + element.length; + }, 0), + ) + .resume() + .on("error", err => { + expect(err.message).to.equal("Failed mapping"); + t.pass(); + }) + .on("end", t.end); + + source.push("ab"); + source.push("cd"); + source.push("ef"); + source.push(null); +}); diff --git a/tests/replace.spec.ts b/tests/replace.spec.ts new file mode 100644 index 0000000..5829f8e --- /dev/null +++ b/tests/replace.spec.ts @@ -0,0 +1,80 @@ +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { replace } from "../src"; + +test.cb( + "replace() replaces occurrences of the given string in the streamed elements with the specified " + + "replacement string", + t => { + t.plan(3); + const source = new Readable({ objectMode: true }); + const expectedElements = ["abc", "xyf", "ghi"]; + let i = 0; + source + .pipe(replace("de", "xy")) + .on("data", part => { + expect(part).to.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("abc"); + source.push("def"); + source.push("ghi"); + source.push(null); + }, +); + +test.cb( + "replace() replaces occurrences of the given regular expression in the streamed elements with " + + "the specified replacement string", + t => { + t.plan(3); + const source = new Readable({ objectMode: true }); + const expectedElements = ["abc", "xyz", "ghi"]; + let i = 0; + source + .pipe(replace(/^def$/, "xyz")) + .on("data", part => { + expect(part).to.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("abc"); + source.push("def"); + source.push("ghi"); + source.push(null); + }, +); + +test.cb( + "replace() replaces occurrences of the given multi-byte character even if it spans multiple chunks", + t => { + t.plan(3); + const source = new Readable({ objectMode: true }); + const expectedElements = ["ø", "O", "a"]; + let i = 0; + source + .pipe(replace("ö", "O")) + .on("data", part => { + expect(part).to.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push(Buffer.from("ø").slice(0, 1)); // 2-byte character spanning two chunks + source.push(Buffer.from("ø").slice(1, 2)); + source.push(Buffer.from("ö").slice(0, 1)); // 2-byte character spanning two chunks + source.push(Buffer.from("ö").slice(1, 2)); + source.push("a"); + source.push(null); + }, +); diff --git a/tests/split.spec.ts b/tests/split.spec.ts new file mode 100644 index 0000000..1819e2b --- /dev/null +++ b/tests/split.spec.ts @@ -0,0 +1,98 @@ +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { split } from "../src"; + +test.cb("split() splits chunks using the default separator (\\n)", t => { + t.plan(5); + const source = new Readable({ objectMode: true }); + const expectedParts = ["ab", "c", "d", "ef", ""]; + let i = 0; + source + .pipe(split()) + .on("data", part => { + expect(part).to.equal(expectedParts[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("ab\n"); + source.push("c"); + source.push("\n"); + source.push("d"); + source.push("\nef\n"); + source.push(null); +}); + +test.cb("split() splits chunks using the specified separator", t => { + t.plan(6); + const source = new Readable({ objectMode: true }); + const expectedParts = ["ab", "c", "d", "e", "f", ""]; + let i = 0; + source + .pipe(split("|")) + .on("data", (part: string) => { + expect(part).to.equal(expectedParts[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("ab|"); + source.push("c|d"); + source.push("|"); + source.push("e"); + source.push("|f|"); + source.push(null); +}); + +test.cb( + "split() splits utf8 encoded buffers using the specified separator", + t => { + t.plan(3); + const expectedElements = ["a", "b", "c"]; + let i = 0; + const through = split(","); + const buf = Buffer.from("a,b,c"); + through + .on("data", element => { + expect(element).to.equal(expectedElements[i]); + i++; + t.pass(); + }) + .on("error", t.end) + .on("end", t.end); + + for (let j = 0; j < buf.length; ++j) { + through.write(buf.slice(j, j + 1)); + } + through.end(); + }, +); + +test.cb( + "split() splits utf8 encoded buffers with multi-byte characters using the specified separator", + t => { + t.plan(3); + const expectedElements = ["一", "一", "一"]; + let i = 0; + const through = split(","); + const buf = Buffer.from("一,一,一"); // Those spaces are multi-byte utf8 characters (code: 4E00) + through + .on("data", element => { + expect(element).to.equal(expectedElements[i]); + i++; + t.pass(); + }) + .on("error", t.end) + .on("end", t.end); + + for (let j = 0; j < buf.length; ++j) { + through.write(buf.slice(j, j + 1)); + } + through.end(); + }, +); diff --git a/tests/stringify.spec.ts b/tests/stringify.spec.ts new file mode 100644 index 0000000..7452e99 --- /dev/null +++ b/tests/stringify.spec.ts @@ -0,0 +1,61 @@ +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { stringify } from "../src"; + +test.cb("stringify() stringifies the streamed elements as JSON", t => { + t.plan(4); + const source = new Readable({ objectMode: true }); + const expectedElements = [ + '"abc"', + "0", + '{"a":"a","b":"b","c":"c"}', + '["a","b","c"]', + ]; + let i = 0; + source + .pipe(stringify()) + .on("data", part => { + expect(part).to.deep.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("abc"); + source.push(0); + source.push({ a: "a", b: "b", c: "c" }); + source.push(["a", "b", "c"]); + source.push(null); +}); + +test.cb( + "stringify() stringifies the streamed elements as pretty-printed JSON", + t => { + t.plan(4); + const source = new Readable({ objectMode: true }); + const expectedElements = [ + '"abc"', + "0", + '{\n "a": "a",\n "b": "b",\n "c": "c"\n}', + '[\n "a",\n "b",\n "c"\n]', + ]; + let i = 0; + source + .pipe(stringify({ pretty: true })) + .on("data", part => { + expect(part).to.deep.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("abc"); + source.push(0); + source.push({ a: "a", b: "b", c: "c" }); + source.push(["a", "b", "c"]); + source.push(null); + }, +); diff --git a/tests/unbatch.spec.ts b/tests/unbatch.spec.ts new file mode 100644 index 0000000..d48b1b9 --- /dev/null +++ b/tests/unbatch.spec.ts @@ -0,0 +1,26 @@ +import { Readable } from "stream"; +import test from "ava"; +import { expect } from "chai"; +import { unbatch, batch } from "../src"; + +test.cb("unbatch() unbatches", t => { + t.plan(3); + const source = new Readable({ objectMode: true }); + const expectedElements = ["a", "b", "c"]; + let i = 0; + source + .pipe(batch(3)) + .pipe(unbatch()) + .on("data", (element: string) => { + expect(element).to.equal(expectedElements[i]); + t.pass(); + i++; + }) + .on("error", t.end) + .on("end", t.end); + + source.push("a"); + source.push("b"); + source.push("c"); + source.push(null); +}); diff --git a/tsconfig.json b/tsconfig.json index 8df64fa..3a85e23 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,18 +1,21 @@ { - "compilerOptions": { - "noImplicitAny": true, - "strictNullChecks": true, - "noImplicitReturns": true, - "noUnusedLocals": true, - "noImplicitThis": true, - "forceConsistentCasingInFileNames": true, - "suppressImplicitAnyIndexErrors": true, - "outDir": "./dist", - "module": "commonjs", - "target": "es5", - "lib": ["es2016"], - "sourceMap": true, - "declaration": true - }, - "include": ["src/**/*.ts"] + "compilerOptions": { + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitReturns": true, + "noUnusedLocals": false, + "noImplicitThis": true, + "forceConsistentCasingInFileNames": true, + "suppressImplicitAnyIndexErrors": true, + "outDir": "./dist", + "module": "commonjs" + }, + "target": "es5", + "lib": [ + "es2016" + ], + "sourceMap": true, + "declaration": true, + "include": ["src/**/*"], + "exclude": ["tests", "node_modules"] } diff --git a/tslint.json b/tslint.json index b1c37a5..03b2e44 100644 --- a/tslint.json +++ b/tslint.json @@ -7,8 +7,9 @@ "rules": { "no-console": false, "no-implicit-dependencies": [true, "dev"], - "prettier": true, + "prettier": [true, ".prettierrc"], "ordered-imports": false, - "interface-name": false + "interface-name": false, + "object-literal-sort-keys": false } } diff --git a/yarn.lock b/yarn.lock index 082e22d..ba435ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20,13 +20,13 @@ "@babel/plugin-transform-exponentiation-operator" "^7.0.0" "@babel/plugin-transform-modules-commonjs" "^7.0.0" -"@ava/babel-preset-transform-test-files@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@ava/babel-preset-transform-test-files/-/babel-preset-transform-test-files-4.0.0.tgz#95d426f5982f934567ae5a21e43eac0a463d6feb" - integrity sha512-V9hYHA/ZLb4I8imrrG8PT0mzgThjWWmahPV+mrQUZobVnsekBUDrf0JsfXVm4guS3binWxWn+MmQt+V81hTizA== +"@ava/babel-preset-transform-test-files@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@ava/babel-preset-transform-test-files/-/babel-preset-transform-test-files-5.0.0.tgz#e06fc762069511e597531cc1120e22216aac6981" + integrity sha512-rqgyQwkT0+j2JzYP51dOv80u33rzAvjBtXRzUON+7+6u26mjoudRXci2+1s18rat8r4uOlZfbzm114YS6pwmYw== dependencies: "@ava/babel-plugin-throws-helper" "^3.0.0" - babel-plugin-espower "^3.0.0" + babel-plugin-espower "^3.0.1" "@ava/write-file-atomic@^2.2.0": version "2.2.0" @@ -37,41 +37,41 @@ imurmurhash "^0.1.4" slide "^1.1.5" -"@babel/code-frame@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" - integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" + integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== dependencies: "@babel/highlight" "^7.0.0" -"@babel/core@^7.1.5": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.1.6.tgz#3733cbee4317429bc87c62b29cf8587dba7baeb3" - integrity sha512-Hz6PJT6e44iUNpAn8AoyAs6B3bl60g7MJQaI0rZEar6ECzh6+srYO1xlIdssio34mPaUtAb1y+XlkkSJzok3yw== +"@babel/core@^7.4.0": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.5.tgz#17b2686ef0d6bc58f963dddd68ab669755582c30" + integrity sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg== dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.1.6" - "@babel/helpers" "^7.1.5" - "@babel/parser" "^7.1.6" - "@babel/template" "^7.1.2" - "@babel/traverse" "^7.1.6" - "@babel/types" "^7.1.6" + "@babel/code-frame" "^7.5.5" + "@babel/generator" "^7.5.5" + "@babel/helpers" "^7.5.5" + "@babel/parser" "^7.5.5" + "@babel/template" "^7.4.4" + "@babel/traverse" "^7.5.5" + "@babel/types" "^7.5.5" convert-source-map "^1.1.0" debug "^4.1.0" json5 "^2.1.0" - lodash "^4.17.10" + lodash "^4.17.13" resolve "^1.3.2" semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.0.0", "@babel/generator@^7.1.5", "@babel/generator@^7.1.6": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.1.6.tgz#001303cf87a5b9d093494a4bf251d7b5d03d3999" - integrity sha512-brwPBtVvdYdGxtenbQgfCdDPmtkmUBZPjUoK5SXJEBuHaA5BCubh9ly65fzXz7R6o5rA76Rs22ES8Z+HCc0YIQ== +"@babel/generator@^7.0.0", "@babel/generator@^7.4.0", "@babel/generator@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf" + integrity sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ== dependencies: - "@babel/types" "^7.1.6" + "@babel/types" "^7.5.5" jsesc "^2.5.1" - lodash "^4.17.10" + lodash "^4.17.13" source-map "^0.5.0" trim-right "^1.0.1" @@ -121,29 +121,29 @@ dependencies: "@babel/types" "^7.0.0" -"@babel/helper-module-transforms@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.1.0.tgz#470d4f9676d9fad50b324cdcce5fbabbc3da5787" - integrity sha512-0JZRd2yhawo79Rcm4w0LwSMILFmFXjugG3yqf+P/UsKsRS1mJCmMwwlHDlMg7Avr9LrvSpp4ZSULO9r8jpCzcw== +"@babel/helper-module-transforms@^7.4.4": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz#f84ff8a09038dcbca1fd4355661a500937165b4a" + integrity sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/helper-simple-access" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.0.0" - "@babel/template" "^7.1.0" - "@babel/types" "^7.0.0" - lodash "^4.17.10" + "@babel/helper-split-export-declaration" "^7.4.4" + "@babel/template" "^7.4.4" + "@babel/types" "^7.5.5" + lodash "^4.17.13" "@babel/helper-plugin-utils@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== -"@babel/helper-regex@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.0.0.tgz#2c1718923b57f9bbe64705ffe5640ac64d9bdb27" - integrity sha512-TR0/N0NDCcUIUEbqV6dCO+LptmmSQFQ7q70lfcEB4URsjD0E1HzicrwUH+ap6BAQ2jhCX9Q4UqZy4wilujWlkg== +"@babel/helper-regex@^7.4.4": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351" + integrity sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw== dependencies: - lodash "^4.17.10" + lodash "^4.17.13" "@babel/helper-remap-async-to-generator@^7.1.0": version "7.1.0" @@ -164,158 +164,159 @@ "@babel/template" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-split-export-declaration@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz#3aae285c0311c2ab095d997b8c9a94cad547d813" - integrity sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag== +"@babel/helper-split-export-declaration@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" + integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q== dependencies: - "@babel/types" "^7.0.0" + "@babel/types" "^7.4.4" "@babel/helper-wrap-function@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.1.0.tgz#8cf54e9190706067f016af8f75cb3df829cc8c66" - integrity sha512-R6HU3dete+rwsdAfrOzTlE9Mcpk4RjU3aX3gi9grtmugQY0u79X7eogUvfXA5sI81Mfq1cn6AgxihfN33STjJA== + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz#c4e0012445769e2815b55296ead43a958549f6fa" + integrity sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ== dependencies: "@babel/helper-function-name" "^7.1.0" "@babel/template" "^7.1.0" "@babel/traverse" "^7.1.0" - "@babel/types" "^7.0.0" + "@babel/types" "^7.2.0" -"@babel/helpers@^7.1.5": - version "7.1.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.1.5.tgz#68bfc1895d685f2b8f1995e788dbfe1f6ccb1996" - integrity sha512-2jkcdL02ywNBry1YNFAH/fViq4fXG0vdckHqeJk+75fpQ2OH+Az6076tX/M0835zA45E0Cqa6pV5Kiv9YOqjEg== +"@babel/helpers@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.5.5.tgz#63908d2a73942229d1e6685bc2a0e730dde3b75e" + integrity sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g== dependencies: - "@babel/template" "^7.1.2" - "@babel/traverse" "^7.1.5" - "@babel/types" "^7.1.5" + "@babel/template" "^7.4.4" + "@babel/traverse" "^7.5.5" + "@babel/types" "^7.5.5" "@babel/highlight@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" - integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw== + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" + integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ== dependencies: chalk "^2.0.0" esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.2", "@babel/parser@^7.1.6": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.1.6.tgz#16e97aca1ec1062324a01c5a6a7d0df8dd189854" - integrity sha512-dWP6LJm9nKT6ALaa+bnL247GHHMWir3vSlZ2+IHgHgktZQx0L3Uvq2uAWcuzIe+fujRsYWBW2q622C5UvGK9iQ== +"@babel/parser@^7.0.0", "@babel/parser@^7.4.4", "@babel/parser@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b" + integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g== "@babel/plugin-proposal-async-generator-functions@^7.0.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.1.0.tgz#41c1a702e10081456e23a7b74d891922dd1bb6ce" - integrity sha512-Fq803F3Jcxo20MXUSDdmZZXrPe6BWyGcWBPPNB/M7WaUYESKDeKMOGIxEzQOjGSmW/NWb6UaPZrtTB2ekhB/ew== + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz#b289b306669dce4ad20b0252889a15768c9d417e" + integrity sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-remap-async-to-generator" "^7.1.0" - "@babel/plugin-syntax-async-generators" "^7.0.0" + "@babel/plugin-syntax-async-generators" "^7.2.0" "@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.0.0.tgz#9a17b547f64d0676b6c9cecd4edf74a82ab85e7e" - integrity sha512-14fhfoPcNu7itSen7Py1iGN0gEm87hX/B+8nZPqkdmANyyYWYMY2pjA3r8WXbWVKMzfnSNS0xY8GVS0IjXi/iw== + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz#61939744f71ba76a3ae46b5eea18a54c16d22e58" + integrity sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" "@babel/plugin-proposal-optional-catch-binding@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.0.0.tgz#b610d928fe551ff7117d42c8bb410eec312a6425" - integrity sha512-JPqAvLG1s13B/AuoBjdBYvn38RqW6n1TzrQO839/sIpqLpbnXKacsAgpZHzLD83Sm8SDXMkkrAvEnJ25+0yIpw== + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5" + integrity sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.0.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" -"@babel/plugin-syntax-async-generators@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.0.0.tgz#bf0891dcdbf59558359d0c626fdc9490e20bc13c" - integrity sha512-im7ged00ddGKAjcZgewXmp1vxSZQQywuQXe2B1A7kajjZmDeY/ekMPmWr9zJgveSaQH0k7BcGrojQhcK06l0zA== +"@babel/plugin-syntax-async-generators@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz#69e1f0db34c6f5a0cf7e2b3323bf159a76c8cb7f" + integrity sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-object-rest-spread@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.0.0.tgz#37d8fbcaf216bd658ea1aebbeb8b75e88ebc549b" - integrity sha512-5A0n4p6bIiVe5OvQPxBnesezsgFJdHhSs3uFSvaPdMqtsovajLZ+G2vZyvNe10EzJBWWo3AcHGKhAFUxqwp2dw== +"@babel/plugin-syntax-object-rest-spread@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e" + integrity sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-optional-catch-binding@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.0.0.tgz#886f72008b3a8b185977f7cb70713b45e51ee475" - integrity sha512-Wc+HVvwjcq5qBg1w5RG9o9RVzmCaAg/Vp0erHCKpAYV8La6I94o4GQAmFYNmkzoMO6gzoOSulpKeSSz6mPEoZw== +"@babel/plugin-syntax-optional-catch-binding@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz#a94013d6eda8908dfe6a477e7f9eda85656ecf5c" + integrity sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-transform-async-to-generator@^7.0.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.1.0.tgz#109e036496c51dd65857e16acab3bafdf3c57811" - integrity sha512-rNmcmoQ78IrvNCIt/R9U+cixUHeYAzgusTFgIAv+wQb9HJU4szhpDD6e5GCACmj/JP5KxuCwM96bX3L9v4ZN/g== + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz#89a3848a0166623b5bc481164b5936ab947e887e" + integrity sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-remap-async-to-generator" "^7.1.0" "@babel/plugin-transform-dotall-regex@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.0.0.tgz#73a24da69bc3c370251f43a3d048198546115e58" - integrity sha512-00THs8eJxOJUFVx1w8i1MBF4XH4PsAjKjQ1eqN/uCH3YKwP21GCKfrn6YZFZswbOk9+0cw1zGQPHVc1KBlSxig== + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz#361a148bc951444312c69446d76ed1ea8e4450c3" + integrity sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.0.0" - regexpu-core "^4.1.3" + "@babel/helper-regex" "^7.4.4" + regexpu-core "^4.5.4" "@babel/plugin-transform-exponentiation-operator@^7.0.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.1.0.tgz#9c34c2ee7fd77e02779cfa37e403a2e1003ccc73" - integrity sha512-uZt9kD1Pp/JubkukOGQml9tqAeI8NkE98oZnHZ2qHRElmeKCodbTZgOEUtujSCSLhHSBWbzNiFSDIMC4/RBTLQ== + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz#a63868289e5b4007f7054d46491af51435766008" + integrity sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A== dependencies: "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-transform-modules-commonjs@^7.0.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.1.0.tgz#0a9d86451cbbfb29bd15186306897c67f6f9a05c" - integrity sha512-wtNwtMjn1XGwM0AXPspQgvmE6msSJP15CX2RVfpTSTNPLhKhaOjaIfBaVfj4iUZ/VrFSodcFedwtPg/NxwQlPA== + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz#425127e6045231360858eeaa47a71d75eded7a74" + integrity sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ== dependencies: - "@babel/helper-module-transforms" "^7.1.0" + "@babel/helper-module-transforms" "^7.4.4" "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-simple-access" "^7.1.0" + babel-plugin-dynamic-import-node "^2.3.0" -"@babel/template@^7.1.0", "@babel/template@^7.1.2": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.1.2.tgz#090484a574fef5a2d2d7726a674eceda5c5b5644" - integrity sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag== +"@babel/template@^7.1.0", "@babel/template@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237" + integrity sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.1.2" - "@babel/types" "^7.1.2" + "@babel/parser" "^7.4.4" + "@babel/types" "^7.4.4" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.5", "@babel/traverse@^7.1.6": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.1.6.tgz#c8db9963ab4ce5b894222435482bd8ea854b7b5c" - integrity sha512-CXedit6GpISz3sC2k2FsGCUpOhUqKdyL0lqNrImQojagnUMXf8hex4AxYFRuMkNGcvJX5QAFGzB5WJQmSv8SiQ== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb" + integrity sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ== dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.1.6" + "@babel/code-frame" "^7.5.5" + "@babel/generator" "^7.5.5" "@babel/helper-function-name" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.0.0" - "@babel/parser" "^7.1.6" - "@babel/types" "^7.1.6" + "@babel/helper-split-export-declaration" "^7.4.4" + "@babel/parser" "^7.5.5" + "@babel/types" "^7.5.5" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.10" + lodash "^4.17.13" -"@babel/types@^7.0.0", "@babel/types@^7.1.2", "@babel/types@^7.1.5", "@babel/types@^7.1.6": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.1.6.tgz#0adb330c3a281348a190263aceb540e10f04bcce" - integrity sha512-DMiUzlY9DSjVsOylJssxLHSgj6tWM9PRFJOGW/RaOglVOK9nzTxoOMfTfRQXGUCUQ/HmlG2efwC+XqUEJ5ay4w== +"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a" + integrity sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw== dependencies: esutils "^2.0.2" - lodash "^4.17.10" + lodash "^4.17.13" to-fast-properties "^2.0.0" "@concordance/react@^2.0.0": @@ -325,15 +326,68 @@ dependencies: arrify "^1.0.1" -"@types/chai@^4.1.7": - version "4.1.7" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.7.tgz#1b8e33b61a8c09cbe1f85133071baa0dbf9fa71a" - integrity sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA== +"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.6.0.tgz#ec7670432ae9c8eb710400d112c201a362d83393" + integrity sha512-w4/WHG7C4WWFyE5geCieFJF6MZkbW4VAriol5KlmQXpAQdxvV0p26sqNZOW6Qyw6Y0l9K4g+cHvvczR2sEEpqg== + dependencies: + type-detect "4.0.8" -"@types/node@^10.12.10": - version "10.12.10" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.10.tgz#4fa76e6598b7de3f0cb6ec3abacc4f59e5b3a2ce" - integrity sha512-8xZEYckCbUVgK8Eg7lf5Iy4COKJ5uXlnIOnePN0WUwSQggy9tolM+tDJf7wMOnT/JT/W9xDYIaYggt3mRV2O5w== +"@sinonjs/formatio@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.1.tgz#52310f2f9bcbc67bdac18c94ad4901b95fde267e" + integrity sha512-tsHvOB24rvyvV2+zKMmPkZ7dXX6LSLKZ7aOtXY6Edklp0uRcgGpOsQTTGTcWViFyx4uhWc6GV8QdnALbIbIdeQ== + dependencies: + "@sinonjs/commons" "^1" + "@sinonjs/samsam" "^3.1.0" + +"@sinonjs/samsam@^3.1.0", "@sinonjs/samsam@^3.3.3": + version "3.3.3" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.3.tgz#46682efd9967b259b81136b9f120fd54585feb4a" + integrity sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ== + dependencies: + "@sinonjs/commons" "^1.3.0" + array-from "^2.1.1" + lodash "^4.17.15" + +"@sinonjs/text-encoding@^0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" + integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== + +"@types/chai@^4.1.7": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.0.tgz#2478260021408dec32c123a7cad3414beb811a07" + integrity sha512-zw8UvoBEImn392tLjxoavuonblX/4Yb9ha4KBU10FirCfwgzhKO0dvyJSF9ByxV1xK1r2AgnAi/tvQaLgxQqxA== + +"@types/events@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + +"@types/glob@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" + integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== + dependencies: + "@types/events" "*" + "@types/minimatch" "*" + "@types/node" "*" + +"@types/minimatch@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + +"@types/node@*", "@types/node@^12.7.2": + version "12.7.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.2.tgz#c4e63af5e8823ce9cc3f0b34f7b998c2171f0c44" + integrity sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg== + +"@types/sinon@^7.0.13": + version "7.0.13" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.0.13.tgz#ca039c23a9e27ebea53e0901ef928ea2a1a6d313" + integrity sha512-d7c/C/+H/knZ3L8/cxhicHUiTDxdgap0b/aNJfsmLwFu/iOP17mdgbQsbHA3SJmrzsjD0l3UEE5SN4xxuz5ung== abbrev@1: version "1.1.1" @@ -347,10 +401,10 @@ ansi-align@^2.0.0: dependencies: string-width "^2.0.0" -ansi-escapes@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" - integrity sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw== +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== ansi-regex@^2.0.0: version "2.1.1" @@ -362,15 +416,10 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-regex@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.0.0.tgz#70de791edf021404c3fd615aa89118ae0432e5a9" - integrity sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w== - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== ansi-styles@^3.2.1: version "3.2.1" @@ -400,6 +449,11 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +arg@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.1.tgz#485f8e7c390ce4c5f78257dbea80d4be11feda4c" + integrity sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -422,17 +476,22 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= -array-differ@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" - integrity sha1-7/UuN1gknTO+QCuLuOVkuytdQDE= +array-differ@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-2.1.0.tgz#4b9c1c3f14b906757082925769e8ab904f4801b1" + integrity sha512-KbUpJgx909ZscOc/7CLATBFam7P1Z1QRQInvgT0UztM9Q72aGKCunKASAl7WNW0tnPmPyEMeMhdsfWhfmW037w== array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= -array-union@^1.0.1: +array-from@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" + integrity sha1-z+nYwmYoudxa7MYqn12PHzUsEZU= + +array-union@^1.0.1, array-union@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= @@ -445,9 +504,9 @@ array-uniq@^1.0.1: integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= array-uniq@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-2.0.0.tgz#0009e30306e37a6dd2e2e2480db5316fdade1583" - integrity sha512-O3QZEr+3wDj7otzF7PjNGs6CA3qmYMLvt5xGkjY/V0VxS+ovvqVo/5wKM/OVOAyuX4DTh9H31zE/yKtO66hTkg== + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-2.1.0.tgz#46603d5e28e79bfd02b046fcc1d77c6820bd8e98" + integrity sha512-bdHxtev7FN6+MXI1YFW0Q8mQ8dTJc2S8AMfju+ZR77pbg2yAdVyDlwkaUI7Har0LyOMRFPHrJ9lYdyjZZswdlQ== array-unique@^0.3.2: version "0.3.2" @@ -469,10 +528,10 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= -async-each@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" - integrity sha1-GdOGodntxufByF04iu28xW0zYC0= +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== atob@^2.1.1: version "2.1.2" @@ -480,28 +539,28 @@ atob@^2.1.1: integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== ava@^1.0.0-rc.2: - version "1.0.0-rc.2" - resolved "https://registry.yarnpkg.com/ava/-/ava-1.0.0-rc.2.tgz#a2f63211d2ad1924fa4671ff3b29212b1da656af" - integrity sha512-MxCW+bY+ddID5SrZZHbkuCiwsLup3Dn/bAgnrl6BzHYRI4RF9Yk5zv6S2L1TjqUF1LvRS8RiqZBrD+G6db+fEg== + version "1.4.1" + resolved "https://registry.yarnpkg.com/ava/-/ava-1.4.1.tgz#4a59289e0c9728e492ec3a5be21d1072636be172" + integrity sha512-wKpgOPTL7hJSBWpfbU4SA8rlsTZrph9g9g7qYDV7M6uK1rKeW8oCUJWRwCd8B24S4N0Y5myf6cTEnA66WIk0sA== dependencies: "@ava/babel-preset-stage-4" "^2.0.0" - "@ava/babel-preset-transform-test-files" "^4.0.0" + "@ava/babel-preset-transform-test-files" "^5.0.0" "@ava/write-file-atomic" "^2.2.0" - "@babel/core" "^7.1.5" - "@babel/generator" "^7.1.5" - "@babel/plugin-syntax-async-generators" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.0.0" + "@babel/core" "^7.4.0" + "@babel/generator" "^7.4.0" + "@babel/plugin-syntax-async-generators" "^7.2.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" "@concordance/react" "^2.0.0" - ansi-escapes "^3.1.0" + ansi-escapes "^3.2.0" ansi-styles "^3.2.1" arr-flatten "^1.1.0" array-union "^1.0.1" array-uniq "^2.0.0" arrify "^1.0.0" bluebird "^3.5.3" - chalk "^2.4.1" - chokidar "^2.0.4" + chalk "^2.4.2" + chokidar "^2.1.5" chunkd "^1.0.0" ci-parallel-vars "^1.0.0" clean-stack "^2.0.0" @@ -513,22 +572,22 @@ ava@^1.0.0-rc.2: concordance "^4.0.0" convert-source-map "^1.6.0" currently-unhandled "^0.4.1" - debug "^4.1.0" - del "^3.0.0" + debug "^4.1.1" + del "^4.0.0" dot-prop "^4.2.0" emittery "^0.4.1" empower-core "^1.2.0" equal-length "^1.0.0" escape-string-regexp "^1.0.5" - esm "^3.0.84" + esm "^3.2.20" figures "^2.0.0" find-up "^3.0.0" - get-port "^4.0.0" + get-port "^4.2.0" globby "^7.1.1" ignore-by-default "^1.0.0" import-local "^2.0.0" indent-string "^3.2.0" - is-ci "^1.2.1" + is-ci "^2.0.0" is-error "^2.2.1" is-observable "^1.1.0" is-plain-object "^2.0.4" @@ -540,45 +599,43 @@ ava@^1.0.0-rc.2: lodash.difference "^4.3.0" lodash.flatten "^4.2.0" loud-rejection "^1.2.0" - make-dir "^1.3.0" + make-dir "^2.1.0" matcher "^1.1.1" md5-hex "^2.0.0" meow "^5.0.0" ms "^2.1.1" - multimatch "^2.1.0" + multimatch "^3.0.0" observable-to-promise "^0.5.0" - ora "^3.0.0" - package-hash "^2.0.0" - pkg-conf "^2.1.0" + ora "^3.2.0" + package-hash "^3.0.0" + pkg-conf "^3.0.0" plur "^3.0.1" pretty-ms "^4.0.0" require-precompiled "^0.1.0" resolve-cwd "^2.0.0" slash "^2.0.0" - source-map-support "^0.5.9" - stack-utils "^1.0.1" - strip-ansi "^5.0.0" + source-map-support "^0.5.11" + stack-utils "^1.0.2" + strip-ansi "^5.2.0" strip-bom-buf "^1.0.0" supertap "^1.0.0" - supports-color "^5.5.0" + supports-color "^6.1.0" trim-off-newlines "^1.0.1" trim-right "^1.0.1" unique-temp-dir "^1.0.0" update-notifier "^2.5.0" -babel-code-frame@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" - integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= +babel-plugin-dynamic-import-node@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" + integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" + object.assign "^4.1.0" -babel-plugin-espower@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/babel-plugin-espower/-/babel-plugin-espower-3.0.0.tgz#8dadfa5ec2b9c82e3c4aa0a2d14fbd3ff6d40061" - integrity sha512-f2IUz5kQyrwXnShcv7tvGxf76QkrEl00ENYgd6R0VMrz4xqlwBLZXFs5vse2vehs1Z+T2sXTP3UWX2QxMorzzw== +babel-plugin-espower@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-espower/-/babel-plugin-espower-3.0.1.tgz#180db17126f88e754105b8b5216d21e520a6bd4e" + integrity sha512-Ms49U7VIAtQ/TtcqRbD6UBmJBUCSxiC3+zPc+eGqxKUIFO1lTshyEDRUjhoAbd2rWfwYf3cZ62oXozrd8W6J0A== dependencies: "@babel/generator" "^7.0.0" "@babel/parser" "^7.0.0" @@ -607,14 +664,14 @@ base@^0.11.1: pascalcase "^0.1.1" binary-extensions@^1.0.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14" - integrity sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg== + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== bluebird@^3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" - integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw== + version "3.5.5" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" + integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== boxen@^1.2.1: version "1.3.0" @@ -637,7 +694,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^2.3.0, braces@^2.3.1: +braces@^2.3.1, braces@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== @@ -653,12 +710,12 @@ braces@^2.3.0, braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" -buffer-from@^1.0.0, buffer-from@^1.1.0: +buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== -builtin-modules@^1.0.0, builtin-modules@^1.1.1: +builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= @@ -724,21 +781,10 @@ chai@^4.2.0: pathval "^1.1.0" type-detect "^4.0.5" -chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" - integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" @@ -749,30 +795,29 @@ check-error@^1.0.2: resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= -chokidar@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" - integrity sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ== +chokidar@^2.1.5: + version "2.1.6" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5" + integrity sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g== dependencies: anymatch "^2.0.0" - async-each "^1.0.0" - braces "^2.3.0" + async-each "^1.0.1" + braces "^2.3.2" glob-parent "^3.1.0" - inherits "^2.0.1" + inherits "^2.0.3" is-binary-path "^1.0.0" is-glob "^4.0.0" - lodash.debounce "^4.0.8" - normalize-path "^2.1.1" + normalize-path "^3.0.0" path-is-absolute "^1.0.0" - readdirp "^2.0.0" - upath "^1.0.5" + readdirp "^2.2.1" + upath "^1.1.1" optionalDependencies: - fsevents "^1.2.2" + fsevents "^1.2.7" chownr@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" - integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== + version "1.1.2" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6" + integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A== chunkd@^1.0.0: version "1.0.0" @@ -784,6 +829,11 @@ ci-info@^1.5.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + ci-parallel-vars@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/ci-parallel-vars/-/ci-parallel-vars-1.0.0.tgz#af97729ed1c7381911ca37bcea263d62638701b3" @@ -800,9 +850,9 @@ class-utils@^0.3.5: static-extend "^0.1.1" clean-stack@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.0.0.tgz#301bfa9e8dd2d3d984c0e542f7aa67b996f63e0a" - integrity sha512-VEoL9Qh7I8s8iHnV53DaeWSt8NJ0g3khMfK6NiCPB7H657juhro+cSw2O88uo3bo0c0X5usamtXk0/Of0wXa5A== + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== clean-yaml-object@^0.1.0: version "0.1.0" @@ -821,10 +871,10 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" -cli-spinners@^1.1.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" - integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg== +cli-spinners@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" + integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== cli-truncate@^1.1.0: version "1.1.0" @@ -872,9 +922,9 @@ color-name@1.1.3: integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= commander@^2.12.1: - version "2.19.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" - integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== + version "2.20.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" + integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== common-path-prefix@^1.0.0: version "1.0.0" @@ -882,9 +932,9 @@ common-path-prefix@^1.0.0: integrity sha1-zVL28HEuC6q5fW+XModPIvR3UsA= component-emitter@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" - integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== concat-map@0.0.1: version "0.0.1" @@ -943,9 +993,9 @@ copy-descriptor@^0.1.0: integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-js@^2.0.0: - version "2.5.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" - integrity sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw== + version "2.6.9" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" + integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== core-util-is@~1.0.0: version "1.0.2" @@ -987,17 +1037,24 @@ date-time@^2.1.0: dependencies: time-zone "^1.0.0" -debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: +debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87" - integrity sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg== +debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== dependencies: ms "^2.1.1" @@ -1043,6 +1100,13 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" +define-properties@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" @@ -1065,17 +1129,18 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" -del@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5" - integrity sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU= +del@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" + integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== dependencies: + "@types/glob" "^7.1.1" globby "^6.1.0" - is-path-cwd "^1.0.0" - is-path-in-cwd "^1.0.0" - p-map "^1.1.1" - pify "^3.0.0" - rimraf "^2.2.8" + is-path-cwd "^2.0.0" + is-path-in-cwd "^2.0.0" + p-map "^2.0.0" + pify "^4.0.1" + rimraf "^2.6.3" delegates@^1.0.0: version "1.0.0" @@ -1087,17 +1152,21 @@ detect-libc@^1.0.2: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= -diff@^3.1.0, diff@^3.2.0: +diff@^3.2.0, diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== +diff@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.1.tgz#0c667cb467ebbb5cea7f14f135cc2dba7780a8ff" + integrity sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q== + dir-glob@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" - integrity sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag== + version "2.2.2" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== dependencies: - arrify "^1.0.1" path-type "^3.0.0" dot-prop@^4.1.0, dot-prop@^4.2.0: @@ -1142,7 +1211,7 @@ es6-error@^4.0.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.4, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.4, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -1155,10 +1224,10 @@ eslint-plugin-prettier@^2.2.0: fast-diff "^1.1.1" jest-docblock "^21.0.0" -esm@^3.0.84: - version "3.0.84" - resolved "https://registry.yarnpkg.com/esm/-/esm-3.0.84.tgz#bb108989f4673b32d4f62406869c28eed3815a63" - integrity sha512-SzSGoZc17S7P+12R9cg21Bdb7eybX25RnIeRZ80xZs+VZ3kdQKzqTp2k4hZJjR7p9l0186TTXSgrxzlMDBktlw== +esm@^3.2.20: + version "3.2.25" + resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" + integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== espower-location-detector@^1.0.0: version "1.0.0" @@ -1183,14 +1252,14 @@ espurify@^1.6.0: core-js "^2.0.0" estraverse@^4.0.0, estraverse@^4.1.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== esutils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" - integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== execa@^0.7.0: version "0.7.0" @@ -1296,9 +1365,9 @@ fragment-cache@^0.2.1: map-cache "^0.2.2" fs-minipass@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" - integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ== + version "1.2.6" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" + integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ== dependencies: minipass "^2.2.1" @@ -1307,13 +1376,18 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^1.2.2: - version "1.2.4" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" - integrity sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg== +fsevents@^1.2.7: + version "1.2.9" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" + integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== dependencies: - nan "^2.9.2" - node-pre-gyp "^0.10.0" + nan "^2.12.1" + node-pre-gyp "^0.12.0" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== gauge@~2.7.3: version "2.7.4" @@ -1334,10 +1408,10 @@ get-func-name@^2.0.0: resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= -get-port@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.0.0.tgz#373c85960138ee20027c070e3cb08019fea29816" - integrity sha512-Yy3yNI2oShgbaWg4cmPhWjkZfktEvpKI09aDX4PZzNtlU9obuYrX7x2mumQsrNxlF+Ls7OtMQW/u+X4s896bOQ== +get-port@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.2.0.tgz#e37368b1e863b7629c43c5a323625f95cf24b119" + integrity sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw== get-stream@^3.0.0: version "3.0.0" @@ -1357,10 +1431,10 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: - version "7.1.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" - integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== +glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -1377,9 +1451,9 @@ global-dirs@^0.1.0: ini "^1.3.4" globals@^11.1.0: - version "11.9.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.9.0.tgz#bde236808e987f290768a93d065060d78e6ab249" - integrity sha512-5cJVtyXWH8PiJPVLZzzoIizXx944O4OmRro5MWKx5fT4MgcN7OfaMutPeaTdJCCURwbWdhhcCWcKIffPnmTzBg== + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globby@^6.1.0: version "6.1.0" @@ -1421,23 +1495,21 @@ got@^6.7.1: unzip-response "^2.0.1" url-parse-lax "^1.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.2: - version "4.1.15" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" - integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" + integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -1474,10 +1546,17 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" +hasha@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-3.0.0.tgz#52a32fab8569d41ca69a61ff1a214f8eb7c8bd39" + integrity sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk= + dependencies: + is-stream "^1.0.1" + hosted-git-info@^2.1.4: - version "2.7.1" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" - integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== + version "2.8.4" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546" + integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ== iconv-lite@^0.4.4: version "0.4.24" @@ -1534,10 +1613,10 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +inherits@2, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ini@^1.3.4, ini@~1.3.0: version "1.3.5" @@ -1580,20 +1659,20 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-builtin-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" - integrity sha1-VAVy0096wxGfj3bDDLwbHgN6/74= - dependencies: - builtin-modules "^1.0.0" - -is-ci@^1.0.10, is-ci@^1.2.1: +is-ci@^1.0.10: version "1.2.1" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg== dependencies: ci-info "^1.5.0" +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -1627,9 +1706,9 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: kind-of "^6.0.2" is-error@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-error/-/is-error-2.2.1.tgz#684a96d84076577c98f4cdb40c6d26a5123bf19c" - integrity sha1-aEqW2EB2V3yY9M20DG0mpRI78Zw= + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-error/-/is-error-2.2.2.tgz#c10ade187b3c93510c5470a5567833ee25649843" + integrity sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg== is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" @@ -1668,9 +1747,9 @@ is-glob@^3.1.0: is-extglob "^2.1.0" is-glob@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" - integrity sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A= + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" @@ -1713,17 +1792,17 @@ is-observable@^1.1.0: dependencies: symbol-observable "^1.1.0" -is-path-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" - integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0= +is-path-cwd@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== -is-path-in-cwd@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" - integrity sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ== +is-path-in-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" + integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== dependencies: - is-path-inside "^1.0.0" + is-path-inside "^2.1.0" is-path-inside@^1.0.0: version "1.0.1" @@ -1732,12 +1811,19 @@ is-path-inside@^1.0.0: dependencies: path-is-inside "^1.0.1" +is-path-inside@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" + integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== + dependencies: + path-is-inside "^1.0.2" + is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= -is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: +is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== @@ -1759,7 +1845,7 @@ is-retry-allowed@^1.0.0: resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" integrity sha1-EaBgVotnM5REAz0BJaYaINVk+zQ= -is-stream@^1.0.0, is-stream@^1.1.0: +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -1779,6 +1865,11 @@ is-windows@^1.0.2: resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -1811,20 +1902,15 @@ js-string-escape@^1.0.1: resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" integrity sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8= -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= - js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.10.0, js-yaml@^3.7.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" - integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A== +js-yaml@^3.10.0, js-yaml@^3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -1851,6 +1937,11 @@ json5@^2.1.0: dependencies: minimist "^1.2.0" +just-extend@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc" + integrity sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw== + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -1897,6 +1988,17 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" +load-json-file@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-5.3.0.tgz#4d3c1e01fa1c03ea78a60ac7af932c9ce53403f3" + integrity sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw== + dependencies: + graceful-fs "^4.1.15" + parse-json "^4.0.0" + pify "^4.0.1" + strip-bom "^3.0.0" + type-fest "^0.3.0" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -1928,7 +2030,7 @@ lodash.clonedeepwith@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.clonedeepwith/-/lodash.clonedeepwith-4.5.0.tgz#6ee30573a03a1a60d670a62ef33c10cf1afdbdd4" integrity sha1-buMFc6A6GmDWcKYu8zwQzxr9vdQ= -lodash.debounce@^4.0.3, lodash.debounce@^4.0.8: +lodash.debounce@^4.0.3: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= @@ -1954,14 +2056,14 @@ lodash.islength@^4.0.1: integrity sha1-Tpho1FJXXXUK/9NYyXlUPcIO1Xc= lodash.merge@^4.6.1: - version "4.6.1" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54" - integrity sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ== + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.10: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== +lodash@^4.17.13, lodash@^4.17.15: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== log-symbols@^2.2.0: version "2.2.0" @@ -1970,6 +2072,11 @@ log-symbols@^2.2.0: dependencies: chalk "^2.0.1" +lolex@^4.1.0, lolex@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-4.2.0.tgz#ddbd7f6213ca1ea5826901ab1222b65d714b3cd7" + integrity sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg== + loud-rejection@^1.0.0, loud-rejection@^1.2.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -1984,20 +2091,28 @@ lowercase-keys@^1.0.0: integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== lru-cache@^4.0.1: - version "4.1.4" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.4.tgz#51cc46e8e6d9530771c857e24ccc720ecdbcc031" - integrity sha512-EPstzZ23znHUVLKj+lcXO1KvZkrlw+ZirdwvOmnAnA/1PB4ggyXJ77LRkCqkff+ShQ+cqoxCxLQOh4cKITO5iA== + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== dependencies: pseudomap "^1.0.2" - yallist "^3.0.2" + yallist "^2.1.2" -make-dir@^1.0.0, make-dir@^1.3.0: +make-dir@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== dependencies: pify "^3.0.0" +make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + make-error@^1.1.1: version "1.3.5" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" @@ -2086,7 +2201,7 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -minimatch@^3.0.0, minimatch@^3.0.4: +minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -2111,7 +2226,7 @@ minimist@^1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -minipass@^2.2.1, minipass@^2.3.4: +minipass@^2.2.1, minipass@^2.3.5: version "2.3.5" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== @@ -2119,17 +2234,17 @@ minipass@^2.2.1, minipass@^2.3.4: safe-buffer "^5.1.2" yallist "^3.0.0" -minizlib@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.1.tgz#6734acc045a46e61d596a43bb9d9cd326e19cc42" - integrity sha512-TrfjCjk4jLhcJyGMYymBH6oTXcWjYbUAXTHDbtnWHjZC25h0cdajHuPE1zxb4DVmu8crfh+HwH/WMuyLG0nHBg== +minizlib@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" + integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== dependencies: minipass "^2.2.1" mixin-deep@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" - integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ== + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== dependencies: for-in "^1.0.2" is-extendable "^1.0.1" @@ -2147,24 +2262,24 @@ ms@2.0.0: integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= ms@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -multimatch@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" - integrity sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis= +multimatch@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-3.0.0.tgz#0e2534cc6bc238d9ab67e1b9cd5fcd85a6dbf70b" + integrity sha512-22foS/gqQfANZ3o+W7ST2x25ueHDVNWl/b9OlGcLpy/iKxjCpvcNCM51YCenUi7Mt/jAjjqv8JwZRs8YP5sRjA== dependencies: - array-differ "^1.0.0" - array-union "^1.0.1" - arrify "^1.0.0" - minimatch "^3.0.0" + array-differ "^2.0.3" + array-union "^1.0.2" + arrify "^1.0.1" + minimatch "^3.0.4" -nan@^2.9.2: - version "2.11.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.1.tgz#90e22bccb8ca57ea4cd37cc83d3819b52eea6766" - integrity sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA== +nan@^2.12.1: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== nanomatch@^1.2.9: version "1.2.13" @@ -2184,18 +2299,29 @@ nanomatch@^1.2.9: to-regex "^3.0.1" needle@^2.2.1: - version "2.2.4" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e" - integrity sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA== + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== dependencies: - debug "^2.1.2" + debug "^3.2.6" iconv-lite "^0.4.4" sax "^1.2.4" -node-pre-gyp@^0.10.0: - version "0.10.3" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" - integrity sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A== +nise@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.2.tgz#b6d29af10e48b321b307e10e065199338eeb2652" + integrity sha512-/6RhOUlicRCbE9s+94qCUsyE+pKlVJ5AhIv+jEE7ESKwnbXqulKZ1FYU+XAtHHWE9TinYvAxDUJAb912PwPoWA== + dependencies: + "@sinonjs/formatio" "^3.2.1" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + lolex "^4.1.0" + path-to-regexp "^1.7.0" + +node-pre-gyp@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" + integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== dependencies: detect-libc "^1.0.2" mkdirp "^0.5.1" @@ -2217,12 +2343,12 @@ nopt@^4.0.1: osenv "^0.1.4" normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.4.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" - integrity sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw== + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" - is-builtin-module "^1.0.0" + resolve "^1.10.0" semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" @@ -2233,15 +2359,20 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + npm-bundled@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.5.tgz#3c1732b7ba936b3a10325aef616467c0ccbcc979" - integrity sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g== + version "1.0.6" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" + integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== npm-packlist@^1.1.6: - version "1.1.12" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.12.tgz#22bde2ebc12e72ca482abd67afc51eb49377243a" - integrity sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g== + version "1.4.4" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44" + integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw== dependencies: ignore-walk "^3.0.1" npm-bundled "^1.0.1" @@ -2282,6 +2413,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-keys@^1.0.11, object-keys@^1.0.12: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" @@ -2289,6 +2425,16 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -2318,16 +2464,16 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -ora@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-3.0.0.tgz#8179e3525b9aafd99242d63cc206fd64732741d0" - integrity sha512-LBS97LFe2RV6GJmXBi6OKcETKyklHNMV0xw7BtsVn2MlsgsydyZetSCbCANr+PFLmDyv4KV88nn0eCKza665Mg== +ora@^3.2.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" + integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== dependencies: - chalk "^2.3.1" + chalk "^2.4.2" cli-cursor "^2.1.0" - cli-spinners "^1.1.0" + cli-spinners "^2.0.0" log-symbols "^2.2.0" - strip-ansi "^4.0.0" + strip-ansi "^5.2.0" wcwidth "^1.0.1" os-homedir@^1.0.0: @@ -2361,9 +2507,9 @@ p-limit@^1.1.0: p-try "^1.0.0" p-limit@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec" - integrity sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A== + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2" + integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ== dependencies: p-try "^2.0.0" @@ -2381,10 +2527,10 @@ p-locate@^3.0.0: dependencies: p-limit "^2.0.0" -p-map@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" - integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== p-try@^1.0.0: version "1.0.0" @@ -2392,18 +2538,18 @@ p-try@^1.0.0: integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= p-try@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" - integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ== + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -package-hash@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-2.0.0.tgz#78ae326c89e05a4d813b68601977af05c00d2a0d" - integrity sha1-eK4ybIngWk2BO2hgGXevBcANKg0= +package-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-3.0.0.tgz#50183f2d36c9e3e528ea0a8605dff57ce976f88e" + integrity sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA== dependencies: - graceful-fs "^4.1.11" + graceful-fs "^4.1.15" + hasha "^3.0.0" lodash.flattendeep "^4.4.0" - md5-hex "^2.0.0" release-zalgo "^1.0.0" package-json@^4.0.0: @@ -2425,9 +2571,9 @@ parse-json@^4.0.0: json-parse-better-errors "^1.0.1" parse-ms@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.0.0.tgz#7b3640295100caf3fa0100ccceb56635b62f9d62" - integrity sha512-AddiXFSLLCqj+tCRJ9MrUtHZB4DWojO3tk0NVZ+g5MaMQHF2+p2ktqxuoXyPFLljz/aUK0Nfhd/uGWnhXVXEyA== + version "2.1.0" + resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" + integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== pascalcase@^0.1.1: version "0.1.1" @@ -2449,7 +2595,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-is-inside@^1.0.1: +path-is-inside@^1.0.1, path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= @@ -2459,11 +2605,18 @@ path-key@^2.0.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-parse@^1.0.5: +path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-to-regexp@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" + integrity sha1-Wf3g9DW62suhA6hOnTvGTpa5k30= + dependencies: + isarray "0.0.1" + path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -2486,6 +2639,11 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" @@ -2498,13 +2656,13 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -pkg-conf@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/pkg-conf/-/pkg-conf-2.1.0.tgz#2126514ca6f2abfebd168596df18ba57867f0058" - integrity sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg= +pkg-conf@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-conf/-/pkg-conf-3.1.0.tgz#d9f9c75ea1bae0e77938cde045b276dac7cc69ae" + integrity sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ== dependencies: - find-up "^2.0.0" - load-json-file "^4.0.0" + find-up "^3.0.0" + load-json-file "^5.2.0" pkg-dir@^3.0.0: version "3.0.0" @@ -2514,9 +2672,9 @@ pkg-dir@^3.0.0: find-up "^3.0.0" plur@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/plur/-/plur-3.0.1.tgz#268652d605f816699b42b86248de73c9acd06a7c" - integrity sha512-lJl0ojUynAM1BZn58Pas2WT/TXeC1+bS+UqShl0x9+49AtOn7DixRXVzaC8qrDOIxNDmepKnLuMTH7NQmkX0PA== + version "3.1.1" + resolved "https://registry.yarnpkg.com/plur/-/plur-3.1.1.tgz#60267967866a8d811504fe58f2faaba237546a5b" + integrity sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w== dependencies: irregular-plurals "^2.0.0" @@ -2531,9 +2689,9 @@ prepend-http@^1.0.1: integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= prettier@^1.14.3: - version "1.15.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.2.tgz#d31abe22afa4351efa14c7f8b94b58bb7452205e" - integrity sha512-YgPLFFA0CdKL4Eg2IHtUSjzj/BWgszDHiNQAe0VAIBse34148whfdzLagRL+QiKS+YfK5ftB6X4v/MBw8yCoug== + version "1.18.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" + integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw== pretty-ms@^4.0.0: version "4.0.0" @@ -2543,9 +2701,9 @@ pretty-ms@^4.0.0: parse-ms "^2.0.0" process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== pseudomap@^1.0.2: version "1.0.2" @@ -2597,7 +2755,7 @@ readable-stream@^2.0.2, readable-stream@^2.0.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readdirp@^2.0.0: +readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== @@ -2614,10 +2772,10 @@ redent@^2.0.0: indent-string "^3.0.0" strip-indent "^2.0.0" -regenerate-unicode-properties@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz#107405afcc4a190ec5ed450ecaa00ed0cafa7a4c" - integrity sha512-s5NGghCE4itSlUS+0WUj88G6cfMVMmH8boTPNvABf8od+2dhT9WDlWu8n01raQAJZMOK8Ch6jSexaRO7swd6aw== +regenerate-unicode-properties@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" + integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== dependencies: regenerate "^1.4.0" @@ -2634,22 +2792,22 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexpu-core@^4.1.3: - version "4.2.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.2.0.tgz#a3744fa03806cffe146dea4421a3e73bdcc47b1d" - integrity sha512-Z835VSnJJ46CNBttalHD/dB+Sj2ezmY6Xp38npwU87peK6mqOzOpV8eYktdkLTEkzzD+JsTcxd84ozd8I14+rw== +regexpu-core@^4.5.4: + version "4.5.5" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.5.tgz#aaffe61c2af58269b3e516b61a73790376326411" + integrity sha512-FpI67+ky9J+cDizQUJlIlNZFKual/lUkFr1AG6zOCpwZ9cLrg8UUVakyUQJD7fCDIe9Z2nwTQJNPyonatNmDFQ== dependencies: regenerate "^1.4.0" - regenerate-unicode-properties "^7.0.0" - regjsgen "^0.4.0" - regjsparser "^0.3.0" + regenerate-unicode-properties "^8.1.0" + regjsgen "^0.5.0" + regjsparser "^0.6.0" unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.0.2" + unicode-match-property-value-ecmascript "^1.1.0" registry-auth-token@^3.0.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" - integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ== + version "3.4.0" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.4.0.tgz#d7446815433f5d5ed6431cd5dca21048f66b397e" + integrity sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A== dependencies: rc "^1.1.6" safe-buffer "^5.0.1" @@ -2661,15 +2819,15 @@ registry-url@^3.0.3: dependencies: rc "^1.0.1" -regjsgen@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.4.0.tgz#c1eb4c89a209263f8717c782591523913ede2561" - integrity sha512-X51Lte1gCYUdlwhF28+2YMO0U6WeN0GLpgpA7LK7mbdDnkQYiwvEpmpe0F/cv5L14EbxgrdayAG3JETBv0dbXA== +regjsgen@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" + integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA== -regjsparser@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.3.0.tgz#3c326da7fcfd69fa0d332575a41c8c0cdf588c96" - integrity sha512-zza72oZBBHzt64G7DxdqrOo/30bhHkwMUoT0WqfGu98XLd7N+1tsy5MJ96Bk4MD0y74n629RhmrGW6XlnLLwCA== +regjsparser@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c" + integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ== dependencies: jsesc "~0.5.0" @@ -2717,12 +2875,12 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.3.2: - version "1.8.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" - integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== +resolve@^1.10.0, resolve@^1.3.2: + version "1.12.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" + integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== dependencies: - path-parse "^1.0.5" + path-parse "^1.0.6" restore-cursor@^2.0.0: version "2.0.0" @@ -2737,14 +2895,19 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -rimraf@^2.2.8, rimraf@^2.6.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" - integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w== +rimraf@^2.6.1, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: - glob "^7.0.5" + glob "^7.1.3" -safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.0.1, safe-buffer@^5.1.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -2773,10 +2936,10 @@ semver-diff@^2.0.0: dependencies: semver "^5.0.3" -"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.1: - version "5.6.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" - integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== +"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.1, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== serialize-error@^2.1.0: version "2.1.0" @@ -2788,20 +2951,10 @@ set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -set-value@^0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" - integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE= - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.1" - to-object-path "^0.3.0" - -set-value@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" - integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg== +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== dependencies: extend-shallow "^2.0.1" is-extendable "^0.1.1" @@ -2825,6 +2978,19 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= +sinon@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.4.2.tgz#ecd54158fef2fcfbdb231a3fa55140e8cb02ad6c" + integrity sha512-pY5RY99DKelU3pjNxcWo6XqeB1S118GBcVIIdDi6V+h6hevn1izcg2xv1hTHW/sViRXU7sUOxt4wTUJ3gsW2CQ== + dependencies: + "@sinonjs/commons" "^1.4.0" + "@sinonjs/formatio" "^3.2.1" + "@sinonjs/samsam" "^3.3.3" + diff "^3.5.0" + lolex "^4.2.0" + nise "^1.5.2" + supports-color "^5.5.0" + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -2888,10 +3054,10 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.6, source-map-support@^0.5.9: - version "0.5.9" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f" - integrity sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA== +source-map-support@^0.5.11, source-map-support@^0.5.6: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -2912,9 +3078,9 @@ source-map@^0.6.0: integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== spdx-correct@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.2.tgz#19bb409e91b47b1ad54159243f7312a858db3c2e" - integrity sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ== + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" @@ -2933,9 +3099,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz#a59efc09784c2a5bada13cfeaf5c75dd214044d2" - integrity sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg== + version "3.0.5" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" + integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" @@ -2949,7 +3115,7 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= -stack-utils@^1.0.1: +stack-utils@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== @@ -3000,12 +3166,12 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.0.0.tgz#f78f68b5d0866c20b2c9b8c61b5298508dc8756f" - integrity sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow== +strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: - ansi-regex "^4.0.0" + ansi-regex "^4.1.0" strip-bom-buf@^1.0.0: version "1.0.0" @@ -3045,11 +3211,6 @@ supertap@^1.0.0: serialize-error "^2.1.0" strip-ansi "^4.0.0" -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -3057,6 +3218,13 @@ supports-color@^5.3.0, supports-color@^5.5.0: dependencies: has-flag "^3.0.0" +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + symbol-observable@^0.2.2: version "0.2.4" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-0.2.4.tgz#95a83db26186d6af7e7a18dbd9760a2f86d08f40" @@ -3068,17 +3236,17 @@ symbol-observable@^1.0.4, symbol-observable@^1.1.0: integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== tar@^4: - version "4.4.8" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" - integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ== + version "4.4.10" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" + integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA== dependencies: chownr "^1.1.1" fs-minipass "^1.2.5" - minipass "^2.3.4" - minizlib "^1.1.1" + minipass "^2.3.5" + minizlib "^1.2.1" mkdirp "^0.5.0" safe-buffer "^5.1.2" - yallist "^3.0.2" + yallist "^3.0.3" term-size@^1.2.0: version "1.2.0" @@ -3142,29 +3310,26 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= -ts-node@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf" - integrity sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw== +ts-node@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.3.0.tgz#e4059618411371924a1fb5f3b125915f324efb57" + integrity sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ== dependencies: - arrify "^1.0.0" - buffer-from "^1.1.0" - diff "^3.1.0" + arg "^4.1.0" + diff "^4.0.1" make-error "^1.1.1" - minimist "^1.2.0" - mkdirp "^0.5.1" source-map-support "^0.5.6" - yn "^2.0.0" + yn "^3.0.0" tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" - integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== tslint-config-prettier@^1.16.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.16.0.tgz#4627d0e2639554d89210e480093c381b5186963f" - integrity sha512-zu6RAcpBtqdvhT6KpBh9kRPYATjOf9BnRi718kNqVKFjEgSE4rFrPprFju1YJrkOa3RbtbWI1ZSuLd2NBX1MDw== + version "1.18.0" + resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz#75f140bde947d35d8f0d238e0ebf809d64592c37" + integrity sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg== tslint-plugin-prettier@^2.0.1: version "2.0.1" @@ -3176,39 +3341,45 @@ tslint-plugin-prettier@^2.0.1: tslib "^1.7.1" tslint@^5.11.0: - version "5.11.0" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed" - integrity sha1-mPMMAurjzecAYgHkwzywi0hYHu0= + version "5.18.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.18.0.tgz#f61a6ddcf372344ac5e41708095bbf043a147ac6" + integrity sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w== dependencies: - babel-code-frame "^6.22.0" + "@babel/code-frame" "^7.0.0" builtin-modules "^1.1.1" chalk "^2.3.0" commander "^2.12.1" diff "^3.2.0" glob "^7.1.1" - js-yaml "^3.7.0" + js-yaml "^3.13.1" minimatch "^3.0.4" + mkdirp "^0.5.1" resolve "^1.3.2" semver "^5.3.0" tslib "^1.8.0" - tsutils "^2.27.2" + tsutils "^2.29.0" -tsutils@^2.27.2: +tsutils@^2.29.0: version "2.29.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== dependencies: tslib "^1.8.1" -type-detect@^4.0.0, type-detect@^4.0.5: +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -typescript@^3.1.6: - version "3.1.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.6.tgz#b6543a83cfc8c2befb3f4c8fba6896f5b0c9be68" - integrity sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA== +type-fest@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" + integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ== + +typescript@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" + integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== uid2@0.0.3: version "0.0.3" @@ -3228,25 +3399,25 @@ unicode-match-property-ecmascript@^1.0.4: unicode-canonical-property-names-ecmascript "^1.0.4" unicode-property-aliases-ecmascript "^1.0.4" -unicode-match-property-value-ecmascript@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz#9f1dc76926d6ccf452310564fd834ace059663d4" - integrity sha512-Rx7yODZC1L/T8XKo/2kNzVAQaRE88AaMvI1EF/Xnj3GW2wzN6fop9DDWuFAKUVFH7vozkz26DzP0qyWLKLIVPQ== +unicode-match-property-value-ecmascript@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" + integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== unicode-property-aliases-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz#5a533f31b4317ea76f17d807fa0d116546111dd0" - integrity sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg== + version "1.0.5" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" + integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== union-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" - integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ= + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== dependencies: arr-union "^3.1.0" get-value "^2.0.6" is-extendable "^0.1.1" - set-value "^0.4.3" + set-value "^2.0.1" unique-string@^1.0.0: version "1.0.0" @@ -3277,10 +3448,10 @@ unzip-response@^2.0.1: resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c= -upath@^1.0.5: - version "1.1.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" - integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw== +upath@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" + integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== update-notifier@^2.5.0: version "2.5.0" @@ -3367,9 +3538,9 @@ wrappy@1: integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= write-file-atomic@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab" - integrity sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA== + version "2.4.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== dependencies: graceful-fs "^4.1.11" imurmurhash "^0.1.4" @@ -3381,11 +3552,16 @@ xdg-basedir@^3.0.0: integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ= xtend@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -yallist@^3.0.0, yallist@^3.0.2: +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yallist@^3.0.0, yallist@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== @@ -3397,7 +3573,7 @@ yargs-parser@^10.0.0: dependencies: camelcase "^4.1.0" -yn@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" - integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo= +yn@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==