the-moby-effect
Version:
Moby/Docker API client built using effect-ts
75 lines • 4.65 kB
JavaScript
import * as Array from "effect/Array";
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 Match from "effect/Match";
import * as Predicate from "effect/Predicate";
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 * as Socket from "effect/unstable/socket/Socket";
import { makeMultiplexedChannel, MultiplexedHeaderType } from "./multiplexed.js";
import { demuxStdioRawToSeparateSinks } from "./raw.js";
/**
* The pack demux driver runs exactly once, forked into the caller's scope,
* and the returned multiplexed channel is pure queue plumbing. This keeps the
* demux deterministic - the raw stdio inputs start being consumed as soon as
* pack is called, and there is no racing over which consumer starts the
* driver.
*
* @internal
*/
export const pack = /*#__PURE__*/Function.dual(arguments_ => "stdin" in arguments_[0] || "stdout" in arguments_[0] || "stderr" in arguments_[0], /*#__PURE__*/Effect.fnUntraced(function* (stdio, options) {
// Internal buffers. The consumer queues carry the driver's failure so
// that a demux error surfaces on the returned multiplexed channel.
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 raw stdio inputs into the consumer queues
// and pumps the stdin producer queue into them. On completion the
// consumer queues are ended; on failure the failure is forwarded to
// them so it surfaces on the returned multiplexed channel.
const driver = yield* demuxStdioRawToSeparateSinks(stdio, {
stdin: Stream.fromQueue(stdinProducerQueue),
stdout: Sink.forEachArray(chunk => Queue.offerAll(stdoutConsumerQueue, chunk)),
stderr: Sink.forEachArray(chunk => Queue.offerAll(stderrConsumerQueue, chunk))
}, {
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);
// Convert the streams to the multiplexed streams
const textEncoder = new TextEncoder();
const encode = Function.pipe(Match.type(), Match.when(Predicate.isUint8Array, data => data), Match.when(Predicate.isString, data => textEncoder.encode(data)), Match.when(Socket.isCloseEvent, () => new Uint8Array()), Match.exhaustive);
const mapOutEntry = type => data => {
const encoded = encode(data);
const size = encoded.length;
const result = new Uint8Array(8 + encoded.length);
// Set the header
result[0] = type;
result[1] = 0;
result[2] = 0;
result[3] = 0;
// Set the size bytes in big-endian order
result[4] = size >>> 24 & 0xff;
result[5] = size >>> 16 & 0xff;
result[6] = size >>> 8 & 0xff;
result[7] = size & 0xff;
result.set(encoded, 8);
return result;
};
// Forwards this 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, Array.map(chunk, encode))), Effect.forever, Pull.catchDone(() => Queue.end(stdinProducerQueue)), Effect.catchCause(cause => Queue.failCause(stdinProducerQueue, cause)), Effect.asVoid);
// The stdin side completes (or fails) with the driver.
const independentStdinChannel = Channel.fromEffectDrain(Fiber.join(driver)).pipe(Channel.embedInput(feedStdin));
const independentStdoutChannel = Stream.fromQueue(stdoutConsumerQueue).pipe(Stream.encodeText, Stream.map(mapOutEntry(MultiplexedHeaderType.Stdout)), Stream.toChannel);
const independentStderrChannel = Stream.fromQueue(stderrConsumerQueue).pipe(Stream.encodeText, Stream.map(mapOutEntry(MultiplexedHeaderType.Stderr)), Stream.toChannel);
const haltStrategy = {
haltStrategy: "both"
};
const mixedOutputChannel = Channel.merge(independentStdoutChannel, independentStderrChannel, haltStrategy);
return makeMultiplexedChannel(Channel.merge(independentStdinChannel, mixedOutputChannel, haltStrategy));
}));
//# sourceMappingURL=pack.js.map