Source: through.js


/**
  * @module through
  * @author Marcos GJ 
  * @license MIT
  * @description 
  * # Through
  * [![NPM][NPM1][NPM2]]
  *
  * [![Build Status][1]][2] [![dependency status][3]][4] [![dev dependency status][5]][6]
  * 
  * ##\# A basic wrapper function around [`through2@0.4.x`](https://github.com/rvagg/through2)
  *
  * This is the base function used by [`super-stream`](https://github.com/markuz-gj/super-stream) as a standalone module. Also it is a drop in replacement for `through2`
  *
  * Why shouldn't you use `through2` instead of this module?  
  * You wouldn't if all you need is a basic helper for creating [`stream.Transform`](http://nodejs.org/api/stream.html#stream_class_stream_transform).
  *
  * But if you need some functional style transforms and other stream utilities and reduce your dependencies at the same time, **_this_** is your basic through stream you are looking for.
  * 
  * See also ...
 */
var buf, ctor, defaults, factory, isFunction, obj, through, through2;

through2 = require("through2");

isFunction = require("lodash.isfunction");

defaults = require("lodash.defaults");


/**
  * @instance
  *
  * @param {Object=} options - Same options as seen [here](/jsdoc/module-through.html#factory)
  * @param {Function=} transform - `_transform` function as described [here](http://nodejs.org/docs/latest/api/stream.html#stream_class_stream_transform)
  * @param {Function=} flush - `_flush` function as described [here](http://nodejs.org/docs/latest/api/stream.html#stream_class_stream_transform) (same link as above)
  * @return {transformStream} - A instance of `Transform` stream from `readable-stream@1.0.x`
  *
  * @desc 
  * ##\## Basic usage:
  *
  * ```javascript
  * var expect = require('chai').expect;
  *
  * var streamA = through.obj(function(counter, enc, done){
  *   counter += 1;
  *   done(null, counter);
  * });
  * var streamB = through({objectMode: true}, function(counter, enc, done){
  *   counter += 1;
  *   done(null, counter);
  * });
  *
  * thr = through.factory({objectMode: true});
  *
  * streamA.pipe(streamB).pipe(thr(function(counter, enc, done){
  *   expect(counter).to.be.equal(2);
  * }));
  *
  * streamA.write(0);
  * ```
  *
  * ```javascript
  * var streamA = through(function(chunk, enc, done){
  *   data = chunk.toString();
  *   done(null, new Buffer(data +'-'+ data));
  * });
  *
  * thrObj = through.factory({objectMode: true});
  * var streamB = thrObj.buf(function(chunk, enc, done){
  *   expect(chunk.toString()).to.be.equal('myData-myData');
  *   done();
  * });
  *
  * streamA.write(new Buffer('myData'));
  * ```
 */

through = function(cfg) {
  return function(options, transform, flush) {
    if (isFunction(options)) {
      flush = transform;
      transform = options;
      options = cfg;
    } else {
      options = defaults(options, cfg);
    }
    if (arguments.length === 0) {
      options = cfg;
    }
    return through2(options, transform, flush);
  };
};


/** 
  * @static
  * @param {Object=} options - Same options as seen [here](/jsdoc/module-through.html#factory)
  * @param {Function=} transform - `_transform` function as described [here](http://nodejs.org/docs/latest/api/stream.html#stream_class_stream_transform)
  * @param {Function=} flush - `_flush` function as described [here](http://nodejs.org/docs/latest/api/stream.html#stream_class_stream_transform) (same link as above)
  * @return {Transform} - A pre-configured `Transform` contructor from `readable-stream@1.0.x`
  *
  * @desc 
  * ##\## _ through.ctor([options], [transformFn], [flushFn]); _
  * Note: This is the same `ctor` method from `through2`  
  * If called without arguments, returns a passthrough `Transform` 
  *
  * ```javascript
  * var Ctor = through.ctor({objectMode: true}, transformFn, flushFn);
  * streamA = new Ctor();
  *
  * // no need for the new operator
  * streamB = Ctor(); 
  *
  * //overriding options
  * streamC = Ctor({objectMode: false}); 
  *
  * ```
 */

ctor = function(options, transform, flush) {
  return through2.ctor(options, transform, flush);
};


/** 
  * @static
  * @param {Function=} transform - `_transform` function as described [here](http://nodejs.org/docs/latest/api/stream.html#stream_class_stream_transform)
  * @param {Function=} flush - `_flush` function as described [here](http://nodejs.org/docs/latest/api/stream.html#stream_class_stream_transform) (same link as above)
  * @return {transformStream} - A instance of `Transform` stream from `readable-stream@1.0.x`
  *
  * @desc 
  * ##\## _ through.obj([transfromFn], [flushFn]) _
  * It is a conveniece method for `through({objectMode: true}, transformFn, flushFn);`  
  * If called without arguments, returns a passthrough `Transform` 
  *
  * Note: This is the same `obj` method from `through2`
  *
  * ```javascript
  * var streamObj = thr.obj(function(string, enc, done){
  *   expect(string).to.be.deep.equal({data: 'myData'});
  *   done();
  * });
  * streamObj.write({data: 'myData'});
  * ```
 */

obj = function(transform, flush) {
  return through2.obj(transform, flush);
};


/** 
  * @static
  * @param {Function=} transform - `_transform` function as described [here](http://nodejs.org/docs/latest/api/stream.html#stream_class_stream_transform)
  * @param {Function=} flush - `_flush` function as described [here](http://nodejs.org/docs/latest/api/stream.html#stream_class_stream_transform) (same link as above)
  * @return {transformStream} - A instance of `Transform` stream from `readable-stream@1.0.x`
  *
  * @desc 
  * ##\## _ through.buf([transfromFn], [flushFn]) _
  * It is a conveniece method for `through({objectMode: false}, transformFn, flushFn);`  
  * If called without arguments, returns a passthrough `Transform` 
  *
  * ```javascript
  * // see the factory method.
  * var thr = through.factory({objectMode: true});
  * var myData = new Buffer('my data');

  * var streamBuf = thr.buf(function(chunk, enc, done){
  *   expect(chunk).to.be.equal(myData);
  *   expect(chunk).to.not.be.equal('my data');
  *   done();
  * }):
  * streamBuf.write(myData);
  * ```
 */

buf = function(transform, flush) {
  return through2({
    objectMode: false
  }, transform, flush);
};


/** 
  * @static
  *
  * @param {Object=} options - Object passed to `stream.Transfrom` constructor as described [here](http://nodejs.org/docs/latest/api/stream.html#stream_new_stream_readable_options) and [here](http://nodejs.org/docs/latest/api/stream.html#stream_new_stream_duplex_options)
  * @param {Number=} [options.highWaterMark = 16kb]- The maximum number of bytes to store in the internal buffer before ceasing to read from the underlying resource.
  * @param {?String=} [options.encoding = null] -  If specified, then buffers will be decoded to strings using the specified encoding.
  * @param {Boolean=} [options.objectMode = false] - Whether this stream should behave as a stream of objects. Meaning that stream.read(n) returns a single value instead of a Buffer of size n.
  * @param {Boolean=} [options.allowHalfOpen = true] - If set to false, then the stream will automatically end the readable side when the writable side ends and vice versa.
  * @return {through} - A through function with `options` pre-configured as default
  *
  * @desc 
  * ##\## _ through.factory([options]); _
  * A factory method for creating a custom `through` instance.  
  *
  * ```javascript
  * var thrObj = through.factory({objectMode: true});
  *
  * var streamObj = thrObj(function(string, enc, done){
  *   expect(string).to.be.equal('my data');
  *   done();
  * });
  * streamObj.write('my data');
  * ```
  *
  * ```javascript
  * var myData = new Buffer('my data');
  * var thrBuf = through.factory({objectMode: false, highWaterMark: 1000*Math.pow(2,6)});
  *
  * var streamBuf = thrBuf(function(chunk, enc, done){
  *   expect(chunk).to.be.equal(myData);
  *   expect(chunk).to.not.be.equal('my data');
  *   done();
  * }):
  * streamBuf.write(myData);
  * ```
 */

factory = function(cfg) {
  var fn;
  if (cfg == null) {
    cfg = {};
  }
  fn = through(cfg);
  fn.factory = factory;
  fn.ctor = ctor;
  fn.obj = obj;
  fn.buf = buf;
  return fn;
};

module.exports = factory();