code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.removeDuplicateSlashes = exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.isPatternRelatedToParentDirectory = exports.getPatternsOutsideCurrentDirectory = exports.getPatternsInsideCurrentDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0;
const path = require("path");
const globParent = require("glob-parent");
const micromatch = require("micromatch");
const GLOBSTAR = '**';
const ESCAPE_SYMBOL = '\\';
const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/;
const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[[^[]*]/;
const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\([^(]*\|[^|]*\)/;
const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\([^(]*\)/;
const BRACE_EXPANSION_SEPARATORS_RE = /,|\.\./;
/**
 * Matches a sequence of two or more consecutive slashes, excluding the first two slashes at the beginning of the string.
 * The latter is due to the presence of the device path at the beginning of the UNC path.
 */
const DOUBLE_SLASH_RE = /(?!^)\/{2,}/g;
function isStaticPattern(pattern, options = {}) {
    return !isDynamicPattern(pattern, options);
}
exports.isStaticPattern = isStaticPattern;
function isDynamicPattern(pattern, options = {}) {
    /**
     * A special case with an empty string is necessary for matching patterns that start with a forward slash.
     * An empty string cannot be a dynamic pattern.
     * For example, the pattern `/lib/*` will be spread into parts: '', 'lib', '*'.
     */
    if (pattern === '') {
        return false;
    }
    /**
     * When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check
     * filepath directly (without read directory).
     */
    if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) {
        return true;
    }
    if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) {
        return true;
    }
    if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) {
        return true;
    }
    if (options.braceExpansion !== false && hasBraceExpansion(pattern)) {
        return true;
    }
    return false;
}
exports.isDynamicPattern = isDynamicPattern;
function hasBraceExpansion(pattern) {
    const openingBraceIndex = pattern.indexOf('{');
    if (openingBraceIndex === -1) {
        return false;
    }
    const closingBraceIndex = pattern.indexOf('}', openingBraceIndex + 1);
    if (closingBraceIndex === -1) {
        return false;
    }
    const braceContent = pattern.slice(openingBraceIndex, closingBraceIndex);
    return BRACE_EXPANSION_SEPARATORS_RE.test(braceContent);
}
function convertToPositivePattern(pattern) {
    return isNegativePattern(pattern) ? pattern.slice(1) : pattern;
}
exports.convertToPositivePattern = convertToPositivePattern;
function convertToNegativePattern(pattern) {
    return '!' + pattern;
}
exports.convertToNegativePattern = convertToNegativePattern;
function isNegativePattern(pattern) {
    return pattern.startsWith('!') && pattern[1] !== '(';
}
exports.isNegativePattern = isNegativePattern;
function isPositivePattern(pattern) {
    return !isNegativePattern(pattern);
}
exports.isPositivePattern = isPositivePattern;
function getNegativePatterns(patterns) {
    return patterns.filter(isNegativePattern);
}
exports.getNegativePatterns = getNegativePatterns;
function getPositivePatterns(patterns) {
    return patterns.filter(isPositivePattern);
}
exports.getPositivePatterns = getPositivePatterns;
/**
 * Returns patterns that can be applied inside the current directory.
 *
 * @example
 * // ['./*', '*', 'a/*']
 * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*'])
 */
function getPatternsInsideCurrentDirectory(patterns) {
    return patterns.filter((pattern) => !isPatternRelatedToParentDirectory(pattern));
}
exports.getPatternsInsideCurrentDirectory = getPatternsInsideCurrentDirectory;
/**
 * Returns patterns to be expanded relative to (outside) the current directory.
 *
 * @example
 * // ['../*', './../*']
 * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*'])
 */
function getPatternsOutsideCurrentDirectory(patterns) {
    return patterns.filter(isPatternRelatedToParentDirectory);
}
exports.getPatternsOutsideCurrentDirectory = getPatternsOutsideCurrentDirectory;
function isPatternRelatedToParentDirectory(pattern) {
    return pattern.startsWith('..') || pattern.startsWith('./..');
}
exports.isPatternRelatedToParentDirectory = isPatternRelatedToParentDirectory;
function getBaseDirectory(pattern) {
    return globParent(pattern, { flipBackslashes: false });
}
exports.getBaseDirectory = getBaseDirectory;
function hasGlobStar(pattern) {
    return pattern.includes(GLOBSTAR);
}
exports.hasGlobStar = hasGlobStar;
function endsWithSlashGlobStar(pattern) {
    return pattern.endsWith('/' + GLOBSTAR);
}
exports.endsWithSlashGlobStar = endsWithSlashGlobStar;
function isAffectDepthOfReadingPattern(pattern) {
    const basename = path.basename(pattern);
    return endsWithSlashGlobStar(pattern) || isStaticPattern(basename);
}
exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
function expandPatternsWithBraceExpansion(patterns) {
    return patterns.reduce((collection, pattern) => {
        return collection.concat(expandBraceExpansion(pattern));
    }, []);
}
exports.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion;
function expandBraceExpansion(pattern) {
    const patterns = micromatch.braces(pattern, { expand: true, nodupes: true, keepEscaping: true });
    /**
     * Sort the patterns by length so that the same depth patterns are processed side by side.
     * `a/{b,}/{c,}/*` – `['a///*', 'a/b//*', 'a//c/*', 'a/b/c/*']`
     */
    patterns.sort((a, b) => a.length - b.length);
    /**
     * Micromatch can return an empty string in the case of patterns like `{a,}`.
     */
    return patterns.filter((pattern) => pattern !== '');
}
exports.expandBraceExpansion = expandBraceExpansion;
function getPatternParts(pattern, options) {
    let { parts } = micromatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true }));
    /**
     * The scan method returns an empty array in some cases.
     * See micromatch/picomatch#58 for more details.
     */
    if (parts.length === 0) {
        parts = [pattern];
    }
    /**
     * The scan method does not return an empty part for the pattern with a forward slash.
     * This is another part of micromatch/picomatch#58.
     */
    if (parts[0].startsWith('/')) {
        parts[0] = parts[0].slice(1);
        parts.unshift('');
    }
    return parts;
}
exports.getPatternParts = getPatternParts;
function makeRe(pattern, options) {
    return micromatch.makeRe(pattern, options);
}
exports.makeRe = makeRe;
function convertPatternsToRe(patterns, options) {
    return patterns.map((pattern) => makeRe(pattern, options));
}
exports.convertPatternsToRe = convertPatternsToRe;
function matchAny(entry, patternsRe) {
    return patternsRe.some((patternRe) => patternRe.test(entry));
}
exports.matchAny = matchAny;
/**
 * This package only works with forward slashes as a path separator.
 * Because of this, we cannot use the standard `path.normalize` method, because on Windows platform it will use of backslashes.
 */
function removeDuplicateSlashes(pattern) {
    return pattern.replace(DOUBLE_SLASH_RE, '/');
}
exports.removeDuplicateSlashes = removeDuplicateSlashes;


code:
/// <reference types="node" />
/// <reference types="node" />
import { Readable } from 'stream';
export declare function merge(streams: Readable[]): NodeJS.ReadableStream;

license: mit


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.merge = void 0;
const merge2 = require("merge2");
function merge(streams) {
    const mergedStream = merge2(streams);
    streams.forEach((stream) => {
        stream.once('error', (error) => mergedStream.emit('error', error));
    });
    mergedStream.once('close', () => propagateCloseEventToSources(streams));
    mergedStream.once('end', () => propagateCloseEventToSources(streams));
    return mergedStream;
}
exports.merge = merge;
function propagateCloseEventToSources(streams) {
    streams.forEach((stream) => stream.emit('close'));
}


code:
export declare function isString(input: unknown): input is string;
export declare function isEmpty(input: string): boolean;


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isEmpty = exports.isString = void 0;
function isString(input) {
    return typeof input === 'string';
}
exports.isString = isString;
function isEmpty(input) {
    return input === '';
}
exports.isEmpty = isEmpty;


fast-glob

description: It's a very fast and efficient glob library for Node.js

license: MIT

repository: mrmlnc/fast-glob

keywords: glob patterns fast implementation

clean: rimraf out

lint: eslint "src/**/*.ts" --cache

compile: tsc

test: mocha "out/**/*.spec.js" -s 0

test:e2e: mocha "out/**/*.e2e.js" -s 0

test:e2e:sync: mocha "out/**/*.e2e.js" -s 0 --grep "\(sync\)"

test:e2e:async: mocha "out/**/*.e2e.js" -s 0 --grep "\(async\)"

test:e2e:stream: mocha "out/**/*.e2e.js" -s 0 --grep "\(stream\)"

build: npm run clean && npm run compile && npm run lint && npm test

watch: npm run clean && npm run compile -- --sourceMap --watch

bench:async: npm run bench:product:async && npm run bench:regression:async

bench:stream: npm run bench:product:stream && npm run bench:regression:stream

bench:sync: npm run bench:product:sync && npm run bench:regression:sync

bench:product: npm run bench:product:async && npm run bench:product:sync && npm run bench:product:stream

bench:product:async: hereby bench:product:async

bench:product:sync: hereby bench:product:sync

bench:product:stream: hereby bench:product:stream

bench:regression: npm run bench:regression:async && npm run bench:regression:sync && npm run bench:regression:stream

bench:regression:async: hereby bench:regression:async

bench:regression:sync: hereby bench:regression:sync

bench:regression:stream: hereby bench:regression:stream


code:
extends: eslint:recommended
env:
  node: true
  browser: true
rules:
  block-scoped-var: 2
  callback-return: 2
  dot-notation: 2
  indent: 2
  linebreak-style: [2, unix]
  new-cap: 2
  no-console: [2, allow: [warn, error]]
  no-else-return: 2
  no-eq-null: 2
  no-fallthrough: 2
  no-invalid-this: 2
  no-return-assign: 2
  no-shadow: 1
  no-trailing-spaces: 2
  no-use-before-define: [2, nofunc]
  quotes: [2, single, avoid-escape]
  semi: [2, always]
  strict: [2, global]
  valid-jsdoc: [2, requireReturn: false]
  no-control-regex: 0
  no-useless-escape: 2


code:
language: node_js
node_js:
  - "8"
  - "10"
  - "12"
  - "13"
after_script:
  - coveralls < coverage/lcov.info


code:
# fast-json-stable-stringify

Deterministic `JSON.stringify()` - a faster version of [@substack](https://github.com/substack)'s json-stable-strigify without [jsonify](https://github.com/substack/jsonify).

You can also pass in a custom comparison function.

[![Build Status](https://travis-ci.org/epoberezkin/fast-json-stable-stringify.svg?branch=master)](https://travis-ci.org/epoberezkin/fast-json-stable-stringify)
[![Coverage Status](https://coveralls.io/repos/github/epoberezkin/fast-json-stable-stringify/badge.svg?branch=master)](https://coveralls.io/github/epoberezkin/fast-json-stable-stringify?branch=master)

# example

``` js
var stringify = require('fast-json-stable-stringify');
var obj = { c: 8, b: [{z:6,y:5,x:4},7], a: 3 };
console.log(stringify(obj));
```

output:

```
{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}
```

# methods

``` js
var stringify = require('fast-json-stable-stringify')
```

## var str = stringify(obj, opts)

Return a deterministic stringified string `str` from the object `obj`.

## options

### cmp

If `opts` is given, you can supply an `opts.cmp` to have a custom comparison
function for object keys. Your function `opts.cmp` is called with these
parameters:

``` js
opts.cmp({ key: akey, value: avalue }, { key: bkey, value: bvalue })
```

For example, to sort on the object key names in reverse order you could write:

``` js
var stringify = require('fast-json-stable-stringify');

var obj = { c: 8, b: [{z:6,y:5,x:4},7], a: 3 };
var s = stringify(obj, function (a, b) {
    return a.key < b.key ? 1 : -1;
});
console.log(s);
```

which results in the output string:

```
{"c":8,"b":[{"z":6,"y":5,"x":4},7],"a":3}
```

Or if you wanted to sort on the object values in reverse order, you could write:

```
var stringify = require('fast-json-stable-stringify');

var obj = { d: 6, c: 5, b: [{z:3,y:2,x:1},9], a: 10 };
var s = stringify(obj, function (a, b) {
    return a.value < b.value ? 1 : -1;
});
console.log(s);
```

which outputs:

```
{"d":6,"c":5,"b":[{"z":3,"y":2,"x":1},9],"a":10}
```

### cycles

Pass `true` in `opts.cycles` to stringify circular property as `__cycle__` - the result will not be a valid JSON string in this case.

TypeError will be thrown in case of circular object without this option.

# install

With [npm](https://npmjs.org) do:

```
npm install fast-json-stable-stringify
```

# benchmark

To run benchmark (requires Node.js 6+):
```
node benchmark
```

Results:
```
fast-json-stable-stringify x 17,189 ops/sec ±1.43% (83 runs sampled)
json-stable-stringify x 13,634 ops/sec ±1.39% (85 runs sampled)
fast-stable-stringify x 20,212 ops/sec ±1.20% (84 runs sampled)
faster-stable-stringify x 15,549 ops/sec ±1.12% (84 runs sampled)
The fastest is fast-stable-stringify
```

## Enterprise support

fast-json-stable-stringify package is a part of [Tidelift enterprise subscription](https://tidelift.com/subscription/pkg/npm-fast-json-stable-stringify?utm_source=npm-fast-json-stable-stringify&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) - it provides a centralised commercial support to open-source software users, in addition to the support provided by software maintainers.

## Security contact

To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure. Please do NOT report security vulnerability via GitHub issues.

# license

[MIT](https://github.com/epoberezkin/fast-json-stable-stringify/blob/master/LICENSE)


code:
'use strict';

const Benchmark = require('benchmark');
const suite = new Benchmark.Suite;
const testData = require('./test.json');

const stringifyPackages = {
  // 'JSON.stringify': JSON.stringify,
  'fast-json-stable-stringify': require('../index'),
  'json-stable-stringify': true,
  'fast-stable-stringify': true,
  'faster-stable-stringify': true
};

for (const name in stringifyPackages) {
  let func = stringifyPackages[name];
  if (func === true) func = require(name);

  suite.add(name, function() {
    func(testData);
  });
}

suite
  .on('cycle', (event) => console.log(String(event.target)))
  .on('complete', function () {
    console.log('The fastest is ' + this.filter('fastest').map('name'));
  })
  .run({async: true});


Carey Savage

eyeColor: brown

gender: female

company: VERAQ

address: 458 Willow Street, Henrietta, California, 7234

about: Nisi reprehenderit nulla ad officia pariatur non dolore laboris irure cupidatat laborum. Minim eu ex Lorem adipisicing exercitation irure minim sunt est enim mollit incididunt voluptate nulla. Ut mollit anim reprehenderit et aliqua ex esse aliquip. Aute sit duis deserunt do incididunt consequat minim qui dolor commodo deserunt et voluptate.

greeting: Hello, Carey Savage! You have 4 unread messages.

favoriteFruit: banana

Elinor Pearson

eyeColor: blue

company: FLEXIGEN

address: 600 Bayview Avenue, Draper, Montana, 3088

about: Mollit commodo ea sit Lorem velit. Irure anim esse Lorem sint quis officia ut. Aliqua nisi dolore in aute deserunt mollit ex ea in mollit.

greeting: Hello, Elinor Pearson! You have 3 unread messages.

favoriteFruit: strawberry

Irwin Davidson

eyeColor: green

gender: male

company: DANJA

address: 439 Cook Street, Chapin, Kentucky, 7398

about: Irure velit non commodo aliqua exercitation ut nostrud minim magna. Dolor ad ad ut irure eu. Non pariatur dolor eiusmod ipsum do et exercitation cillum. Et amet laboris minim eiusmod ullamco magna ea reprehenderit proident sunt.

greeting: Hello, Irwin Davidson! You have 2 unread messages.


code:
var stringify = require('../');

var obj = { c: 8, b: [{z:6,y:5,x:4},7], a: 3 };
var s = stringify(obj, function (a, b) {
    return a.key < b.key ? 1 : -1;
});
console.log(s);


code:
var stringify = require('../');
var obj = { c: 8, b: [{z:6,y:5,x:4},7], a: 3 };
console.log(stringify(obj));


code:
var stringify = require('../');
var obj = { c: 6, b: [4,5], a: 3 };
console.log(stringify(obj));


code:
var stringify = require('../');

var obj = { d: 6, c: 5, b: [{z:3,y:2,x:1},9], a: 10 };
var s = stringify(obj, function (a, b) {
    return a.value < b.value ? 1 : -1;
});
console.log(s);


code:
declare module 'fast-json-stable-stringify' {
  function stringify(obj: any): string;
  export = stringify;
}


code:
'use strict';

module.exports = function (data, opts) {
    if (!opts) opts = {};
    if (typeof opts === 'function') opts = { cmp: opts };
    var cycles = (typeof opts.cycles === 'boolean') ? opts.cycles : false;

    var cmp = opts.cmp && (function (f) {
        return function (node) {
            return function (a, b) {
                var aobj = { key: a, value: node[a] };
                var bobj = { key: b, value: node[b] };
                return f(aobj, bobj);
            };
        };
    })(opts.cmp);

    var seen = [];
    return (function stringify (node) {
        if (node && node.toJSON && typeof node.toJSON === 'function') {
            node = node.toJSON();
        }

        if (node === undefined) return;
        if (typeof node == 'number') return isFinite(node) ? '' + node : 'null';
        if (typeof node !== 'object') return JSON.stringify(node);

        var i, out;
        if (Array.isArray(node)) {
            out = '[';
            for (i = 0; i < node.length; i++) {
                if (i) out += ',';
                out += stringify(node[i]) || 'null';
            }
            return out + ']';
        }

        if (node === null) return 'null';

        if (seen.indexOf(node) !== -1) {
            if (cycles) return JSON.stringify('__cycle__');
            throw new TypeError('Converting circular structure to JSON');
        }

        var seenIndex = seen.push(node) - 1;
        var keys = Object.keys(node).sort(cmp && cmp(node));
        out = '';
        for (i = 0; i < keys.length; i++) {
            var key = keys[i];
            var value = stringify(node[key]);

            if (!value) continue;
            if (out) out += ',';
            out += JSON.stringify(key) + ':' + value;
        }
        seen.splice(seenIndex, 1);
        return '{' + out + '}';
    })(data);
};


fast-json-stable-stringify

description: deterministic `JSON.stringify()` - a faster version of substack's json-stable-strigify without jsonify

fast-stable-stringify: latest

faster-stable-stringify: latest

json-stable-stringify: latest

eslint: eslint index.js test

test-spec: tape test/*.js

test: npm run eslint && nyc npm run test-spec

type: git

keywords: json stringify deterministic hash stable

reporter: lcov text-summary


code:
'use strict';

var test = require('tape');
var stringify = require('../');

test('custom comparison function', function (t) {
    t.plan(1);
    var obj = { c: 8, b: [{z:6,y:5,x:4},7], a: 3 };
    var s = stringify(obj, function (a, b) {
        return a.key < b.key ? 1 : -1;
    });
    t.equal(s, '{"c":8,"b":[{"z":6,"y":5,"x":4},7],"a":3}');
});


code:
'use strict';

var test = require('tape');
var stringify = require('../');

test('nested', function (t) {
    t.plan(1);
    var obj = { c: 8, b: [{z:6,y:5,x:4},7], a: 3 };
    t.equal(stringify(obj), '{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}');
});

test('cyclic (default)', function (t) {
    t.plan(1);
    var one = { a: 1 };
    var two = { a: 2, one: one };
    one.two = two;
    try {
        stringify(one);
    } catch (ex) {
        t.equal(ex.toString(), 'TypeError: Converting circular structure to JSON');
    }
});

test('cyclic (specifically allowed)', function (t) {
    t.plan(1);
    var one = { a: 1 };
    var two = { a: 2, one: one };
    one.two = two;
    t.equal(stringify(one, {cycles:true}), '{"a":1,"two":{"a":2,"one":"__cycle__"}}');
});

test('repeated non-cyclic value', function(t) {
    t.plan(1);
    var one = { x: 1 };
    var two = { a: one, b: one };
    t.equal(stringify(two), '{"a":{"x":1},"b":{"x":1}}');
});

test('acyclic but with reused obj-property pointers', function (t) {
    t.plan(1);
    var x = { a: 1 };
    var y = { b: x, c: x };
    t.equal(stringify(y), '{"b":{"a":1},"c":{"a":1}}');
});


code:
'use strict';

var test = require('tape');
var stringify = require('../');

test('simple object', function (t) {
    t.plan(1);
    var obj = { c: 6, b: [4,5], a: 3, z: null };
    t.equal(stringify(obj), '{"a":3,"b":[4,5],"c":6,"z":null}');
});

test('object with undefined', function (t) {
    t.plan(1);
    var obj = { a: 3, z: undefined };
    t.equal(stringify(obj), '{"a":3}');
});

test('object with null', function (t) {
    t.plan(1);
    var obj = { a: 3, z: null };
    t.equal(stringify(obj), '{"a":3,"z":null}');
});

test('object with NaN and Infinity', function (t) {
    t.plan(1);
    var obj = { a: 3, b: NaN, c: Infinity };
    t.equal(stringify(obj), '{"a":3,"b":null,"c":null}');
});

test('array with undefined', function (t) {
    t.plan(1);
    var obj = [4, undefined, 6];
    t.equal(stringify(obj), '[4,null,6]');
});

test('object with empty string', function (t) {
    t.plan(1);
    var obj = { a: 3, z: '' };
    t.equal(stringify(obj), '{"a":3,"z":""}');
});

test('array with empty string', function (t) {
    t.plan(1);
    var obj = [4, '', 6];
    t.equal(stringify(obj), '[4,"",6]');
});


code:
'use strict';

var test = require('tape');
var stringify = require('../');

test('toJSON function', function (t) {
    t.plan(1);
    var obj = { one: 1, two: 2, toJSON: function() { return { one: 1 }; } };
    t.equal(stringify(obj), '{"one":1}' );
});

test('toJSON returns string', function (t) {
    t.plan(1);
    var obj = { one: 1, two: 2, toJSON: function() { return 'one'; } };
    t.equal(stringify(obj), '"one"');
});

test('toJSON returns array', function (t) {
    t.plan(1);
    var obj = { one: 1, two: 2, toJSON: function() { return ['one']; } };
    t.equal(stringify(obj), '["one"]');
});


code:
# fast-levenshtein - Levenshtein algorithm in Javascript

[![Build Status](https://secure.travis-ci.org/hiddentao/fast-levenshtein.png)](http://travis-ci.org/hiddentao/fast-levenshtein)
[![NPM module](https://badge.fury.io/js/fast-levenshtein.png)](https://badge.fury.io/js/fast-levenshtein)
[![NPM downloads](https://img.shields.io/npm/dm/fast-levenshtein.svg?maxAge=2592000)](https://www.npmjs.com/package/fast-levenshtein)
[![Follow on Twitter](https://img.shields.io/twitter/url/http/shields.io.svg?style=social&label=Follow&maxAge=2592000)](https://twitter.com/hiddentao)

An efficient Javascript implementation of the [Levenshtein algorithm](http://en.wikipedia.org/wiki/Levenshtein_distance) with locale-specific collator support.

## Features

* Works in node.js and in the browser.
* Better performance than other implementations by not needing to store the whole matrix ([more info](http://www.codeproject.com/Articles/13525/Fast-memory-efficient-Levenshtein-algorithm)).
* Locale-sensitive string comparisions if needed.
* Comprehensive test suite and performance benchmark.
* Small: <1 KB minified and gzipped

## Installation

### node.js

Install using [npm](http://npmjs.org/):

```bash
$ npm install fast-levenshtein
```

### Browser

Using bower:

```bash
$ bower install fast-levenshtein
```

If you are not using any module loader system then the API will then be accessible via the `window.Levenshtein` object.

## Examples

**Default usage**

```javascript
var levenshtein = require('fast-levenshtein');

var distance = levenshtein.get('back', 'book');   // 2
var distance = levenshtein.get('我愛你', '我叫你');   // 1
```

**Locale-sensitive string comparisons**

It supports using [Intl.Collator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator) for locale-sensitive  string comparisons:

```javascript
var levenshtein = require('fast-levenshtein');

levenshtein.get('mikailovitch', 'Mikhaïlovitch', { useCollator: true});
// 1
```

## Building and Testing

To build the code and run the tests:

```bash
$ npm install -g grunt-cli
$ npm install
$ npm run build
```

## Performance

_Thanks to [Titus Wormer](https://github.com/wooorm) for [encouraging me](https://github.com/hiddentao/fast-levenshtein/issues/1) to do this._

Benchmarked against other node.js levenshtein distance modules (on Macbook Air 2012, Core i7, 8GB RAM):

```bash
Running suite Implementation comparison [benchmark/speed.js]...
>> levenshtein-edit-distance x 234 ops/sec ±3.02% (73 runs sampled)
>> levenshtein-component x 422 ops/sec ±4.38% (83 runs sampled)
>> levenshtein-deltas x 283 ops/sec ±3.83% (78 runs sampled)
>> natural x 255 ops/sec ±0.76% (88 runs sampled)
>> levenshtein x 180 ops/sec ±3.55% (86 runs sampled)
>> fast-levenshtein x 1,792 ops/sec ±2.72% (95 runs sampled)
Benchmark done.
Fastest test is fast-levenshtein at 4.2x faster than levenshtein-component
```

You can run this benchmark yourself by doing:

```bash
$ npm install
$ npm run build
$ npm run benchmark
```

## Contributing

If you wish to submit a pull request please update and/or create new tests for any changes you make and ensure the grunt build passes.

See [CONTRIBUTING.md](https://github.com/hiddentao/fast-levenshtein/blob/master/CONTRIBUTING.md) for details.

## License

MIT - see [LICENSE.md](https://github.com/hiddentao/fast-levenshtein/blob/master/LICENSE.md)


code:
(function() {
  'use strict';

  var collator;
  try {
    collator = (typeof Intl !== "undefined" && typeof Intl.Collator !== "undefined") ? Intl.Collator("generic", { sensitivity: "base" }) : null;
  } catch (err){
    console.log("Collator could not be initialized and wouldn't be used");
  }
  // arrays to re-use
  var prevRow = [],
    str2Char = [];

  /**
   * Based on the algorithm at http://en.wikipedia.org/wiki/Levenshtein_distance.
   */
  var Levenshtein = {
    /**
     * Calculate levenshtein distance of the two strings.
     *
     * @param str1 String the first string.
     * @param str2 String the second string.
     * @param [options] Additional options.
     * @param [options.useCollator] Use `Intl.Collator` for locale-sensitive string comparison.
     * @return Integer the levenshtein distance (0 and above).
     */
    get: function(str1, str2, options) {
      var useCollator = (options && collator && options.useCollator);

      var str1Len = str1.length,
        str2Len = str2.length;

      // base cases
      if (str1Len === 0) return str2Len;
      if (str2Len === 0) return str1Len;

      // two rows
      var curCol, nextCol, i, j, tmp;

      // initialise previous row
      for (i=0; i<str2Len; ++i) {
        prevRow[i] = i;
        str2Char[i] = str2.charCodeAt(i);
      }
      prevRow[str2Len] = str2Len;

      var strCmp;
      if (useCollator) {
        // calculate current row distance from previous row using collator
        for (i = 0; i < str1Len; ++i) {
          nextCol = i + 1;

          for (j = 0; j < str2Len; ++j) {
            curCol = nextCol;

            // substution
            strCmp = 0 === collator.compare(str1.charAt(i), String.fromCharCode(str2Char[j]));

            nextCol = prevRow[j] + (strCmp ? 0 : 1);

            // insertion
            tmp = curCol + 1;
            if (nextCol > tmp) {
              nextCol = tmp;
            }
            // deletion
            tmp = prevRow[j + 1] + 1;
            if (nextCol > tmp) {
              nextCol = tmp;
            }

            // copy current col value into previous (in preparation for next iteration)
            prevRow[j] = curCol;
          }

          // copy last col value into previous (in preparation for next iteration)
          prevRow[j] = nextCol;
        }
      }
      else {
        // calculate current row distance from previous row without collator
        for (i = 0; i < str1Len; ++i) {
          nextCol = i + 1;

          for (j = 0; j < str2Len; ++j) {
            curCol = nextCol;

            // substution
            strCmp = str1.charCodeAt(i) === str2Char[j];

            nextCol = prevRow[j] + (strCmp ? 0 : 1);

            // insertion
            tmp = curCol + 1;
            if (nextCol > tmp) {
              nextCol = tmp;
            }
            // deletion
            tmp = prevRow[j + 1] + 1;
            if (nextCol > tmp) {
              nextCol = tmp;
            }

            // copy current col value into previous (in preparation for next iteration)
            prevRow[j] = curCol;
          }

          // copy last col value into previous (in preparation for next iteration)
          prevRow[j] = nextCol;
        }
      }
      return nextCol;
    }

  };

  // amd
  if (typeof define !== "undefined" && define !== null && define.amd) {
    define(function() {
      return Levenshtein;
    });
  }
  // commonjs
  else if (typeof module !== "undefined" && module !== null && typeof exports !== "undefined" && module.exports === exports) {
    module.exports = Levenshtein;
  }
  // web worker
  else if (typeof self !== "undefined" && typeof self.postMessage === 'function' && typeof self.importScripts === 'function') {
    self.Levenshtein = Levenshtein;
  }
  // browser main thread
  else if (typeof window !== "undefined" && window !== null) {
    window.Levenshtein = Levenshtein;
  }
}());


fast-levenshtein

description: Efficient implementation of Levenshtein algorithm  with locale-specific collator support.

build: grunt build

prepublish: npm run build

benchmark: grunt benchmark

test: mocha

keywords: levenshtein distance string

author: Ramesh Nair <ram@hiddentao.com> (http://www.hiddentao.com/)


code:
version: 2
updates:
- package-ecosystem: npm
  directory: "/"
  schedule:
    interval: daily
  open-pull-requests-limit: 10
  ignore:
  - dependency-name: standard
    versions:
    - 16.0.3


code:
name: ci

on: [push, pull_request]

jobs:
  legacy:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: ['0.10', '0.12', 4.x, 6.x, 8.x]

    steps:
      - uses: actions/checkout@v3
        with:
          persist-credentials: false

      - name: Use Node.js
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}

      - name: Install
        run: |
          npm install --production && npm install tape

      - name: Run tests
        run: |
          npm run legacy

  test:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [10.x, 12.x, 13.x, 14.x, 15.x, 16.x, 18.x, 20.x]

    steps:
      - uses: actions/checkout@v3
        with:
          persist-credentials: false

      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}

      - name: Install
        run: |
          npm install

      - name: Run tests
        run: |
          npm run test

  types:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
        with:
          persist-credentials: false

      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: Install
        run: |
          npm install

      - name: Run types tests
        run: |
          npm run typescript


code:
# fastq

![ci][ci-url]
[![npm version][npm-badge]][npm-url]

Fast, in memory work queue.

Benchmarks (1 million tasks):

* setImmediate: 812ms
* fastq: 854ms
* async.queue: 1298ms
* neoAsync.queue: 1249ms

Obtained on node 12.16.1, on a dedicated server.

If you need zero-overhead series function call, check out
[fastseries](http://npm.im/fastseries). For zero-overhead parallel
function call, check out [fastparallel](http://npm.im/fastparallel).

[![js-standard-style](https://raw.githubusercontent.com/feross/standard/master/badge.png)](https://github.com/feross/standard)

  * Installation
  * Usage
  * API
  * Licence & copyright

## Install

`npm i fastq --save`

## Usage (callback API)

```js
'use strict'

const queue = require('fastq')(worker, 1)

queue.push(42, function (err, result) {
  if (err) { throw err }
  console.log('the result is', result)
})

function worker (arg, cb) {
  cb(null, arg * 2)
}
```

## Usage (promise API)

```js
const queue = require('fastq').promise(worker, 1)

async function worker (arg) {
  return arg * 2
}

async function run () {
  const result = await queue.push(42)
  console.log('the result is', result)
}

run()
```

### Setting "this"

```js
'use strict'

const that = { hello: 'world' }
const queue = require('fastq')(that, worker, 1)

queue.push(42, function (err, result) {
  if (err) { throw err }
  console.log(this)
  console.log('the result is', result)
})

function worker (arg, cb) {
  console.log(this)
  cb(null, arg * 2)
}
```

### Using with TypeScript (callback API)

```ts
'use strict'

import * as fastq from "fastq";
import type { queue, done } from "fastq";

type Task = {
  id: number
}

const q: queue<Task> = fastq(worker, 1)

q.push({ id: 42})

function worker (arg: Task, cb: done) {
  console.log(arg.id)
  cb(null)
}
```

### Using with TypeScript (promise API)

```ts
'use strict'

import * as fastq from "fastq";
import type { queueAsPromised } from "fastq";

type Task = {
  id: number
}

const q: queueAsPromised<Task> = fastq.promise(asyncWorker, 1)

q.push({ id: 42}).catch((err) => console.error(err))

async function asyncWorker (arg: Task): Promise<void> {
  // No need for a try-catch block, fastq handles errors automatically
  console.log(arg.id)
}
```

## API

* fastqueue()
* queue#push()
* queue#unshift()
* queue#pause()
* queue#resume()
* queue#idle()
* queue#length()
* queue#getQueue()
* queue#kill()
* queue#killAndDrain()
* queue#error()
* queue#concurrency
* queue#drain
* queue#empty
* queue#saturated
* fastqueue.promise()

-------------------------------------------------------

### fastqueue([that], worker, concurrency)

Creates a new queue.

Arguments:

* `that`, optional context of the `worker` function.
* `worker`, worker function, it would be called with `that` as `this`,
  if that is specified.
* `concurrency`, number of concurrent tasks that could be executed in
  parallel.

-------------------------------------------------------

### queue.push(task, done)

Add a task at the end of the queue. `done(err, result)` will be called
when the task was processed.

-------------------------------------------------------

### queue.unshift(task, done)

Add a task at the beginning of the queue. `done(err, result)` will be called
when the task was processed.

-------------------------------------------------------

### queue.pause()

Pause the processing of tasks. Currently worked tasks are not
stopped.

-------------------------------------------------------

### queue.resume()

Resume the processing of tasks.

-------------------------------------------------------

### queue.idle()

Returns `false` if there are tasks being processed or waiting to be processed.
`true` otherwise.

-------------------------------------------------------

### queue.length()

Returns the number of tasks waiting to be processed (in the queue).

-------------------------------------------------------

### queue.getQueue()

Returns all the tasks be processed (in the queue). Returns empty array when there are no tasks

-------------------------------------------------------

### queue.kill()

Removes all tasks waiting to be processed, and reset `drain` to an empty
function.

-------------------------------------------------------

### queue.killAndDrain()

Same than `kill` but the `drain` function will be called before reset to empty.

-------------------------------------------------------

### queue.error(handler)

Set a global error handler. `handler(err, task)` will be called
each time a task is completed, `err` will be not null if the task has thrown an error.

-------------------------------------------------------

### queue.concurrency

Property that returns the number of concurrent tasks that could be executed in
parallel. It can be altered at runtime.

-------------------------------------------------------

### queue.drain

Function that will be called when the last
item from the queue has been processed by a worker.
It can be altered at runtime.

-------------------------------------------------------

### queue.empty

Function that will be called when the last
item from the queue has been assigned to a worker.
It can be altered at runtime.

-------------------------------------------------------

### queue.saturated

Function that will be called when the queue hits the concurrency
limit.
It can be altered at runtime.

-------------------------------------------------------

### fastqueue.promise([that], worker(arg), concurrency)

Creates a new queue with `Promise` apis. It also offers all the methods
and properties of the object returned by [`fastqueue`](#fastqueue) with the modified
[`push`](#pushPromise) and [`unshift`](#unshiftPromise) methods.

Node v10+ is required to use the promisified version.

Arguments:
* `that`, optional context of the `worker` function.
* `worker`, worker function, it would be called with `that` as `this`,
  if that is specified. It MUST return a `Promise`.
* `concurrency`, number of concurrent tasks that could be executed in
  parallel.

#### queue.push(task) => Promise

Add a task at the end of the queue. The returned `Promise`  will be fulfilled (rejected)
when the task is completed successfully (unsuccessfully).

This promise could be ignored as it will not lead to a `'unhandledRejection'`.

#### queue.unshift(task) => Promise

Add a task at the beginning of the queue. The returned `Promise`  will be fulfilled (rejected)
when the task is completed successfully (unsuccessfully).

This promise could be ignored as it will not lead to a `'unhandledRejection'`.

#### queue.drained() => Promise

Wait for the queue to be drained. The returned `Promise` will be resolved when all tasks in the queue have been processed by a worker.

This promise could be ignored as it will not lead to a `'unhandledRejection'`.

## License

ISC

[ci-url]: https://github.com/mcollina/fastq/workflows/ci/badge.svg
[npm-badge]: https://badge.fury.io/js/fastq.svg
[npm-url]: https://badge.fury.io/js/fastq


code:
'use strict'

const max = 1000000
const fastqueue = require('./')(worker, 1)
const { promisify } = require('util')
const immediate = promisify(setImmediate)
const qPromise = require('./').promise(immediate, 1)
const async = require('async')
const neo = require('neo-async')
const asyncqueue = async.queue(worker, 1)
const neoqueue = neo.queue(worker, 1)

function bench (func, done) {
  const key = max + '*' + func.name
  let count = -1

  console.time(key)
  end()

  function end () {
    if (++count < max) {
      func(end)
    } else {
      console.timeEnd(key)
      if (done) {
        done()
      }
    }
  }
}

function benchFastQ (done) {
  fastqueue.push(42, done)
}

function benchAsyncQueue (done) {
  asyncqueue.push(42, done)
}

function benchNeoQueue (done) {
  neoqueue.push(42, done)
}

function worker (arg, cb) {
  setImmediate(cb)
}

function benchSetImmediate (cb) {
  worker(42, cb)
}

function benchFastQPromise (done) {
  qPromise.push(42).then(function () { done() }, done)
}

function runBench (done) {
  async.eachSeries([
    benchSetImmediate,
    benchFastQ,
    benchNeoQueue,
    benchAsyncQueue,
    benchFastQPromise
  ], bench, done)
}

runBench(runBench)


code:
'use strict'

/* eslint-disable no-var */

var queue = require('./')(worker, 1)

queue.push(42, function (err, result) {
  if (err) { throw err }
  console.log('the result is', result)
})

function worker (arg, cb) {
  cb(null, 42 * 2)
}


code:
import { promise as queueAsPromised } from './queue.js'

/* eslint-disable */

const queue = queueAsPromised(worker, 1)

console.log('the result is', await queue.push(42))

async function worker (arg) {
  return 42 * 2
}


code:
declare function fastq<C, T = any, R = any>(context: C, worker: fastq.worker<C, T, R>, concurrency: number): fastq.queue<T, R>
declare function fastq<C, T = any, R = any>(worker: fastq.worker<C, T, R>, concurrency: number): fastq.queue<T, R>

declare namespace fastq {
  type worker<C, T = any, R = any> = (this: C, task: T, cb: fastq.done<R>) => void
  type asyncWorker<C, T = any, R = any> = (this: C, task: T) => Promise<R>
  type done<R = any> = (err: Error | null, result?: R) => void
  type errorHandler<T = any> = (err: Error, task: T) => void

  interface queue<T = any, R = any> {
    push(task: T, done?: done<R>): void
    unshift(task: T, done?: done<R>): void
    pause(): any
    resume(): any
    running(): number
    idle(): boolean
    length(): number
    getQueue(): T[]
    kill(): any
    killAndDrain(): any
    error(handler: errorHandler<T>): void
    concurrency: number
    drain(): any
    empty: () => void
    saturated: () => void
  }

  interface queueAsPromised<T = any, R = any> extends queue<T, R> {
    push(task: T): Promise<R>
    unshift(task: T): Promise<R>
    drained(): Promise<void>
  }

  function promise<C, T = any, R = any>(context: C, worker: fastq.asyncWorker<C, T, R>, concurrency: number): fastq.queueAsPromised<T, R>
  function promise<C, T = any, R = any>(worker: fastq.asyncWorker<C, T, R>, concurrency: number): fastq.queueAsPromised<T, R>
}

export = fastq


fastq

description: Fast, in memory work queue

lint: standard --verbose | snazzy

unit: nyc --lines 100 --branches 100 --functions 100 --check-coverage --reporter=text tape test/test.js test/promise.js

coverage: nyc --reporter=html --reporter=cobertura --reporter=text tape test/test.js test/promise.js

test:report: npm run lint && npm run unit:report

test: npm run lint && npm run unit

typescript: tsc --project ./test/tsconfig.json

legacy: tape test/test.js

pre-commit: test typescript

keywords: fast queue async worker

license: ISC


code:
'use strict'

/* eslint-disable no-var */

var reusify = require('reusify')

function fastqueue (context, worker, _concurrency) {
  if (typeof context === 'function') {
    _concurrency = worker
    worker = context
    context = null
  }

  if (!(_concurrency >= 1)) {
    throw new Error('fastqueue concurrency must be equal to or greater than 1')
  }

  var cache = reusify(Task)
  var queueHead = null
  var queueTail = null
  var _running = 0
  var errorHandler = null

  var self = {
    push: push,
    drain: noop,
    saturated: noop,
    pause: pause,
    paused: false,

    get concurrency () {
      return _concurrency
    },
    set concurrency (value) {
      if (!(value >= 1)) {
        throw new Error('fastqueue concurrency must be equal to or greater than 1')
      }
      _concurrency = value

      if (self.paused) return
      for (; queueHead && _running < _concurrency;) {
        _running++
        release()
      }
    },

    running: running,
    resume: resume,
    idle: idle,
    length: length,
    getQueue: getQueue,
    unshift: unshift,
    empty: noop,
    kill: kill,
    killAndDrain: killAndDrain,
    error: error
  }

  return self

  function running () {
    return _running
  }

  function pause () {
    self.paused = true
  }

  function length () {
    var current = queueHead
    var counter = 0

    while (current) {
      current = current.next
      counter++
    }

    return counter
  }

  function getQueue () {
    var current = queueHead
    var tasks = []

    while (current) {
      tasks.push(current.value)
      current = current.next
    }

    return tasks
  }

  function resume () {
    if (!self.paused) return
    self.paused = false
    if (queueHead === null) {
      _running++
      release()
      return
    }
    for (; queueHead && _running < _concurrency;) {
      _running++
      release()
    }
  }

  function idle () {
    return _running === 0 && self.length() === 0
  }

  function push (value, done) {
    var current = cache.get()

    current.context = context
    current.release = release
    current.value = value
    current.callback = done || noop
    current.errorHandler = errorHandler

    if (_running >= _concurrency || self.paused) {
      if (queueTail) {
        queueTail.next = current
        queueTail = current
      } else {
        queueHead = current
        queueTail = current
        self.saturated()
      }
    } else {
      _running++
      worker.call(context, current.value, current.worked)
    }
  }

  function unshift (value, done) {
    var current = cache.get()

    current.context = context
    current.release = release
    current.value = value
    current.callback = done || noop
    current.errorHandler = errorHandler

    if (_running >= _concurrency || self.paused) {
      if (queueHead) {
        current.next = queueHead
        queueHead = current
      } else {
        queueHead = current
        queueTail = current
        self.saturated()
      }
    } else {
      _running++
      worker.call(context, current.value, current.worked)
    }
  }

  function release (holder) {
    if (holder) {
      cache.release(holder)
    }
    var next = queueHead
    if (next && _running <= _concurrency) {
      if (!self.paused) {
        if (queueTail === queueHead) {
          queueTail = null
        }
        queueHead = next.next
        next.next = null
        worker.call(context, next.value, next.worked)
        if (queueTail === null) {
          self.empty()
        }
      } else {
        _running--
      }
    } else if (--_running === 0) {
      self.drain()
    }
  }

  function kill () {
    queueHead = null
    queueTail = null
    self.drain = noop
  }

  function killAndDrain () {
    queueHead = null
    queueTail = null
    self.drain()
    self.drain = noop
  }

  function error (handler) {
    errorHandler = handler
  }
}

function noop () {}

function Task () {
  this.value = null
  this.callback = noop
  this.next = null
  this.release = noop
  this.context = null
  this.errorHandler = null

  var self = this

  this.worked = function worked (err, result) {
    var callback = self.callback
    var errorHandler = self.errorHandler
    var val = self.value
    self.value = null
    self.callback = noop
    if (self.errorHandler) {
      errorHandler(err, val)
    }
    callback.call(self.context, err, result)
    self.release(self)
  }
}

function queueAsPromised (context, worker, _concurrency) {
  if (typeof context === 'function') {
    _concurrency = worker
    worker = context
    context = null
  }

  function asyncWrapper (arg, cb) {
    worker.call(this, arg)
      .then(function (res) {
        cb(null, res)
      }, cb)
  }

  var queue = fastqueue(context, asyncWrapper, _concurrency)

  var pushCb = queue.push
  var unshiftCb = queue.unshift

  queue.push = push
  queue.unshift = unshift
  queue.drained = drained

  return queue

  function push (value) {
    var p = new Promise(function (resolve, reject) {
      pushCb(value, function (err, result) {
        if (err) {
          reject(err)
          return
        }
        resolve(result)
      })
    })

    // Let's fork the promise chain to
    // make the error bubble up to the user but
    // not lead to a unhandledRejection
    p.catch(noop)

    return p
  }

  function unshift (value) {
    var p = new Promise(function (resolve, reject) {
      unshiftCb(value, function (err, result) {
        if (err) {
          reject(err)
          return
        }
        resolve(result)
      })
    })

    // Let's fork the promise chain to
    // make the error bubble up to the user but
    // not lead to a unhandledRejection
    p.catch(noop)

    return p
  }

  function drained () {
    if (queue.idle()) {
      return new Promise(function (resolve) {
        resolve()
      })
    }

    var previousDrain = queue.drain

    var p = new Promise(function (resolve) {
      queue.drain = function () {
        previousDrain()
        resolve()
      }
    })

    return p
  }
}

module.exports = fastqueue
module.exports.promise = queueAsPromised


code:
import * as fastq from '../'
import { promise as queueAsPromised } from '../'

// Basic example

const queue = fastq(worker, 1)

queue.push('world', (err, result) => {
  if (err) throw err
  console.log('the result is', result)
})

queue.push('push without cb')

queue.concurrency

queue.drain()

queue.empty = () => undefined

console.log('the queue tasks are', queue.getQueue())

queue.idle()

queue.kill()

queue.killAndDrain()

queue.length

queue.pause()

queue.resume()

queue.running()

queue.saturated = () => undefined

queue.unshift('world', (err, result) => {
  if (err) throw err
  console.log('the result is', result)
})

queue.unshift('unshift without cb')

function worker(task: any, cb: fastq.done) {
  cb(null, 'hello ' + task)
}

// Generics example

interface GenericsContext {
  base: number;
}

const genericsQueue = fastq<GenericsContext, number, string>({ base: 6 }, genericsWorker, 1)

genericsQueue.push(7, (err, done) => {
  if (err) throw err
  console.log('the result is', done)
})

genericsQueue.unshift(7, (err, done) => {
  if (err) throw err
  console.log('the result is', done)
})

function genericsWorker(this: GenericsContext, task: number, cb: fastq.done<string>) {
  cb(null, 'the meaning of life is ' + (this.base * task))
}

const queue2 = queueAsPromised(asyncWorker, 1)

async function asyncWorker(task: any) {
  return 'hello ' + task
}

async function run () {
  await queue.push(42)
  await queue.unshift(42)
}

run()


code:
'use strict'

const test = require('tape')
const buildQueue = require('../').promise
const { promisify } = require('util')
const sleep = promisify(setTimeout)
const immediate = promisify(setImmediate)

test('concurrency', function (t) {
  t.plan(2)
  t.throws(buildQueue.bind(null, worker, 0))
  t.doesNotThrow(buildQueue.bind(null, worker, 1))

  async function worker (arg) {
    return true
  }
})

test('worker execution', async function (t) {
  const queue = buildQueue(worker, 1)

  const result = await queue.push(42)

  t.equal(result, true, 'result matches')

  async function worker (arg) {
    t.equal(arg, 42)
    return true
  }
})

test('limit', async function (t) {
  const queue = buildQueue(worker, 1)

  const [res1, res2] = await Promise.all([queue.push(10), queue.push(0)])
  t.equal(res1, 10, 'the result matches')
  t.equal(res2, 0, 'the result matches')

  async function worker (arg) {
    await sleep(arg)
    return arg
  }
})

test('multiple executions', async function (t) {
  const queue = buildQueue(worker, 1)
  const toExec = [1, 2, 3, 4, 5]
  const expected = ['a', 'b', 'c', 'd', 'e']
  let count = 0

  await Promise.all(toExec.map(async function (task, i) {
    const result = await queue.push(task)
    t.equal(result, expected[i], 'the result matches')
  }))

  async function worker (arg) {
    t.equal(arg, toExec[count], 'arg matches')
    return expected[count++]
  }
})

test('drained', async function (t) {
  const queue = buildQueue(worker, 2)

  const toExec = new Array(10).fill(10)
  let count = 0

  async function worker (arg) {
    await sleep(arg)
    count++
  }

  toExec.forEach(function (i) {
    queue.push(i)
  })

  await queue.drained()

  t.equal(count, toExec.length)

  toExec.forEach(function (i) {
    queue.push(i)
  })

  await queue.drained()

  t.equal(count, toExec.length * 2)
})

test('drained with exception should not throw', async function (t) {
  const queue = buildQueue(worker, 2)

  const toExec = new Array(10).fill(10)

  async function worker () {
    throw new Error('foo')
  }

  toExec.forEach(function (i) {
    queue.push(i)
  })

  await queue.drained()
})

test('drained with drain function', async function (t) {
  let drainCalled = false
  const queue = buildQueue(worker, 2)

  queue.drain = function () {
    drainCalled = true
  }

  const toExec = new Array(10).fill(10)
  let count = 0

  async function worker (arg) {
    await sleep(arg)
    count++
  }

  toExec.forEach(function () {
    queue.push()
  })

  await queue.drained()

  t.equal(count, toExec.length)
  t.equal(drainCalled, true)
})

test('drained while idle should resolve', async function (t) {
  const queue = buildQueue(worker, 2)

  async function worker (arg) {
    await sleep(arg)
  }

  await queue.drained()
})

test('drained while idle should not call the drain function', async function (t) {
  let drainCalled = false
  const queue = buildQueue(worker, 2)

  queue.drain = function () {
    drainCalled = true
  }

  async function worker (arg) {
    await sleep(arg)
  }

  await queue.drained()

  t.equal(drainCalled, false)
})

test('set this', async function (t) {
  t.plan(1)
  const that = {}
  const queue = buildQueue(that, worker, 1)

  await queue.push(42)

  async function worker (arg) {
    t.equal(this, that, 'this matches')
  }
})

test('unshift', async function (t) {
  const queue = buildQueue(worker, 1)
  const expected = [1, 2, 3, 4]

  await Promise.all([
    queue.push(1),
    queue.push(4),
    queue.unshift(3),
    queue.unshift(2)
  ])

  t.is(expected.length, 0)

  async function worker (arg) {
    t.equal(expected.shift(), arg, 'tasks come in order')
  }
})

test('push with worker throwing error', async function (t) {
  t.plan(5)
  const q = buildQueue(async function (task, cb) {
    throw new Error('test error')
  }, 1)
  q.error(function (err, task) {
    t.ok(err instanceof Error, 'global error handler should catch the error')
    t.match(err.message, /test error/, 'error message should be "test error"')
    t.equal(task, 42, 'The task executed should be passed')
  })
  try {
    await q.push(42)
  } catch (err) {
    t.ok(err instanceof Error, 'push callback should catch the error')
    t.match(err.message, /test error/, 'error message should be "test error"')
  }
})

test('unshift with worker throwing error', async function (t) {
  t.plan(2)
  const q = buildQueue(async function (task, cb) {
    throw new Error('test error')
  }, 1)
  try {
    await q.unshift(42)
  } catch (err) {
    t.ok(err instanceof Error, 'push callback should catch the error')
    t.match(err.message, /test error/, 'error message should be "test error"')
  }
})

test('no unhandledRejection (push)', async function (t) {
  function handleRejection () {
    t.fail('unhandledRejection')
  }
  process.once('unhandledRejection', handleRejection)
  const q = buildQueue(async function (task, cb) {
    throw new Error('test error')
  }, 1)

  q.push(42)

  await immediate()
  process.removeListener('unhandledRejection', handleRejection)
})

test('no unhandledRejection (unshift)', async function (t) {
  function handleRejection () {
    t.fail('unhandledRejection')
  }
  process.once('unhandledRejection', handleRejection)
  const q = buildQueue(async function (task, cb) {
    throw new Error('test error')
  }, 1)

  q.unshift(42)

  await immediate()
  process.removeListener('unhandledRejection', handleRejection)
})


code:
'use strict'

/* eslint-disable no-var */

var test = require('tape')
var buildQueue = require('../')

test('concurrency', function (t) {
  t.plan(6)
  t.throws(buildQueue.bind(null, worker, 0))
  t.throws(buildQueue.bind(null, worker, NaN))
  t.doesNotThrow(buildQueue.bind(null, worker, 1))

  var queue = buildQueue(worker, 1)
  t.throws(function () {
    queue.concurrency = 0
  })
  t.throws(function () {
    queue.concurrency = NaN
  })
  t.doesNotThrow(function () {
    queue.concurrency = 2
  })

  function worker (arg, cb) {
    cb(null, true)
  }
})

test('worker execution', function (t) {
  t.plan(3)

  var queue = buildQueue(worker, 1)

  queue.push(42, function (err, result) {
    t.error(err, 'no error')
    t.equal(result, true, 'result matches')
  })

  function worker (arg, cb) {
    t.equal(arg, 42)
    cb(null, true)
  }
})

test('limit', function (t) {
  t.plan(4)

  var expected = [10, 0]
  var queue = buildQueue(worker, 1)

  queue.push(10, result)
  queue.push(0, result)

  function result (err, arg) {
    t.error(err, 'no error')
    t.equal(arg, expected.shift(), 'the result matches')
  }

  function worker (arg, cb) {
    setTimeout(cb, arg, null, arg)
  }
})

test('multiple executions', function (t) {
  t.plan(15)

  var queue = buildQueue(worker, 1)
  var toExec = [1, 2, 3, 4, 5]
  var count = 0

  toExec.forEach(function (task) {
    queue.push(task, done)
  })

  function done (err, result) {
    t.error(err, 'no error')
    t.equal(result, toExec[count - 1], 'the result matches')
  }

  function worker (arg, cb) {
    t.equal(arg, toExec[count], 'arg matches')
    count++
    setImmediate(cb, null, arg)
  }
})

test('multiple executions, one after another', function (t) {
  t.plan(15)

  var queue = buildQueue(worker, 1)
  var toExec = [1, 2, 3, 4, 5]
  var count = 0

  queue.push(toExec[0], done)

  function done (err, result) {
    t.error(err, 'no error')
    t.equal(result, toExec[count - 1], 'the result matches')
    if (count < toExec.length) {
      queue.push(toExec[count], done)
    }
  }

  function worker (arg, cb) {
    t.equal(arg, toExec[count], 'arg matches')
    count++
    setImmediate(cb, null, arg)
  }
})

test('set this', function (t) {
  t.plan(3)

  var that = {}
  var queue = buildQueue(that, worker, 1)

  queue.push(42, function (err, result) {
    t.error(err, 'no error')
    t.equal(this, that, 'this matches')
  })

  function worker (arg, cb) {
    t.equal(this, that, 'this matches')
    cb(null, true)
  }
})

test('drain', function (t) {
  t.plan(4)

  var queue = buildQueue(worker, 1)
  var worked = false

  queue.push(42, function (err, result) {
    t.error(err, 'no error')
    t.equal(result, true, 'result matches')
  })

  queue.drain = function () {
    t.equal(true, worked, 'drained')
  }

  function worker (arg, cb) {
    t.equal(arg, 42)
    worked = true
    setImmediate(cb, null, true)
  }
})

test('pause && resume', function (t) {
  t.plan(13)

  var queue = buildQueue(worker, 1)
  var worked = false
  var expected = [42, 24]

  t.notOk(queue.paused, 'it should not be paused')

  queue.pause()

  queue.push(42, function (err, result) {
    t.error(err, 'no error')
    t.equal(result, true, 'result matches')
  })

  queue.push(24, function (err, result) {
    t.error(err, 'no error')
    t.equal(result, true, 'result matches')
  })

  t.notOk(worked, 'it should be paused')
  t.ok(queue.paused, 'it should be paused')

  queue.resume()
  queue.pause()
  queue.resume()
  queue.resume() // second resume is a no-op

  function worker (arg, cb) {
    t.notOk(queue.paused, 'it should not be paused')
    t.ok(queue.running() <= queue.concurrency, 'should respect the concurrency')
    t.equal(arg, expected.shift())
    worked = true
    process.nextTick(function () { cb(null, true) })
  }
})

test('pause in flight && resume', function (t) {
  t.plan(16)

  var queue = buildQueue(worker, 1)
  var expected = [42, 24, 12]

  t.notOk(queue.paused, 'it should not be paused')

  queue.push(42, function (err, result) {
    t.error(err, 'no error')
    t.equal(result, true, 'result matches')
    t.ok(queue.paused, 'it should be paused')
    process.nextTick(function () {
      queue.resume()
      queue.pause()
      queue.resume()
    })
  })

  queue.push(24, function (err, result) {
    t.error(err, 'no error')
    t.equal(result, true, 'result matches')
    t.notOk(queue.paused, 'it should not be paused')
  })

  queue.push(12, function (err, result) {
    t.error(err, 'no error')
    t.equal(result, true, 'result matches')
    t.notOk(queue.paused, 'it should not be paused')
  })

  queue.pause()

  function worker (arg, cb) {
    t.ok(queue.running() <= queue.concurrency, 'should respect the concurrency')
    t.equal(arg, expected.shift())
    process.nextTick(function () { cb(null, true) })
  }
})

test('altering concurrency', function (t) {
  t.plan(24)

  var queue = buildQueue(worker, 1)

  queue.push(24, workDone)
  queue.push(24, workDone)
  queue.push(24, workDone)

  queue.pause()

  queue.concurrency = 3 // concurrency changes are ignored while paused
  queue.concurrency = 2

  queue.resume()

  t.equal(queue.running(), 2, '2 jobs running')

  queue.concurrency = 3

  t.equal(queue.running(), 3, '3 jobs running')

  queue.concurrency = 1

  t.equal(queue.running(), 3, '3 jobs running') // running jobs can't be killed

  queue.push(24, workDone)
  queue.push(24, workDone)
  queue.push(24, workDone)
  queue.push(24, workDone)

  function workDone (err, result) {
    t.error(err, 'no error')
    t.equal(result, true, 'result matches')
  }

  function worker (arg, cb) {
    t.ok(queue.running() <= queue.concurrency, 'should respect the concurrency')
    setImmediate(function () {
      cb(null, true)
    })
  }
})

test('idle()', function (t) {
  t.plan(12)

  var queue = buildQueue(worker, 1)

  t.ok(queue.idle(), 'queue is idle')

  queue.push(42, function (err, result) {
    t.error(err, 'no error')
    t.equal(result, true, 'result matches')
    t.notOk(queue.idle(), 'queue is not idle')
  })

  queue.push(42, function (err, result) {
    t.error(err, 'no error')
    t.equal(result, true, 'result matches')
    // it will go idle after executing this function
    setImmediate(function () {
      t.ok(queue.idle(), 'queue is now idle')
    })
  })

  t.notOk(queue.idle(), 'queue is not idle')

  function worker (arg, cb) {
    t.notOk(queue.idle(), 'queue is not idle')
    t.equal(arg, 42)
    setImmediate(cb, null, true)
  }
})

test('saturated', function (t) {
  t.plan(9)

  var queue = buildQueue(worker, 1)
  var preworked = 0
  var worked = 0

  queue.saturated = function () {
    t.pass('saturated')
    t.equal(preworked, 1, 'started 1 task')
    t.equal(worked, 0, 'worked zero task')
  }

  queue.push(42, done)
  queue.push(42, done)

  function done (err, result) {
    t.error(err, 'no error')
    t.equal(result, true, 'result matches')
  }

  function worker (arg, cb) {
    t.equal(arg, 42)
    preworked++
    setImmediate(function () {
      worked++
      cb(null, true)
    })
  }
})

test('length', function (t) {
  t.plan(7)

  var queue = buildQueue(worker, 1)

  t.equal(queue.length(), 0, 'nothing waiting')
  queue.push(42, done)
  t.equal(queue.length(), 0, 'nothing waiting')
  queue.push(42, done)
  t.equal(queue.length(), 1, 'one task waiting')
  queue.push(42, done)
  t.equal(queue.length(), 2, 'two tasks waiting')

  function done (err, result) {
    t.error(err, 'no error')
  }

  function worker (arg, cb) {
    setImmediate(function () {
      cb(null, true)
    })
  }
})

test('getQueue', function (t) {
  t.plan(10)

  var queue = buildQueue(worker, 1)

  t.equal(queue.getQueue().length, 0, 'nothing waiting')
  queue.push(42, done)
  t.equal(queue.getQueue().length, 0, 'nothing waiting')
  queue.push(42, done)
  t.equal(queue.getQueue().length, 1, 'one task waiting')
  t.equal(queue.getQueue()[0], 42, 'should be equal')
  queue.push(43, done)
  t.equal(queue.getQueue().length, 2, 'two tasks waiting')
  t.equal(queue.getQueue()[0], 42, 'should be equal')
  t.equal(queue.getQueue()[1], 43, 'should be equal')

  function done (err, result) {
    t.error(err, 'no error')
  }

  function worker (arg, cb) {
    setImmediate(function () {
      cb(null, true)
    })
  }
})

test('unshift', function (t) {
  t.plan(8)

  var queue = buildQueue(worker, 1)
  var expected = [1, 2, 3, 4]

  queue.push(1, done)
  queue.push(4, done)
  queue.unshift(3, done)
  queue.unshift(2, done)

  function done (err, result) {
    t.error(err, 'no error')
  }

  function worker (arg, cb) {
    t.equal(expected.shift(), arg, 'tasks come in order')
    setImmediate(function () {
      cb(null, true)
    })
  }
})

test('unshift && empty', function (t) {
  t.plan(2)

  var queue = buildQueue(worker, 1)
  var completed = false

  queue.pause()

  queue.empty = function () {
    t.notOk(completed, 'the task has not completed yet')
  }

  queue.unshift(1, done)

  queue.resume()

  function done (err, result) {
    completed = true
    t.error(err, 'no error')
  }

  function worker (arg, cb) {
    setImmediate(function () {
      cb(null, true)
    })
  }
})

test('push && empty', function (t) {
  t.plan(2)

  var queue = buildQueue(worker, 1)
  var completed = false

  queue.pause()

  queue.empty = function () {
    t.notOk(completed, 'the task has not completed yet')
  }

  queue.push(1, done)

  queue.resume()

  function done (err, result) {
    completed = true
    t.error(err, 'no error')
  }

  function worker (arg, cb) {
    setImmediate(function () {
      cb(null, true)
    })
  }
})

test('kill', function (t) {
  t.plan(5)

  var queue = buildQueue(worker, 1)
  var expected = [1]

  var predrain = queue.drain

  queue.drain = function drain () {
    t.fail('drain should never be called')
  }

  queue.push(1, done)
  queue.push(4, done)
  queue.unshift(3, done)
  queue.unshift(2, done)
  queue.kill()

  function done (err, result) {
    t.error(err, 'no error')
    setImmediate(function () {
      t.equal(queue.length(), 0, 'no queued tasks')
      t.equal(queue.running(), 0, 'no running tasks')
      t.equal(queue.drain, predrain, 'drain is back to default')
    })
  }

  function worker (arg, cb) {
    t.equal(expected.shift(), arg, 'tasks come in order')
    setImmediate(function () {
      cb(null, true)
    })
  }
})

test('killAndDrain', function (t) {
  t.plan(6)

  var queue = buildQueue(worker, 1)
  var expected = [1]

  var predrain = queue.drain

  queue.drain = function drain () {
    t.pass('drain has been called')
  }

  queue.push(1, done)
  queue.push(4, done)
  queue.unshift(3, done)
  queue.unshift(2, done)
  queue.killAndDrain()

  function done (err, result) {
    t.error(err, 'no error')
    setImmediate(function () {
      t.equal(queue.length(), 0, 'no queued tasks')
      t.equal(queue.running(), 0, 'no running tasks')
      t.equal(queue.drain, predrain, 'drain is back to default')
    })
  }

  function worker (arg, cb) {
    t.equal(expected.shift(), arg, 'tasks come in order')
    setImmediate(function () {
      cb(null, true)
    })
  }
})

test('pause && idle', function (t) {
  t.plan(11)

  var queue = buildQueue(worker, 1)
  var worked = false

  t.notOk(queue.paused, 'it should not be paused')
  t.ok(queue.idle(), 'should be idle')

  queue.pause()

  queue.push(42, function (err, result) {
    t.error(err, 'no error')
    t.equal(result, true, 'result matches')
  })

  t.notOk(worked, 'it should be paused')
  t.ok(queue.paused, 'it should be paused')
  t.notOk(queue.idle(), 'should not be idle')

  queue.resume()

  t.notOk(queue.paused, 'it should not be paused')
  t.notOk(queue.idle(), 'it should not be idle')

  function worker (arg, cb) {
    t.equal(arg, 42)
    worked = true
    process.nextTick(cb.bind(null, null, true))
    process.nextTick(function () {
      t.ok(queue.idle(), 'is should be idle')
    })
  }
})

test('push without cb', function (t) {
  t.plan(1)

  var queue = buildQueue(worker, 1)

  queue.push(42)

  function worker (arg, cb) {
    t.equal(arg, 42)
    cb()
  }
})

test('unshift without cb', function (t) {
  t.plan(1)

  var queue = buildQueue(worker, 1)

  queue.unshift(42)

  function worker (arg, cb) {
    t.equal(arg, 42)
    cb()
  }
})

test('push with worker throwing error', function (t) {
  t.plan(5)
  var q = buildQueue(function (task, cb) {
    cb(new Error('test error'), null)
  }, 1)
  q.error(function (err, task) {
    t.ok(err instanceof Error, 'global error handler should catch the error')
    t.match(err.message, /test error/, 'error message should be "test error"')
    t.equal(task, 42, 'The task executed should be passed')
  })
  q.push(42, function (err) {
    t.ok(err instanceof Error, 'push callback should catch the error')
    t.match(err.message, /test error/, 'error message should be "test error"')
  })
})

test('unshift with worker throwing error', function (t) {
  t.plan(5)
  var q = buildQueue(function (task, cb) {
    cb(new Error('test error'), null)
  }, 1)
  q.error(function (err, task) {
    t.ok(err instanceof Error, 'global error handler should catch the error')
    t.match(err.message, /test error/, 'error message should be "test error"')
    t.equal(task, 42, 'The task executed should be passed')
  })
  q.unshift(42, function (err) {
    t.ok(err instanceof Error, 'unshift callback should catch the error')
    t.match(err.message, /test error/, 'error message should be "test error"')
  })
})

test('pause/resume should trigger drain event', function (t) {
  t.plan(1)

  var queue = buildQueue(worker, 1)
  queue.pause()
  queue.drain = function () {
    t.pass('drain should be called')
  }

  function worker (arg, cb) {
    cb(null, true)
  }

  queue.resume()
})


code:
### 0.11.4 / 2021-05-24

- Prevent the client hanging if `close()` is called when already closing

### 0.11.3 / 2019-06-10

- Fix a race condition that caused a timeout not to be cancelled immediately
  when the WebSocket is closed
- Change license from MIT to Apache 2.0

### 0.11.2 / 2019-06-10

(This version was pulled due to an error when publishing)

### 0.11.1 / 2017-01-22

- Forcibly close the I/O stream after a timeout if the peer does not respond
  after calling `close()`

### 0.11.0 / 2016-02-24

- Introduce a `net` option to the `Client` class for setting things like, say,
  `servername`

### 0.10.0 / 2015-07-08

- Add the standard `code` and `reason` parameters to the `close` method

### 0.9.4 / 2015-03-08

- Don't send input to the driver before `start()` is called

### 0.9.3 / 2015-02-19

- Make sure the TCP socket is not left open when closing the connection

### 0.9.2 / 2014-12-21

- Only emit `error` once, and don't emit it after `close`

### 0.9.1 / 2014-12-18

- Check that all options to the WebSocket constructor are recognized

### 0.9.0 / 2014-12-13

- Allow protocol extensions to be passed into websocket-extensions

### 0.8.1 / 2014-11-12

- Send the correct hostname when upgrading a connection to TLS

### 0.8.0 / 2014-11-08

- Support connections via HTTP proxies
- Close the connection cleanly if we're still waiting for a handshake response

### 0.7.3 / 2014-10-04

- Allow sockets to be closed when they are in any state other than `CLOSED`

### 0.7.2 / 2013-12-29

- Make sure the `close` event is emitted by clients on Node v0.10

### 0.7.1 / 2013-12-03

- Support the `maxLength` websocket-driver option
- Make the client emit `error` events on network errors

### 0.7.0 / 2013-09-09

- Allow the server to send custom headers with EventSource responses

### 0.6.1 / 2013-07-05

- Add `ca` option to the client for specifying certificate authorities
- Start the server driver asynchronously so that `onopen` handlers can be added

### 0.6.0 / 2013-05-12

- Add support for custom headers

### 0.5.0 / 2013-05-05

- Extract the protocol handlers into the `websocket-driver` library
- Support the Node streaming API

### 0.4.4 / 2013-02-14

- Emit the `close` event if TCP is closed before CLOSE frame is acked

### 0.4.3 / 2012-07-09

- Add `Connection: close` to EventSource response
- Handle situations where `request.socket` is undefined

### 0.4.2 / 2012-04-06

- Add WebSocket error code `1011`.
- Handle URLs with no path correctly by sending `GET /`

### 0.4.1 / 2012-02-26

- Treat anything other than a `Buffer` as a string when calling `send()`

### 0.4.0 / 2012-02-13

- Add `ping()` method to server-side `WebSocket` and `EventSource`
- Buffer `send()` calls until the draft-76 handshake is complete
- Fix HTTPS problems on Node 0.7

### 0.3.1 / 2012-01-16

- Call `setNoDelay(true)` on `net.Socket` objects to reduce latency

### 0.3.0 / 2012-01-13

- Add support for `EventSource` connections

### 0.2.0 / 2011-12-21

- Add support for `Sec-WebSocket-Protocol` negotiation
- Support `hixie-76` close frames and 75/76 ignored segments
- Improve performance of HyBi parsing/framing functions
- Decouple parsers from TCP and reduce write volume

### 0.1.2 / 2011-12-05

- Detect closed sockets on the server side when TCP connection breaks
- Make `hixie-76` sockets work through HAProxy

### 0.1.1 / 2011-11-30

- Fix `addEventListener()` interface methods

### 0.1.0 / 2011-11-27

- Initial release, based on WebSocket components from Faye


code:
# faye-websocket

This is a general-purpose WebSocket implementation extracted from the
[Faye](http://faye.jcoglan.com) project. It provides classes for easily building
WebSocket servers and clients in Node. It does not provide a server itself, but
rather makes it easy to handle WebSocket connections within an existing
[Node](https://nodejs.org/) application. It does not provide any abstraction
other than the standard [WebSocket
API](https://html.spec.whatwg.org/multipage/comms.html#network).

It also provides an abstraction for handling
[EventSource](https://html.spec.whatwg.org/multipage/comms.html#server-sent-events)
connections, which are one-way connections that allow the server to push data to
the client. They are based on streaming HTTP responses and can be easier to access
via proxies than WebSockets.

## Installation

```
$ npm install faye-websocket
```

## Handling WebSocket connections in Node

You can handle WebSockets on the server side by listening for HTTP Upgrade
requests, and creating a new socket for the request. This socket object exposes
the usual WebSocket methods for receiving and sending messages. For example this
is how you'd implement an echo server:

```js
var WebSocket = require('faye-websocket'),
    http      = require('http');

var server = http.createServer();

server.on('upgrade', function(request, socket, body) {
  if (WebSocket.isWebSocket(request)) {
    var ws = new WebSocket(request, socket, body);

    ws.on('message', function(event) {
      ws.send(event.data);
    });

    ws.on('close', function(event) {
      console.log('close', event.code, event.reason);
      ws = null;
    });
  }
});

server.listen(8000);
```

`WebSocket` objects are also duplex streams, so you could replace the
`ws.on('message', ...)` line with:

```js
    ws.pipe(ws);
```

Note that under certain circumstances (notably a draft-76 client connecting
through an HTTP proxy), the WebSocket handshake will not be complete after you
call `new WebSocket()` because the server will not have received the entire
handshake from the client yet. In this case, calls to `ws.send()` will buffer
the message in memory until the handshake is complete, at which point any
buffered messages will be sent to the client.

If you need to detect when the WebSocket handshake is complete, you can use the
`onopen` event.

If the connection's protocol version supports it, you can call `ws.ping()` to
send a ping message and wait for the client's response. This method takes a
message string, and an optional callback that fires when a matching pong message
is received. It returns `true` if and only if a ping message was sent. If the
client does not support ping/pong, this method sends no data and returns
`false`.

```js
ws.ping('Mic check, one, two', function() {
  // fires when pong is received
});
```

## Using the WebSocket client

The client supports both the plain-text `ws` protocol and the encrypted `wss`
protocol, and has exactly the same interface as a socket you would use in a web
browser. On the wire it identifies itself as `hybi-13`.

```js
var WebSocket = require('faye-websocket'),
    ws        = new WebSocket.Client('ws://www.example.com/');

ws.on('open', function(event) {
  console.log('open');
  ws.send('Hello, world!');
});

ws.on('message', function(event) {
  console.log('message', event.data);
});

ws.on('close', function(event) {
  console.log('close', event.code, event.reason);
  ws = null;
});
```

The WebSocket client also lets you inspect the status and headers of the
handshake response via its `statusCode` and `headers` properties.

To connect via a proxy, set the `proxy` option to the HTTP origin of the proxy,
including any authorization information, custom headers and TLS config you
require. Only the `origin` setting is required.

```js
var ws = new WebSocket.Client('ws://www.example.com/', [], {
  proxy: {
    origin:  'http://username:password@proxy.example.com',
    headers: { 'User-Agent': 'node' },
    tls:     { cert: fs.readFileSync('client.crt') }
  }
});
```

The `tls` value is an object that will be passed to
[`tls.connect()`](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback).

## Subprotocol negotiation

The WebSocket protocol allows peers to select and identify the application
protocol to use over the connection. On the client side, you can set which
protocols the client accepts by passing a list of protocol names when you
construct the socket:

```js
var ws = new WebSocket.Client('ws://www.example.com/', ['irc', 'amqp']);
```

On the server side, you can likewise pass in the list of protocols the server
supports after the other constructor arguments:

```js
var ws = new WebSocket(request, socket, body, ['irc', 'amqp']);
```

If the client and server agree on a protocol, both the client- and server-side
socket objects expose the selected protocol through the `ws.protocol` property.

## Protocol extensions

faye-websocket is based on the
[websocket-extensions](https://github.com/faye/websocket-extensions-node)
framework that allows extensions to be negotiated via the
`Sec-WebSocket-Extensions` header. To add extensions to a connection, pass an
array of extensions to the `:extensions` option. For example, to add
[permessage-deflate](https://github.com/faye/permessage-deflate-node):

```js
var deflate = require('permessage-deflate');

var ws = new WebSocket(request, socket, body, [], { extensions: [deflate] });
```

## Initialization options

Both the server- and client-side classes allow an options object to be passed in
at initialization time, for example:

```js
var ws = new WebSocket(request, socket, body, protocols, options);
var ws = new WebSocket.Client(url, protocols, options);
```

`protocols` is an array of subprotocols as described above, or `null`.
`options` is an optional object containing any of these fields:

- `extensions` - an array of
  [websocket-extensions](https://github.com/faye/websocket-extensions-node)
  compatible extensions, as described above
- `headers` - an object containing key-value pairs representing HTTP headers to
  be sent during the handshake process
- `maxLength` - the maximum allowed size of incoming message frames, in bytes.
  The default value is `2^26 - 1`, or 1 byte short of 64 MiB.
- `ping` - an integer that sets how often the WebSocket should send ping frames,
  measured in seconds

The client accepts some additional options:

- `proxy` - settings for a proxy as described above
- `net` - an object containing settings for the origin server that will be
  passed to
  [`net.connect()`](https://nodejs.org/api/net.html#net_socket_connect_options_connectlistener)
- `tls` - an object containing TLS settings for the origin server, this will be
  passed to
  [`tls.connect()`](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback)
- `ca` - (legacy) a shorthand for passing `{ tls: { ca: value } }`

## WebSocket API

Both server- and client-side `WebSocket` objects support the following API.

- **`on('open', function(event) {})`** fires when the socket connection is
  established. Event has no attributes.
- **`on('message', function(event) {})`** fires when the socket receives a
  message. Event has one attribute, **`data`**, which is either a `String` (for
  text frames) or a `Buffer` (for binary frames).
- **`on('error', function(event) {})`** fires when there is a protocol error due
  to bad data sent by the other peer. This event is purely informational, you do
  not need to implement error recover.
- **`on('close', function(event) {})`** fires when either the client or the
  server closes the connection. Event has two optional attributes, **`code`**
  and **`reason`**, that expose the status code and message sent by the peer
  that closed the connection.
- **`send(message)`** accepts either a `String` or a `Buffer` and sends a text
  or binary message over the connection to the other peer.
- **`ping(message, function() {})`** sends a ping frame with an optional message
  and fires the callback when a matching pong is received.
- **`close(code, reason)`** closes the connection, sending the given status code
  and reason text, both of which are optional.
- **`version`** is a string containing the version of the `WebSocket` protocol
  the connection is using.
- **`protocol`** is a string (which may be empty) identifying the subprotocol
  the socket is using.

## Handling EventSource connections in Node

EventSource connections provide a very similar interface, although because they
only allow the server to send data to the client, there is no `onmessage` API.
EventSource allows the server to push text messages to the client, where each
message has an optional event-type and ID.

```js
var WebSocket   = require('faye-websocket'),
    EventSource = WebSocket.EventSource,
    http        = require('http');

var server = http.createServer();

server.on('request', function(request, response) {
  if (EventSource.isEventSource(request)) {
    var es = new EventSource(request, response);
    console.log('open', es.url, es.lastEventId);

    // Periodically send messages
    var loop = setInterval(function() { es.send('Hello') }, 1000);

    es.on('close', function() {
      clearInterval(loop);
      es = null;
    });

  } else {
    // Normal HTTP request
    response.writeHead(200, { 'Content-Type': 'text/plain' });
    response.end('Hello');
  }
});

server.listen(8000);
```

The `send` method takes two optional parameters, `event` and `id`. The default
event-type is `'message'` with no ID. For example, to send a `notification`
event with ID `99`:

```js
es.send('Breaking News!', { event: 'notification', id: '99' });
```

The `EventSource` object exposes the following properties:

- **`url`** is a string containing the URL the client used to create the
  EventSource.
- **`lastEventId`** is a string containing the last event ID received by the
  client. You can use this when the client reconnects after a dropped connection
  to determine which messages need resending.

When you initialize an EventSource with ` new EventSource()`, you can pass
configuration options after the `response` parameter. Available options are:

- **`headers`** is an object containing custom headers to be set on the
  EventSource response.
- **`retry`** is a number that tells the client how long (in seconds) it should
  wait after a dropped connection before attempting to reconnect.
- **`ping`** is a number that tells the server how often (in seconds) to send
  'ping' packets to the client to keep the connection open, to defeat timeouts
  set by proxies. The client will ignore these messages.

For example, this creates a connection that allows access from any origin, pings
every 15 seconds and is retryable every 10 seconds if the connection is broken:

```js
var es = new EventSource(request, response, {
  headers: { 'Access-Control-Allow-Origin': '*' },
  ping:    15,
  retry:   10
});
```

You can send a ping message at any time by calling `es.ping()`. Unlike
WebSocket, the client does not send a response to this; it is merely to send
some data over the wire to keep the connection alive.


code:
'use strict';

var Stream      = require('stream').Stream,
    util        = require('util'),
    driver      = require('websocket-driver'),
    Headers     = require('websocket-driver/lib/websocket/driver/headers'),
    API         = require('./websocket/api'),
    EventTarget = require('./websocket/api/event_target'),
    Event       = require('./websocket/api/event');

var EventSource = function(request, response, options) {
  this.writable = true;
  options = options || {};

  this._stream = response.socket;
  this._ping   = options.ping  || this.DEFAULT_PING;
  this._retry  = options.retry || this.DEFAULT_RETRY;

  var scheme       = driver.isSecureRequest(request) ? 'https:' : 'http:';
  this.url         = scheme + '//' + request.headers.host + request.url;
  this.lastEventId = request.headers['last-event-id'] || '';
  this.readyState  = API.CONNECTING;

  var headers = new Headers(),
      self    = this;

  if (options.headers) {
    for (var key in options.headers) headers.set(key, options.headers[key]);
  }

  if (!this._stream || !this._stream.writable) return;
  process.nextTick(function() { self._open() });

  this._stream.setTimeout(0);
  this._stream.setNoDelay(true);

  var handshake = 'HTTP/1.1 200 OK\r\n' +
                  'Content-Type: text/event-stream\r\n' +
                  'Cache-Control: no-cache, no-store\r\n' +
                  'Connection: close\r\n' +
                  headers.toString() +
                  '\r\n' +
                  'retry: ' + Math.floor(this._retry * 1000) + '\r\n\r\n';

  this._write(handshake);

  this._stream.on('drain', function() { self.emit('drain') });

  if (this._ping)
    this._pingTimer = setInterval(function() { self.ping() }, this._ping * 1000);

  ['error', 'end'].forEach(function(event) {
    self._stream.on(event, function() { self.close() });
  });
};
util.inherits(EventSource, Stream);

EventSource.isEventSource = function(request) {
  if (request.method !== 'GET') return false;
  var accept = (request.headers.accept || '').split(/\s*,\s*/);
  return accept.indexOf('text/event-stream') >= 0;
};

var instance = {
  DEFAULT_PING:   10,
  DEFAULT_RETRY:  5,

  _write: function(chunk) {
    if (!this.writable) return false;
    try {
      return this._stream.write(chunk, 'utf8');
    } catch (e) {
      return false;
    }
  },

  _open: function() {
    if (this.readyState !== API.CONNECTING) return;

    this.readyState = API.OPEN;

    var event = new Event('open');
    event.initEvent('open', false, false);
    this.dispatchEvent(event);
  },

  write: function(message) {
    return this.send(message);
  },

  end: function(message) {
    if (message !== undefined) this.write(message);
    this.close();
  },

  send: function(message, options) {
    if (this.readyState > API.OPEN) return false;

    message = String(message).replace(/(\r\n|\r|\n)/g, '$1data: ');
    options = options || {};

    var frame = '';
    if (options.event) frame += 'event: ' + options.event + '\r\n';
    if (options.id)    frame += 'id: '    + options.id    + '\r\n';
    frame += 'data: ' + message + '\r\n\r\n';

    return this._write(frame);
  },

  ping: function() {
    return this._write(':\r\n\r\n');
  },

  close: function() {
    if (this.readyState > API.OPEN) return false;

    this.readyState = API.CLOSED;
    this.writable = false;
    if (this._pingTimer) clearInterval(this._pingTimer);
    if (this._stream) this._stream.end();

    var event = new Event('close');
    event.initEvent('close', false, false);
    this.dispatchEvent(event);

    return true;
  }
};

for (var method in instance) EventSource.prototype[method] = instance[method];
for (var key in EventTarget) EventSource.prototype[key] = EventTarget[key];

module.exports = EventSource;


code:
// API references:
//
// * https://html.spec.whatwg.org/multipage/comms.html#network
// * https://dom.spec.whatwg.org/#interface-eventtarget
// * https://dom.spec.whatwg.org/#interface-event

'use strict';

var util   = require('util'),
    driver = require('websocket-driver'),
    API    = require('./websocket/api');

var WebSocket = function(request, socket, body, protocols, options) {
  options = options || {};

  this._stream = socket;
  this._driver = driver.http(request, { maxLength: options.maxLength, protocols: protocols });

  var self = this;
  if (!this._stream || !this._stream.writable) return;
  if (!this._stream.readable) return this._stream.end();

  var catchup = function() { self._stream.removeListener('data', catchup) };
  this._stream.on('data', catchup);

  API.call(this, options);

  process.nextTick(function() {
    self._driver.start();
    self._driver.io.write(body);
  });
};
util.inherits(WebSocket, API);

WebSocket.isWebSocket = function(request) {
  return driver.isWebSocket(request);
};

WebSocket.validateOptions = function(options, validKeys) {
  driver.validateOptions(options, validKeys);
};

WebSocket.WebSocket   = WebSocket;
WebSocket.Client      = require('./websocket/client');
WebSocket.EventSource = require('./eventsource');

module.exports        = WebSocket;


code:
'use strict';

var Stream      = require('stream').Stream,
    util        = require('util'),
    driver      = require('websocket-driver'),
    EventTarget = require('./api/event_target'),
    Event       = require('./api/event');

var API = function(options) {
  options = options || {};
  driver.validateOptions(options, ['headers', 'extensions', 'maxLength', 'ping', 'proxy', 'tls', 'ca']);

  this.readable = this.writable = true;

  var headers = options.headers;
  if (headers) {
    for (var name in headers) this._driver.setHeader(name, headers[name]);
  }

  var extensions = options.extensions;
  if (extensions) {
    [].concat(extensions).forEach(this._driver.addExtension, this._driver);
  }

  this._ping          = options.ping;
  this._pingId        = 0;
  this.readyState     = API.CONNECTING;
  this.bufferedAmount = 0;
  this.protocol       = '';
  this.url            = this._driver.url;
  this.version        = this._driver.version;

  var self = this;

  this._driver.on('open',    function(e) { self._open() });
  this._driver.on('message', function(e) { self._receiveMessage(e.data) });
  this._driver.on('close',   function(e) { self._beginClose(e.reason, e.code) });

  this._driver.on('error', function(error) {
    self._emitError(error.message);
  });
  this.on('error', function() {});

  this._driver.messages.on('drain', function() {
    self.emit('drain');
  });

  if (this._ping)
    this._pingTimer = setInterval(function() {
      self._pingId += 1;
      self.ping(self._pingId.toString());
    }, this._ping * 1000);

  this._configureStream();

  if (!this._proxy) {
    this._stream.pipe(this._driver.io);
    this._driver.io.pipe(this._stream);
  }
};
util.inherits(API, Stream);

API.CONNECTING = 0;
API.OPEN       = 1;
API.CLOSING    = 2;
API.CLOSED     = 3;

API.CLOSE_TIMEOUT = 30000;

var instance = {
  write: function(data) {
    return this.send(data);
  },

  end: function(data) {
    if (data !== undefined) this.send(data);
    this.close();
  },

  pause: function() {
    return this._driver.messages.pause();
  },

  resume: function() {
    return this._driver.messages.resume();
  },

  send: function(data) {
    if (this.readyState > API.OPEN) return false;
    if (!(data instanceof Buffer)) data = String(data);
    return this._driver.messages.write(data);
  },

  ping: function(message, callback) {
    if (this.readyState > API.OPEN) return false;
    return this._driver.ping(message, callback);
  },

  close: function(code, reason) {
    if (code === undefined) code = 1000;
    if (reason === undefined) reason = '';

    if (code !== 1000 && (code < 3000 || code > 4999))
      throw new Error("Failed to execute 'close' on WebSocket: " +
                      "The code must be either 1000, or between 3000 and 4999. " +
                      code + " is neither.");

    if (this.readyState < API.CLOSING) {
      var self = this;
      this._closeTimer = setTimeout(function() {
        self._beginClose('', 1006);
      }, API.CLOSE_TIMEOUT);
    }

    if (this.readyState !== API.CLOSED) this.readyState = API.CLOSING;

    this._driver.close(reason, code);
  },

  _configureStream: function() {
    var self = this;

    this._stream.setTimeout(0);
    this._stream.setNoDelay(true);

    ['close', 'end'].forEach(function(event) {
      this._stream.on(event, function() { self._finalizeClose() });
    }, this);

    this._stream.on('error', function(error) {
      self._emitError('Network error: ' + self.url + ': ' + error.message);
      self._finalizeClose();
    });
  },

  _open: function() {
    if (this.readyState !== API.CONNECTING) return;

    this.readyState = API.OPEN;
    this.protocol = this._driver.protocol || '';

    var event = new Event('open');
    event.initEvent('open', false, false);
    this.dispatchEvent(event);
  },

  _receiveMessage: function(data) {
    if (this.readyState > API.OPEN) return false;

    if (this.readable) this.emit('data', data);

    var event = new Event('message', { data: data });
    event.initEvent('message', false, false);
    this.dispatchEvent(event);
  },

  _emitError: function(message) {
    if (this.readyState >= API.CLOSING) return;

    var event = new Event('error', { message: message });
    event.initEvent('error', false, false);
    this.dispatchEvent(event);
  },

  _beginClose: function(reason, code) {
    if (this.readyState === API.CLOSED) return;
    this.readyState = API.CLOSING;
    this._closeParams = [reason, code];

    if (this._stream) {
      this._stream.destroy();
      if (!this._stream.readable) this._finalizeClose();
    }
  },

  _finalizeClose: function() {
    if (this.readyState === API.CLOSED) return;
    this.readyState = API.CLOSED;

    if (this._closeTimer) clearTimeout(this._closeTimer);
    if (this._pingTimer) clearInterval(this._pingTimer);
    if (this._stream) this._stream.end();

    if (this.readable) this.emit('end');
    this.readable = this.writable = false;

    var reason = this._closeParams ? this._closeParams[0] : '',
        code   = this._closeParams ? this._closeParams[1] : 1006;

    var event = new Event('close', { code: code, reason: reason });
    event.initEvent('close', false, false);
    this.dispatchEvent(event);
  }
};

for (var method in instance) API.prototype[method] = instance[method];
for (var key in EventTarget) API.prototype[key] = EventTarget[key];

module.exports = API;


code:
'use strict';

var Event = function(eventType, options) {
  this.type = eventType;
  for (var key in options)
    this[key] = options[key];
};

Event.prototype.initEvent = function(eventType, canBubble, cancelable) {
  this.type       = eventType;
  this.bubbles    = canBubble;
  this.cancelable = cancelable;
};

Event.prototype.stopPropagation = function() {};
Event.prototype.preventDefault  = function() {};

Event.CAPTURING_PHASE = 1;
Event.AT_TARGET       = 2;
Event.BUBBLING_PHASE  = 3;

module.exports = Event;


code:
'use strict';

var Event = require('./event');

var EventTarget = {
  onopen:     null,
  onmessage:  null,
  onerror:    null,
  onclose:    null,

  addEventListener: function(eventType, listener, useCapture) {
    this.on(eventType, listener);
  },

  removeEventListener: function(eventType, listener, useCapture) {
    this.removeListener(eventType, listener);
  },

  dispatchEvent: function(event) {
    event.target = event.currentTarget = this;
    event.eventPhase = Event.AT_TARGET;

    if (this['on' + event.type])
      this['on' + event.type](event);

    this.emit(event.type, event);
  }
};

module.exports = EventTarget;


code:
'use strict';

var util   = require('util'),
    net    = require('net'),
    tls    = require('tls'),
    url    = require('url'),
    driver = require('websocket-driver'),
    API    = require('./api'),
    Event  = require('./api/event');

var DEFAULT_PORTS    = { 'http:': 80, 'https:': 443, 'ws:':80, 'wss:': 443 },
    SECURE_PROTOCOLS = ['https:', 'wss:'];

var Client = function(_url, protocols, options) {
  options = options || {};

  this.url     = _url;
  this._driver = driver.client(this.url, { maxLength: options.maxLength, protocols: protocols });

  ['open', 'error'].forEach(function(event) {
    this._driver.on(event, function() {
      self.headers    = self._driver.headers;
      self.statusCode = self._driver.statusCode;
    });
  }, this);

  var proxy      = options.proxy || {},
      endpoint   = url.parse(proxy.origin || this.url),
      port       = endpoint.port || DEFAULT_PORTS[endpoint.protocol],
      secure     = SECURE_PROTOCOLS.indexOf(endpoint.protocol) >= 0,
      onConnect  = function() { self._onConnect() },
      netOptions = options.net || {},
      originTLS  = options.tls || {},
      socketTLS  = proxy.origin ? (proxy.tls || {}) : originTLS,
      self       = this;

  netOptions.host = socketTLS.host = endpoint.hostname;
  netOptions.port = socketTLS.port = port;

  originTLS.ca = originTLS.ca || options.ca;
  socketTLS.servername = socketTLS.servername || endpoint.hostname;

  this._stream = secure
               ? tls.connect(socketTLS, onConnect)
               : net.connect(netOptions, onConnect);

  if (proxy.origin) this._configureProxy(proxy, originTLS);

  API.call(this, options);
};
util.inherits(Client, API);

Client.prototype._onConnect = function() {
  var worker = this._proxy || this._driver;
  worker.start();
};

Client.prototype._configureProxy = function(proxy, originTLS) {
  var uri    = url.parse(this.url),
      secure = SECURE_PROTOCOLS.indexOf(uri.protocol) >= 0,
      self   = this,
      name;

  this._proxy = this._driver.proxy(proxy.origin);

  if (proxy.headers) {
    for (name in proxy.headers) this._proxy.setHeader(name, proxy.headers[name]);
  }

  this._proxy.pipe(this._stream, { end: false });
  this._stream.pipe(this._proxy);

  this._proxy.on('connect', function() {
    if (secure) {
      var options = { socket: self._stream, servername: uri.hostname };
      for (name in originTLS) options[name] = originTLS[name];
      self._stream = tls.connect(options);
      self._configureStream();
    }
    self._driver.io.pipe(self._stream);
    self._stream.pipe(self._driver.io);
    self._driver.start();
  });

  this._proxy.on('error', function(error) {
    self._driver.emit('error', error);
  });
};

module.exports = Client;


faye-websocket

description: Standards-compliant WebSocket server and client

author: James Coglan <jcoglan@gmail.com> (http://jcoglan.com/)

keywords: websocket eventsource

test: jstest spec/runner.js