Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: sindresorhus/get-stream
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v6.0.1
Choose a base ref
...
head repository: sindresorhus/get-stream
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v7.0.0
Choose a head ref
  • 3 commits
  • 8 files changed
  • 2 contributors

Commits on Sep 19, 2022

  1. Unverified

    This user has not yet uploaded their public signing key.
    Copy the full SHA
    8f004f9 View commit details

Commits on May 26, 2023

  1. Unverified

    This user has not yet uploaded their public signing key.
    Copy the full SHA
    70571f8 View commit details
  2. 7.0.0

    sindresorhus committed May 26, 2023
    Copy the full SHA
    0a81854 View commit details
Showing with 209 additions and 360 deletions.
  1. +5 −5 .github/workflows/main.yml
  2. +0 −52 buffer-stream.js
  3. +65 −93 index.d.ts
  4. +41 −38 index.js
  5. +7 −18 index.test-d.ts
  6. +13 −12 package.json
  7. +62 −66 readme.md
  8. +16 −76 test.js
10 changes: 5 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -10,12 +10,12 @@ jobs:
fail-fast: false
matrix:
node-version:
- 14
- 12
- 10
- 20
- 18
- 16
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm install
52 changes: 0 additions & 52 deletions buffer-stream.js

This file was deleted.

158 changes: 65 additions & 93 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,105 +1,77 @@
/// <reference types="node"/>
import {Stream} from 'stream';
import {type Stream} from 'node:stream';
import {type Buffer} from 'node:buffer';

declare class MaxBufferErrorClass extends Error {
export class MaxBufferError extends Error {
readonly name: 'MaxBufferError';
constructor();
}

declare namespace getStream {
interface Options {
/**
Maximum length of the returned string. If it exceeds this value before the stream ends, the promise will be rejected with a `MaxBufferError` error.
@default Infinity
*/
readonly maxBuffer?: number;
}

interface OptionsWithEncoding<EncodingType = BufferEncoding> extends Options {
/**
[Encoding](https://nodejs.org/api/buffer.html#buffer_buffer) of the incoming stream.
@default 'utf8'
*/
readonly encoding?: EncodingType;
}

type MaxBufferError = MaxBufferErrorClass;
}

declare const getStream: {
/**
Get the `stream` as a string.
@returns A promise that resolves when the end event fires on the stream, indicating that there is no more data to be read. The stream is switched to flowing mode.
@example
```
import * as fs from 'fs';
import getStream = require('get-stream');
(async () => {
const stream = fs.createReadStream('unicorn.txt');
console.log(await getStream(stream));
// ,,))))))));,
// __)))))))))))))),
// \|/ -\(((((''''((((((((.
// -*-==//////(('' . `)))))),
// /|\ ))| o ;-. '((((( ,(,
// ( `| / ) ;))))' ,_))^;(~
// | | | ,))((((_ _____------~~~-. %,;(;(>';'~
// o_); ; )))(((` ~---~ `:: \ %%~~)(v;(`('~
// ; ''''```` `: `:::|\,__,%% );`'; ~
// | _ ) / `:|`----' `-'
// ______/\/~ | / /
// /~;;.____/;;' / ___--,-( `;;;/
// / // _;______;'------~~~~~ /;;/\ /
// // | | / ; \;;,\
// (<_ | ; /',/-----' _>
// \_| ||_ //~;~~~~~~~~~
// `\_| (,~~
// \~\
// ~~
})();
```
*/
(stream: Stream, options?: getStream.OptionsWithEncoding): Promise<string>;

export type Options = {
/**
Get the `stream` as a buffer.
Maximum length of the returned string. If it exceeds this value before the stream ends, the promise will be rejected with a `MaxBufferError` error.
It honors the `maxBuffer` option as above, but it refers to byte length rather than string length.
@default Infinity
*/
buffer(
stream: Stream,
options?: getStream.Options
): Promise<Buffer>;
readonly maxBuffer?: number;
};

export type OptionsWithEncoding<EncodingType = BufferEncoding> = {
/**
Get the `stream` as an array of values.
The [encoding](https://nodejs.org/api/buffer.html#buffers-and-character-encodings) of the incoming stream.
It honors both the `maxBuffer` and `encoding` options. The behavior changes slightly based on the encoding chosen:
- When `encoding` is unset, it assumes an [object mode stream](https://nodesource.com/blog/understanding-object-streams/) and collects values emitted from `stream` unmodified. In this case `maxBuffer` refers to the number of items in the array (not the sum of their sizes).
- When `encoding` is set to `buffer`, it collects an array of buffers. `maxBuffer` refers to the summed byte lengths of every buffer in the array.
- When `encoding` is set to anything else, it collects an array of strings. `maxBuffer` refers to the summed character lengths of every string in the array.
@default 'utf8'
*/
array<StreamObjectModeType>(
stream: Stream,
options?: getStream.Options
): Promise<StreamObjectModeType[]>;
array(
stream: Stream,
options: getStream.OptionsWithEncoding<'buffer'>
): Promise<Buffer[]>;
array(
stream: Stream,
options: getStream.OptionsWithEncoding<BufferEncoding>
): Promise<string[]>;

MaxBufferError: typeof MaxBufferErrorClass;
};

export = getStream;
readonly encoding?: EncodingType;
} & Options;

/**
Get the given `stream` as a string.
@returns A promise that resolves when the end event fires on the stream, indicating that there is no more data to be read. The stream is switched to flowing mode.
@example
```
import fs from 'node:fs';
import getStream from 'get-stream';
const stream = fs.createReadStream('unicorn.txt');
console.log(await getStream(stream));
// ,,))))))));,
// __)))))))))))))),
// \|/ -\(((((''''((((((((.
// -*-==//////(('' . `)))))),
// /|\ ))| o ;-. '((((( ,(,
// ( `| / ) ;))))' ,_))^;(~
// | | | ,))((((_ _____------~~~-. %,;(;(>';'~
// o_); ; )))(((` ~---~ `:: \ %%~~)(v;(`('~
// ; ''''```` `: `:::|\,__,%% );`'; ~
// | _ ) / `:|`----' `-'
// ______/\/~ | / /
// /~;;.____/;;' / ___--,-( `;;;/
// / // _;______;'------~~~~~ /;;/\ /
// // | | / ; \;;,\
// (<_ | ; /',/-----' _>
// \_| ||_ //~;~~~~~~~~~
// `\_| (,~~
// \~\
// ~~
```
*/
export default function getStream(stream: Stream, options?: OptionsWithEncoding): Promise<string>;

/**
Get the given `stream` as a buffer.
It honors the `maxBuffer` option as above, but it refers to byte length rather than string length.
@example
```
import {getStreamAsBuffer} from 'get-stream';
const stream = fs.createReadStream('unicorn.png');
console.log(await getStreamAsBuffer(stream));
```
*/
export function getStreamAsBuffer(stream: Stream, options?: Options): Promise<Buffer>;
79 changes: 41 additions & 38 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,64 @@
'use strict';
const {constants: BufferConstants} = require('buffer');
const stream = require('stream');
const {promisify} = require('util');
const bufferStream = require('./buffer-stream');
import {Buffer, constants as BufferConstants} from 'node:buffer';
import {PassThrough as PassThroughStream} from 'node:stream';
import {pipeline as streamPipeline} from 'node:stream/promises';

const streamPipelinePromisified = promisify(stream.pipeline);
export class MaxBufferError extends Error {
name = 'MaxBufferError';

class MaxBufferError extends Error {
constructor() {
super('maxBuffer exceeded');
this.name = 'MaxBufferError';
}
}

async function getStream(inputStream, options) {
export default async function getStream(inputStream, options) {
if (!inputStream) {
throw new Error('Expected a stream');
}

options = {
maxBuffer: Infinity,
...options
maxBuffer: Number.POSITIVE_INFINITY,
...options,
};

const {maxBuffer} = options;
const stream = bufferStream(options);
let {encoding = 'utf8'} = options;
const isBuffer = encoding === 'buffer';

await new Promise((resolve, reject) => {
const rejectPromise = error => {
// Don't retrieve an oversized buffer.
if (error && stream.getBufferedLength() <= BufferConstants.MAX_LENGTH) {
error.bufferedData = stream.getBufferedValue();
}
if (isBuffer) {
encoding = null;
}

reject(error);
};
const stream = new PassThroughStream();

(async () => {
try {
await streamPipelinePromisified(inputStream, stream);
resolve();
} catch (error) {
rejectPromise(error);
}
})();
if (encoding) {
stream.setEncoding(encoding);
}

await streamPipeline(inputStream, stream);

let length = 0;
const chunks = [];

stream.on('data', () => {
if (stream.getBufferedLength() > maxBuffer) {
rejectPromise(new MaxBufferError());
const getBufferedValue = () => isBuffer ? Buffer.concat(chunks, length) : chunks.join('');

for await (const chunk of stream) {
chunks.push(chunk);
length += chunk.length;

if (length > maxBuffer) {
const error = new MaxBufferError();

if (length <= BufferConstants.MAX_LENGTH) {
error.bufferedData = getBufferedValue();
}
});
});

return stream.getBufferedValue();
throw error;
}
}

return getBufferedValue();
}

module.exports = getStream;
module.exports.buffer = (stream, options) => getStream(stream, {...options, encoding: 'buffer'});
module.exports.array = (stream, options) => getStream(stream, {...options, array: true});
module.exports.MaxBufferError = MaxBufferError;
export async function getStreamAsBuffer(stream, options) {
return getStream(stream, {...options, encoding: 'buffer'});
}
25 changes: 7 additions & 18 deletions index.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,17 @@
import * as fs from 'fs';
import {type Buffer} from 'node:buffer';
import {type Stream} from 'node:stream';
import fs from 'node:fs';
import {expectType} from 'tsd';
import getStream = require('.');
import {MaxBufferError} from '.';
import getStream, {getStreamAsBuffer, MaxBufferError} from './index.js';

const stream = fs.createReadStream('foo');
const stream = fs.createReadStream('foo') as Stream;

expectType<Promise<string>>(getStream(stream));
expectType<Promise<string>>(getStream(stream, {maxBuffer: 10}));
expectType<Promise<string>>(getStream(stream, {encoding: 'utf8'}));

expectType<Promise<Buffer>>(getStream.buffer(stream));
expectType<Promise<Buffer>>(getStream.buffer(stream, {maxBuffer: 10}));

expectType<Promise<unknown[]>>(getStream.array(stream));
expectType<Promise<{}[]>>(getStream.array<{}>(stream));
expectType<Promise<unknown[]>>(getStream.array(stream, {maxBuffer: 10}));
expectType<Promise<Buffer[]>>(getStream.array(stream, {encoding: 'buffer'}));
expectType<Promise<Buffer[]>>(
getStream.array(stream, {maxBuffer: 10, encoding: 'buffer'})
);
expectType<Promise<string[]>>(getStream.array(stream, {encoding: 'utf8'}));
expectType<Promise<string[]>>(
getStream.array(stream, {maxBuffer: 10, encoding: 'utf8'})
);
expectType<Promise<Buffer>>(getStreamAsBuffer(stream));
expectType<Promise<Buffer>>(getStreamAsBuffer(stream, {maxBuffer: 10}));

const maxBufferError = new MaxBufferError();
expectType<MaxBufferError>(maxBufferError);
Loading