-
-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8f004f9
commit 70571f8
Showing
8 changed files
with
207 additions
and
359 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
Oops, something went wrong.