From a239a1be9e4e4fa007733892eebd8e22d76e3842 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Fri, 1 Aug 2025 10:58:49 +0200 Subject: [PATCH 1/5] introduces StreamLike interface as common subset of AsyncIterator, RDF/JS Stream and Node's Readable interfaces --- .gitignore | 1 + package-lock.json | 31 +++++++++++++--- package.json | 1 + src/quadstore.ts | 17 +++++---- src/types/index.ts | 13 +++++-- src/utils/consumeinbatches.ts | 24 ++++++------ src/utils/consumeonebyone.ts | 24 ++++++------ src/utils/stuff.ts | 16 ++++---- test/browser/index.ts | 2 + test/node.ts | 18 +++++++++ test/others/typings.ts | 70 +++++++++++++++++++++++++++++++++++ test/tsconfig.json | 2 +- test/utils/stuff.js | 1 + 13 files changed, 168 insertions(+), 52 deletions(-) create mode 100644 test/others/typings.ts diff --git a/.gitignore b/.gitignore index 5927c59..28c1e20 100644 --- a/.gitignore +++ b/.gitignore @@ -334,6 +334,7 @@ dist/cjs .rdf-test-suite-cache test/**/*.d.ts +test/**/*.js.map test/backends/*.js test/browser/index.js test/browser/index.bundle.js diff --git a/package-lock.json b/package-lock.json index 657a34e..434d727 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@types/chai": "^5.2.2", "@types/mocha": "^10.0.10", "@types/n3": "^1.26.0", + "@types/node": "^24.1.0", "@types/node-static": "^0.7.11", "browser-level": "^3.0.0", "chai": "^5.2.0", @@ -419,9 +420,13 @@ } }, "node_modules/@types/node": { - "version": "18.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", + "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } }, "node_modules/@types/node-static": { "version": "0.7.11", @@ -3390,6 +3395,12 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -4153,9 +4164,12 @@ } }, "@types/node": { - "version": "18.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", + "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", + "requires": { + "undici-types": "~7.8.0" + } }, "@types/node-static": { "version": "0.7.11", @@ -6238,6 +6252,11 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true }, + "undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==" + }, "update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", diff --git a/package.json b/package.json index 5551fba..ede0c65 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "@types/chai": "^5.2.2", "@types/mocha": "^10.0.10", "@types/n3": "^1.26.0", + "@types/node": "^24.1.0", "@types/node-static": "^0.7.11", "browser-level": "^3.0.0", "chai": "^5.2.0", diff --git a/src/quadstore.ts b/src/quadstore.ts index 08596e4..9be9ded 100644 --- a/src/quadstore.ts +++ b/src/quadstore.ts @@ -23,7 +23,7 @@ import type { Pattern, StoreOpts, VoidResult, - TSReadable, + StreamLike, TermName, Prefixes, QuadArrayResultWithInternals, @@ -36,6 +36,7 @@ import type { } from 'abstract-level'; import { EventEmitter } from 'events'; import { + AsyncIterator, EmptyIterator, wrap, } from 'asynciterator'; @@ -120,7 +121,7 @@ export class Quadstore implements Store { await this.db.clear(); } - match(subject?: Quad_Subject, predicate?: Quad_Predicate, object?: Quad_Object, graph?: Quad_Graph, opts: GetOpts = emptyObject): Stream { + match(subject?: Quad_Subject, predicate?: Quad_Predicate, object?: Quad_Object, graph?: Quad_Graph, opts: GetOpts = emptyObject): AsyncIterator { // This is required due to the fact that Comunica may invoke the `.match()` // method in generalized RDF mode, under which the subject may be a literal // term. @@ -145,17 +146,17 @@ export class Quadstore implements Store { return results.approximateSize; } - import(source: Stream): EventEmitter { + import(source: StreamLike): EventEmitter { const emitter = new EventEmitter(); - this.putStream(>source, {}) + this.putStream(source, {}) .then(() => { emitter.emit('end'); }) .catch((err) => { emitter.emit('error', err); }); return emitter; } - remove(source: Stream): EventEmitter { + remove(source: StreamLike): EventEmitter { const emitter = new EventEmitter(); - this.delStream(>source, {}) + this.delStream(source, {}) .then(() => emitter.emit('end')) .catch((err) => emitter.emit('error', err)); return emitter; @@ -294,7 +295,7 @@ export class Quadstore implements Store { return await getStream(this, pattern, opts); } - async putStream(source: TSReadable, opts: PutStreamOpts = emptyObject): Promise { + async putStream(source: StreamLike, opts: PutStreamOpts = emptyObject): Promise { this.ensureReady(); const batchSize = opts.batchSize || 1; if (batchSize === 1) { @@ -305,7 +306,7 @@ export class Quadstore implements Store { return { type: ResultType.VOID }; } - async delStream(source: TSReadable, opts: DelStreamOpts = emptyObject): Promise { + async delStream(source: StreamLike, opts: DelStreamOpts = emptyObject): Promise { this.ensureReady(); const batchSize = opts.batchSize || 1; if (batchSize === 1) { diff --git a/src/types/index.ts b/src/types/index.ts index f106e3c..be12242 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,10 +1,10 @@ -import type { Readable } from 'stream'; import type { AbstractChainedBatch, AbstractLevel } from 'abstract-level' import type { AsyncIterator } from 'asynciterator'; import type { Literal, DataFactory, Quad_Subject, Quad_Predicate, Quad_Object, Quad_Graph, Quad, Term } from '@rdfjs/types'; import type { Scope } from '../scope/index.js'; import type { AbstractIteratorOptions } from 'abstract-level'; +import type { EventEmitter } from 'events'; export interface BatchOpts { /** @@ -27,8 +27,6 @@ export interface PatchOpts extends BatchOpts { export type TermName = 'subject' | 'predicate' | 'object' | 'graph'; -export type TSReadable = Readable | AsyncIterator; - export enum ResultType { VOID = 'void', QUADS = 'quads', @@ -151,3 +149,12 @@ export type TermWriter = E extends 'T' ? { write(node: T, serialized: SerializedTerm, prefixes: Prefixes, rangeMode: boolean, encodedValue: string): void } : { write(node: T, serialized: SerializedTerm, prefixes: Prefixes): void } ; + +export interface StreamLike extends EventEmitter { + read(): T | null; + destroy?: () => void; + on(event: 'readable', listener: () => void): this; + on(event: 'end', listener: () => void): this; + on(event: 'error', listener: (error: Error) => void): this; + on(event: 'data', listener: (item: T) => void): this; +} diff --git a/src/utils/consumeinbatches.ts b/src/utils/consumeinbatches.ts index d056375..02eee3a 100644 --- a/src/utils/consumeinbatches.ts +++ b/src/utils/consumeinbatches.ts @@ -1,7 +1,7 @@ -import { TSReadable } from '../types/index.js'; +import { StreamLike } from '../types/index.js'; -export const consumeInBatches = async (readable: TSReadable, batchSize: number, onEachBatch: (items: T[]) => any): Promise => { +export const consumeInBatches = async (source: StreamLike, batchSize: number, onEachBatch: (items: T[]) => any): Promise => { return new Promise((resolve, reject) => { let bufpos = 0; let looping = false; @@ -39,7 +39,7 @@ export const consumeInBatches = async (readable: TSReadable, batchSize: nu flushAndResolve(); return; } - while (bufpos < batchSize && (item = readable.read()) !== null) { + while (bufpos < batchSize && (item = source.read()) !== null) { buffer[bufpos++] = item; } if (item === null) { @@ -54,17 +54,15 @@ export const consumeInBatches = async (readable: TSReadable, batchSize: nu } }; const cleanup = () => { - readable.removeListener('end', onEnd); - readable.removeListener('error', onError); - readable.removeListener('readable', onReadable); - if (typeof readable.destroy === 'function') { - readable.destroy(); - } + source.removeListener('end', onEnd); + source.removeListener('error', onError); + source.removeListener('readable', onReadable); + source.destroy?.(); }; - readable.on('end', onEnd); - readable.on('error', onError); - readable.on('readable', onReadable); - if (readable.readable !== false) { + source.on('end', onEnd); + source.on('error', onError); + source.on('readable', onReadable); + if ('readable' in source && source.readable) { loop(); } }); diff --git a/src/utils/consumeonebyone.ts b/src/utils/consumeonebyone.ts index 27addc4..6229b31 100644 --- a/src/utils/consumeonebyone.ts +++ b/src/utils/consumeonebyone.ts @@ -1,14 +1,14 @@ -import { TSReadable } from '../types/index.js'; +import { StreamLike } from '../types/index.js'; -export const consumeOneByOne = async (iterator: TSReadable, onEachItem: (item: T) => any) => { +export const consumeOneByOne = async (source: StreamLike, onEachItem: (item: T) => any) => { return new Promise((resolve, reject) => { let item; let ended = false; let looping = false; const loop = () => { looping = true; - if ((item = iterator.read()) !== null) { + if ((item = source.read()) !== null) { Promise.resolve(onEachItem(item)) .then(loop) .catch(onError); @@ -36,18 +36,16 @@ export const consumeOneByOne = async (iterator: TSReadable, onEachItem: (i } }; const cleanup = () => { - iterator.removeListener('end', onEnd); - iterator.removeListener('error', onError); - iterator.removeListener('readable', onReadable); - if (typeof iterator.destroy === 'function') { - iterator.destroy(); - } + source.removeListener('end', onEnd); + source.removeListener('error', onError); + source.removeListener('readable', onReadable); + source.destroy?.(); }; - iterator.on('end', onEnd); - iterator.on('error', onError); - iterator.on('readable', onReadable); + source.on('end', onEnd); + source.on('error', onError); + source.on('readable', onReadable); // readable might be undefined in older versions of userland readable-stream - if (iterator.readable !== false) { + if ('readable' in source && source.readable) { loop(); } }); diff --git a/src/utils/stuff.ts b/src/utils/stuff.ts index 5fb1a53..709f87b 100644 --- a/src/utils/stuff.ts +++ b/src/utils/stuff.ts @@ -1,7 +1,7 @@ import type { EventEmitter } from 'events'; import type { AbstractLevel } from 'abstract-level'; -import type { TSReadable, TermName } from '../types/index.js'; +import type { TermName, StreamLike } from '../types/index.js'; export const isObject = (o: any): boolean => { return typeof(o) === 'object' && o !== null; @@ -20,16 +20,16 @@ export const ensureAbstractLevel = (o: any, key: string) => { } }; -export const streamToArray = (readStream: TSReadable): Promise => { +export const streamToArray = (source: StreamLike): Promise => { return new Promise((resolve, reject) => { const chunks: T[] = []; const onData = (chunk: T) => { chunks.push(chunk); }; const cleanup = () => { - readStream.removeListener('data', onData); - readStream.removeListener('error', onError); - readStream.destroy(); + source.removeListener('data', onData); + source.removeListener('error', onError); + source.destroy?.(); }; const onEnd = () => { cleanup(); @@ -39,9 +39,9 @@ export const streamToArray = (readStream: TSReadable): Promise => { cleanup(); reject(err); }; - readStream.on('error', onError); - readStream.on('end', onEnd); - readStream.on('data', onData); + source.on('error', onError); + source.on('end', onEnd); + source.on('data', onData); }); } diff --git a/test/browser/index.ts b/test/browser/index.ts index 18dda9f..514b244 100644 --- a/test/browser/index.ts +++ b/test/browser/index.ts @@ -2,7 +2,9 @@ import { runMemoryLevelTests } from '../backends/memorylevel.js'; import { runBrowserLevelTests } from '../backends/browserlevel.js'; import { runOtherTests } from '../others/others.js'; +import { runTypingsTests } from '../others/typings.js'; runOtherTests(); +runTypingsTests(); runMemoryLevelTests(); runBrowserLevelTests(); diff --git a/test/node.ts b/test/node.ts index 20f9925..0c10de4 100644 --- a/test/node.ts +++ b/test/node.ts @@ -1,9 +1,27 @@ +import type { Readable } from 'stream'; +import type { StreamLike } from '../dist/types/index.js'; + import { runMemoryLevelTests } from './backends/memorylevel.js'; import { runClassicLevelTests } from './backends/classiclevel.js'; import { runOtherTests } from './others/others.js'; +import { runTypingsTests } from './others/typings.js'; runOtherTests(); +runTypingsTests(); runMemoryLevelTests(); runClassicLevelTests(); +describe('Typings (Node.js)', () => { + + describe('StreamLike', () => { + + it('should be extended by Node\'s native Readable interface', () => { + const t: StreamLike = ({} as Readable); + }); + + }); + +}); + + diff --git a/test/others/typings.ts b/test/others/typings.ts new file mode 100644 index 0000000..aec549f --- /dev/null +++ b/test/others/typings.ts @@ -0,0 +1,70 @@ + +import type { Stream, Quad } from '@rdfjs/types'; +import type { StreamLike } from '../../dist/types/index.js'; +import type { AsyncIterator } from 'asynciterator'; + +import { Quadstore } from '../../dist/quadstore.js'; +import { MemoryLevel } from 'memory-level'; +import { DataFactory } from 'rdf-data-factory'; + + +export const runTypingsTests = () => { + + describe('Typings', () => { + + const store = new Quadstore({ + backend: new MemoryLevel(), + dataFactory: new DataFactory(), + }); + + describe('StreamLike', () => { + + it('should extend the RDF/JS Stream interface when using the RDF/JS Quad interface as the type parameter', () => { + const t: Stream = ({} as StreamLike); + }); + + it('should not extend the RDF/JS Stream interface when using anything else but the RDF/JS Quad interface as the type parameter', () => { + const t: StreamLike<'foo'> extends Stream ? true : false = false; + }); + + it('should be extended by the AsyncIterator interface', () => { + const t: StreamLike<'foo'> = ({} as AsyncIterator<'foo'>); + }); + + }); + + describe('Quadstore.prototype.match()', () => { + + it('should return an AsyncIterator instance', () => { + const t: AsyncIterator = store.match(); + }); + + it('should return a StreamLike object', () => { + const t: StreamLike = store.match(); + }); + + it('should return an iterable object', () => { + const t: AsyncIterable = store.match(); + }); + + }); + + describe('Quadstore.prototype.getStream()', () => { + + it('should return an AsyncIterator instance', async () => { + const t: AsyncIterator = (await store.getStream({})).iterator; + }); + + it('should return a StreamLike object', async () => { + const t: StreamLike = (await store.getStream({})).iterator; + }); + + it('should return an iterable object', async () => { + const t: AsyncIterable = (await store.getStream({})).iterator; + }); + + }); + + }); + +}; diff --git a/test/tsconfig.json b/test/tsconfig.json index 484a431..8f4e352 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -47,7 +47,7 @@ "declaration": false, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ // "declarationMap": true, /* Create sourcemaps for d.ts files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ "outDir": "./", /* Specify an output folder for all emitted files. */ // "removeComments": true, /* Disable emitting comments. */ diff --git a/test/utils/stuff.js b/test/utils/stuff.js index 40bc4ac..e4a773c 100644 --- a/test/utils/stuff.js +++ b/test/utils/stuff.js @@ -28,3 +28,4 @@ export const equalsUint8Array = (a, b) => { } return true; }; +//# sourceMappingURL=stuff.js.map \ No newline at end of file From e7499778588f241da7a0a983011b7423c01289e4 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Fri, 1 Aug 2025 11:16:56 +0200 Subject: [PATCH 2/5] adds test to ensure type-level compat between AsyncIterator and RDF/JS --- test/others/typings.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/others/typings.ts b/test/others/typings.ts index aec549f..9b132d5 100644 --- a/test/others/typings.ts +++ b/test/others/typings.ts @@ -33,6 +33,14 @@ export const runTypingsTests = () => { }); + describe('AsyncIterator', () => { + + it('should extend the RDF/JS Stream interface when using the RDF/JS Quad interface as the type parameter', () => { + const t: Stream = ({} as AsyncIterator); + }); + + }); + describe('Quadstore.prototype.match()', () => { it('should return an AsyncIterator instance', () => { From bd2ccfd6bbc025ee84fbe3098cf002ac4fcbe0d5 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Fri, 1 Aug 2025 11:24:26 +0200 Subject: [PATCH 3/5] adds attribute to StreamLike interface --- src/types/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/index.ts b/src/types/index.ts index be12242..5cbe430 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -153,6 +153,7 @@ export type TermWriter = E extends 'T' export interface StreamLike extends EventEmitter { read(): T | null; destroy?: () => void; + readable?: boolean; on(event: 'readable', listener: () => void): this; on(event: 'end', listener: () => void): this; on(event: 'error', listener: (error: Error) => void): this; From c4acf956bbcf779a9a60d91e42b8d6c6e3161ac1 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Fri, 1 Aug 2025 11:25:18 +0200 Subject: [PATCH 4/5] 15.4.0-beta.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 434d727..a09329d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "quadstore", - "version": "15.3.0", + "version": "15.4.0-beta.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "quadstore", - "version": "15.3.0", + "version": "15.4.0-beta.0", "license": "MIT", "dependencies": { "@rdfjs/types": "^2.0.1", diff --git a/package.json b/package.json index ede0c65..c2cfa18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "quadstore", - "version": "15.3.0", + "version": "15.4.0-beta.0", "description": "Quadstore is a LevelDB-backed RDF graph database / triplestore for JavaScript runtimes (browsers, Node.js, Deno, Bun, ...) that implements the RDF/JS interfaces and supports SPARQL queries and querying across named graphs.", "keywords": [ "node", From 270428c9490cb87ce73e940a8a82fcd72eaea557 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Sat, 2 Aug 2025 17:30:55 +0200 Subject: [PATCH 5/5] more tests to ensure compat between the new StreamLike interface and Node's own (and untyped) Readable class, slight refactor for enabling tests conditionally across different test suites --- test/browser/index.ts | 2 +- test/browser/webpack.config.cjs | 6 +++++- test/node.ts | 19 +----------------- test/others/typings.ts | 35 +++++++++++++++++++++++++++++++-- 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/test/browser/index.ts b/test/browser/index.ts index 514b244..e794ae5 100644 --- a/test/browser/index.ts +++ b/test/browser/index.ts @@ -5,6 +5,6 @@ import { runOtherTests } from '../others/others.js'; import { runTypingsTests } from '../others/typings.js'; runOtherTests(); -runTypingsTests(); +runTypingsTests(false); runMemoryLevelTests(); runBrowserLevelTests(); diff --git a/test/browser/webpack.config.cjs b/test/browser/webpack.config.cjs index 5f0480a..625ea8e 100644 --- a/test/browser/webpack.config.cjs +++ b/test/browser/webpack.config.cjs @@ -20,6 +20,10 @@ module.exports = { usedExports: true, concatenateModules: false }, - resolve: {}, + resolve: { + fallback: { + stream: false, + }, + }, plugins: [] }; diff --git a/test/node.ts b/test/node.ts index 0c10de4..2e81639 100644 --- a/test/node.ts +++ b/test/node.ts @@ -1,27 +1,10 @@ -import type { Readable } from 'stream'; -import type { StreamLike } from '../dist/types/index.js'; - import { runMemoryLevelTests } from './backends/memorylevel.js'; import { runClassicLevelTests } from './backends/classiclevel.js'; import { runOtherTests } from './others/others.js'; import { runTypingsTests } from './others/typings.js'; runOtherTests(); -runTypingsTests(); +runTypingsTests(true); runMemoryLevelTests(); runClassicLevelTests(); - -describe('Typings (Node.js)', () => { - - describe('StreamLike', () => { - - it('should be extended by Node\'s native Readable interface', () => { - const t: StreamLike = ({} as Readable); - }); - - }); - -}); - - diff --git a/test/others/typings.ts b/test/others/typings.ts index 9b132d5..bad74f3 100644 --- a/test/others/typings.ts +++ b/test/others/typings.ts @@ -7,8 +7,7 @@ import { Quadstore } from '../../dist/quadstore.js'; import { MemoryLevel } from 'memory-level'; import { DataFactory } from 'rdf-data-factory'; - -export const runTypingsTests = () => { +export const runTypingsTests = (isNode: boolean) => { describe('Typings', () => { @@ -31,6 +30,12 @@ export const runTypingsTests = () => { const t: StreamLike<'foo'> = ({} as AsyncIterator<'foo'>); }); + isNode && it('should be extended by Node\'s native Readable interface', async () => { + const { Readable } = await import('stream'); + const ta: StreamLike = new Readable(); + const tq: StreamLike = new Readable(); + }); + }); describe('AsyncIterator', () => { @@ -73,6 +78,32 @@ export const runTypingsTests = () => { }); + describe('Quadstore.prototype.putStream()', () => { + + isNode && it('should accept an instance of Node\'s native Readable class', async () => { + const { Readable } = await import('stream'); + await store.putStream(new Readable({ + read() { + this.push(null); + } + })); + }); + + }); + + describe('Quadstore.prototype.delStream()', () => { + + isNode && it('should accept an instance of Node\'s native Readable class', async () => { + const { Readable } = await import('stream'); + await store.delStream(new Readable({ + read() { + this.push(null); + } + })); + }); + + }); + }); };