refactor: Remove streamify array dependency

This commit is contained in:
Joachim Van Herwegen 2021-08-02 13:29:52 +02:00
parent 63e88578c3
commit 2ae95bd167
12 changed files with 70 additions and 106 deletions

18
package-lock.json generated
View File

@ -29,7 +29,6 @@
"@types/redis": "^2.8.30",
"@types/redlock": "^4.0.1",
"@types/sparqljs": "^3.1.2",
"@types/streamify-array": "^1.0.0",
"@types/url-join": "^4.0.0",
"@types/uuid": "^8.3.0",
"@types/ws": "^7.4.5",
@ -60,7 +59,6 @@
"redlock": "^4.2.0",
"sparqlalgebrajs": "^3.0.0",
"sparqljs": "^3.4.2",
"streamify-array": "^1.0.1",
"url-join": "^4.0.1",
"uuid": "^8.3.2",
"winston": "^3.3.3",
@ -5293,14 +5291,6 @@
"integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==",
"dev": true
},
"node_modules/@types/streamify-array": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/streamify-array/-/streamify-array-1.0.0.tgz",
"integrity": "sha512-qBRnXKNEF8ejRM7TODp3bXIFnHjDfrUM3cTpCU8hnkrI5FHH708wGTo4jc/2VnyNDd73sNYtt3un2pT+9E1y1A==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/superagent": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.11.tgz",
@ -21403,14 +21393,6 @@
"integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==",
"dev": true
},
"@types/streamify-array": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/streamify-array/-/streamify-array-1.0.0.tgz",
"integrity": "sha512-qBRnXKNEF8ejRM7TODp3bXIFnHjDfrUM3cTpCU8hnkrI5FHH708wGTo4jc/2VnyNDd73sNYtt3un2pT+9E1y1A==",
"requires": {
"@types/node": "*"
}
},
"@types/superagent": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.11.tgz",

View File

@ -95,7 +95,6 @@
"@types/redis": "^2.8.30",
"@types/redlock": "^4.0.1",
"@types/sparqljs": "^3.1.2",
"@types/streamify-array": "^1.0.0",
"@types/url-join": "^4.0.0",
"@types/uuid": "^8.3.0",
"@types/ws": "^7.4.5",
@ -126,7 +125,6 @@
"redlock": "^4.2.0",
"sparqlalgebrajs": "^3.0.0",
"sparqljs": "^3.4.2",
"streamify-array": "^1.0.1",
"url-join": "^4.0.1",
"uuid": "^8.3.2",
"winston": "^3.3.3",

View File

@ -3,12 +3,11 @@ import arrayifyStream from 'arrayify-stream';
import type { ParserOptions } from 'n3';
import { StreamParser, StreamWriter } from 'n3';
import type { Quad } from 'rdf-js';
import streamifyArray from 'streamify-array';
import type { Guarded } from './GuardedStream';
import { pipeSafely } from './StreamUtil';
import { guardedStreamFrom, pipeSafely } from './StreamUtil';
export function serializeQuads(quads: Quad[], contentType?: string): Guarded<Readable> {
return pipeSafely(streamifyArray(quads), new StreamWriter({ format: contentType }));
return pipeSafely(guardedStreamFrom(quads), new StreamWriter({ format: contentType }));
}
/**

View File

@ -1,6 +1,5 @@
import { Readable } from 'stream';
import arrayifyStream from 'arrayify-stream';
import streamifyArray from 'streamify-array';
import { AcceptPreferenceParser } from '../../src/ldp/http/AcceptPreferenceParser';
import { BasicRequestParser } from '../../src/ldp/http/BasicRequestParser';
import { ContentTypeParser } from '../../src/ldp/http/metadata/ContentTypeParser';
@ -8,6 +7,7 @@ import { OriginalUrlExtractor } from '../../src/ldp/http/OriginalUrlExtractor';
import { RawBodyParser } from '../../src/ldp/http/RawBodyParser';
import { RepresentationMetadata } from '../../src/ldp/representation/RepresentationMetadata';
import type { HttpRequest } from '../../src/server/HttpRequest';
import { guardedStreamFrom } from '../../src/util/StreamUtil';
describe('A BasicRequestParser with simple input parsers', (): void => {
const targetExtractor = new OriginalUrlExtractor();
@ -17,7 +17,7 @@ describe('A BasicRequestParser with simple input parsers', (): void => {
const requestParser = new BasicRequestParser({ targetExtractor, preferenceParser, metadataParser, bodyParser });
it('can parse an incoming request.', async(): Promise<void> => {
const request = streamifyArray([ '<http://test.com/s> <http://test.com/p> <http://test.com/o>.' ]) as HttpRequest;
const request = guardedStreamFrom([ '<http://test.com/s> <http://test.com/p> <http://test.com/o>.' ]) as HttpRequest;
request.method = 'POST';
request.url = '/';
request.headers = {

View File

@ -1,10 +1,10 @@
import arrayifyStream from 'arrayify-stream';
import streamifyArray from 'streamify-array';
import type { BodyParserArgs } from '../../../../src/ldp/http/BodyParser';
import { RawBodyParser } from '../../../../src/ldp/http/RawBodyParser';
import { RepresentationMetadata } from '../../../../src/ldp/representation/RepresentationMetadata';
import 'jest-rdf';
import type { HttpRequest } from '../../../../src/server/HttpRequest';
import { guardedStreamFrom } from '../../../../src/util/StreamUtil';
describe('A RawBodyparser', (): void => {
const bodyParser = new RawBodyParser();
@ -19,34 +19,34 @@ describe('A RawBodyparser', (): void => {
});
it('returns empty output if there is no content length or transfer encoding.', async(): Promise<void> => {
input.request = streamifyArray([ '' ]) as HttpRequest;
input.request = guardedStreamFrom([ '' ]) as HttpRequest;
input.request.headers = {};
await expect(bodyParser.handle(input)).resolves.toBeUndefined();
});
// https://github.com/solid/community-server/issues/498
it('returns empty output if the content length is 0 and there is no content type.', async(): Promise<void> => {
input.request = streamifyArray([ '' ]) as HttpRequest;
input.request = guardedStreamFrom([ '' ]) as HttpRequest;
input.request.headers = { 'content-length': '0' };
await expect(bodyParser.handle(input)).resolves.toBeUndefined();
});
it('errors when a content length is specified without content type.', async(): Promise<void> => {
input.request = streamifyArray([ 'abc' ]) as HttpRequest;
input.request = guardedStreamFrom([ 'abc' ]) as HttpRequest;
input.request.headers = { 'content-length': '1' };
await expect(bodyParser.handle(input)).rejects
.toThrow('HTTP request body was passed without a Content-Type header');
});
it('errors when a transfer encoding is specified without content type.', async(): Promise<void> => {
input.request = streamifyArray([ 'abc' ]) as HttpRequest;
input.request = guardedStreamFrom([ 'abc' ]) as HttpRequest;
input.request.headers = { 'transfer-encoding': 'chunked' };
await expect(bodyParser.handle(input)).rejects
.toThrow('HTTP request body was passed without a Content-Type header');
});
it('returns a Representation if there is empty data.', async(): Promise<void> => {
input.request = streamifyArray([]) as HttpRequest;
input.request = guardedStreamFrom([]) as HttpRequest;
input.request.headers = { 'content-length': '0', 'content-type': 'text/turtle' };
const result = (await bodyParser.handle(input))!;
expect(result).toEqual({
@ -58,7 +58,7 @@ describe('A RawBodyparser', (): void => {
});
it('returns a Representation if there is non-empty data.', async(): Promise<void> => {
input.request = streamifyArray([ '<http://test.com/s> <http://test.com/p> <http://test.com/o>.' ]) as HttpRequest;
input.request = guardedStreamFrom([ '<http://test.com/s> <http://test.com/p> <http://test.com/o>.' ]) as HttpRequest;
input.request.headers = { 'transfer-encoding': 'chunked', 'content-type': 'text/turtle' };
const result = (await bodyParser.handle(input))!;
expect(result).toEqual({

View File

@ -3,13 +3,13 @@ import { namedNode, quad } from '@rdfjs/data-model';
import arrayifyStream from 'arrayify-stream';
import { Algebra } from 'sparqlalgebrajs';
import * as algebra from 'sparqlalgebrajs';
import streamifyArray from 'streamify-array';
import type { BodyParserArgs } from '../../../../src/ldp/http/BodyParser';
import { SparqlUpdateBodyParser } from '../../../../src/ldp/http/SparqlUpdateBodyParser';
import { RepresentationMetadata } from '../../../../src/ldp/representation/RepresentationMetadata';
import type { HttpRequest } from '../../../../src/server/HttpRequest';
import { BadRequestHttpError } from '../../../../src/util/errors/BadRequestHttpError';
import { UnsupportedMediaTypeHttpError } from '../../../../src/util/errors/UnsupportedMediaTypeHttpError';
import { guardedStreamFrom } from '../../../../src/util/StreamUtil';
describe('A SparqlUpdateBodyParser', (): void => {
const bodyParser = new SparqlUpdateBodyParser();
@ -32,7 +32,7 @@ describe('A SparqlUpdateBodyParser', (): void => {
});
it('errors when handling invalid SPARQL updates.', async(): Promise<void> => {
input.request = streamifyArray([ 'VERY INVALID UPDATE' ]) as HttpRequest;
input.request = guardedStreamFrom([ 'VERY INVALID UPDATE' ]) as HttpRequest;
await expect(bodyParser.handle(input)).rejects.toThrow(BadRequestHttpError);
});
@ -40,7 +40,7 @@ describe('A SparqlUpdateBodyParser', (): void => {
const mock = jest.spyOn(algebra, 'translate').mockImplementationOnce((): any => {
throw 'apple';
});
input.request = streamifyArray(
input.request = guardedStreamFrom(
[ 'DELETE DATA { <http://test.com/s> <http://test.com/p> <http://test.com/o> }' ],
) as HttpRequest;
await expect(bodyParser.handle(input)).rejects.toThrow(BadRequestHttpError);
@ -48,7 +48,7 @@ describe('A SparqlUpdateBodyParser', (): void => {
});
it('converts SPARQL updates to algebra.', async(): Promise<void> => {
input.request = streamifyArray(
input.request = guardedStreamFrom(
[ 'DELETE DATA { <http://test.com/s> <http://test.com/p> <http://test.com/o> }' ],
) as HttpRequest;
const result = await bodyParser.handle(input);
@ -67,7 +67,7 @@ describe('A SparqlUpdateBodyParser', (): void => {
});
it('accepts relative references.', async(): Promise<void> => {
input.request = streamifyArray(
input.request = guardedStreamFrom(
[ 'INSERT DATA { <#it> <http://test.com/p> <http://test.com/o> }' ],
) as HttpRequest;
input.metadata.identifier = namedNode('http://test.com/my-document.ttl');

View File

@ -1,7 +1,7 @@
import 'jest-rdf';
import { Readable } from 'stream';
import { namedNode } from '@rdfjs/data-model';
import arrayifyStream from 'arrayify-stream';
import streamifyArray from 'streamify-array';
import { BasicRepresentation } from '../../../../src/ldp/representation/BasicRepresentation';
import { RepresentationMetadata } from '../../../../src/ldp/representation/RepresentationMetadata';
import { INTERNAL_QUADS } from '../../../../src/util/ContentTypes';
@ -34,7 +34,7 @@ describe('BasicRepresentation', (): void => {
});
it('creates a representation with (unguarded data, metadata).', (): void => {
const data = streamifyArray([ '' ]);
const data = Readable.from([ '' ]);
const metadata = new RepresentationMetadata();
const representation = new BasicRepresentation(data, metadata);
expect(representation.data).toBe(data);

View File

@ -1,15 +1,14 @@
import { EventEmitter } from 'events';
import fs from 'fs';
import { PassThrough } from 'stream';
import { PassThrough, Readable } from 'stream';
import { createResponse } from 'node-mocks-http';
import streamifyArray from 'streamify-array';
import { StaticAssetHandler } from '../../../../src/server/middleware/StaticAssetHandler';
import { NotFoundHttpError } from '../../../../src/util/errors/NotFoundHttpError';
import type { SystemError } from '../../../../src/util/errors/SystemError';
import { getModuleRoot, joinFilePath } from '../../../../src/util/PathUtil';
const createReadStream = jest.spyOn(fs, 'createReadStream')
.mockImplementation((): any => streamifyArray([ 'file contents' ]));
.mockImplementation((): any => Readable.from([ 'file contents' ]));
describe('A StaticAssetHandler', (): void => {
const handler = new StaticAssetHandler({

View File

@ -1,6 +1,6 @@
import { namedNode, triple } from '@rdfjs/data-model';
import rdfSerializer from 'rdf-serialize';
import streamifyArray from 'streamify-array';
import { BasicRepresentation } from '../../../../src/ldp/representation/BasicRepresentation';
import type { Representation } from '../../../../src/ldp/representation/Representation';
import { RepresentationMetadata } from '../../../../src/ldp/representation/RepresentationMetadata';
import type { RepresentationPreferences } from '../../../../src/ldp/representation/RepresentationPreferences';
@ -48,14 +48,12 @@ describe('A QuadToRdfConverter', (): void => {
});
it('converts quads to Turtle.', async(): Promise<void> => {
const representation = {
data: streamifyArray([ triple(
namedNode('http://test.com/s'),
namedNode('http://test.com/p'),
namedNode('http://test.com/o'),
) ]),
metadata,
} as Representation;
const representation = new BasicRepresentation([ triple(
namedNode('http://test.com/s'),
namedNode('http://test.com/p'),
namedNode('http://test.com/o'),
) ],
metadata);
const preferences: RepresentationPreferences = { type: { 'text/turtle': 1 }};
const result = await converter.handle({ identifier, representation, preferences });
expect(result).toMatchObject({
@ -72,14 +70,12 @@ describe('A QuadToRdfConverter', (): void => {
it('converts quads with prefixes to Turtle.', async(): Promise<void> => {
metadata.addQuad(DC.terms.namespace, PREFERRED_PREFIX_TERM, 'dc');
metadata.addQuad('http://test.com/', PREFERRED_PREFIX_TERM, 'test');
const representation = {
data: streamifyArray([ triple(
namedNode('http://test.com/s'),
DC.terms.modified,
namedNode('http://test.com/o'),
) ]),
metadata,
} as Representation;
const representation = new BasicRepresentation([ triple(
namedNode('http://test.com/s'),
DC.terms.modified,
namedNode('http://test.com/o'),
) ],
metadata);
const preferences: RepresentationPreferences = { type: { 'text/turtle': 1 }};
const result = await converter.handle({ identifier, representation, preferences });
expect(result.metadata.contentType).toEqual('text/turtle');
@ -93,14 +89,12 @@ test:s dc:modified test:o.
});
it('uses the base IRI when converting quads to Turtle.', async(): Promise<void> => {
const representation = {
data: streamifyArray([ triple(
namedNode('http://example.org/foo/bar/'),
namedNode('http://example.org/foo/bar/#abc'),
namedNode('http://example.org/foo/bar/def/ghi'),
) ]),
metadata,
} as Representation;
const representation = new BasicRepresentation([ triple(
namedNode('http://example.org/foo/bar/'),
namedNode('http://example.org/foo/bar/#abc'),
namedNode('http://example.org/foo/bar/def/ghi'),
) ],
metadata);
const preferences: RepresentationPreferences = { type: { 'text/turtle': 1 }};
const result = await converter.handle({ identifier, representation, preferences });
expect(result.metadata.contentType).toEqual('text/turtle');
@ -112,14 +106,12 @@ test:s dc:modified test:o.
it('converts quads to JSON-LD.', async(): Promise<void> => {
metadata.contentType = INTERNAL_QUADS;
const representation = {
data: streamifyArray([ triple(
namedNode('http://test.com/s'),
namedNode('http://test.com/p'),
namedNode('http://test.com/o'),
) ]),
metadata,
} as Representation;
const representation = new BasicRepresentation([ triple(
namedNode('http://test.com/s'),
namedNode('http://test.com/p'),
namedNode('http://test.com/o'),
) ],
metadata);
const preferences: RepresentationPreferences = { type: { 'application/ld+json': 1 }};
const result = await converter.handle({ identifier, representation, preferences });
expect(result).toMatchObject({

View File

@ -3,7 +3,7 @@ import { Readable } from 'stream';
import { namedNode, triple } from '@rdfjs/data-model';
import arrayifyStream from 'arrayify-stream';
import rdfParser from 'rdf-parse';
import streamifyArray from 'streamify-array';
import { BasicRepresentation } from '../../../../src/ldp/representation/BasicRepresentation';
import type { Representation } from '../../../../src/ldp/representation/Representation';
import { RepresentationMetadata } from '../../../../src/ldp/representation/RepresentationMetadata';
import type { RepresentationPreferences } from '../../../../src/ldp/representation/RepresentationPreferences';
@ -40,10 +40,9 @@ describe('A RdfToQuadConverter', (): void => {
it('converts turtle to quads.', async(): Promise<void> => {
const metadata = new RepresentationMetadata('text/turtle');
const representation = {
data: streamifyArray([ '<http://test.com/s> <http://test.com/p> <http://test.com/o>.' ]),
metadata,
} as Representation;
const representation = new BasicRepresentation(
'<http://test.com/s> <http://test.com/p> <http://test.com/o>.', metadata,
);
const preferences: RepresentationPreferences = { type: { [INTERNAL_QUADS]: 1 }};
const result = await converter.handle({ identifier, representation, preferences });
expect(result).toEqual({
@ -61,10 +60,9 @@ describe('A RdfToQuadConverter', (): void => {
it('converts JSON-LD to quads.', async(): Promise<void> => {
const metadata = new RepresentationMetadata('application/ld+json');
const representation = {
data: streamifyArray([ '{"@id": "http://test.com/s", "http://test.com/p": { "@id": "http://test.com/o" }}' ]),
metadata,
} as Representation;
const representation = new BasicRepresentation(
'{"@id": "http://test.com/s", "http://test.com/p": { "@id": "http://test.com/o" }}', metadata,
);
const preferences: RepresentationPreferences = { type: { [INTERNAL_QUADS]: 1 }};
const result = await converter.handle({ identifier, representation, preferences });
expect(result).toEqual({
@ -82,10 +80,9 @@ describe('A RdfToQuadConverter', (): void => {
it('throws an BadRequestHttpError on invalid triple data.', async(): Promise<void> => {
const metadata = new RepresentationMetadata('text/turtle');
const representation = {
data: streamifyArray([ '<http://test.com/s> <http://test.com/p> <http://test.co' ]),
metadata,
} as Representation;
const representation = new BasicRepresentation(
'<http://test.com/s> <http://test.com/p> <http://test.co', metadata,
);
const preferences: RepresentationPreferences = { type: { [INTERNAL_QUADS]: 1 }};
const result = await converter.handle({ identifier, representation, preferences });
expect(result).toEqual({

View File

@ -1,6 +1,5 @@
import { PassThrough } from 'stream';
import { PassThrough, Readable } from 'stream';
import arrayifyStream from 'arrayify-stream';
import streamifyArray from 'streamify-array';
import type { Logger } from '../../../src/logging/Logger';
import { getLoggerFor } from '../../../src/logging/LogUtil';
import { isHttpRequest } from '../../../src/server/HttpRequest';
@ -19,7 +18,7 @@ jest.mock('../../../src/server/HttpRequest', (): any => ({
describe('StreamUtil', (): void => {
describe('#readableToString', (): void => {
it('concatenates all elements of a Readable.', async(): Promise<void> => {
const stream = streamifyArray([ 'a', 'b', 'c' ]);
const stream = Readable.from([ 'a', 'b', 'c' ]);
await expect(readableToString(stream)).resolves.toEqual('abc');
});
});
@ -30,7 +29,7 @@ describe('StreamUtil', (): void => {
});
it('pipes data from one stream to the other.', async(): Promise<void> => {
const input = streamifyArray([ 'data' ]);
const input = Readable.from([ 'data' ]);
const output = new PassThrough();
const piped = pipeSafely(input, output);
await expect(readableToString(piped)).resolves.toEqual('data');
@ -50,7 +49,7 @@ describe('StreamUtil', (): void => {
});
it('supports mapping errors to something else.', async(): Promise<void> => {
const input = streamifyArray([ 'data' ]);
const input = Readable.from([ 'data' ]);
input.read = (): any => {
input.emit('error', new Error('error'));
return null;
@ -61,7 +60,7 @@ describe('StreamUtil', (): void => {
});
it('logs specific safer errors as debug.', async(): Promise<void> => {
const input = streamifyArray([ 'data' ]);
const input = Readable.from([ 'data' ]);
input.read = (): any => {
input.emit('error', new Error('Cannot call write after a stream was destroyed'));
return null;
@ -123,7 +122,7 @@ describe('StreamUtil', (): void => {
it('can map errors if the input is an HttpRequest.', async(): Promise<void> => {
(isHttpRequest as unknown as jest.Mock).mockReturnValueOnce(true);
const input = streamifyArray([ 'data' ]);
const input = Readable.from([ 'data' ]);
input.read = (): any => {
input.emit('error', new Error('error'));
return null;
@ -136,7 +135,7 @@ describe('StreamUtil', (): void => {
describe('#transformSafely', (): void => {
it('can transform a stream without arguments.', async(): Promise<void> => {
const source = streamifyArray([ 'data' ]);
const source = Readable.from([ 'data' ]);
const transformed = transformSafely(source);
transformed.setEncoding('utf8');
const result = await arrayifyStream(transformed);
@ -144,7 +143,7 @@ describe('StreamUtil', (): void => {
});
it('can transform a stream synchronously.', async(): Promise<void> => {
const source = streamifyArray([ 'data' ]);
const source = Readable.from([ 'data' ]);
const transformed = transformSafely<string>(source, {
encoding: 'utf8',
transform(data: string): void {
@ -160,7 +159,7 @@ describe('StreamUtil', (): void => {
});
it('can transform a stream asynchronously.', async(): Promise<void> => {
const source = streamifyArray([ 'data' ]);
const source = Readable.from([ 'data' ]);
const transformed = transformSafely<string>(source, {
encoding: 'utf8',
async transform(data: string): Promise<void> {
@ -187,7 +186,7 @@ describe('StreamUtil', (): void => {
it('catches synchronous errors on transform.', async(): Promise<void> => {
const error = new Error('stream error');
const source = streamifyArray([ 'data' ]);
const source = Readable.from([ 'data' ]);
const transformed = transformSafely<string>(source, {
transform(): never {
throw error;
@ -198,7 +197,7 @@ describe('StreamUtil', (): void => {
it('catches synchronous errors on flush.', async(): Promise<void> => {
const error = new Error('stream error');
const source = streamifyArray([ 'data' ]);
const source = Readable.from([ 'data' ]);
const transformed = transformSafely<string>(source, {
async flush(): Promise<never> {
await new Promise((resolve): any => setImmediate(resolve));
@ -210,7 +209,7 @@ describe('StreamUtil', (): void => {
it('catches asynchronous errors on transform.', async(): Promise<void> => {
const error = new Error('stream error');
const source = streamifyArray([ 'data' ]);
const source = Readable.from([ 'data' ]);
const transformed = transformSafely<string>(source, {
transform(): never {
throw error;
@ -221,7 +220,7 @@ describe('StreamUtil', (): void => {
it('catches asynchronous errors on flush.', async(): Promise<void> => {
const error = new Error('stream error');
const source = streamifyArray([ 'data' ]);
const source = Readable.from([ 'data' ]);
const transformed = transformSafely<string>(source, {
async flush(): Promise<never> {
await new Promise((resolve): any => setImmediate(resolve));

View File

@ -1,7 +1,5 @@
import type { Dirent, Stats } from 'fs';
import { PassThrough } from 'stream';
import streamifyArray from 'streamify-array';
import { PassThrough, Readable } from 'stream';
import type { SystemError } from '../../src/util/errors/SystemError';
const portNames = [
@ -95,7 +93,7 @@ export function mockFs(rootFilepath?: string, time?: Date): { data: any } {
const mock = {
createReadStream(path: string): any {
const { folder, name } = getFolder(path);
return streamifyArray([ folder[name] ]);
return Readable.from([ folder[name] ]);
},
createWriteStream(path: string): any {
const { folder, name } = getFolder(path);