Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use dedicated error class for End-Of-Stream Error #57

Merged
merged 2 commits into from
Dec 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .dependabot/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@ update_configs:
dependency_name: "coveralls"
- match:
dependency_name: "nyc"
-match:
dependency_name: "eslint"
-match:
dependency_name: "@typescript-eslint/eslint-plugin"
-match:
dependency_name: "@typescript-eslint/parser"
14 changes: 14 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/quotes": ["error", "single", { "avoidEscape": true }]
},
"parserOptions": {
"sourceType": "module"
},
"env": {
"browser": false,
"node": true
}
}
87 changes: 53 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,66 +17,85 @@ similar as you would read from a file.

## Usage

### Installation

```shell script
npm install --save then-read-stream
```

The `then-read-stream` contains one class: `StreamReader`, which reads from a [stream.Readable](https://nodejs.org/api/stream.html#stream_class_stream_readable).

### Compatibility

NPM module is compliant with [ECMAScript 2017 (ES8)](https://en.wikipedia.org/wiki/ECMAScript#8th_Edition_-_ECMAScript_2017).

#### Examples
## Examples

In the following example we read the first 16 bytes from a stream and store them in our buffer.
Source code of examples can be found [here](test/examples.ts).

```js
import * as fs from 'fs';
import { StreamReader } from 'then-read-stream';
const fs = require('fs');
const { StreamReader } = require('then-read-stream');

const readable = fs.createReadStream('JPEG_example_JPG_RIP_001.jpg');
const streamReader = new StreamReader(readable);
(async () => {

const buffer = Buffer.alloc(16);
const fileReadStream = fs.createReadStream('JPEG_example_JPG_RIP_001.jpg');
const streamReader = new StreamReader(fileReadStream);
const buffer = Buffer.alloc(16);

const bytesRead = await streamReader.read(buffer, 0, 16);
// buffer contains 16 bytes, if the end-of-stream has not been reached
})();
```

return streamReader.read(buffer, 0, 16)
.then( bytesRead => {
// buf, contains bytesRead, which will be 16 if the end-of-stream has not been reached
});
End-of-stream detection:
```js
(async () => {

const fileReadStream = fs.createReadStream('JPEG_example_JPG_RIP_001.jpg');
const streamReader = new StreamReader(fileReadStream);
const buffer = Buffer.alloc(16);

try {
await streamReader.read(buffer, 0, 16);
// buffer contains 16 bytes, if the end-of-stream has not been reached
} catch(error) {
if (error instanceof EndOfStreamError) {
console.log('End-of-stream reached');
}
}
})();
```

With peek you can read ahead:
```js
import * as fs from 'fs';
import { StreamReader } from 'then-read-stream';
const fs = require('fs');
const { StreamReader } = require('then-read-stream');

const fileReadStream = fs.createReadStream('JPEG_example_JPG_RIP_001.jpg');
const streamReader = new StreamReader(fileReadStream);
const buffer = Buffer.alloc(20);

return streamReader.peek(buffer, 0, 3)
.then(bytesRead => {
if (bytesRead === 3 && buffer[0] === 0xFF && buffer[1] === 0xD8 && buffer[2] === 0xFF) {
console.log('This is a JPEG file');
return streamReader.read(buffer, 0, 20); // Read JPEG header
} else {
throw Error('Expected a JPEG file');
}
})
.then(bytesRead => {
if (bytesRead === 20) {
console.log('Got the JPEG header');
} else {
throw Error('Failed to read JPEG header');
}
});
(async () => {
let bytesRead = await streamReader.peek(buffer, 0, 3);
if (bytesRead === 3 && buffer[0] === 0xFF && buffer[1] === 0xD8 && buffer[2] === 0xFF) {
console.log('This is a JPEG file');
} else {
throw Error('Expected a JPEG file');
}

bytesRead = await streamReader.read(buffer, 0, 20); // Read JPEG header
if (bytesRead === 20) {
console.log('Got the JPEG header');
} else {
throw Error('Failed to read JPEG header');
}
})();
```

If you have to skip a part of the data, you can use ignore:
```js
return streamReader.ignore(16)
.then( bytesIgnored => {
if (bytesIgnored < 16){
console.log(`Remaining stream length was ${bytesIgnored}, expected 16`);
}
});
await streamReader.ignore(16);
```

11 changes: 11 additions & 0 deletions lib/EndOfFileStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const defaultMessages = 'End-Of-Stream';

/**
* Thrown on read operation of the end of file or stream has been reached
*/
export class EndOfStreamError extends Error {

constructor() {
super(defaultMessages);
}
}
11 changes: 4 additions & 7 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as assert from 'assert';
import * as stream from 'stream';
import { EndOfStreamError } from './EndOfFileStream';
export { EndOfStreamError } from './EndOfFileStream';

interface IReadRequest {
buffer: Buffer | Uint8Array,
Expand All @@ -25,11 +27,6 @@ class Deferred<T> {

const maxStreamReadSize = 1 * 1024 * 1024; // Maximum request length on read-stream operation

/**
* Error message
*/
export const endOfStream = 'End-Of-Stream';

export class StreamReader {

/**
Expand All @@ -49,7 +46,7 @@ export class StreamReader {
if (!s.read || !s.once) {
throw new Error('Expected an instance of stream.Readable');
}
this.s.once('end', () => this.reject(new Error(endOfStream)));
this.s.once('end', () => this.reject(new EndOfStreamError()));
this.s.once('error', err => this.reject(err));
this.s.once('close', () => this.reject(new Error('Stream closed')));
}
Expand Down Expand Up @@ -81,7 +78,7 @@ export class StreamReader {
}

if (this.peekQueue.length === 0 && this.endOfStream) {
throw new Error(endOfStream);
throw new EndOfStreamError();
}

let remaining = length;
Expand Down
14 changes: 9 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
"compile-src": "tsc -p lib",
"compile-test": "tsc -p test",
"compile": "npm run compile-src && yarn run compile-test",
"lint-ts": "tslint 'lib/**/*.ts' --exclude 'lib/**/*.d.ts' 'test/**/*.ts' --exclude 'test/**/*.d.ts'",
"eslint": "eslint lib test --ext .ts --ignore-pattern *.d.ts",
"lint-md": "remark -u preset-lint-recommended .",
"lint": "npm run lint-md && npm run lint-ts",
"lint": "npm run lint-md && npm run eslint",
"test": "mocha --require ts-node/register --require source-map-support/register --full-trace test/*.ts",
"test-coverage": "nyc npm run test",
"send-coveralls": "nyc report --reporter=text-lcov | coveralls",
Expand All @@ -28,7 +28,7 @@
"url": "git+https://github.com/Borewit/then-read-stream"
},
"license": "MIT",
"main": "./lib/index",
"main": "lib/index.js",
"typings": "lib/index",
"bugs": {
"url": "https://github.com/Borewit/then-read-stream/issues"
Expand All @@ -40,16 +40,20 @@
"devDependencies": {
"@types/mocha": "^5.2.7",
"@types/node": "^13.1.0",
"@typescript-eslint/eslint-plugin": "^2.13.0",
"@typescript-eslint/parser": "^2.13.0",
"add": "^2.0.6",
"chai": "^4.2.0",
"coveralls": "^3.0.8",
"del-cli": "^3.0.0",
"eslint": "^6.8.0",
"mocha": "^6.2.2",
"nyc": "^15.0.0",
"remark-cli": "^7.0.1",
"remark-preset-lint-recommended": "^3.0.2",
"ts-node": "^8.5.2",
"tslint": "^5.20.1",
"typescript": "^3.7.2"
"typescript": "^3.7.2",
"yarn": "^1.21.1"
},
"keywords": [
"readable",
Expand Down
4 changes: 2 additions & 2 deletions test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { EventEmitter } from 'events';
import * as fs from 'fs';
import * as Path from 'path';
import { Readable } from 'stream';
import { endOfStream, StreamReader } from '../lib';
import { EndOfStreamError, StreamReader } from '../lib';
import { SourceStream } from './util';

describe('StreamReader', () => {
Expand Down Expand Up @@ -58,7 +58,7 @@ describe('StreamReader', () => {
await streamReader.read(buf, 0, 1);
assert.fail('Should reject due to end-of-stream');
} catch (err) {
assert.equal(err.message, endOfStream);
assert.instanceOf(err, EndOfStreamError);
}
});
});
Expand Down
2 changes: 1 addition & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@
},
"rulesDirectory": [
]
}
}
Loading