| # BFJ |
| |
| [](https://gitlab.com/philbooth/bfj/pipelines) |
| [](https://www.npmjs.com/package/bfj) |
| [](https://www.npmjs.com/package/bfj) |
| [](https://opensource.org/licenses/MIT) |
| |
| Big-Friendly JSON. Asynchronous streaming functions for large JSON data sets. |
| |
| * [Why would I want those?](#why-would-i-want-those) |
| * [Is it fast?](#is-it-fast) |
| * [What functions does it implement?](#what-functions-does-it-implement) |
| * [How do I install it?](#how-do-i-install-it) |
| * [How do I read a JSON file?](#how-do-i-read-a-json-file) |
| * [How do I parse a stream of JSON?](#how-do-i-parse-a-stream-of-json) |
| * [How do I selectively parse individual items from a JSON stream?](#how-do-i-selectively-parse-individual-items-from-a-json-stream) |
| * [How do I write a JSON file?](#how-do-i-write-a-json-file) |
| * [How do I create a stream of JSON?](#how-do-i-create-a-stream-of-json) |
| * [How do I create a JSON string?](#how-do-i-create-a-json-string) |
| * [What other methods are there?](#what-other-methods-are-there) |
| * [bfj.walk (stream, options)](#bfjwalk-stream-options) |
| * [bfj.eventify (data, options)](#bfjeventify-data-options) |
| * [What options can I specify?](#what-options-can-i-specify) |
| * [Options for parsing functions](#options-for-parsing-functions) |
| * [Options for serialisation functions](#options-for-serialisation-functions) |
| * [Is it possible to pause parsing or serialisation from calling code?](#is-it-possible-to-pause-parsing-or-serialisation-from-calling-code) |
| * [Can it handle newline-delimited JSON (NDJSON)?](#can-it-handle-newline-delimited-json-ndjson) |
| * [Why does it default to bluebird promises?](#why-does-it-default-to-bluebird-promises) |
| * [Can I specify a different promise implementation?](#can-i-specify-a-different-promise-implementation) |
| * [Is there a change log?](#is-there-a-change-log) |
| * [How do I set up the dev environment?](#how-do-i-set-up-the-dev-environment) |
| * [What versions of Node.js does it support?](#what-versions-of-nodejs-does-it-support) |
| * [What license is it released under?](#what-license-is-it-released-under) |
| |
| ## Why would I want those? |
| |
| If you need |
| to parse huge JSON strings |
| or stringify huge JavaScript data sets, |
| it monopolises the event loop |
| and can lead to out-of-memory exceptions. |
| BFJ implements asynchronous functions |
| and uses pre-allocated fixed-length arrays |
| to try and alleviate those issues. |
| |
| ## Is it fast? |
| |
| No. |
| |
| BFJ yields frequently |
| to avoid monopolising the event loop, |
| interrupting its own execution |
| to let other event handlers run. |
| The frequency of those yields |
| can be controlled with the [`yieldRate` option](#what-options-can-i-specify), |
| but fundamentally it is not designed for speed. |
| |
| Furthermore, |
| when serialising data to a stream, |
| BFJ uses a fixed-length buffer |
| to avoid exhausting available memory. |
| Whenever that buffer is full, |
| serialisation is paused |
| until the receiving stream processes some more data, |
| regardless of the value of `yieldRate`. |
| You can control the size of the buffer |
| using the [`bufferLength` option](#options-for-serialisation-functions) |
| but really, |
| if you need quick results, |
| BFJ is not for you. |
| |
| ## What functions does it implement? |
| |
| Nine functions |
| are exported. |
| |
| Five are |
| concerned with |
| parsing, or |
| turning JSON strings |
| into JavaScript data: |
| |
| * [`read`](#how-do-i-read-a-json-file) |
| asynchronously parses |
| a JSON file from disk. |
| |
| * [`parse` and `unpipe`](#how-do-i-parse-a-stream-of-json) |
| are for asynchronously parsing |
| streams of JSON. |
| |
| * [`match`](#how-do-i-selectively-parse-individual-items-from-a-json-stream) |
| selectively parses individual items |
| from a JSON stream. |
| |
| * [`walk`](#bfjwalk-stream-options) |
| asynchronously walks |
| a stream, |
| emitting events |
| as it encounters |
| JSON tokens. |
| Analagous to a |
| [SAX parser][sax]. |
| |
| The other four functions |
| handle the reverse transformations, |
| serialising |
| JavaScript data |
| to JSON: |
| |
| * [`write`](#how-do-i-write-a-json-file) |
| asynchronously serialises data |
| to a JSON file on disk. |
| |
| * [`streamify`](#how-do-i-create-a-stream-of-json) |
| asynchronously serialises data |
| to a stream of JSON. |
| |
| * [`stringify`](#how-do-i-create-a-json-string) |
| asynchronously serialises data |
| to a JSON string. |
| |
| * [`eventify`](#bfjeventify-data-options) |
| asynchronously traverses |
| a data structure |
| depth-first, |
| emitting events |
| as it encounters items. |
| By default |
| it coerces |
| promises, buffers and iterables |
| to JSON-friendly values. |
| |
| ## How do I install it? |
| |
| If you're using npm: |
| |
| ``` |
| npm i bfj --save |
| ``` |
| |
| Or if you just want |
| the git repo: |
| |
| ``` |
| git clone git@gitlab.com:philbooth/bfj.git |
| ``` |
| |
| ## How do I read a JSON file? |
| |
| ```js |
| const bfj = require('bfj'); |
| |
| bfj.read(path, options) |
| .then(data => { |
| // :) |
| }) |
| .catch(error => { |
| // :( |
| }); |
| ``` |
| |
| `read` returns a [bluebird promise][promise] and |
| asynchronously parses |
| a JSON file |
| from disk. |
| |
| It takes two arguments; |
| the path to the JSON file |
| and an [options](#options-for-parsing-functions) object. |
| |
| If there are |
| no syntax errors, |
| the returned promise is resolved |
| with the parsed data. |
| If syntax errors occur, |
| the promise is rejected |
| with the first error. |
| |
| ## How do I parse a stream of JSON? |
| |
| ```js |
| const bfj = require('bfj'); |
| |
| // By passing a readable stream to bfj.parse(): |
| bfj.parse(fs.createReadStream(path), options) |
| .then(data => { |
| // :) |
| }) |
| .catch(error => { |
| // :( |
| }); |
| |
| // ...or by passing the result from bfj.unpipe() to stream.pipe(): |
| request({ url }).pipe(bfj.unpipe((error, data) => { |
| if (error) { |
| // :( |
| } else { |
| // :) |
| } |
| })) |
| ``` |
| |
| * `parse` returns a [bluebird promise][promise] |
| and asynchronously parses |
| a stream of JSON data. |
| |
| It takes two arguments; |
| a [readable stream][readable] |
| from which |
| the JSON |
| will be parsed |
| and an [options](#options-for-parsing-functions) object. |
| |
| If there are |
| no syntax errors, |
| the returned promise is resolved |
| with the parsed data. |
| If syntax errors occur, |
| the promise is rejected |
| with the first error. |
| |
| * `unpipe` returns a [writable stream][writable] |
| that can be passed to [`stream.pipe`][pipe], |
| then parses JSON data |
| read from the stream. |
| |
| It takes two arguments; |
| a callback function |
| that will be called |
| after parsing is complete |
| and an [options](#options-for-parsing-functions) object. |
| |
| If there are no errors, |
| the callback is invoked |
| with the result as the second argument. |
| If errors occur, |
| the first error is passed |
| the callback |
| as the first argument. |
| |
| ## How do I selectively parse individual items from a JSON stream? |
| |
| ```js |
| const bfj = require('bfj'); |
| |
| // Call match with your stream and a selector predicate/regex/string |
| const dataStream = bfj.match(jsonStream, selector, options); |
| |
| // Get data out of the returned stream with event handlers |
| dataStream.on('data', item => { /* ... */ }); |
| dataStream.on('end', () => { /* ... */); |
| dataStream.on('error', () => { /* ... */); |
| dataStream.on('dataError', () => { /* ... */); |
| |
| // ...or you can pipe it to another stream |
| dataStream.pipe(someOtherStream); |
| ``` |
| |
| `match` returns a readable, object-mode stream |
| and asynchronously parses individual matching items |
| from an input JSON stream. |
| |
| It takes three arguments: |
| a [readable stream][readable] |
| from which the JSON will be parsed; |
| a selector argument for determining matches, |
| which may be a string, a regular expression or a predicate function; |
| and an [options](#options-for-parsing-functions) object. |
| |
| If the selector is a string, |
| it will be compared to property keys |
| to determine whether |
| each item in the data is a match. |
| If it is a regular expression, |
| the comparison will be made |
| by calling the [RegExp `test` method][regexp-test] |
| with the property key. |
| Predicate functions will be called with three arguments: |
| `key`, `value` and `depth`. |
| If the result of the predicate is a truthy value |
| then the item will be deemed a match. |
| |
| If there are any syntax errors in the JSON, |
| a `dataError` event will be emitted. |
| If any other errors occur, |
| an `error` event will be emitted. |
| |
| ## How do I write a JSON file? |
| |
| ```js |
| const bfj = require('bfj'); |
| |
| bfj.write(path, data, options) |
| .then(() => { |
| // :) |
| }) |
| .catch(error => { |
| // :( |
| }); |
| ``` |
| |
| `write` returns a [bluebird promise][promise] |
| and asynchronously serialises a data structure |
| to a JSON file on disk. |
| The promise is resolved |
| when the file has been written, |
| or rejected with the error |
| if writing failed. |
| |
| It takes three arguments; |
| the path to the JSON file, |
| the data structure to serialise |
| and an [options](#options-for-serialisation-functions) object. |
| |
| ## How do I create a stream of JSON? |
| |
| ```js |
| const bfj = require('bfj'); |
| |
| const stream = bfj.streamify(data, options); |
| |
| // Get data out of the stream with event handlers |
| stream.on('data', chunk => { /* ... */ }); |
| stream.on('end', () => { /* ... */); |
| stream.on('error', () => { /* ... */); |
| stream.on('dataError', () => { /* ... */); |
| |
| // ...or you can pipe it to another stream |
| stream.pipe(someOtherStream); |
| ``` |
| |
| `streamify` returns a [readable stream][readable] |
| and asynchronously serialises |
| a data structure to JSON, |
| pushing the result |
| to the returned stream. |
| |
| It takes two arguments; |
| the data structure to serialise |
| and an [options](#options-for-serialisation-functions) object. |
| |
| If there a circular reference is encountered in the data |
| and `options.circular` is not set to `'ignore'`, |
| a `dataError` event will be emitted. |
| If any other errors occur, |
| an `error` event will be emitted. |
| |
| ## How do I create a JSON string? |
| |
| ```js |
| const bfj = require('bfj'); |
| |
| bfj.stringify(data, options) |
| .then(json => { |
| // :) |
| }) |
| .catch(error => { |
| // :( |
| }); |
| ``` |
| |
| `stringify` returns a [bluebird promise][promise] and |
| asynchronously serialises a data structure |
| to a JSON string. |
| The promise is resolved |
| to the JSON string |
| when serialisation is complete. |
| |
| It takes two arguments; |
| the data structure to serialise |
| and an [options](#options-for-serialisation-functions) object. |
| |
| ## What other methods are there? |
| |
| ### bfj.walk (stream, options) |
| |
| ```js |
| const bfj = require('bfj'); |
| |
| const emitter = bfj.walk(fs.createReadStream(path), options); |
| |
| emitter.on(bfj.events.array, () => { /* ... */ }); |
| emitter.on(bfj.events.object, () => { /* ... */ }); |
| emitter.on(bfj.events.property, name => { /* ... */ }); |
| emitter.on(bfj.events.string, value => { /* ... */ }); |
| emitter.on(bfj.events.number, value => { /* ... */ }); |
| emitter.on(bfj.events.literal, value => { /* ... */ }); |
| emitter.on(bfj.events.endArray, () => { /* ... */ }); |
| emitter.on(bfj.events.endObject, () => { /* ... */ }); |
| emitter.on(bfj.events.error, error => { /* ... */ }); |
| emitter.on(bfj.events.dataError, error => { /* ... */ }); |
| emitter.on(bfj.events.end, () => { /* ... */ }); |
| ``` |
| |
| `walk` returns an [event emitter][eventemitter] |
| and asynchronously walks |
| a stream of JSON data, |
| emitting events |
| as it encounters |
| tokens. |
| |
| It takes two arguments; |
| a [readable stream][readable] |
| from which |
| the JSON |
| will be read |
| and an [options](#options-for-parsing-functions) object. |
| |
| The emitted events |
| are defined |
| as public properties |
| of an object, |
| `bfj.events`: |
| |
| * `bfj.events.array` |
| indicates that |
| an array context |
| has been entered |
| by encountering |
| the `[` character. |
| |
| * `bfj.events.endArray` |
| indicates that |
| an array context |
| has been left |
| by encountering |
| the `]` character. |
| |
| * `bfj.events.object` |
| indicates that |
| an object context |
| has been entered |
| by encountering |
| the `{` character. |
| |
| * `bfj.events.endObject` |
| indicates that |
| an object context |
| has been left |
| by encountering |
| the `}` character. |
| |
| * `bfj.events.property` |
| indicates that |
| a property |
| has been encountered |
| in an object. |
| The listener |
| will be passed |
| the name of the property |
| as its argument |
| and the next event |
| to be emitted |
| will represent |
| the property's value. |
| |
| * `bfj.events.string` |
| indicates that |
| a string |
| has been encountered. |
| The listener |
| will be passed |
| the value |
| as its argument. |
| |
| * `bfj.events.number` |
| indicates that |
| a number |
| has been encountered. |
| The listener |
| will be passed |
| the value |
| as its argument. |
| |
| * `bfj.events.literal` |
| indicates that |
| a JSON literal |
| (either `true`, `false` or `null`) |
| has been encountered. |
| The listener |
| will be passed |
| the value |
| as its argument. |
| |
| * `bfj.events.error` |
| indicates that |
| an error was caught |
| from one of the event handlers |
| in user code. |
| The listener |
| will be passed |
| the `Error` instance |
| as its argument. |
| |
| * `bfj.events.dataError` |
| indicates that |
| a syntax error was encountered |
| in the incoming JSON stream. |
| The listener |
| will be passed |
| an `Error` instance |
| decorated with `actual`, `expected`, `lineNumber` and `columnNumber` properties |
| as its argument. |
| |
| * `bfj.events.end` |
| indicates that |
| the end of the input |
| has been reached |
| and the stream is closed. |
| |
| * `bfj.events.endLine` |
| indicates that a root-level newline character |
| has been encountered in an [NDJSON](#can-it-handle-newline-delimited-json-ndjson) stream. |
| Only emitted if the `ndjson` [option](#options-for-parsing-functions) is set. |
| |
| If you are using `bfj.walk` |
| to sequentially parse items in an array, |
| you might also be interested in |
| the [bfj-collections] module. |
| |
| ### bfj.eventify (data, options) |
| |
| ```js |
| const bfj = require('bfj'); |
| |
| const emitter = bfj.eventify(data, options); |
| |
| emitter.on(bfj.events.array, () => { /* ... */ }); |
| emitter.on(bfj.events.object, () => { /* ... */ }); |
| emitter.on(bfj.events.property, name => { /* ... */ }); |
| emitter.on(bfj.events.string, value => { /* ... */ }); |
| emitter.on(bfj.events.number, value => { /* ... */ }); |
| emitter.on(bfj.events.literal, value => { /* ... */ }); |
| emitter.on(bfj.events.endArray, () => { /* ... */ }); |
| emitter.on(bfj.events.endObject, () => { /* ... */ }); |
| emitter.on(bfj.events.error, error => { /* ... */ }); |
| emitter.on(bfj.events.dataError, error => { /* ... */ }); |
| emitter.on(bfj.events.end, () => { /* ... */ }); |
| ``` |
| |
| `eventify` returns an [event emitter][eventemitter] |
| and asynchronously traverses |
| a data structure depth-first, |
| emitting events as it |
| encounters items. |
| By default it coerces |
| promises, buffers and iterables |
| to JSON-friendly values. |
| |
| It takes two arguments; |
| the data structure to traverse |
| and an [options](#options-for-serialisation-functions) object. |
| |
| The emitted events |
| are defined |
| as public properties |
| of an object, |
| `bfj.events`: |
| |
| * `bfj.events.array` |
| indicates that |
| an array |
| has been encountered. |
| |
| * `bfj.events.endArray` |
| indicates that |
| the end of an array |
| has been encountered. |
| |
| * `bfj.events.object` |
| indicates that |
| an object |
| has been encountered. |
| |
| * `bfj.events.endObject` |
| indicates that |
| the end of an object |
| has been encountered. |
| |
| * `bfj.events.property` |
| indicates that |
| a property |
| has been encountered |
| in an object. |
| The listener |
| will be passed |
| the name of the property |
| as its argument |
| and the next event |
| to be emitted |
| will represent |
| the property's value. |
| |
| * `bfj.events.string` |
| indicates that |
| a string |
| has been encountered. |
| The listener |
| will be passed |
| the value |
| as its argument. |
| |
| * `bfj.events.number` |
| indicates that |
| a number |
| has been encountered. |
| The listener |
| will be passed |
| the value |
| as its argument. |
| |
| * `bfj.events.literal` |
| indicates that |
| a JSON literal |
| (either `true`, `false` or `null`) |
| has been encountered. |
| The listener |
| will be passed |
| the value |
| as its argument. |
| |
| * `bfj.events.error` |
| indicates that |
| an error was caught |
| from one of the event handlers |
| in user code. |
| The listener |
| will be passed |
| the `Error` instance |
| as its argument. |
| |
| * `bfj.events.dataError` |
| indicates that |
| a circular reference was encountered in the data |
| and the `circular` option was not set to `'ignore'`. |
| The listener |
| will be passed |
| an `Error` instance |
| as its argument. |
| |
| * `bfj.events.end` |
| indicates that |
| the end of the data |
| has been reached and |
| no further events |
| will be emitted. |
| |
| ## What options can I specify? |
| |
| ### Options for parsing functions |
| |
| * `options.reviver`: |
| Transformation function, |
| invoked depth-first |
| against the parsed |
| data structure. |
| This option |
| is analagous to the |
| [reviver parameter for JSON.parse][reviver]. |
| |
| * `options.yieldRate`: |
| The number of data items to process |
| before yielding to the event loop. |
| Smaller values yield to the event loop more frequently, |
| meaning less time will be consumed by bfj per tick |
| but the overall parsing time will be slower. |
| Larger values yield to the event loop less often, |
| meaning slower tick times but faster overall parsing time. |
| The default value is `16384`. |
| |
| * `options.Promise`: |
| Promise constructor that will be used |
| for promises returned by all methods. |
| If you set this option, |
| please be aware that some promise implementations |
| (including native promises) |
| may cause your process to die |
| with out-of-memory exceptions. |
| Defaults to [bluebird's implementation][promise], |
| which does not have that problem. |
| |
| * `options.ndjson`: |
| If set to `true`, |
| newline characters at the root level |
| will be treated as delimiters between |
| discrete chunks of JSON. |
| See [NDJSON](#can-it-handle-newline-delimited-json-ndjson) for more information. |
| |
| * `options.numbers`: |
| For `bfj.match` only, |
| set this to `true` |
| if you wish to match against numbers |
| with a string or regular expression |
| `selector` argument. |
| |
| * `options.bufferLength`: |
| For `bfj.match` only, |
| the length of the match buffer. |
| Smaller values use less memory |
| but may result in a slower parse time. |
| The default value is `1024`. |
| |
| * `options.highWaterMark`: |
| For `bfj.match` only, |
| set this if you would like to |
| pass a value for the `highWaterMark` option |
| to the readable stream constructor. |
| |
| ### Options for serialisation functions |
| |
| * `options.space`: |
| Indentation string |
| or the number of spaces |
| to indent |
| each nested level by. |
| This option |
| is analagous to the |
| [space parameter for JSON.stringify][space]. |
| |
| * `options.promises`: |
| By default, |
| promises are coerced |
| to their resolved value. |
| Set this property |
| to `'ignore'` |
| for improved performance |
| if you don't need |
| to coerce promises. |
| |
| * `options.buffers`: |
| By default, |
| buffers are coerced |
| using their `toString` method. |
| Set this property |
| to `'ignore'` |
| for improved performance |
| if you don't need |
| to coerce buffers. |
| |
| * `options.maps`: |
| By default, |
| maps are coerced |
| to plain objects. |
| Set this property |
| to `'ignore'` |
| for improved performance |
| if you don't need |
| to coerce maps. |
| |
| * `options.iterables`: |
| By default, |
| other iterables |
| (i.e. not arrays, strings or maps) |
| are coerced |
| to arrays. |
| Set this property |
| to `'ignore'` |
| for improved performance |
| if you don't need |
| to coerce iterables. |
| |
| * `options.circular`: |
| By default, |
| circular references |
| will cause the write |
| to fail. |
| Set this property |
| to `'ignore'` |
| if you'd prefer |
| to silently skip past |
| circular references |
| in the data. |
| |
| * `options.bufferLength`: |
| The length of the write buffer. |
| Smaller values use less memory |
| but may result in a slower serialisation time. |
| The default value is `1024`. |
| |
| * `options.highWaterMark`: |
| Set this if you would like to |
| pass a value for the `highWaterMark` option |
| to the readable stream constructor. |
| |
| * `options.yieldRate`: |
| The number of data items to process |
| before yielding to the event loop. |
| Smaller values yield to the event loop more frequently, |
| meaning less time will be consumed by bfj per tick |
| but the overall serialisation time will be slower. |
| Larger values yield to the event loop less often, |
| meaning slower tick times but faster overall serialisation time. |
| The default value is `16384`. |
| |
| * `options.Promise`: |
| Promise constructor that will be used |
| for promises returned by all methods. |
| If you set this option, |
| please be aware that some promise implementations |
| (including native promises) |
| may cause your process to die |
| with out-of-memory exceptions. |
| Defaults to [bluebird's implementation][promise], |
| which does not have that problem. |
| |
| ## Is it possible to pause parsing or serialisation from calling code? |
| |
| Yes it is! |
| Both [`walk`](#bfjwalk-stream-options) |
| and [`eventify`](#bfjeventify-data-options) |
| decorate their returned event emitters |
| with a `pause` method |
| that will prevent any further events being emitted. |
| The `pause` method itself |
| returns a `resume` function |
| that you can call to indicate |
| that processing should continue. |
| |
| For example: |
| |
| ```js |
| const bfj = require('bfj'); |
| const emitter = bfj.walk(fs.createReadStream(path), options); |
| |
| // Later, when you want to pause parsing: |
| |
| const resume = emitter.pause(); |
| |
| // Then when you want to resume: |
| |
| resume(); |
| ``` |
| |
| ## Can it handle [newline-delimited JSON (NDJSON)](http://ndjson.org/)? |
| |
| Yes. |
| If you pass the `ndjson` [option](#options-for-parsing-functions) |
| to `bfj.walk`, `bfj.match` or `bfj.parse`, |
| newline characters at the root level |
| will act as delimiters between |
| discrete JSON values: |
| |
| * `bfj.walk` will emit a `bfj.events.endLine` event |
| each time it encounters a newline character. |
| |
| * `bfj.match` will just ignore the newlines |
| while it continues looking for matching items. |
| |
| * `bfj.parse` will resolve with the first value |
| and pause the underlying stream. |
| If it's called again with the same stream, |
| it will resume processing |
| and resolve with the second value. |
| To parse the entire stream, |
| calls should be made sequentially one-at-a-time |
| until the returned promise |
| resolves to `undefined` |
| (`undefined` is not a valid JSON token). |
| |
| `bfj.unpipe` and `bfj.read` will not parse NDJSON. |
| |
| ## Why does it default to bluebird promises? |
| |
| Until version `4.2.4`, |
| native promises were used. |
| But they were found |
| to cause out-of-memory errors |
| when serialising large amounts of data to JSON, |
| due to [well-documented problems |
| with the native promise implementation](https://alexn.org/blog/2017/10/11/javascript-promise-leaks-memory.html). |
| So in version `5.0.0`, |
| bluebird promises were used instead. |
| In version `5.1.0`, |
| an option was added |
| that enables callers to specify |
| the promise constructor to use. |
| Use it at your own risk. |
| |
| ## Can I specify a different promise implementation? |
| |
| Yes. |
| Just pass the `Promise` option |
| to any method. |
| If you get out-of-memory errors |
| when using that option, |
| consider changing your promise implementation. |
| |
| ## Is there a change log? |
| |
| [Yes][history]. |
| |
| ## How do I set up the dev environment? |
| |
| The development environment |
| relies on [Node.js][node], |
| [ESLint], |
| [Mocha], |
| [Chai], |
| [Proxyquire] and |
| [Spooks]. |
| Assuming that |
| you already have |
| node and NPM |
| set up, |
| you just need |
| to run |
| `npm install` |
| to install |
| all of the dependencies |
| as listed in `package.json`. |
| |
| You can |
| lint the code |
| with the command |
| `npm run lint`. |
| |
| You can |
| run the tests |
| with the command |
| `npm test`. |
| |
| ## What versions of Node.js does it support? |
| |
| As of [version `3.0.0`](HISTORY.md#300), |
| only Node.js versions 6 or greater |
| are supported |
| because of the dependency |
| on [Hoopy](https://gitlab.com/philbooth/hoopy). |
| Previous versions supported |
| node 4 and later. |
| |
| A separate `node-4` branch was maintained |
| until version `5.4.1`, |
| which had feature parity version-for-version |
| with releases from `master`. |
| Releases from the `node-4` branch |
| are available in npm |
| under the package name [`bfj-node4`](https://www.npmjs.com/package/bfj-node4). |
| |
| ## What license is it released under? |
| |
| [MIT][license]. |
| |
| [ci-image]: https://secure.travis-ci.org/philbooth/bfj.png?branch=master |
| [ci-status]: http://travis-ci.org/#!/philbooth/bfj |
| [sax]: http://en.wikipedia.org/wiki/Simple_API_for_XML |
| [promise]: http://bluebirdjs.com/docs/api-reference.html |
| [bfj-collections]: https://github.com/hash-bang/bfj-collections |
| [eventemitter]: https://nodejs.org/api/events.html#events_class_eventemitter |
| [readable]: https://nodejs.org/api/stream.html#stream_readable_streams |
| [writable]: https://nodejs.org/api/stream.html#stream_writable_streams |
| [pipe]: https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options |
| [regexp-test]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test |
| [reviver]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Using_the_reviver_parameter |
| [space]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_space_argument |
| [history]: HISTORY.md |
| [node]: https://nodejs.org/en/ |
| [eslint]: http://eslint.org/ |
| [mocha]: https://mochajs.org/ |
| [chai]: http://chaijs.com/ |
| [proxyquire]: https://github.com/thlorenz/proxyquire |
| [spooks]: https://gitlab.com/philbooth/spooks.js |
| [license]: COPYING |