the-moby-effect
Version:
Moby/Docker API client built using effect-ts
48 lines • 3.44 kB
JavaScript
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