UNPKG

the-moby-effect

Version:
48 lines 3.44 kB
import * as Channel from "effect/Channel"; import * as Effect from "effect/Effect"; import * as Fiber from "effect/Fiber"; import * as Function from "effect/Function"; import * as Pull from "effect/Pull"; import * as Queue from "effect/Queue"; import * as Sink from "effect/Sink"; import * as Stream from "effect/Stream"; import { demuxMultiplexedToSeparateSinks, isMultiplexedChannel, isMultiplexedSocket } from "./multiplexed.js"; import { makeRawChannel } from "./raw.js"; /** * The fan demux driver runs exactly once, forked into the caller's scope, and * the returned channels are pure queue plumbing. This keeps the demux * deterministic - there is no racing over which consumer starts the driver, * and consuming any subset of the returned channels (in any order) observes * all of the data. * * @internal */ export const fan = /*#__PURE__*/Function.dual(arguments_ => isMultiplexedChannel(arguments_[0]) || isMultiplexedSocket(arguments_[0]), /*#__PURE__*/Effect.fnUntraced(function* (multiplexedInput, options) { // Internal buffers. The consumer queues carry the driver's failure so // that a demux error surfaces on whichever channels are being read. const capacity = options.requestedCapacity; const stdoutConsumerQueue = yield* Queue.bounded(typeof capacity === "number" ? capacity : capacity.stdoutCapacity); const stderrConsumerQueue = yield* Queue.bounded(typeof capacity === "number" ? capacity : capacity.stderrCapacity); const stdinProducerQueue = yield* Queue.bounded(typeof capacity === "number" ? capacity : capacity.stdinCapacity); // The driver demuxes the multiplexed input into the consumer queues // and pumps the stdin producer queue into it. On completion the // consumer queues are ended; on failure the failure is forwarded to // them so it surfaces on whichever channels are being read. const driver = yield* demuxMultiplexedToSeparateSinks(multiplexedInput, Stream.fromQueue(stdinProducerQueue), Sink.forEachArray(chunk => Queue.offerAll(stdoutConsumerQueue, chunk)), Sink.forEachArray(chunk => Queue.offerAll(stderrConsumerQueue, chunk)), { bufferSize: 32, encoding: options.encoding }).pipe(Effect.tap(() => Effect.andThen(Queue.end(stdoutConsumerQueue), Queue.end(stderrConsumerQueue))), Effect.tapCause(cause => Effect.andThen(Queue.failCause(stdoutConsumerQueue, cause), Queue.failCause(stderrConsumerQueue, cause))), Effect.forkScoped); // Forwards the stdin channel's input into the stdin producer queue, // propagating input errors and the end-of-input signal. const feedStdin = upstream => upstream.pipe(Effect.flatMap(chunk => Queue.offerAll(stdinProducerQueue, chunk)), Effect.forever, Pull.catchDone(() => Queue.end(stdinProducerQueue)), Effect.catchCause(cause => Queue.failCause(stdinProducerQueue, cause)), Effect.asVoid); // The stdin channel completes (or fails) with the driver. const independentStdinChannel = Channel.fromEffectDrain(Fiber.join(driver)).pipe(Channel.embedInput(feedStdin), makeRawChannel); const independentStdoutChannel = Stream.fromQueue(stdoutConsumerQueue).pipe(Stream.encodeText, Stream.toChannel, makeRawChannel); const independentStderrChannel = Stream.fromQueue(stderrConsumerQueue).pipe(Stream.encodeText, Stream.toChannel, makeRawChannel); return { stdin: independentStdinChannel, stdout: independentStdoutChannel, stderr: independentStderrChannel }; })); //# sourceMappingURL=fan.js.map