From 5921f52509766f7c09d4466fce405351241e274e Mon Sep 17 00:00:00 2001 From: Bobby Galli Date: Wed, 25 Aug 2021 13:43:37 -0400 Subject: [PATCH 1/2] Removes dependency on node.js core modules Fixes #34 --- src/symbols/exists.ts | 11 - src/symbols/s3-api-client/fs.ts | 189 ++++++++++++++++++ .../s3-api-client/s3-api-client.spec.ts | 42 ++-- src/symbols/s3-api-client/s3-api-client.ts | 15 +- .../symbols-api-client.e2e.ts | 11 +- .../symbols-api-client.spec.ts | 33 +-- .../symbols-api-client/symbols-api-client.ts | 21 +- 7 files changed, 232 insertions(+), 90 deletions(-) delete mode 100644 src/symbols/exists.ts create mode 100644 src/symbols/s3-api-client/fs.ts diff --git a/src/symbols/exists.ts b/src/symbols/exists.ts deleted file mode 100644 index 13a2a4e..0000000 --- a/src/symbols/exists.ts +++ /dev/null @@ -1,11 +0,0 @@ -import fs from 'fs'; - -export function exists(file: string): boolean { - try { - fs.accessSync(file); - } catch { - return false; - } - - return true; -} \ No newline at end of file diff --git a/src/symbols/s3-api-client/fs.ts b/src/symbols/s3-api-client/fs.ts new file mode 100644 index 0000000..4ee02db --- /dev/null +++ b/src/symbols/s3-api-client/fs.ts @@ -0,0 +1,189 @@ +// We support uploading files via a read stream, but depending on fs directly is not supported in browser. +// Webpack will fail when building a package that relies on fs (even for types). +// This is a copy from fs.d.ts for the type returned by fs.createReadStream() to satisfy the TypeScript compiler. +export interface ReadStream { + close(): void; + bytesRead: number; + path: string | Buffer; + pending: boolean; + + /** + * events.EventEmitter + * 1. open + * 2. close + * 3. ready + */ + addListener(event: "close", listener: () => void): this; + addListener(event: "data", listener: (chunk: Buffer | string) => void): this; + addListener(event: "end", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "open", listener: (fd: number) => void): this; + addListener(event: "pause", listener: () => void): this; + addListener(event: "readable", listener: () => void): this; + addListener(event: "ready", listener: () => void): this; + addListener(event: "resume", listener: () => void): this; + addListener(event: string | symbol, listener: (...args: any[]) => void): this; + + on(event: "close", listener: () => void): this; + on(event: "data", listener: (chunk: Buffer | string) => void): this; + on(event: "end", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "open", listener: (fd: number) => void): this; + on(event: "pause", listener: () => void): this; + on(event: "readable", listener: () => void): this; + on(event: "ready", listener: () => void): this; + on(event: "resume", listener: () => void): this; + on(event: string | symbol, listener: (...args: any[]) => void): this; + + once(event: "close", listener: () => void): this; + once(event: "data", listener: (chunk: Buffer | string) => void): this; + once(event: "end", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "open", listener: (fd: number) => void): this; + once(event: "pause", listener: () => void): this; + once(event: "readable", listener: () => void): this; + once(event: "ready", listener: () => void): this; + once(event: "resume", listener: () => void): this; + once(event: string | symbol, listener: (...args: any[]) => void): this; + + prependListener(event: "close", listener: () => void): this; + prependListener(event: "data", listener: (chunk: Buffer | string) => void): this; + prependListener(event: "end", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "open", listener: (fd: number) => void): this; + prependListener(event: "pause", listener: () => void): this; + prependListener(event: "readable", listener: () => void): this; + prependListener(event: "ready", listener: () => void): this; + prependListener(event: "resume", listener: () => void): this; + prependListener(event: string | symbol, listener: (...args: any[]) => void): this; + + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "data", listener: (chunk: Buffer | string) => void): this; + prependOnceListener(event: "end", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "open", listener: (fd: number) => void): this; + prependOnceListener(event: "pause", listener: () => void): this; + prependOnceListener(event: "readable", listener: () => void): this; + prependOnceListener(event: "ready", listener: () => void): this; + prependOnceListener(event: "resume", listener: () => void): this; + prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this; +} + +// interface Readable extends NodeJS.ReadableStream { +// readable: boolean; +// readonly readableEncoding: BufferEncoding | null; +// readonly readableEnded: boolean; +// readonly readableFlowing: boolean | null; +// readonly readableHighWaterMark: number; +// readonly readableLength: number; +// readonly readableObjectMode: boolean; +// destroyed: boolean; +// constructor(opts?: ReadableOptions); +// _construct?(callback: (error?: Error | null) => void): void; +// _read(size: number): void; +// read(size?: number): any; +// setEncoding(encoding: BufferEncoding): this; +// pause(): this; +// resume(): this; +// isPaused(): boolean; +// unpipe(destination?: NodeJS.WritableStream): this; +// unshift(chunk: any, encoding?: BufferEncoding): void; +// wrap(oldStream: NodeJS.ReadableStream): this; +// push(chunk: any, encoding?: BufferEncoding): boolean; +// _destroy(error: Error | null, callback: (error?: Error | null) => void): void; +// destroy(error?: Error): void; + +// /** +// * Event emitter +// * The defined events on documents including: +// * 1. close +// * 2. data +// * 3. end +// * 4. error +// * 5. pause +// * 6. readable +// * 7. resume +// */ +// addListener(event: "close", listener: () => void): this; +// addListener(event: "data", listener: (chunk: any) => void): this; +// addListener(event: "end", listener: () => void): this; +// addListener(event: "error", listener: (err: Error) => void): this; +// addListener(event: "pause", listener: () => void): this; +// addListener(event: "readable", listener: () => void): this; +// addListener(event: "resume", listener: () => void): this; +// addListener(event: string | symbol, listener: (...args: any[]) => void): this; + +// emit(event: "close"): boolean; +// emit(event: "data", chunk: any): boolean; +// emit(event: "end"): boolean; +// emit(event: "error", err: Error): boolean; +// emit(event: "pause"): boolean; +// emit(event: "readable"): boolean; +// emit(event: "resume"): boolean; +// emit(event: string | symbol, ...args: any[]): boolean; + +// on(event: "close", listener: () => void): this; +// on(event: "data", listener: (chunk: any) => void): this; +// on(event: "end", listener: () => void): this; +// on(event: "error", listener: (err: Error) => void): this; +// on(event: "pause", listener: () => void): this; +// on(event: "readable", listener: () => void): this; +// on(event: "resume", listener: () => void): this; +// on(event: string | symbol, listener: (...args: any[]) => void): this; + +// once(event: "close", listener: () => void): this; +// once(event: "data", listener: (chunk: any) => void): this; +// once(event: "end", listener: () => void): this; +// once(event: "error", listener: (err: Error) => void): this; +// once(event: "pause", listener: () => void): this; +// once(event: "readable", listener: () => void): this; +// once(event: "resume", listener: () => void): this; +// once(event: string | symbol, listener: (...args: any[]) => void): this; + +// prependListener(event: "close", listener: () => void): this; +// prependListener(event: "data", listener: (chunk: any) => void): this; +// prependListener(event: "end", listener: () => void): this; +// prependListener(event: "error", listener: (err: Error) => void): this; +// prependListener(event: "pause", listener: () => void): this; +// prependListener(event: "readable", listener: () => void): this; +// prependListener(event: "resume", listener: () => void): this; +// prependListener(event: string | symbol, listener: (...args: any[]) => void): this; + +// prependOnceListener(event: "close", listener: () => void): this; +// prependOnceListener(event: "data", listener: (chunk: any) => void): this; +// prependOnceListener(event: "end", listener: () => void): this; +// prependOnceListener(event: "error", listener: (err: Error) => void): this; +// prependOnceListener(event: "pause", listener: () => void): this; +// prependOnceListener(event: "readable", listener: () => void): this; +// prependOnceListener(event: "resume", listener: () => void): this; +// prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this; + +// removeListener(event: "close", listener: () => void): this; +// removeListener(event: "data", listener: (chunk: any) => void): this; +// removeListener(event: "end", listener: () => void): this; +// removeListener(event: "error", listener: (err: Error) => void): this; +// removeListener(event: "pause", listener: () => void): this; +// removeListener(event: "readable", listener: () => void): this; +// removeListener(event: "resume", listener: () => void): this; +// removeListener(event: string | symbol, listener: (...args: any[]) => void): this; + +// [Symbol.asyncIterator](): AsyncIterableIterator; +// } + +// interface ReadableOptions extends StreamOptions { +// encoding?: BufferEncoding; +// read?(this: Readable, size: number): void; +// } + +// interface StreamOptions extends Abortable { +// emitClose?: boolean; +// highWaterMark?: number; +// objectMode?: boolean; +// construct?(this: T, callback: (error?: Error | null) => void): void; +// destroy?(this: T, error: Error | null, callback: (error: Error | null) => void): void; +// autoDestroy?: boolean; +// } + +// interface Stream { +// constructor(opts?: ReadableOptions); +// } \ No newline at end of file diff --git a/src/symbols/s3-api-client/s3-api-client.spec.ts b/src/symbols/s3-api-client/s3-api-client.spec.ts index 8923b02..f150428 100644 --- a/src/symbols/s3-api-client/s3-api-client.spec.ts +++ b/src/symbols/s3-api-client/s3-api-client.spec.ts @@ -1,46 +1,34 @@ -import * as ExistsModule from '../exists'; import { S3ApiClient } from './s3-api-client'; describe('S3ApiClient', () => { describe('uploadFileToPresignedUrl', () => { - let s3ApiClient; - let fakeReadStream; + let s3ApiClient: S3ApiClient; let fakeSuccessResponse; beforeEach(() => { - fakeReadStream = '🦦'; fakeSuccessResponse = { status: 200 }; - spyOn(ExistsModule, 'exists').and.returnValue(true); s3ApiClient = new S3ApiClient(); - s3ApiClient._fs = jasmine.createSpyObj('fs', ['createReadStream']); - s3ApiClient._fs.createReadStream.and.returnValue(fakeReadStream); - s3ApiClient._fetch = jasmine.createSpy(); - s3ApiClient._fetch.and.resolveTo(fakeSuccessResponse); - }); - - it('should throw if file does not exist', async () => { - const path = '/file/not/found'; - (ExistsModule.exists).and.returnValue(false); - await expectAsync(s3ApiClient.uploadFileToPresignedUrl('url', path)).toBeRejectedWithError(`File does not exist at path: ${path}!`); + (s3ApiClient as any)._fetch = jasmine.createSpy(); + (s3ApiClient as any)._fetch.and.resolveTo(fakeSuccessResponse); }); it('should call fetch with presignedUrl', async () => { const url = 'https://bugsplat.com'; - const path = '/some/fake/path'; + const file = '🐛.txt' as any; - await s3ApiClient.uploadFileToPresignedUrl(url, path); + await s3ApiClient.uploadFileToPresignedUrl(url, file, 1); - expect(s3ApiClient._fetch).toHaveBeenCalledWith(url, jasmine.anything()); + expect((s3ApiClient as any)._fetch).toHaveBeenCalledWith(url, jasmine.anything()); }); it('should call fetch with init containing method and headers', async () => { const url = 'https://bugsplat.com'; - const path = '/some/fake/path'; + const file = '🐛.txt' as any; const size = 1337; - await s3ApiClient.uploadFileToPresignedUrl(url, path, size); + await s3ApiClient.uploadFileToPresignedUrl(url, file, size); - expect(s3ApiClient._fetch).toHaveBeenCalledWith( + expect((s3ApiClient as any)._fetch).toHaveBeenCalledWith( jasmine.anything(), jasmine.objectContaining({ method: 'PUT', @@ -48,17 +36,17 @@ describe('S3ApiClient', () => { 'content-type': 'application/octet-stream', 'content-length': `${size}` }, - body: fakeReadStream + body: file }) ); }); it('should return response', async () => { const url = 'https://bugsplat.com'; - const path = '/some/fake/path'; + const file = '🐛.txt' as any; const size = 1337; - const response = await s3ApiClient.uploadFileToPresignedUrl(url, path, size); + const response = await s3ApiClient.uploadFileToPresignedUrl(url, file, size); expect(response).toEqual(fakeSuccessResponse); }); @@ -66,11 +54,11 @@ describe('S3ApiClient', () => { describe('error', () => { it('should throw if response status is not 200', async () => { const url = 'https://bugsplat.com'; - const path = '/some/fake/path'; + const file = '🐛.txt' as any; const size = 1337; - s3ApiClient._fetch.and.resolveTo({ status: 500 }); + (s3ApiClient as any)._fetch.and.resolveTo({ status: 500 }); - await expectAsync(s3ApiClient.uploadFileToPresignedUrl(url, path, size)).toBeRejectedWithError(`Error uploading ${path} to presignedUrl`); + await expectAsync(s3ApiClient.uploadFileToPresignedUrl(url, file, size)).toBeRejectedWithError(`Error uploading to presignedUrl ${url}`); }); }); }); diff --git a/src/symbols/s3-api-client/s3-api-client.ts b/src/symbols/s3-api-client/s3-api-client.ts index fe680e8..006a828 100644 --- a/src/symbols/s3-api-client/s3-api-client.ts +++ b/src/symbols/s3-api-client/s3-api-client.ts @@ -1,28 +1,21 @@ import fetchPonyfill from 'fetch-ponyfill'; -import fs from 'fs'; -import { exists } from '../exists'; +import type fs from 'fs'; export class S3ApiClient { private _fetch = fetchPonyfill().fetch; - private _fs = fs; - - async uploadFileToPresignedUrl(presignedUrl: string, file: string, size: number): Promise { - if (!exists(file)) { - throw new Error(`File does not exist at path: ${file}!`); - } - + async uploadFileToPresignedUrl(presignedUrl: string, file: File | fs.ReadStream, size: number): Promise { const response = await this._fetch(presignedUrl, { method: 'PUT', headers: { 'content-type': 'application/octet-stream', 'content-length': `${size}` }, - body: this._fs.createReadStream(file), + body: file as BodyInit }); if (response.status !== 200) { - throw new Error(`Error uploading ${file} to presignedUrl`); + throw new Error(`Error uploading to presignedUrl ${presignedUrl}`); } return response; diff --git a/src/symbols/symbols-api-client/symbols-api-client.e2e.ts b/src/symbols/symbols-api-client/symbols-api-client.e2e.ts index b08c461..aa30449 100644 --- a/src/symbols/symbols-api-client/symbols-api-client.e2e.ts +++ b/src/symbols/symbols-api-client/symbols-api-client.e2e.ts @@ -1,3 +1,5 @@ +import fs from "fs"; +import path from 'path'; import { config } from "../../../spec/config"; import { BugSplatApiClient } from "../../common"; import { SymbolsApiClient } from "./symbols-api-client"; @@ -32,7 +34,14 @@ describe('SymbolsApiClient', () => { describe('post', () => { it('should return 200 for post with valid database, application, version and files', async () => { - const file = './dist/cjs/index.js.map'; + const filePath = './dist/cjs/index.js.map'; + const name = path.basename(filePath); + const size = fs.statSync(filePath).size; + const file = { + name, + size, + file: fs.createReadStream(filePath) + }; const response = await client.post( database, application, diff --git a/src/symbols/symbols-api-client/symbols-api-client.spec.ts b/src/symbols/symbols-api-client/symbols-api-client.spec.ts index a30190e..c8e6d74 100644 --- a/src/symbols/symbols-api-client/symbols-api-client.spec.ts +++ b/src/symbols/symbols-api-client/symbols-api-client.spec.ts @@ -3,7 +3,6 @@ import path from 'path'; import { createFakeBugSplatApiClient } from '../../../spec/fakes/common/bugsplat-api-client'; import { createFakeFormData } from '../../../spec/fakes/common/form-data'; import { createFakeResponseBody } from '../../../spec/fakes/common/response'; -import * as ExistsModule from '../exists'; import { SymbolsApiClient } from './symbols-api-client'; import * as S3ApiClientModule from '../s3-api-client/s3-api-client'; import { of } from 'rxjs'; @@ -87,22 +86,17 @@ describe('SymbolsApiClient', () => { let fakeS3ApiClient; let files; let result; - let stat; - let size; let timer; beforeEach(async () => { - files = ['file/📄']; - size = 1337; - stat = jasmine.createSpy(); - stat.and.returnValue({ size }); + files = [{ + name: '📄.sym', + size: 1337 + }]; timer = jasmine.createSpy(); timer.and.returnValue(of(0)); - (symbolsApiClient)._stat = stat; (symbolsApiClient)._timer = timer; - spyOn(ExistsModule, 'exists').and.returnValue(true); - fakeS3ApiClient = jasmine.createSpyObj('S3ApiClient', ['uploadFileToPresignedUrl']); fakeS3ApiClient.uploadFileToPresignedUrl.and.resolveTo(fakeSuccessResponse); spyOn(S3ApiClientModule, 'S3ApiClient').and.returnValue(fakeS3ApiClient); @@ -115,23 +109,12 @@ describe('SymbolsApiClient', () => { ); }); - it('should throw if file does not exist', async () => { - (ExistsModule.exists).and.returnValue(false); - - await expectAsync(symbolsApiClient.post( - database, - application, - version, - files - )).toBeRejectedWithError(`File does not exist at path: ${files[0]}!`); - }); - it('should append dbName, appName, appVersion, size and symFileName to FormData', () => { expect(fakeFormData.append).toHaveBeenCalledWith('dbName', database); expect(fakeFormData.append).toHaveBeenCalledWith('appName', application); expect(fakeFormData.append).toHaveBeenCalledWith('appVersion', version); - expect(fakeFormData.append).toHaveBeenCalledWith('size', size.toString()); - expect(fakeFormData.append).toHaveBeenCalledWith('symFileName', path.basename(files[0])); + expect(fakeFormData.append).toHaveBeenCalledWith('size', files[0].size.toString()); + expect(fakeFormData.append).toHaveBeenCalledWith('symFileName', path.basename(files[0].name)); }); it('should call fetch with correct route', () => { @@ -153,7 +136,7 @@ describe('SymbolsApiClient', () => { }); it('should call uploadFileToPresignedUrl with url, file and size', () => { - expect(fakeS3ApiClient.uploadFileToPresignedUrl).toHaveBeenCalledWith(url, files[0], size); + expect(fakeS3ApiClient.uploadFileToPresignedUrl).toHaveBeenCalledWith(url, files[0].file, files[0].size); }); it('should sleep between requests', () => { @@ -176,7 +159,7 @@ describe('SymbolsApiClient', () => { application, version, files - )).toBeRejectedWithError(`Error getting presignedUrl for ${path.basename(files[0])}`); + )).toBeRejectedWithError(`Error getting presignedUrl for ${files[0].name}`); }); it('should throw if response json Status is \'Failed\'', async () => { diff --git a/src/symbols/symbols-api-client/symbols-api-client.ts b/src/symbols/symbols-api-client/symbols-api-client.ts index 565af5a..04cd75b 100644 --- a/src/symbols/symbols-api-client/symbols-api-client.ts +++ b/src/symbols/symbols-api-client/symbols-api-client.ts @@ -1,16 +1,12 @@ -import fs from 'fs'; -import path from 'path'; +import type fs from 'fs'; import { lastValueFrom, timer } from 'rxjs'; -import util from 'util'; import { ApiClient, BugSplatResponse } from '../../common'; -import { exists } from '../exists'; import { S3ApiClient } from '../s3-api-client/s3-api-client'; -const stat = util.promisify(fs.stat); export class SymbolsApiClient { private readonly route = '/api/symbols'; - private _stat = stat; + private _timer = timer; constructor(private _client: ApiClient) { } @@ -45,17 +41,12 @@ export class SymbolsApiClient { database: string, application: string, version: string, - files: Array + files: Array<{ name: string, size: number, file: File | fs.ReadStream }> ): Promise> { const promises = files .map(async (file) => { - if (!exists(file)) { - throw new Error(`File does not exist at path: ${file}!`); - } - - const stats = await this._stat(file); - const size = stats.size; - const name = path.basename(file); + const name = file.name; + const size = file.size; const presignedUrl = await this.getPresignedUrl( database, application, @@ -65,7 +56,7 @@ export class SymbolsApiClient { ); const s3Client = new S3ApiClient(); - const response = s3Client.uploadFileToPresignedUrl(presignedUrl, file, size); + const response = s3Client.uploadFileToPresignedUrl(presignedUrl, file.file, size); await lastValueFrom(this._timer(1000)); return response; From 8338baba873b697acda4c7679450f05a700dd053 Mon Sep 17 00:00:00 2001 From: Bobby Galli Date: Wed, 25 Aug 2021 13:58:29 -0400 Subject: [PATCH 2/2] Removed fs redefinition --- src/symbols/s3-api-client/fs.ts | 189 -------------------------------- 1 file changed, 189 deletions(-) delete mode 100644 src/symbols/s3-api-client/fs.ts diff --git a/src/symbols/s3-api-client/fs.ts b/src/symbols/s3-api-client/fs.ts deleted file mode 100644 index 4ee02db..0000000 --- a/src/symbols/s3-api-client/fs.ts +++ /dev/null @@ -1,189 +0,0 @@ -// We support uploading files via a read stream, but depending on fs directly is not supported in browser. -// Webpack will fail when building a package that relies on fs (even for types). -// This is a copy from fs.d.ts for the type returned by fs.createReadStream() to satisfy the TypeScript compiler. -export interface ReadStream { - close(): void; - bytesRead: number; - path: string | Buffer; - pending: boolean; - - /** - * events.EventEmitter - * 1. open - * 2. close - * 3. ready - */ - addListener(event: "close", listener: () => void): this; - addListener(event: "data", listener: (chunk: Buffer | string) => void): this; - addListener(event: "end", listener: () => void): this; - addListener(event: "error", listener: (err: Error) => void): this; - addListener(event: "open", listener: (fd: number) => void): this; - addListener(event: "pause", listener: () => void): this; - addListener(event: "readable", listener: () => void): this; - addListener(event: "ready", listener: () => void): this; - addListener(event: "resume", listener: () => void): this; - addListener(event: string | symbol, listener: (...args: any[]) => void): this; - - on(event: "close", listener: () => void): this; - on(event: "data", listener: (chunk: Buffer | string) => void): this; - on(event: "end", listener: () => void): this; - on(event: "error", listener: (err: Error) => void): this; - on(event: "open", listener: (fd: number) => void): this; - on(event: "pause", listener: () => void): this; - on(event: "readable", listener: () => void): this; - on(event: "ready", listener: () => void): this; - on(event: "resume", listener: () => void): this; - on(event: string | symbol, listener: (...args: any[]) => void): this; - - once(event: "close", listener: () => void): this; - once(event: "data", listener: (chunk: Buffer | string) => void): this; - once(event: "end", listener: () => void): this; - once(event: "error", listener: (err: Error) => void): this; - once(event: "open", listener: (fd: number) => void): this; - once(event: "pause", listener: () => void): this; - once(event: "readable", listener: () => void): this; - once(event: "ready", listener: () => void): this; - once(event: "resume", listener: () => void): this; - once(event: string | symbol, listener: (...args: any[]) => void): this; - - prependListener(event: "close", listener: () => void): this; - prependListener(event: "data", listener: (chunk: Buffer | string) => void): this; - prependListener(event: "end", listener: () => void): this; - prependListener(event: "error", listener: (err: Error) => void): this; - prependListener(event: "open", listener: (fd: number) => void): this; - prependListener(event: "pause", listener: () => void): this; - prependListener(event: "readable", listener: () => void): this; - prependListener(event: "ready", listener: () => void): this; - prependListener(event: "resume", listener: () => void): this; - prependListener(event: string | symbol, listener: (...args: any[]) => void): this; - - prependOnceListener(event: "close", listener: () => void): this; - prependOnceListener(event: "data", listener: (chunk: Buffer | string) => void): this; - prependOnceListener(event: "end", listener: () => void): this; - prependOnceListener(event: "error", listener: (err: Error) => void): this; - prependOnceListener(event: "open", listener: (fd: number) => void): this; - prependOnceListener(event: "pause", listener: () => void): this; - prependOnceListener(event: "readable", listener: () => void): this; - prependOnceListener(event: "ready", listener: () => void): this; - prependOnceListener(event: "resume", listener: () => void): this; - prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this; -} - -// interface Readable extends NodeJS.ReadableStream { -// readable: boolean; -// readonly readableEncoding: BufferEncoding | null; -// readonly readableEnded: boolean; -// readonly readableFlowing: boolean | null; -// readonly readableHighWaterMark: number; -// readonly readableLength: number; -// readonly readableObjectMode: boolean; -// destroyed: boolean; -// constructor(opts?: ReadableOptions); -// _construct?(callback: (error?: Error | null) => void): void; -// _read(size: number): void; -// read(size?: number): any; -// setEncoding(encoding: BufferEncoding): this; -// pause(): this; -// resume(): this; -// isPaused(): boolean; -// unpipe(destination?: NodeJS.WritableStream): this; -// unshift(chunk: any, encoding?: BufferEncoding): void; -// wrap(oldStream: NodeJS.ReadableStream): this; -// push(chunk: any, encoding?: BufferEncoding): boolean; -// _destroy(error: Error | null, callback: (error?: Error | null) => void): void; -// destroy(error?: Error): void; - -// /** -// * Event emitter -// * The defined events on documents including: -// * 1. close -// * 2. data -// * 3. end -// * 4. error -// * 5. pause -// * 6. readable -// * 7. resume -// */ -// addListener(event: "close", listener: () => void): this; -// addListener(event: "data", listener: (chunk: any) => void): this; -// addListener(event: "end", listener: () => void): this; -// addListener(event: "error", listener: (err: Error) => void): this; -// addListener(event: "pause", listener: () => void): this; -// addListener(event: "readable", listener: () => void): this; -// addListener(event: "resume", listener: () => void): this; -// addListener(event: string | symbol, listener: (...args: any[]) => void): this; - -// emit(event: "close"): boolean; -// emit(event: "data", chunk: any): boolean; -// emit(event: "end"): boolean; -// emit(event: "error", err: Error): boolean; -// emit(event: "pause"): boolean; -// emit(event: "readable"): boolean; -// emit(event: "resume"): boolean; -// emit(event: string | symbol, ...args: any[]): boolean; - -// on(event: "close", listener: () => void): this; -// on(event: "data", listener: (chunk: any) => void): this; -// on(event: "end", listener: () => void): this; -// on(event: "error", listener: (err: Error) => void): this; -// on(event: "pause", listener: () => void): this; -// on(event: "readable", listener: () => void): this; -// on(event: "resume", listener: () => void): this; -// on(event: string | symbol, listener: (...args: any[]) => void): this; - -// once(event: "close", listener: () => void): this; -// once(event: "data", listener: (chunk: any) => void): this; -// once(event: "end", listener: () => void): this; -// once(event: "error", listener: (err: Error) => void): this; -// once(event: "pause", listener: () => void): this; -// once(event: "readable", listener: () => void): this; -// once(event: "resume", listener: () => void): this; -// once(event: string | symbol, listener: (...args: any[]) => void): this; - -// prependListener(event: "close", listener: () => void): this; -// prependListener(event: "data", listener: (chunk: any) => void): this; -// prependListener(event: "end", listener: () => void): this; -// prependListener(event: "error", listener: (err: Error) => void): this; -// prependListener(event: "pause", listener: () => void): this; -// prependListener(event: "readable", listener: () => void): this; -// prependListener(event: "resume", listener: () => void): this; -// prependListener(event: string | symbol, listener: (...args: any[]) => void): this; - -// prependOnceListener(event: "close", listener: () => void): this; -// prependOnceListener(event: "data", listener: (chunk: any) => void): this; -// prependOnceListener(event: "end", listener: () => void): this; -// prependOnceListener(event: "error", listener: (err: Error) => void): this; -// prependOnceListener(event: "pause", listener: () => void): this; -// prependOnceListener(event: "readable", listener: () => void): this; -// prependOnceListener(event: "resume", listener: () => void): this; -// prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this; - -// removeListener(event: "close", listener: () => void): this; -// removeListener(event: "data", listener: (chunk: any) => void): this; -// removeListener(event: "end", listener: () => void): this; -// removeListener(event: "error", listener: (err: Error) => void): this; -// removeListener(event: "pause", listener: () => void): this; -// removeListener(event: "readable", listener: () => void): this; -// removeListener(event: "resume", listener: () => void): this; -// removeListener(event: string | symbol, listener: (...args: any[]) => void): this; - -// [Symbol.asyncIterator](): AsyncIterableIterator; -// } - -// interface ReadableOptions extends StreamOptions { -// encoding?: BufferEncoding; -// read?(this: Readable, size: number): void; -// } - -// interface StreamOptions extends Abortable { -// emitClose?: boolean; -// highWaterMark?: number; -// objectMode?: boolean; -// construct?(this: T, callback: (error?: Error | null) => void): void; -// destroy?(this: T, error: Error | null, callback: (error: Error | null) => void): void; -// autoDestroy?: boolean; -// } - -// interface Stream { -// constructor(opts?: ReadableOptions); -// } \ No newline at end of file