├── .editorconfig ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .travis.yml ├── .vscode └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── package.json ├── src ├── FromStream.ts ├── FromStreamDisposable.ts ├── addListeners.ts ├── index.ts └── types.ts ├── test ├── index.ts └── tsconfig.json ├── tsconfig.commonjs.json ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | tab_width = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | insert_final_newline = false 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | **Code to reproduce the issue:** 8 | 9 | 10 | **Expected behavior:** 11 | 12 | 13 | **Actual behavior:** 14 | 15 | 16 | **Versions of packages used:** 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | - [ ] I added new tests for the issue I fixed or the feature I built 7 | - [ ] I ran `npm test` for the package I'm modifying 8 | - [ ] I used `npm run commit` instead of `git commit` 9 | 10 | 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Logs 3 | logs 4 | *.log 5 | npm-debug.log* 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional REPL history 38 | .node_repl_history 39 | 40 | # generated files 41 | lib 42 | .tmp 43 | test/test.txt -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mostjs-community/most-node-streams/a1612ea72ab37f770ea18c786b71578533afb4d8/.npmignore -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 6 4 | - 7 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.fontFamily": "'Fira Code Medium', 'Courier New', monospace, 'Droid Sans Fallback'", 3 | 4 | "editor.fontLigatures": true, 5 | 6 | "editor.rulers": [80, 160], 7 | 8 | "editor.wrappingColumn": 120, 9 | 10 | "editor.wrappingIndent": "indent", 11 | 12 | "editor.tabSize": 2, 13 | 14 | "editor.insertSpaces": true, 15 | 16 | "editor.formatOnType": true, 17 | 18 | "typescript.tsdk": "node_modules/typescript/lib" 19 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # [1.0.0](https://github.com/TylorS/most-node-streams/compare/81c3747...v1.0.0) (2017-01-16) 3 | 4 | 5 | ### Bug Fixes 6 | 7 | * **fromReadable:** default to 'data' ([b800406](https://github.com/TylorS/most-node-streams/commit/b800406)) 8 | 9 | 10 | ### Features 11 | 12 | * **most-node-streams:** initial implementation ([81c3747](https://github.com/TylorS/most-node-streams/commit/81c3747)) 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First of all, thank you so much, we need your help. 4 | 5 | ## Contributing a fix or feature 6 | 7 | 1. Fork the repository 8 | 2. Switch to a new branch `git checkout -b [branchName]` 9 | 3. Produce your fix or feature 10 | 4. Use `npm run commit` instead of `git commit` PLEASE! 11 | 5. Submit a pull request for review 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Tylor Steinberger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # most-node-streams 2 | 3 | > Convert Node.js Streams to Most.js Streams 4 | 5 | Allows you to translate basic Node.js Streams into Most.js Streams. 6 | 7 | WARNING: 8 | This does not attempt to cover the use case where you need backpressure or flow control. 9 | If you need backpressure or flow control then please continue to use Node.js Streams. 10 | 11 | ## Let me have it! 12 | ```sh 13 | npm install --save most-node-streams 14 | ``` 15 | 16 | ## API 17 | 18 | #### `fromReadable(stream: NodeJS.ReadableStream, dataEventName?: string): Stream` 19 | 20 | Creates a Most.js Stream from a Node.js Readable Stream. Optionally takes an event name 21 | to recieve events defaulting to `data` if none is provided. 22 | 23 | #### `fromWritable(stream: NodeJS.WritableStream): Stream` 24 | 25 | Creates a Most.js Stream that replicates the values being written to a WritableStream. 26 | 27 | #### `toWritable (nodeStream: NodeJS.WritableStream, mostStream: Stream): Subscription` 28 | 29 | Subscribes to a stream and replicates its values into a NodeJS WritableStream. 30 | 31 | #### `fromStream(nodeStream: NodeStream, options?: FromStreamOptions): Stream` 32 | 33 | This is the function that fromReadable and fromWritable are built from for when you need a little 34 | more configuration. 35 | 36 | ## Types 37 | 38 | #### `FromStreamOptions` 39 | 40 | ```typscript 41 | export interface FromStreamOptions { 42 | endEventName?: string; 43 | dataEventName?: string; 44 | } 45 | ``` 46 | 47 | #### `NodeStram` 48 | 49 | ```typescript 50 | export type NodeStream = 51 | NodeJS.WritableStream | NodeJS.ReadableStream | NodeJS.ReadWriteStream; 52 | ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "most-node-streams", 3 | "version": "1.0.0", 4 | "description": "Convert Node.js streams to Most.js Streams", 5 | "main": "lib/commonjs", 6 | "scripts": { 7 | "test:lint": "tslint src/**/*.ts src/*.ts", 8 | "test:unit": "TS_NODE_PROJECT=test/tsconfig.json mocha -r ts-node/register test/*.ts", 9 | "test": "npm run test:lint && npm run test:unit", 10 | "commit": "git-cz", 11 | "changelog": "conventional-changelog --infile CHANGELOG.md --same-file --release-count 0 --preset angular", 12 | "postchangelog": "git add CHANGELOG.md && git commit -m 'docs(CHANGELOG): append to changelog'", 13 | "build:es2015": "tsc -P tsconfig.json", 14 | "build:commonjs": "tsc -P tsconfig.commonjs.json", 15 | "build": "npm run build:es2015 && npm run build:commonjs", 16 | "preversion": "npm run build", 17 | "postversion": "npm run changelog && git push origin master --tags && npm publish", 18 | "release:minor": "npm version minor -m 'chore(package): v%s'", 19 | "release:major": "npm version major -m 'chore(package): v%s'" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/TylorS/most-node-streams.git" 24 | }, 25 | "keywords": [ 26 | "most", 27 | "node", 28 | "streams", 29 | "convert" 30 | ], 31 | "author": "Tylor Steinberger ", 32 | "license": "MIT", 33 | "bugs": { 34 | "url": "https://github.com/TylorS/most-node-streams/issues" 35 | }, 36 | "homepage": "https://github.com/TylorS/most-node-streams#readme", 37 | "config": { 38 | "ghooks": { 39 | "commit-msg": "node ./node_modules/.bin/validate-commit-msg" 40 | } 41 | }, 42 | "jsnext:main": "lib/es2015/index.js", 43 | "module": "lib/es2015/index.js", 44 | "typings": "lib/es2015/index.d.ts", 45 | "devDependencies": { 46 | "@motorcycle/tslint": "^1.2.0", 47 | "@types/mocha": "^2.2.33", 48 | "@types/node": "^6.0.51", 49 | "commitizen": "^2.8.6", 50 | "conventional-changelog-cli": "^1.2.0", 51 | "cz-conventional-changelog": "^1.2.0", 52 | "ghooks": "^1.3.2", 53 | "mocha": "^3.2.0", 54 | "stdio-mock": "^1.0.1", 55 | "ts-node": "^1.7.0", 56 | "tslint": "^4.0.2", 57 | "typescript": "^2.2.0-dev.20161127", 58 | "validate-commit-msg": "^2.8.2" 59 | }, 60 | "dependencies": { 61 | "@most/multicast": "^1.2.4", 62 | "most": "^1.1.0" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/FromStream.ts: -------------------------------------------------------------------------------- 1 | import { never, Disposable, Sink, Scheduler } from 'most'; 2 | import { MulticastSource } from '@most/multicast'; 3 | import { FromStreamDisposable } from './FromStreamDisposable'; 4 | import { addListeners } from './addListeners'; 5 | import { NodeStream } from './types'; 6 | 7 | export class FromStream extends MulticastSource { 8 | private stream: NodeStream; 9 | private endEventName: string; 10 | private dataEventName: string; 11 | 12 | // From MulticastSource 13 | private _disposable: Disposable; 14 | 15 | constructor(stream: NodeStream, endEventName: string, dataEventName: string) { 16 | super(never().source); 17 | 18 | this.stream = stream; 19 | this.endEventName = endEventName; 20 | this.dataEventName = dataEventName; 21 | } 22 | 23 | public run(sink: Sink, scheduler: Scheduler) { 24 | const n = this.add(sink); 25 | 26 | if (n === 1) { 27 | const stream = this.stream; 28 | 29 | this._disposable = 30 | addListeners(stream, this.endEventName, this.dataEventName, sink, scheduler); 31 | 32 | if (typeof (stream as NodeJS.ReadableStream).resume === 'function') { 33 | (stream as NodeJS.ReadableStream).resume(); 34 | } 35 | } 36 | 37 | return new FromStreamDisposable(this, sink); 38 | } 39 | } -------------------------------------------------------------------------------- /src/FromStreamDisposable.ts: -------------------------------------------------------------------------------- 1 | import { Sink, Disposable } from 'most'; 2 | import { FromStream } from './FromStream'; 3 | 4 | export class FromStreamDisposable implements Disposable { 5 | private source: FromStream; 6 | private sink: Sink; 7 | private disposed: boolean; 8 | 9 | constructor(source: FromStream, sink: Sink) { 10 | this.source = source; 11 | this.sink = sink; 12 | this.disposed = false; 13 | } 14 | 15 | public dispose() { 16 | if (this.disposed) return; 17 | this.disposed = true; 18 | const remaining = this.source.remove(this.sink); 19 | 20 | return remaining === 0 && (this.source as any)._dispose(); 21 | } 22 | } -------------------------------------------------------------------------------- /src/addListeners.ts: -------------------------------------------------------------------------------- 1 | import { Sink, Scheduler, Disposable, PropagateTask } from 'most'; 2 | import { NodeStream } from './types'; 3 | 4 | export function addListeners( 5 | stream: NodeStream, 6 | endEventName: string, 7 | dataEventName: string, 8 | sink: Sink, 9 | scheduler: Scheduler): Disposable 10 | { 11 | const event = (value: any) => scheduler.asap(PropagateTask.event(value, sink)); 12 | const error = (err: Error) => scheduler.asap(PropagateTask.error(err, sink)); 13 | const end = (value: any) => scheduler.asap(PropagateTask.end(value, sink)); 14 | 15 | stream.addListener(dataEventName, event); 16 | stream.addListener(endEventName, end); 17 | stream.addListener('error', error); 18 | 19 | return { 20 | dispose() { 21 | stream.removeListener(dataEventName, event); 22 | stream.removeListener(endEventName, end); 23 | stream.removeListener('error', error); 24 | }, 25 | }; 26 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Stream, Subscription } from 'most'; 2 | import { FromStream } from './FromStream'; 3 | import { NodeStream } from './types'; 4 | 5 | export interface FromStreamOptions { 6 | endEventName?: string; 7 | dataEventName?: string; 8 | } 9 | 10 | export function fromStream( 11 | nodeStream: NodeStream, 12 | options: FromStreamOptions = {}) 13 | { 14 | const { endEventName = 'end', dataEventName = 'data' } = options; 15 | 16 | if (typeof (nodeStream as NodeJS.ReadableStream).pause === 'function') 17 | (nodeStream as NodeJS.ReadableStream).pause(); 18 | 19 | return new Stream(new FromStream(nodeStream, endEventName, dataEventName)); 20 | } 21 | 22 | export function fromReadable( 23 | nodeStream: NodeJS.ReadableStream, 24 | dataEventName = 'data', 25 | ): Stream { 26 | return fromStream(nodeStream, { dataEventName, endEventName: 'end' }); 27 | } 28 | 29 | export function fromWritable(nodeStream: NodeJS.WritableStream) { 30 | return fromStream(nodeStream, { endEventName: 'finish' }); 31 | } 32 | 33 | export function toWritable( 34 | nodeStream: NodeJS.WritableStream, 35 | stream: Stream): Subscription 36 | { 37 | return stream.subscribe({ 38 | next (x: string | Buffer) { 39 | nodeStream.write(x); 40 | }, 41 | error (e: Error) { 42 | nodeStream.emit('error', e); 43 | }, 44 | complete() { 45 | // process.stdout && process.stderr are not closable 46 | if (!(nodeStream as any).isStdio) { 47 | nodeStream.end(); 48 | } 49 | }, 50 | }); 51 | } -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type NodeStream = 2 | NodeJS.ReadableStream | NodeJS.WritableStream | NodeJS.ReadWriteStream; -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import { from } from 'most'; 3 | import { stdio } from 'stdio-mock'; 4 | import { fromReadable, fromWritable, toWritable } from '../src'; 5 | import { join } from 'path'; 6 | import { createReadStream, createWriteStream } from 'fs'; 7 | 8 | describe('Most Node Streams', () => { 9 | describe('fromWritable', () => { 10 | it('captures events from writable stream', () => { 11 | const writeStream = createWriteStream(join(__dirname, 'test.txt')); 12 | const stream = fromWritable(writeStream).map(x => x.toString()); 13 | 14 | const expected = ['1', '2', '3']; 15 | 16 | stream.observe(function (x: any) { 17 | assert.strictEqual(x, expected.join('\n')); 18 | }); 19 | 20 | writeStream.write('1\n'); 21 | writeStream.write('2\n'); 22 | writeStream.write('3'); 23 | }); 24 | }); 25 | 26 | describe('fromReadable', () => { 27 | it('captures events from readable stream', () => { 28 | const readStream = createReadStream(join(__dirname, 'test.txt')); 29 | 30 | const stream = fromReadable(readStream, 'data').map(data => data.toString()); 31 | 32 | const expected = ['1', '2', '3']; 33 | 34 | return stream.observe(function (x: any) { 35 | assert.strictEqual(x, expected.join('\n')); 36 | }); 37 | }); 38 | }); 39 | 40 | describe('toWritable', () => { 41 | it('pushes events to a writable stream', (done) => { 42 | const stdout = stdio().stdout; 43 | const expected = ['1', '2', '3']; 44 | const stream = from(expected); 45 | 46 | toWritable(stdout, stream); 47 | 48 | stdout.addListener('end', () => { 49 | assert.deepEqual(stdout.data(), expected); 50 | done(); 51 | }); 52 | }); 53 | }); 54 | }); -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "moduleResolution": "node", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "lib": [ 8 | "es5", 9 | "es2015" 10 | ], 11 | "noImplicitAny": true, 12 | "sourceMap": true, 13 | "noUnusedParameters": true, 14 | "strictNullChecks": true, 15 | "types": [ 16 | "node", 17 | "mocha" 18 | ] 19 | }, 20 | "include": [ 21 | "./**/*.ts" 22 | ] 23 | } -------------------------------------------------------------------------------- /tsconfig.commonjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "moduleResolution": "node", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "lib": [ 8 | "dom", 9 | "es5", 10 | "es2015" 11 | ], 12 | "noImplicitAny": true, 13 | "sourceMap": true, 14 | "noUnusedParameters": true, 15 | "strictNullChecks": true, 16 | "outDir": "lib/commonjs", 17 | "types": [ 18 | "node" 19 | ] 20 | }, 21 | "files": [ 22 | "src/index.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "moduleResolution": "node", 5 | "module": "es2015", 6 | "target": "es2015", 7 | "lib": [ 8 | "dom", 9 | "es5", 10 | "es2015" 11 | ], 12 | "noImplicitAny": true, 13 | "sourceMap": true, 14 | "noUnusedParameters": true, 15 | "noUnusedLocals": true, 16 | "strictNullChecks": true, 17 | "outDir": "lib/es2015", 18 | "types": [ 19 | "node" 20 | ] 21 | }, 22 | "files": [ 23 | "src/index.ts" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@motorcycle/tslint" 4 | ] 5 | } 6 | --------------------------------------------------------------------------------