import type { Nullable } from "./util.ts"; import type { Writable } from "node:stream"; /** * Thrown synchronously by {@link BackpressureWriter.write} when the pending queue is already at the configured {@link BackpressureWriterInit.highWaterMark} and * accepting the new chunk would push it over. * * Separate from the "writer has aborted" and "underlying stream is dead" failure modes so callers can distinguish backpressure-overflow (back off and retry later) * from terminal failures (give up or escalate) by type rather than by inspecting error message text. * * @category Utilities */ export declare class BackpressureOverflowError extends Error { readonly name: "BackpressureOverflowError"; constructor(message?: string); } /** * Rejected by an individual {@link BackpressureWriter.write} promise when the provider returned a {@link Writable} whose `writable` flag is `false` (the stream has * ended or been destroyed). The writer itself remains alive - a later stream replacement via the provider may revive the pipeline - so this is a per-write rejection, * not a terminal writer error. * * @category Utilities */ export declare class BackpressureClosedStreamError extends Error { readonly name: "BackpressureClosedStreamError"; constructor(message?: string); } /** * Construction-time options for {@link BackpressureWriter}. * * @property highWaterMark - Optional ceiling on the total pending-write depth, including the in-flight entry that is currently awaiting `drain`. When specified, a * `write()` call that would push the pending queue past this depth rejects synchronously with a {@link BackpressureOverflowError} rather * than buffering unboundedly. Omit for unbounded queueing (the caller trusts upstream producers not to outrun the stream by more than * available memory). * @property signal - Optional parent {@link AbortSignal} to compose with the writer's internal controller. When the parent aborts, the writer tears down: * pending writes reject with `signal.reason`, any in-flight drain wait unwinds, and every subsequent `write()` call rejects immediately. * * @category Utilities */ export interface BackpressureWriterInit { highWaterMark?: number; signal?: AbortSignal; } /** * AsyncDisposable backpressure-aware write queue for Node {@link Writable} streams. * * Each call to {@link BackpressureWriter.write} returns a Promise that resolves once the chunk has been written (and any triggered backpressure has drained) or rejects * if the writer aborts mid-write. Concurrent writes serialize through an internal FIFO queue; ordering matches call order. The stream itself is resolved lazily through * a caller-supplied provider on each drain turn, so the writer may outlive any particular stream instance - the provider is consulted per chunk, and a `null` return is * a signal to drop the chunk (the associated write promise still resolves, treating the drop as a success from the caller's perspective). * * @example * * ```ts * import { BackpressureWriter } from "homebridge-plugin-utils"; * * await using writer = new BackpressureWriter(() => ffmpegProcess.stdin ?? null, { signal: session.signal }); * * // Each write awaits its own flush; concurrent writes queue behind prior ones. * await writer.write(segmentOne); * await writer.write(segmentTwo); * ``` * * @category Utilities */ export declare class BackpressureWriter implements AsyncDisposable { #private; /** * The composed abort signal representing this writer's lifetime. Aborts exactly once when {@link BackpressureWriter.abort} is called, when the parent signal fires, * or when the underlying stream surfaces an error that invalidates the writer; `signal.reason` names the cause. */ readonly signal: AbortSignal; /** * Construct a new backpressure-aware writer. * * @param streamProvider - A function that returns the current writable stream, or `null` to drop incoming chunks. Evaluated lazily on each drain-loop iteration. * @param init - Optional init options. See {@link BackpressureWriterInit}. * * @example * * ```ts * await using writer = new BackpressureWriter(() => this.ffmpegProcess?.stdin ?? null, { highWaterMark: 64, signal: session.signal }); * ``` */ constructor(streamProvider: () => Nullable, init?: BackpressureWriterInit); /** * Enqueue `chunk` for writing. Concurrent calls serialize in FIFO order via the internal queue. * * @param chunk - The buffer to write. * * @returns A promise that resolves when the chunk has been flushed to the underlying stream (including any required drain wait), or immediately if the provider * returned `null` at dispatch time (drop semantics). The promise rejects in the following cases: * * - `this.signal.reason` - the writer aborted before or during the write. * - {@link BackpressureOverflowError} (thrown synchronously) - `highWaterMark` is configured and the queue depth already equals or exceeds it. * - {@link BackpressureClosedStreamError} - the provider returned a stream whose `writable` flag is `false`. The writer itself stays alive for a potential later * stream replacement. * * @throws {@link BackpressureOverflowError} when `highWaterMark` is exceeded. */ write(chunk: Buffer): Promise; /** * Abort the writer and tear it down. Defaults to `HbpuAbortError("shutdown")` when no reason is supplied; explicit reasons pass through unchanged. * * Safe to call more than once: subsequent calls are no-ops because the underlying signal only aborts once. Every queued write rejects with the signal's reason; any * in-flight drain wait rejects with the signal's reason as well, and that rejection propagates out of the in-flight `write()` promise. * * @param reason - Optional abort reason. Typically an {@link HbpuAbortError}; platform errors (`TimeoutError`, `AbortError`) also interoperate by convention. */ abort(reason?: unknown): void; /** * `AsyncDisposable` implementation. Aborts the writer (defaulting to `"shutdown"`) and awaits actual drain-loop completion before returning, so callers using * `await using` are guaranteed every pending write has settled by the time the surrounding scope exits. * * @returns A promise that resolves once the drain loop has fully exited. */ [Symbol.asyncDispose](): Promise; /** * `true` once `this.signal` has aborted. Derived from the signal; no independent state. */ get aborted(): boolean; /** * Total number of entries in the pending-write queue, including the in-flight entry (if the drain loop is parked on `events.once(stream, "drain", { signal })`). * Matches the depth that the configured `highWaterMark` is compared against, so adaptive producers watching this value see the same accounting the writer uses * internally. */ get pending(): number; } //# sourceMappingURL=backpressure.d.ts.map