"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.zipWith = exports.zipWith_ = exports.zipRight = exports.zipRight_ = exports.zipParRight = exports.zipParRight_ = exports.zipParLeft = exports.zipParLeft_ = exports.zipPar = exports.zipPar_ = exports.zipLeft = exports.zipLeft_ = exports.zip = exports.zip_ = exports.toTransducer = exports.timed = exports.raceBoth = exports.raceBoth_ = exports.race = exports.race_ = exports.mapM = exports.mapM_ = exports.mapError = exports.mapError_ = exports.map = exports.map_ = exports.foldM = exports.foldM_ = exports.chain = exports.chain_ = exports.dimapChunksM = exports.dimapChunksM_ = exports.dimapChunks = exports.dimapChunks_ = exports.dimapM = exports.dimapM_ = exports.dimap = exports.dimap_ = exports.contramapChunksM = exports.contramapChunksM_ = exports.contramapChunks = exports.contramapChunks_ = exports.contramapM = exports.contramapM_ = exports.contramap = exports.contramap_ = exports.collectAllWhileWith = exports.as = exports.as_ = exports.Sink = void 0;
exports.fromHub = exports.fromQueueWithShutdown = exports.fromQueue = exports.foreachChunk = exports.timedDrain = exports.take = exports.sum = exports.succeed = exports.managed = exports.managed_ = exports.last = exports.head = exports.halt = exports.fromPush = exports.fromEffect = exports.forEachWhile = exports.forEachChunk = exports.forEach = exports.reduceLeftM = exports.reduceLeftChunksM = exports.reduceLeftChunks = exports.reduceLeft = exports.reduceM = exports.reduceChunksM = exports.reduceChunks = exports.reduce = exports.fail = exports.drain = exports.dieMessage = exports.die = exports.count = exports.collectAllToSet = exports.collectAllToMap = exports.collectAllToList = exports.collectAll = exports.accessM = exports.managedPush = exports.provideSomeLayer = exports.provideLayer_ = exports.provideLayer = exports.provideSome = exports.provideSome_ = exports.provideAll = exports.provideAll_ = exports.untilOutputM = exports.untilOutputM_ = exports.dropLeftover = exports.exposeLeftover = exports.zipWithPar = exports.zipWithPar_ = void 0;
exports.fromHubWithShutdown = void 0;
// ets_tracing: off
require("../../Operator");
const C = __importStar(require("../../Cause"));
const Clock_1 = require("../../Clock");
const A = __importStar(require("../../Collections/Immutable/Chunk"));
const List = __importStar(require("../../Collections/Immutable/List"));
const Tp = __importStar(require("../../Collections/Immutable/Tuple"));
const E = __importStar(require("../../Either"));
const Ex = __importStar(require("../../Exit/api"));
const Function_1 = require("../../Function");
const H = __importStar(require("../../Hub"));
const L = __importStar(require("../../Layer"));
const O = __importStar(require("../../Option"));
const Q = __importStar(require("../../Queue"));
const Utils_1 = require("../../Utils");
const T = __importStar(require("../_internal/effect"));
const F = __importStar(require("../_internal/fiber"));
const M = __importStar(require("../_internal/managed"));
const R = __importStar(require("../_internal/ref"));
const Push = __importStar(require("../Push"));
const Transducer_1 = require("../Transducer");
// Important notes while writing sinks and combinators:
// - What return values for sinks mean:
//   Effect.unit - "need more values"
//   Effect.fail([Either.Right(z), l]) - "ended with z and emit leftover l"
//   Effect.fail([Either.Left(e), l]) - "failed with e and emit leftover l"
// - Result of processing of the stream using the sink must not depend on how the stream is chunked
//   (chunking-invariance)
//   pipe(stream, run(sink), Effect.either) === pipe(stream, chunkN(1), run(sink), Effect.either)
// - Sinks should always end when receiving a `None`. It is a defect to not end with some
//   sort of result (even a failure) when receiving a `None`.
// - Sinks can assume they will not be pushed again after emitting a value.
class Sink {
    constructor(push) {
        this.push = push;
    }
}
exports.Sink = Sink;
/**
 * Replaces this sink's result with the provided value.
 */
function as_(self, z) {
    return map_(self, (_) => z);
}
exports.as_ = as_;
/**
 * Replaces this sink's result with the provided value.
 */
function as(z) {
    return (self) => as_(self, z);
}
exports.as = as;
/**
 * Repeatedly runs the sink for as long as its results satisfy
 * the predicate `p`. The sink's results will be accumulated
 * using the stepping function `f`.
 */
function collectAllWhileWith(z) {
    return (p) => (f) => (self) => new Sink((M.chain_(R.makeManagedRef(z), (acc) => {
        return (M.map_(Push.restartable(self.push), ({ tuple: [push, restart] }) => {
            const go = (s, in_, end) => T.catchAll_(T.as_(push(in_), s), ({ tuple: [e, leftover] }) => E.fold_(e, (e) => Push.fail(e, leftover), (z) => {
                if (p(z)) {
                    const s1 = f(s, z);
                    if (A.isEmpty(leftover)) {
                        if (end) {
                            return Push.emit(s1, A.empty());
                        }
                        else {
                            return T.as_(restart, s1);
                        }
                    }
                    else {
                        return T.zipRight_(restart, go(s1, O.some(leftover), end));
                    }
                }
                else {
                    return Push.emit(s, leftover);
                }
            }));
            return (in_) => T.chain_(acc.get, (s) => T.chain_(go(s, in_, O.isNone(in_)), (s1) => acc.set(s1)));
        }));
    })));
}
exports.collectAllWhileWith = collectAllWhileWith;
/**
 * Transforms this sink's input elements.
 */
function contramap_(self, f) {
    return contramapChunks_(self, A.map(f));
}
exports.contramap_ = contramap_;
/**
 * Transforms this sink's input elements.
 */
function contramap(f) {
    return (self) => contramap_(self, f);
}
exports.contramap = contramap;
/**
 * Effectfully transforms this sink's input elements.
 */
function contramapM_(self, f) {
    return contramapChunksM_(self, A.mapM(f));
}
exports.contramapM_ = contramapM_;
/**
 * Effectfully transforms this sink's input elements.
 */
function contramapM(f) {
    return (self) => contramapM_(self, f);
}
exports.contramapM = contramapM;
/**
 * Transforms this sink's input chunks.
 * `f` must preserve chunking-invariance
 */
function contramapChunks_(self, f) {
    return new Sink(M.map_(self.push, (push) => (input) => push(O.map_(input, f))));
}
exports.contramapChunks_ = contramapChunks_;
/**
 * Transforms this sink's input chunks.
 * `f` must preserve chunking-invariance
 */
function contramapChunks(f) {
    return (self) => contramapChunks_(self, f);
}
exports.contramapChunks = contramapChunks;
/**
 * Effectfully transforms this sink's input chunks.
 * `f` must preserve chunking-invariance
 */
function contramapChunksM_(self, f) {
    return new Sink(M.map_(self.push, (push) => {
        return (input) => O.fold_(input, () => push(O.none), (value) => (T.chain_(T.mapError_(f(value), (e) => Tp.tuple(E.left(e), A.empty())), (is) => push(O.some(is)))));
    }));
}
exports.contramapChunksM_ = contramapChunksM_;
/**
 * Effectfully transforms this sink's input chunks.
 * `f` must preserve chunking-invariance
 */
function contramapChunksM(f) {
    return (self) => contramapChunksM_(self, f);
}
exports.contramapChunksM = contramapChunksM;
/**
 * Transforms both inputs and result of this sink using the provided functions.
 */
function dimap_(self, f, g) {
    return map_(contramap_(self, f), g);
}
exports.dimap_ = dimap_;
/**
 * Transforms both inputs and result of this sink using the provided functions.
 */
function dimap(f, g) {
    return (self) => dimap_(self, f, g);
}
exports.dimap = dimap;
/**
 * Effectfully transforms both inputs and result of this sink using the provided functions.
 */
function dimapM_(self, f, g) {
    return mapM_(contramapM_(self, f), g);
}
exports.dimapM_ = dimapM_;
/**
 * Effectfully transforms both inputs and result of this sink using the provided functions.
 */
function dimapM(f, g) {
    return (self) => dimapM_(self, f, g);
}
exports.dimapM = dimapM;
/**
 * Transforms both input chunks and result of this sink using the provided functions.
 */
function dimapChunks_(self, f, g) {
    return map_(contramapChunks_(self, f), g);
}
exports.dimapChunks_ = dimapChunks_;
/**
 * Transforms both input chunks and result of this sink using the provided functions.
 */
function dimapChunks(f, g) {
    return (self) => dimapChunks_(self, f, g);
}
exports.dimapChunks = dimapChunks;
/**
 * Effectfully transforms both input chunks and result of this sink using the provided functions.
 * `f` and `g` must preserve chunking-invariance
 */
function dimapChunksM_(self, f, g) {
    return mapM_(contramapChunksM_(self, f), g);
}
exports.dimapChunksM_ = dimapChunksM_;
/**
 * Effectfully transforms both input chunks and result of this sink using the provided functions.
 * `f` and `g` must preserve chunking-invariance
 */
function dimapChunksM(f, g) {
    return (self) => dimapChunksM_(self, f, g);
}
exports.dimapChunksM = dimapChunksM;
/**
 * Runs this sink until it yields a result, then uses that result to create another
 * sink from the provided function which will continue to run until it yields a result.
 *
 * This function essentially runs sinks in sequence.
 */
function chain_(self, f) {
    return foldM_(self, (e) => fail(e)(), f);
}
exports.chain_ = chain_;
/**
 * Runs this sink until it yields a result, then uses that result to create another
 * sink from the provided function which will continue to run until it yields a result.
 *
 * This function essentially runs sinks in sequence.
 */
function chain(f) {
    return (self) => chain_(self, f);
}
exports.chain = chain;
/**
 * Recovers from errors by accepting one effect to execute for the case of an
 * error, and one effect to execute for the case of success.
 *
 * This method has better performance than `either` since no intermediate
 * value is allocated and does not require subsequent calls to `flatMap` to
 * define the next effect.
 *
 * The error parameter of the returned `IO` may be chosen arbitrarily, since
 * it will depend on the `IO`s returned by the given continuations.
 */
function foldM_(self, failure, success) {
    return new Sink((M.map_(M.bind_(M.bind_(M.bind_(M.bind_(M.do, "switched", () => T.toManaged(R.makeRef(false))), "thisPush", () => self.push), "thatPush", () => T.toManaged(R.makeRef((_) => T.unit))), "openThatPush", () => M.switchable()), ({ openThatPush, switched, thatPush, thisPush }) => {
        return (in_) => T.chain_(switched.get, (sw) => {
            if (!sw) {
                return T.catchAll_(thisPush(in_), (v) => {
                    const leftover = v[1];
                    const nextSink = E.fold_(v[0], failure, success);
                    return (T.chain_(T.tap_(openThatPush(nextSink.push), thatPush.set), (p) => T.zipRight_(switched.set(true), O.fold_(in_, () => (T.zipRight_(T.when_(p(O.some(leftover)), () => !A.isEmpty(leftover)), p(O.none))), () => (T.when_(p(O.some(leftover)), () => !A.isEmpty(leftover)))))));
                });
            }
            else {
                return T.chain_(thatPush.get, (p) => p(in_));
            }
        });
    })));
}
exports.foldM_ = foldM_;
/**
 * Recovers from errors by accepting one effect to execute for the case of an
 * error, and one effect to execute for the case of success.
 *
 * This method has better performance than `either` since no intermediate
 * value is allocated and does not require subsequent calls to `flatMap` to
 * define the next effect.
 *
 * The error parameter of the returned `IO` may be chosen arbitrarily, since
 * it will depend on the `IO`s returned by the given continuations.
 */
function foldM(failure, success) {
    return (self) => foldM_(self, failure, success);
}
exports.foldM = foldM;
/**
 * Transforms this sink's result.
 */
function map_(self, f) {
    return new Sink(M.map_(self.push, (sink) => (inputs) => T.mapError_(sink(inputs), (e) => Tp.tuple(E.map_(e.get(0), f), e.get(1)))));
}
exports.map_ = map_;
/**
 * Transforms this sink's result.
 */
function map(f) {
    return (self) => map_(self, f);
}
exports.map = map;
/**
 * Transforms the errors emitted by this sink using `f`.
 */
function mapError_(self, f) {
    return new Sink(M.map_(self.push, (p) => {
        return (in_) => T.mapError_(p(in_), (e) => Tp.tuple(E.mapLeft_(e.get(0), f), e.get(1)));
    }));
}
exports.mapError_ = mapError_;
/**
 * Transforms the errors emitted by this sink using `f`.
 */
function mapError(f) {
    return (self) => mapError_(self, f);
}
exports.mapError = mapError;
/**
 * Effectfully transforms this sink's result.
 */
function mapM_(self, f) {
    return new Sink(M.map_(self.push, (push) => {
        return (inputs) => T.catchAll_(push(inputs), ({ tuple: [e, left] }) => E.fold_(e, (e) => Push.fail(e, left), (z) => T.foldM_(f(z), (e) => Push.fail(e, left), (z2) => Push.emit(z2, left))));
    }));
}
exports.mapM_ = mapM_;
/**
 * Effectfully transforms this sink's result.
 */
function mapM(f) {
    return (self) => mapM_(self, f);
}
exports.mapM = mapM;
/**
 * Runs both sinks in parallel on the input, , returning the result or the error from the
 * one that finishes first.
 */
function race_(self, that) {
    return map_(raceBoth_(self, that), E.merge);
}
exports.race_ = race_;
/**
 * Runs both sinks in parallel on the input, , returning the result or the error from the
 * one that finishes first.
 */
function race(that) {
    return (self) => race_(self, that);
}
exports.race = race;
/**
 * Runs both sinks in parallel on the input, returning the result or the error from the
 * one that finishes first.
 */
function raceBoth_(self, that) {
    return new Sink((M.map_(M.bind_(M.bind_(M.do, "p1", () => self.push), "p2", () => that.push), ({ p1, p2 }) => (i) => T.raceWith_(p1(i), p2(i), (res1, fib2) => Ex.foldM_(res1, (f) => T.zipRight_(F.interrupt(fib2), T.halt((C.map(({ tuple: [r, leftover] }) => Tp.tuple(E.map_(r, E.left), leftover))(f)))), () => T.mapError_(F.join(fib2), ({ tuple: [r, leftover] }) => Tp.tuple(E.map_(r, E.right), leftover))), (res2, fib1) => Ex.foldM_(res2, (f) => T.zipRight_(F.interrupt(fib1), T.halt((C.map(({ tuple: [r, leftover] }) => Tp.tuple(E.map_(r, E.right), leftover))(f)))), () => T.mapError_(F.join(fib1), ({ tuple: [r, leftover] }) => Tp.tuple(E.map_(r, E.left), leftover)))))));
}
exports.raceBoth_ = raceBoth_;
/**
 * Runs both sinks in parallel on the input, returning the result or the error from the
 * one that finishes first.
 */
function raceBoth(that) {
    return (self) => raceBoth_(self, that);
}
exports.raceBoth = raceBoth;
/**
 * Returns the sink that executes this one and times its execution.
 */
function timed(self) {
    return new Sink((M.zipWith_(self.push, T.toManaged(Clock_1.currentTime), (push, start) => {
        return (in_) => T.catchAll_(push(in_), ({ tuple: [e, leftover] }) => E.fold_(e, (e) => Push.fail(e, leftover), (z) => T.chain_(Clock_1.currentTime, (stop) => Push.emit(Tp.tuple(z, stop - start), leftover))));
    })));
}
exports.timed = timed;
/**
 * Converts this sink to a transducer that feeds incoming elements to the sink
 * and emits the sink's results as outputs. The sink will be restarted when
 * it ends.
 */
function toTransducer(self) {
    return (0, Transducer_1.transducer)(M.map_(Push.restartable(self.push), ({ tuple: [push, restart] }) => {
        const go = (input) => T.foldM_(push(input), ({ tuple: [e, leftover] }) => E.fold_(e, (e) => T.fail(e), (z) => T.zipRight_(restart, A.isEmpty(leftover) || O.isNone(input)
            ? T.succeed(A.single(z))
            : T.map_(go(O.some(leftover)), (more) => A.prepend_(more, z)))), (_) => T.succeed(A.empty()));
        return (input) => go(input);
    }));
}
exports.toTransducer = toTransducer;
/**
 * Feeds inputs to this sink until it yields a result, then switches over to the
 * provided sink until it yields a result, combining the two results in a tuple.
 */
function zip_(self, that) {
    return zipWith_(self, that, Tp.tuple);
}
exports.zip_ = zip_;
/**
 * Feeds inputs to this sink until it yields a result, then switches over to the
 * provided sink until it yields a result, combining the two results in a tuple.
 */
function zip(that) {
    return (self) => zip_(self, that);
}
exports.zip = zip;
/**
 * Like `zip`, but keeps only the result from the `that` sink.
 */
function zipLeft_(self, that) {
    return zipWith_(self, that, (z) => z);
}
exports.zipLeft_ = zipLeft_;
/**
 * Like `zip`, but keeps only the result from the `that` sink.
 */
function zipLeft(that) {
    return (self) => zipLeft_(self, that);
}
exports.zipLeft = zipLeft;
/**
 * Runs both sinks in parallel on the input and combines the results in a tuple.
 */
function zipPar_(self, that) {
    return zipWithPar_(self, that, Tp.tuple);
}
exports.zipPar_ = zipPar_;
/**
 * Runs both sinks in parallel on the input and combines the results in a tuple.
 */
function zipPar(that) {
    return (self) => zipPar_(self, that);
}
exports.zipPar = zipPar;
/**
 * Like `zipPar`, but keeps only the result from this sink.
 */
function zipParLeft_(self, that) {
    return zipWithPar_(self, that, (b, _) => b);
}
exports.zipParLeft_ = zipParLeft_;
/**
 * Like `zipPar`, but keeps only the result from this sink.
 */
function zipParLeft(that) {
    return (self) => zipParLeft_(self, that);
}
exports.zipParLeft = zipParLeft;
/**
 * Like `zipPar`, but keeps only the result from the `that` sink.
 */
function zipParRight_(self, that) {
    return zipWithPar_(self, that, (_, c) => c);
}
exports.zipParRight_ = zipParRight_;
/**
 * Like `zipPar`, but keeps only the result from the `that` sink.
 */
function zipParRight(that) {
    return (self) => zipParRight_(self, that);
}
exports.zipParRight = zipParRight;
/**
 * Like `zip`, but keeps only the result from this sink.
 */
function zipRight_(self, that) {
    return zipWith_(self, that, (_, z1) => z1);
}
exports.zipRight_ = zipRight_;
/**
 * Like `zip`, but keeps only the result from this sink.
 */
function zipRight(that) {
    return (self) => zipRight_(self, that);
}
exports.zipRight = zipRight;
/**
 * Feeds inputs to this sink until it yields a result, then switches over to the
 * provided sink until it yields a result, finally combining the two results with `f`.
 */
function zipWith_(self, that, f) {
    return chain_(self, (z) => map_(that, (_) => f(z, _)));
}
exports.zipWith_ = zipWith_;
/**
 * Feeds inputs to this sink until it yields a result, then switches over to the
 * provided sink until it yields a result, finally combining the two results with `f`.
 */
function zipWith(that, f) {
    return (self) => zipWith_(self, that, f);
}
exports.zipWith = zipWith;
class BothRunning {
    constructor() {
        this._tag = "BothRunning";
    }
}
const bothRunning = new BothRunning();
class LeftDone {
    constructor(value) {
        this.value = value;
        this._tag = "LeftDone";
    }
}
class RightDone {
    constructor(value) {
        this.value = value;
        this._tag = "RightDone";
    }
}
/**
 * Runs both sinks in parallel on the input and combines the results
 * using the provided function.
 */
function zipWithPar_(self, that, f) {
    return new Sink((M.map_(M.bind_(M.bind_(M.bind_(M.do, "ref", () => T.toManaged(R.makeRef(bothRunning))), "p1", () => self.push), "p2", () => that.push), ({ p1, p2, ref }) => {
        return (in_) => T.chain_(ref.get, (state) => {
            const newState = ((0, Utils_1.matchTag)({
                BothRunning: () => {
                    const l = T.foldM_(p1(in_), ({ tuple: [e, l] }) => E.fold_(e, (e) => Push.fail(e, l), (z) => T.succeed(O.some(Tp.tuple(z, l)))), (_) => T.succeed(O.none));
                    const r = T.foldM_(p2(in_), ({ tuple: [e, l] }) => E.fold_(e, (e) => Push.fail(e, l), (z) => T.succeed(O.some(Tp.tuple(z, l)))), (_) => T.succeed(O.none));
                    return T.chain_(T.zipPar_(l, r), ({ tuple: [lr, rr] }) => {
                        if (O.isSome(lr)) {
                            const [z, l] = lr.value.tuple;
                            if (O.isSome(rr)) {
                                const [z1, l1] = rr.value.tuple;
                                return T.fail(Tp.tuple(E.right(f(z, z1)), A.size(l) > A.size(l1) ? l1 : l));
                            }
                            else {
                                return T.succeed(new LeftDone(z));
                            }
                        }
                        else {
                            if (O.isSome(rr)) {
                                const [z1] = rr.value.tuple;
                                return T.succeed(new RightDone(z1));
                            }
                            else {
                                return T.succeed(bothRunning);
                            }
                        }
                    });
                },
                LeftDone: ({ value: z }) => T.as_(T.catchAll_(p2(in_), ({ tuple: [e, leftover] }) => E.fold_(e, (e) => Push.fail(e, leftover), (z1) => Push.emit(f(z, z1), leftover))), state),
                RightDone: ({ value: z1 }) => T.as_(T.catchAll_(p1(in_), ({ tuple: [e, leftover] }) => E.fold_(e, (e) => Push.fail(e, leftover), (z) => Push.emit(f(z, z1), leftover))), state)
            })(state));
            return T.chain_(newState, (ns) => (ns === state ? T.unit : ref.set(ns)));
        });
    })));
}
exports.zipWithPar_ = zipWithPar_;
/**
 * Runs both sinks in parallel on the input and combines the results
 * using the provided function.
 */
function zipWithPar(that, f) {
    return (self) => zipWithPar_(self, that, f);
}
exports.zipWithPar = zipWithPar;
/**
 * Exposes leftover
 */
function exposeLeftover(self) {
    return new Sink(M.map_(self.push, (p) => {
        return (in_) => T.mapError_(p(in_), ({ tuple: [v, leftover] }) => Tp.tuple(E.map_(v, (z) => Tp.tuple(z, leftover)), A.empty()));
    }));
}
exports.exposeLeftover = exposeLeftover;
/**
 * Drops any leftover
 */
function dropLeftover(self) {
    return new Sink(M.map_(self.push, (p) => (in_) => T.mapError_(p(in_), ({ tuple: [v, _] }) => Tp.tuple(v, A.empty()))));
}
exports.dropLeftover = dropLeftover;
function untilOutputMGo(in_, end, push, restart, f) {
    return T.catchAll_(push(in_), ({ tuple: [e, leftover] }) => E.fold_(e, (e) => Push.fail(e, leftover), (z) => T.chain_(T.mapError_(f(z), (err) => Tp.tuple(E.left(err), leftover)), (satisfied) => {
        if (satisfied) {
            return Push.emit(O.some(z), leftover);
        }
        else if (A.isEmpty(leftover)) {
            return end
                ? Push.emit(O.none, A.empty())
                : T.zipRight_(restart, Push.more);
        }
        else {
            return untilOutputMGo(O.some(leftover), end, push, restart, f);
        }
    })));
}
/**
 * Creates a sink that produces values until one verifies
 * the predicate `f`.
 */
function untilOutputM_(self, f) {
    return new Sink(M.map_(Push.restartable(self.push), ({ tuple: [push, restart] }) => (is) => untilOutputMGo(is, O.isNone(is), push, restart, f)));
}
exports.untilOutputM_ = untilOutputM_;
/**
 * Creates a sink that produces values until one verifies
 * the predicate `f`.
 */
function untilOutputM(f) {
    return (self) => untilOutputM_(self, f);
}
exports.untilOutputM = untilOutputM;
/**
 * Provides the sink with its required environment, which eliminates
 * its dependency on `R`.
 */
function provideAll_(self, r) {
    return new Sink(M.map_(M.provideAll_(self.push, r), (push) => (i) => T.provideAll_(push(i), r)));
}
exports.provideAll_ = provideAll_;
/**
 * Provides the sink with its required environment, which eliminates
 * its dependency on `R`.
 */
function provideAll(r) {
    return (self) => provideAll_(self, r);
}
exports.provideAll = provideAll;
/**
 * Provides some of the environment required to run this effect,
 * leaving the remainder `R0`.
 */
function provideSome_(self, f) {
    return new Sink(M.map_(M.provideSome_(self.push, f), (push) => (i) => T.provideSome_(push(i), f)));
}
exports.provideSome_ = provideSome_;
/**
 * Provides some of the environment required to run this effect,
 * leaving the remainder `R0`.
 */
function provideSome(f) {
    return (self) => provideSome_(self, f);
}
exports.provideSome = provideSome;
/**
 * Provides a layer to the `Managed`, which translates it to another level.
 */
function provideLayer(layer) {
    return (self) => provideLayer_(self, layer);
}
exports.provideLayer = provideLayer;
/**
 * Provides a layer to the `Managed`, which translates it to another level.
 */
function provideLayer_(self, layer) {
    return new Sink(M.chain_(L.build(layer), (r) => M.map_(M.provideAll_(self.push, r), (push) => (i) => T.provideAll_(push(i), r))));
}
exports.provideLayer_ = provideLayer_;
/**
 * Splits the environment into two parts, providing one part using the
 * specified layer and leaving the remainder `R0`.
 */
function provideSomeLayer(layer) {
    return (self) => provideLayer(layer["+++"](L.identity()))(self);
}
exports.provideSomeLayer = provideSomeLayer;
/**
 * Creates a Sink from a managed `Push`
 */
function managedPush(push) {
    return new Sink(push);
}
exports.managedPush = managedPush;
/**
 * Accesses the environment of the sink in the context of a sink.
 */
function accessM(f) {
    return new Sink(M.chain_(M.environment(), (env) => f(env).push));
}
exports.accessM = accessM;
/**
 * A sink that collects all of its inputs into an array.
 */
function collectAll() {
    return reduceLeftChunks(A.empty())((s, i) => A.concat_(s, i));
}
exports.collectAll = collectAll;
/**
 * A sink that collects all of its inputs into an list.
 */
function collectAllToList() {
    return reduceLeftChunks(List.empty())((s, i) => List.concat_(s, List.from(i)));
}
exports.collectAllToList = collectAllToList;
/**
 * A sink that collects all of its inputs into a map. The keys are extracted from inputs
 * using the keying function `key`; if multiple inputs use the same key, they are merged
 * using the `f` function.
 */
function collectAllToMap(key) {
    return (f) => new Sink(M.suspend(() => reduceLeftChunks(new Map())((acc, as) => A.reduce_(as, acc, (acc, a) => {
        const k = key(a);
        const v = acc.get(k);
        return acc.set(k, v ? f(v, a) : a);
    })).push));
}
exports.collectAllToMap = collectAllToMap;
/**
 * A sink that collects all of its inputs into a set.
 */
function collectAllToSet() {
    return map_(collectAll(), (as) => new Set(as));
}
exports.collectAllToSet = collectAllToSet;
/**
 * A sink that counts the number of elements fed to it.
 */
exports.count = reduceLeft(0)((s, _) => s + 1);
/**
 * Creates a sink halting with the specified `Throwable`.
 */
function die(e) {
    return halt(C.die(e));
}
exports.die = die;
/**
 * Creates a sink halting with the specified message, wrapped in a
 * `RuntimeException`.
 */
function dieMessage(m) {
    return halt(C.die(new C.RuntimeError(m)));
}
exports.dieMessage = dieMessage;
/**
 * A sink that ignores its inputs.
 */
exports.drain = dropLeftover(forEach((_) => T.unit));
/**
 * A sink that always fails with the specified error.
 */
function fail(e) {
    return () => fromPush((c) => {
        const leftover = O.fold_(c, () => A.empty(), (x) => x);
        return Push.fail(e, leftover);
    });
}
exports.fail = fail;
const reduceChunkGo = (s, chunk, idx, len, contFn, f) => {
    if (idx === len) {
        return [s, O.none];
    }
    else {
        const s1 = f(s, A.unsafeGet_(chunk, idx));
        if (contFn(s1)) {
            return reduceChunkGo(s1, chunk, idx + 1, len, contFn, f);
        }
        else {
            return [s1, O.some(A.drop_(chunk, idx + 1))];
        }
    }
};
/**
 * A sink that folds its inputs with the provided function, termination predicate and initial state.
 */
function reduce(z, contFn, f) {
    if (contFn(z)) {
        return new Sink((M.map_(M.bind_(M.do, "state", () => T.toManaged(R.makeRef(z))), ({ state }) => (is) => O.fold_(is, () => T.chain_(state.get, (s) => Push.emit(s, A.empty())), (is) => T.chain_(state.get, (s) => {
            const [st, l] = reduceChunkGo(s, is, 0, A.size(is), contFn, f);
            return O.fold_(l, () => T.zipRight_(state.set(st), Push.more), (leftover) => Push.emit(st, leftover));
        })))));
    }
    else {
        return succeed(z);
    }
}
exports.reduce = reduce;
/**
 * A sink that folds its input chunks with the provided function, termination predicate and initial state.
 * `contFn` condition is checked only for the initial value and at the end of processing of each chunk.
 * `f` and `contFn` must preserve chunking-invariance.
 */
function reduceChunks(z) {
    return (contFn) => (f) => reduceChunksM(z)(contFn)((z, i) => T.succeed(f(z, i)));
}
exports.reduceChunks = reduceChunks;
/**
 * A sink that effectfully folds its input chunks with the provided function, termination predicate and initial state.
 * `contFn` condition is checked only for the initial value and at the end of processing of each chunk.
 * `f` and `contFn` must preserve chunking-invariance.
 */
function reduceChunksM(z) {
    return (contFn) => (f) => {
        if (contFn(z)) {
            return new Sink((M.map_(M.bind_(M.do, "state", () => T.toManaged(R.makeRef(z))), ({ state }) => {
                return (is) => O.fold_(is, () => T.chain_(state.get, (s) => Push.emit(s, A.empty())), (is) => (T.chain_(T.mapError_(T.chain_(state.get, (_) => f(_, is)), (e) => Tp.tuple(E.left(e), A.empty())), (s) => {
                    if (contFn(s)) {
                        return T.zipRight_(state.set(s), Push.more);
                    }
                    else {
                        return Push.emit(s, A.empty());
                    }
                })));
            })));
        }
        else {
            return succeed(z);
        }
    };
}
exports.reduceChunksM = reduceChunksM;
function reduceMGo(s, chunk, idx, len, contFn, f) {
    if (idx === len) {
        return T.succeed([s, O.none]);
    }
    else {
        return T.foldM_(f(s, A.unsafeGet_(chunk, idx)), (e) => T.fail([e, A.drop_(chunk, idx + 1)]), (s1) => contFn(s1)
            ? reduceMGo(s1, chunk, idx + 1, len, contFn, f)
            : T.succeed([s1, O.some(A.drop_(chunk, idx + 1))]));
    }
}
/**
 * A sink that effectfully folds its inputs with the provided function, termination predicate and initial state.
 *
 * This sink may terminate in the middle of a chunk and discard the rest of it. See the discussion on the
 * ZSink class scaladoc on sinks vs. transducers.
 */
function reduceM(z, contFn, f) {
    if (contFn(z)) {
        return new Sink((M.map_(M.bind_(M.do, "state", () => T.toManaged(R.makeRef(z))), ({ state }) => (is) => O.fold_(is, () => T.chain_(state.get, (s) => Push.emit(s, A.empty())), (is) => T.chain_(state.get, (s) => T.foldM_(reduceMGo(s, is, 0, A.size(is), contFn, f), (err) => Push.fail(...err), ([st, l]) => O.fold_(l, () => T.zipRight_(state.set(st), Push.more), (leftover) => Push.emit(st, leftover))))))));
    }
    else {
        return succeed(z);
    }
}
exports.reduceM = reduceM;
/**
 * A sink that folds its inputs with the provided function and initial state.
 */
function reduceLeft(z) {
    return (f) => dropLeftover(reduce(z, (_) => true, f));
}
exports.reduceLeft = reduceLeft;
/**
 * A sink that folds its input chunks with the provided function and initial state.
 * `f` must preserve chunking-invariance.
 */
function reduceLeftChunks(z) {
    return (f) => dropLeftover(reduceChunks(z)(() => true)(f));
}
exports.reduceLeftChunks = reduceLeftChunks;
/**
 * A sink that effectfully folds its input chunks with the provided function and initial state.
 * `f` must preserve chunking-invariance.
 */
function reduceLeftChunksM(z) {
    return (f) => dropLeftover(reduceChunksM(z)((_) => true)(f));
}
exports.reduceLeftChunksM = reduceLeftChunksM;
/**
 * A sink that effectfully folds its inputs with the provided function and initial state.
 */
function reduceLeftM(z) {
    return (f) => dropLeftover(reduceM(z, (_) => true, f));
}
exports.reduceLeftM = reduceLeftM;
function forEachGo(chunk, idx, len, f) {
    if (idx === len) {
        return Push.more;
    }
    else {
        return (T.foldM_(f(A.unsafeGet_(chunk, idx)), (e) => Push.fail(e, A.drop_(chunk, idx + 1)), () => forEachGo(chunk, idx + 1, len, f)));
    }
}
/**
 * A sink that executes the provided effectful function for every element fed to it.
 */
function forEach(f) {
    return fromPush(O.fold(() => Push.emit(undefined, A.empty()), (is) => forEachGo(is, 0, A.size(is), f)));
}
exports.forEach = forEach;
/**
 * A sink that executes the provided effectful function for every chunk fed to it.
 */
function forEachChunk(f) {
    return fromPush((in_) => O.fold_(in_, () => Push.emit(undefined, A.empty()), (is) => T.zipRight_(T.mapError_(f(is), (e) => Tp.tuple(E.left(e), A.empty())), Push.more)));
}
exports.forEachChunk = forEachChunk;
/**
 * A sink that executes the provided effectful function for every element fed to it
 * until `f` evaluates to `false`.
 */
function forEachWhile(f) {
    const go = (chunk, idx, len) => {
        if (idx === len) {
            return Push.more;
        }
        else {
            return T.foldM_(f(A.unsafeGet_(chunk, idx)), (e) => Push.fail(e, A.drop_(chunk, idx + 1)), (b) => {
                if (b) {
                    return go(chunk, idx + 1, len);
                }
                else {
                    return Push.emit(undefined, A.drop_(chunk, idx));
                }
            });
        }
    };
    return fromPush((in_) => O.fold_(in_, () => Push.emit(undefined, A.empty()), (is) => go(is, 0, A.size(is))));
}
exports.forEachWhile = forEachWhile;
/**
 * Creates a single-value sink produced from an effect
 */
function fromEffect(b) {
    return () => fromPush((in_) => {
        const leftover = O.fold_(in_, () => A.empty(), Function_1.identity);
        return T.foldM_(b, (e) => Push.fail(e, leftover), (z) => Push.emit(z, leftover));
    });
}
exports.fromEffect = fromEffect;
/**
 * Creates a sink from a Push
 */
function fromPush(push) {
    return new Sink(M.succeed(push));
}
exports.fromPush = fromPush;
/**
 * Creates a sink halting with a specified cause.
 */
function halt(e) {
    return fromPush((_) => Push.halt(e));
}
exports.halt = halt;
/**
 * Creates a sink containing the first value.
 */
function head() {
    return new Sink(M.succeed((in_) => O.fold_(in_, () => Push.emit(O.none, A.empty()), (ch) => (A.isEmpty(ch) ? Push.more : Push.emit(A.head(ch), A.empty())))));
}
exports.head = head;
/**
 * Creates a sink containing the last value.
 */
function last() {
    return new Sink((M.map_(M.bind_(M.do, "state", () => T.toManaged(R.makeRef(O.none))), ({ state }) => (is) => T.chain_(state.get, (last) => O.fold_(is, () => Push.emit(last, A.empty()), (ch) => O.fold_(A.last(ch), () => Push.more, (l) => T.zipRight_(state.set(O.some(l)), Push.more)))))));
}
exports.last = last;
/**
 * A sink that depends on another managed value
 * `resource` will be finalized after the processing.
 */
function managed_(resource, fn) {
    return M.chain_(M.fold_(resource, (err) => fail(err)(), (m) => fn(m)), (_) => _.push);
}
exports.managed_ = managed_;
/**
 * A sink that depends on another managed value
 * `resource` will be finalized after the processing.
 */
function managed(resource) {
    return (fn) => managed_(resource, fn);
}
exports.managed = managed;
/**
 * A sink that immediately ends with the specified value.
 */
function succeed(z) {
    return fromPush((c) => {
        const leftover = O.fold_(c, () => A.empty(), (x) => x);
        return Push.emit(z, leftover);
    });
}
exports.succeed = succeed;
/**
 * A sink that sums incoming numeric values.
 */
exports.sum = reduceLeft(0)((a, b) => a + b);
/**
 * A sink that takes the specified number of values.
 */
function take(n) {
    return new Sink((M.map_(M.bind_(M.do, "state", () => T.toManaged(R.makeRef(A.empty()))), ({ state }) => {
        return (is) => T.chain_(state.get, (take) => O.fold_(is, () => {
            if (n >= 0) {
                return Push.emit(take, A.empty());
            }
            else {
                return Push.emit(A.empty(), take);
            }
        }, (ch) => {
            const remaining = n - A.size(take);
            if (remaining <= A.size(ch)) {
                const { tuple: [chunk, leftover] } = A.splitAt_(ch, remaining);
                return T.zipRight_(state.set(A.empty()), Push.emit(A.concat_(take, chunk), leftover));
            }
            else {
                return T.zipRight_(state.set(A.concat_(take, ch)), Push.more);
            }
        }));
    })));
}
exports.take = take;
/**
 * A sink with timed execution.
 */
exports.timedDrain = map_(timed(exports.drain), (_) => _.get(1));
/**
 * A sink that executes the provided effectful function for every chunk fed to it.
 */
function foreachChunk(f) {
    return fromPush((o) => O.fold_(o, () => Push.emit(undefined, A.empty()), (is) => (T.zipRight_(T.mapError_(f(is), (e) => Tp.tuple(E.left(e), A.empty())), Push.more))));
}
exports.foreachChunk = foreachChunk;
/**
 * Create a sink which enqueues each element into the specified queue.
 */
function fromQueue(queue) {
    return forEachChunk((x) => Q.offerAll_(queue, x));
}
exports.fromQueue = fromQueue;
/**
 * Create a sink which enqueues each element into the specified queue.
 * The queue will be shutdown once the stream is closed.
 */
function fromQueueWithShutdown(queue) {
    return new Sink((M.chain_(M.map_(M.make_(T.succeed(queue), Q.shutdown), fromQueue), (_) => _.push)));
}
exports.fromQueueWithShutdown = fromQueueWithShutdown;
/**
 * Create a sink which publishes each element to the specified hub.
 */
function fromHub(hub) {
    return fromQueue(H.toQueue(hub));
}
exports.fromHub = fromHub;
/**
 * Create a sink which publishes each element to the specified hub.
 * The hub will be shutdown once the stream is closed.
 */
function fromHubWithShutdown(hub) {
    return fromQueueWithShutdown(H.toQueue(hub));
}
exports.fromHubWithShutdown = fromHubWithShutdown;
//# sourceMappingURL=index.js.map