/**
 * Stream Transformer
 *
 * Provides composable stream transformation utilities
 * for mapping, filtering, and aggregating stream data.
 */
export class StreamTransformer {
    /**
     * Map each chunk through a transform function
     */
    static async *map(source, transform) {
        for await (const chunk of source) {
            yield await transform(chunk);
        }
    }
    /**
     * Filter chunks based on a predicate
     */
    static async *filter(source, predicate) {
        for await (const chunk of source) {
            if (await predicate(chunk)) {
                yield chunk;
            }
        }
    }
    /**
     * Take first n chunks
     */
    static async *take(source, count) {
        let taken = 0;
        for await (const chunk of source) {
            if (taken >= count)
                return;
            yield chunk;
            taken++;
        }
    }
    /**
     * Skip first n chunks
     */
    static async *skip(source, count) {
        let skipped = 0;
        for await (const chunk of source) {
            if (skipped < count) {
                skipped++;
                continue;
            }
            yield chunk;
        }
    }
    /**
     * Take chunks while predicate is true
     */
    static async *takeWhile(source, predicate) {
        for await (const chunk of source) {
            if (!(await predicate(chunk)))
                return;
            yield chunk;
        }
    }
    /**
     * Skip chunks while predicate is true
     */
    static async *skipWhile(source, predicate) {
        let skipping = true;
        for await (const chunk of source) {
            if (skipping) {
                if (await predicate(chunk))
                    continue;
                skipping = false;
            }
            yield chunk;
        }
    }
    /**
     * Flatten nested async iterables
     */
    static async *flatten(source) {
        for await (const item of source) {
            if (Symbol.asyncIterator in Object(item) || Symbol.iterator in Object(item)) {
                const iterable = item;
                for await (const chunk of iterable) {
                    yield chunk;
                }
            }
            else {
                yield item;
            }
        }
    }
    /**
     * Batch chunks into arrays of specified size
     */
    static async *batch(source, size) {
        let batch = [];
        for await (const chunk of source) {
            batch.push(chunk);
            if (batch.length >= size) {
                yield batch;
                batch = [];
            }
        }
        if (batch.length > 0) {
            yield batch;
        }
    }
    /**
     * Debounce stream - only emit after specified quiet period
     */
    static async *debounce(source, delayMs) {
        let lastChunk;
        let lastTime = 0;
        let pending = null;
        for await (const chunk of source) {
            lastChunk = chunk;
            lastTime = Date.now();
            if (pending)
                continue;
            pending = new Promise((resolve) => {
                setTimeout(() => {
                    if (Date.now() - lastTime >= delayMs && lastChunk !== undefined) {
                        resolve();
                    }
                }, delayMs);
            });
            await pending;
            pending = null;
            if (lastChunk !== undefined) {
                yield lastChunk;
            }
        }
    }
    /**
     * Throttle stream - emit at most once per interval
     */
    static async *throttle(source, intervalMs) {
        let lastEmit = 0;
        for await (const chunk of source) {
            const now = Date.now();
            if (now - lastEmit >= intervalMs) {
                lastEmit = now;
                yield chunk;
            }
        }
    }
    /**
     * Add delay between chunks
     */
    static async *delay(source, delayMs) {
        let first = true;
        for await (const chunk of source) {
            if (!first) {
                await new Promise((resolve) => setTimeout(resolve, delayMs));
            }
            first = false;
            yield chunk;
        }
    }
    /**
     * Buffer chunks until a flush condition is met
     */
    static async *buffer(source, shouldFlush) {
        let buffer = [];
        for await (const chunk of source) {
            if (shouldFlush(buffer, chunk) && buffer.length > 0) {
                yield buffer;
                buffer = [];
            }
            buffer.push(chunk);
        }
        if (buffer.length > 0) {
            yield buffer;
        }
    }
    /**
     * Merge multiple streams into one
     */
    static async *merge(...sources) {
        const iterators = sources.map((s) => s[Symbol.asyncIterator]());
        const results = [];
        // Initialize with first read from each iterator
        iterators.forEach((iter, index) => {
            results[index] = iter.next().then((result) => ({
                value: result.value,
                done: result.done ?? false,
                index,
            }));
        });
        while (results.length > 0) {
            const winner = await Promise.race(results.filter(Boolean));
            if (!winner.done) {
                yield winner.value;
                // Queue next read for this iterator
                results[winner.index] = iterators[winner.index].next().then((result) => ({
                    value: result.value,
                    done: result.done ?? false,
                    index: winner.index,
                }));
            }
            else {
                // Remove completed iterator
                results.splice(winner.index, 1);
                iterators.splice(winner.index, 1);
                // Re-index remaining iterators
                for (let i = 0; i < results.length; i++) {
                    const pending = results[i];
                    if (pending) {
                        results[i] = pending.then((r) => ({ ...r, index: i }));
                    }
                }
            }
        }
    }
    /**
     * Concatenate multiple streams sequentially
     */
    static async *concat(...sources) {
        for (const source of sources) {
            for await (const chunk of source) {
                yield chunk;
            }
        }
    }
    /**
     * Zip multiple streams together
     */
    static async *zip(...sources) {
        const iterators = sources.map((s) => s[Symbol.asyncIterator]());
        while (true) {
            const results = await Promise.all(iterators.map((iter) => iter.next()));
            if (results.some((r) => r.done))
                break;
            yield results.map((r) => r.value);
        }
    }
    /**
     * Create a tee - duplicate stream for multiple consumers
     */
    static tee(source, count = 2) {
        const buffers = Array.from({ length: count }, () => []);
        const doneFlags = Array(count).fill(false);
        let sourceExhausted = false;
        let sourceIterator = null;
        let sourceError = null;
        const readFromSource = async () => {
            if (!sourceIterator) {
                sourceIterator = source[Symbol.asyncIterator]();
            }
            try {
                const result = await sourceIterator.next();
                if (result.done) {
                    sourceExhausted = true;
                }
                else {
                    for (const buffer of buffers) {
                        buffer.push(result.value);
                    }
                }
            }
            catch (error) {
                sourceError = error instanceof Error ? error : new Error(String(error));
                sourceExhausted = true;
            }
        };
        const createIterator = (index) => ({
            [Symbol.asyncIterator]() {
                return {
                    async next() {
                        if (doneFlags[index]) {
                            return { done: true, value: undefined };
                        }
                        if (sourceError) {
                            throw sourceError;
                        }
                        if (buffers[index].length === 0) {
                            if (sourceExhausted) {
                                doneFlags[index] = true;
                                return { done: true, value: undefined };
                            }
                            await readFromSource();
                            if (sourceError)
                                throw sourceError;
                            if (buffers[index].length === 0 && sourceExhausted) {
                                doneFlags[index] = true;
                                return { done: true, value: undefined };
                            }
                        }
                        return { done: false, value: buffers[index].shift() };
                    },
                };
            },
        });
        return Array.from({ length: count }, (_, i) => createIterator(i));
    }
    /**
     * Create a pipeline of transformations
     */
    static pipe(source, ...transforms) {
        let result = source;
        for (const transform of transforms) {
            result = transform(result);
        }
        return result;
    }
    /**
     * Reduce stream to a single value
     */
    static async reduce(source, reducer, initial) {
        let accumulator = initial;
        for await (const chunk of source) {
            accumulator = await reducer(accumulator, chunk);
        }
        return accumulator;
    }
    /**
     * Collect all chunks into an array
     */
    static async collect(source) {
        const chunks = [];
        for await (const chunk of source) {
            chunks.push(chunk);
        }
        return chunks;
    }
    /**
     * Get first chunk
     */
    static async first(source) {
        for await (const chunk of source) {
            return chunk;
        }
        return undefined;
    }
    /**
     * Get last chunk
     */
    static async last(source) {
        let lastChunk;
        for await (const chunk of source) {
            lastChunk = chunk;
        }
        return lastChunk;
    }
    /**
     * Count chunks
     */
    static async count(source) {
        let count = 0;
        for await (const _ of source) {
            count++;
        }
        return count;
    }
    /**
     * Check if any chunk matches predicate
     */
    static async some(source, predicate) {
        for await (const chunk of source) {
            if (await predicate(chunk))
                return true;
        }
        return false;
    }
    /**
     * Check if all chunks match predicate
     */
    static async every(source, predicate) {
        for await (const chunk of source) {
            if (!(await predicate(chunk)))
                return false;
        }
        return true;
    }
    /**
     * Find first chunk matching predicate
     */
    static async find(source, predicate) {
        for await (const chunk of source) {
            if (await predicate(chunk))
                return chunk;
        }
        return undefined;
    }
}
//# sourceMappingURL=stream-transformer.js.map