UNPKG

zip-iterator

Version:

Extract contents from zip archive type using an iterator API using streams or paths. Use stream interface and pipe transforms to add decompression algorithms

87 lines (86 loc) 3.27 kB
/** * EntryEmitter - Entry stream creation and lifecycle management * * Handles creating PassThrough streams for entries and manages the * deferred error emission pattern for race conditions. */ import { PassThrough } from 'extract-base-iterator'; /** * Create a new entry stream * * Creates a PassThrough stream that is initially paused to prevent * data events from being lost before the consumer attaches listeners. */ export function createEntryStream() { const stream = new PassThrough(); // Pause the output stream so data events aren't lost before consumer attaches listeners // Consumer should call resume() or attach listeners which will auto-resume if (typeof stream.pause === 'function') { stream.pause(); } return stream; } /** * Emit error to a stream, deferring if no listeners are attached yet. * * This handles the race condition where the stream ends before the consumer * has a chance to attach error listeners (e.g., small truncated files). * The parser detects errors synchronously before the consumer's forEach callback * has a chance to call entry.create() and attach listeners. * * @param stream The stream to emit error to * @param err The error to emit */ export function emitErrorToStream(stream, err) { // Check if there are already error listeners const hasListeners = stream.listeners && stream.listeners('error').length > 0; if (hasListeners) { // Emit immediately if listeners exist stream.emit('error', err); } else { // Defer emission: patch on/addListener to emit when listener is attached // Store error on stream object for deferred emission const streamWithError = stream; streamWithError._deferredError = err; // Wrap the on/addListener methods to check for deferred error const origOn = stream.on; const patchedOn = function(event, listener) { const result = origOn.call(this, event, listener); // If attaching error listener and we have a deferred error, emit it if (event === 'error' && this._deferredError) { const deferredErr = this._deferredError; this._deferredError = undefined; // Emit asynchronously to ensure listener is fully attached setTimeout(()=>{ this.emit('error', deferredErr); }, 0); } return result; }; stream.on = patchedOn; stream.addListener = patchedOn; } } /** * Emit error to stream if it has listeners, otherwise use deferred emission * * @param stream The stream to emit error to * @param err The error to emit */ export function emitStreamError(stream, err) { if (!stream) return; const listeners = stream.listeners && stream.listeners('error'); if (listeners && listeners.length > 0) { stream.emit('error', err); } else { emitErrorToStream(stream, err); } } /** * End and clean up an entry stream * * @param stream The stream to end */ export function endEntryStream(stream) { if (stream) { stream.end(); } } /** * Export PassThrough for use in other modules */ export { PassThrough } from 'extract-base-iterator';