├── .gitignore ├── License ├── README.md ├── index.js ├── lib └── StreamCache.js ├── package.json └── test └── test-with-child-processes.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Felix Geisendörfer (felix@debuggable.com) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-stream-cache 2 | 3 | A simple way to cache and replay readable streams. 4 | 5 | ## Usage 6 | 7 | ```js 8 | var StreamCache = require('stream-cache'); 9 | var fs = require('fs'); 10 | 11 | var cache = new StreamCache(); 12 | fs.createReadStream(__filename).pipe(cache); 13 | 14 | // Cache can now be piped anywhere, even before the readable stream finishes. 15 | cache.pipe(process.stdout); 16 | ``` 17 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/StreamCache'); 2 | -------------------------------------------------------------------------------- /lib/StreamCache.js: -------------------------------------------------------------------------------- 1 | var Util = require('util'); 2 | var Stream = require('stream').Stream; 3 | 4 | module.exports = StreamCache; 5 | Util.inherits(StreamCache, Stream); 6 | function StreamCache() { 7 | Stream.call(this); 8 | 9 | this.writable = true; 10 | this.readable = true; 11 | 12 | this._buffers = []; 13 | this._dests = []; 14 | this._ended = false; 15 | } 16 | 17 | StreamCache.prototype.write = function(buffer) { 18 | this._buffers.push(buffer); 19 | 20 | this._dests.forEach(function(dest) { 21 | dest.write(buffer); 22 | }); 23 | }; 24 | 25 | StreamCache.prototype.pipe = function(dest, options) { 26 | if (options) { 27 | throw Error('StreamCache#pipe: options are not supported yet.'); 28 | } 29 | 30 | this._buffers.forEach(function(buffer) { 31 | dest.write(buffer); 32 | }); 33 | 34 | if (this._ended) { 35 | dest.end(); 36 | return dest; 37 | } 38 | 39 | this._dests.push(dest); 40 | 41 | return dest; 42 | }; 43 | 44 | StreamCache.prototype.getLength = function() { 45 | return this._buffers.reduce(function(totalLength, buffer) { 46 | return totalLength + buffer.length; 47 | }, 0); 48 | }; 49 | 50 | StreamCache.prototype.end = function() { 51 | this._dests.forEach(function(dest) { 52 | dest.end(); 53 | }); 54 | 55 | this._ended = true; 56 | this._dests = []; 57 | }; 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Felix Geisendörfer (http://debuggable.com/)", 3 | "name": "stream-cache", 4 | "description": "A simple way to cache and replay readable streams.", 5 | "version": "0.0.1", 6 | "homepage": "https://github.com/felixge/node-stream-cache", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/felixge/node-stream-cache.git" 10 | }, 11 | "main": "./index", 12 | "engines": { 13 | "node": "*" 14 | }, 15 | "dependencies": {}, 16 | "devDependencies": {}, 17 | "optionalDependencies": {} 18 | } -------------------------------------------------------------------------------- /test/test-with-child-processes.js: -------------------------------------------------------------------------------- 1 | var StreamCache = require('..'); 2 | var assert = require('assert'); 3 | var spawn = require('child_process').spawn; 4 | 5 | var source = spawn('cat'); 6 | var cache = new StreamCache(); 7 | 8 | var dests = {}; 9 | var dataEvents = {}; 10 | ['a', 'b', 'c'].forEach(function(name) { 11 | var dest = dests[name] = spawn('cat'); 12 | dataEvents[name] = []; 13 | 14 | dest.stdout.setEncoding('utf-8'); 15 | dest.stdout.on('data', function(chunk) { 16 | dataEvents[name].push(chunk); 17 | }); 18 | }); 19 | 20 | cache.pipe(dests.a.stdin); 21 | source.stdout.pipe(cache); 22 | source.stdin.write('Hello'); 23 | 24 | source.stdout.once('data', function() { 25 | cache.pipe(dests.b.stdin); 26 | 27 | source.stdin.write('World'); 28 | source.stdin.end(); 29 | }); 30 | 31 | source.on('exit', function() { 32 | cache.pipe(dests.c.stdin); 33 | }); 34 | 35 | process.on('exit', function() { 36 | var expected = ['Hello', 'World']; 37 | var alternative = ['HelloWorld']; 38 | 39 | assert.deepEqual(dataEvents.a, expected); 40 | 41 | try{ 42 | assert.deepEqual(dataEvents.b, expected); 43 | } catch (err) { 44 | assert.deepEqual(dataEvents.b, alternative); 45 | } 46 | 47 | try{ 48 | assert.deepEqual(dataEvents.c, expected); 49 | } catch (err) { 50 | assert.deepEqual(dataEvents.c, alternative); 51 | } 52 | }); 53 | --------------------------------------------------------------------------------