diff --git a/asynciterator.ts b/asynciterator.ts index 102936d..45e3531 100644 --- a/asynciterator.ts +++ b/asynciterator.ts @@ -791,6 +791,9 @@ export function identity(item: S): typeof item { return item; } +/** Key indicating the current consumer of a source. */ +export const DESTINATION = Symbol('destination'); + /** An iterator that synchronously transforms every item from its source @@ -821,7 +824,7 @@ export class MappingIterator extends AsyncIterator { } // Otherwise, wire up the source for reading else { - this._source._destination = this; + this._source[DESTINATION] = this; this._source.on('end', destinationClose); this._source.on('error', destinationEmitError); this._source.on('readable', destinationSetReadable); @@ -854,7 +857,7 @@ export class MappingIterator extends AsyncIterator { this._source.removeListener('end', destinationClose); this._source.removeListener('error', destinationEmitError); this._source.removeListener('readable', destinationSetReadable); - delete this._source._destination; + delete this._source[DESTINATION]; if (this._destroySource) this._source.destroy(); super._end(destroy); @@ -865,7 +868,7 @@ export class MappingIterator extends AsyncIterator { function ensureSourceAvailable(source?: AsyncIterator, allowDestination = false) { if (!source || !isFunction(source.read) || !isFunction(source.on)) throw new TypeError(`Invalid source: ${source}`); - if (!allowDestination && (source as any)._destination) + if (!allowDestination && (source as any)[DESTINATION]) throw new Error('The source already has a destination'); return source as InternalSource; } @@ -1209,7 +1212,7 @@ export class TransformIterator extends BufferedIterator { // Validate and set source const source = this._source = this._validateSource(value); - source._destination = this; + source[DESTINATION] = this; // Close this iterator if the source has already ended if (source.done) { @@ -1324,7 +1327,7 @@ export class TransformIterator extends BufferedIterator { source.removeListener('end', destinationCloseWhenDone); source.removeListener('error', destinationEmitError); source.removeListener('readable', destinationFillBuffer); - delete source._destination; + delete source[DESTINATION]; if (this._destroySource) source.destroy(); } @@ -1333,20 +1336,20 @@ export class TransformIterator extends BufferedIterator { } function destinationSetReadable(this: InternalSource) { - this._destination!.readable = true; + this[DESTINATION]!.readable = true; } function destinationEmitError(this: InternalSource, error: Error) { - this._destination!.emit('error', error); + this[DESTINATION]!.emit('error', error); } function destinationClose(this: InternalSource) { - this._destination!.close(); + this[DESTINATION]!.close(); } function destinationCloseWhenDone(this: InternalSource) { - (this._destination as any)._closeWhenDone(); + (this[DESTINATION] as any)._closeWhenDone(); } function destinationFillBuffer(this: InternalSource) { - if ((this._destination as any)._sourceStarted !== false) - (this._destination as any)._fillBuffer(); + if ((this[DESTINATION] as any)._sourceStarted !== false) + (this[DESTINATION] as any)._fillBuffer(); } @@ -1564,7 +1567,7 @@ export class MultiTransformIterator extends TransformIterator { // Create the transformer and listen to its events const transformer = (this._createTransformer(item) || new EmptyIterator()) as InternalSource; - transformer._destination = this; + transformer[DESTINATION] = this; transformer.on('end', destinationFillBuffer); transformer.on('readable', destinationFillBuffer); transformer.on('error', destinationEmitError); @@ -1689,7 +1692,7 @@ export class UnionIterator extends BufferedIterator { source = wrap(source) as any as InternalSource; if (!source.done) { this._sources.push(source); - source._destination = this; + source[DESTINATION] = this; source.on('error', destinationEmitError); source.on('readable', destinationFillBuffer); source.on('end', destinationRemoveEmptySources); @@ -1753,7 +1756,7 @@ export class UnionIterator extends BufferedIterator { } function destinationRemoveEmptySources(this: InternalSource) { - (this._destination as any)._removeEmptySources(); + (this[DESTINATION] as any)._removeEmptySources(); } @@ -1795,8 +1798,8 @@ export class ClonedIterator extends TransformIterator { // Validate and set the source const source = this._source = this._validateSource(value); // Create a history reader for the source if none already existed - const history = (source && (source as any)._destination) || - (source._destination = new HistoryReader(source) as any); + const history = (source && (source as any)[DESTINATION]) || + (source[DESTINATION] = new HistoryReader(source) as any); // Close this clone if history is empty and the source has ended if (history.endsAt(0)) { @@ -1827,7 +1830,7 @@ export class ClonedIterator extends TransformIterator { @param {boolean} allowDestination Whether the source can already have a destination */ protected _validateSource(source?: AsyncIterator, allowDestination = false) { - const history = (source && (source as any)._destination); + const history = (source && (source as any)[DESTINATION]); return super._validateSource(source, !history || history instanceof HistoryReader); } @@ -1881,7 +1884,7 @@ export class ClonedIterator extends TransformIterator { let item = null; if (!this.done && source) { // Try to read an item at the current point in history - const history = source._destination as any as HistoryReader; + const history = source[DESTINATION] as any as HistoryReader; if ((item = history.readAt(this._readPosition)) !== null) this._readPosition++; else @@ -1897,7 +1900,7 @@ export class ClonedIterator extends TransformIterator { protected _end(destroy: boolean) { // Unregister from a possible history reader const source = this.source as InternalSource; - const history = source?._destination as any as HistoryReader; + const history = source?.[DESTINATION] as any as HistoryReader; if (history) history.unregister(this); @@ -2043,7 +2046,7 @@ export class WrappingIterator extends AsyncIterator { } // Set up event handling - source._destination = this; + source[DESTINATION] = this; source.on('end', destinationClose); source.on('error', destinationEmitError); source.on('readable', destinationSetReadable); @@ -2070,7 +2073,7 @@ export class WrappingIterator extends AsyncIterator { this._source.removeListener('end', destinationClose); this._source.removeListener('error', destinationEmitError); this._source.removeListener('readable', destinationSetReadable); - delete this._source._destination; + delete this._source[DESTINATION]; this._source = null; } } @@ -2224,7 +2227,7 @@ type SourceExpression = (() => MaybePromise>); type InternalSource = - AsyncIterator & { _destination?: AsyncIterator }; + AsyncIterator & { [DESTINATION]?: AsyncIterator }; // Returns a function that calls `fn` with `self` as `this` pointer. */ function bind(fn: T, self?: object): T { diff --git a/test/ClonedIterator-test.js b/test/ClonedIterator-test.js index f543728..98acefa 100644 --- a/test/ClonedIterator-test.js +++ b/test/ClonedIterator-test.js @@ -5,6 +5,7 @@ import { BufferedIterator, EmptyIterator, ArrayIterator, + DESTINATION, } from '../dist/asynciterator.js'; import { EventEmitter } from 'events'; @@ -120,7 +121,7 @@ describe('ClonedIterator', () => { describe('Cloning an iterator that already has a destination', () => { it('should throw an exception', () => { const source = new AsyncIterator(), destination = new TransformIterator(source); - source.should.have.property('_destination', destination); + source.should.have.property(DESTINATION, destination); (() => source.clone()).should.throw('The source already has a destination'); }); }); diff --git a/test/TransformIterator-test.js b/test/TransformIterator-test.js index 5ad99a0..595775a 100644 --- a/test/TransformIterator-test.js +++ b/test/TransformIterator-test.js @@ -6,6 +6,7 @@ import { TransformIterator, wrap, scheduleTask, + DESTINATION, } from '../dist/asynciterator.js'; import { EventEmitter } from 'events'; @@ -359,7 +360,7 @@ describe('TransformIterator', () => { }); it('should remove itself as destination from the source', () => { - source.should.not.have.key('_destination'); + source.should.not.have.key(DESTINATION); }); }); }); @@ -466,7 +467,7 @@ describe('TransformIterator', () => { }); it('should remove itself as destination from the source', () => { - source.should.not.have.key('_destination'); + source.should.not.have.key(DESTINATION); }); }); }); @@ -575,7 +576,7 @@ describe('TransformIterator', () => { }); it('should remove itself as destination from the source', () => { - source.should.not.have.key('_destination'); + source.should.not.have.key(DESTINATION); }); }); });