4.0 KiB
stream-error
(no submodules)
Purpose
Defines the structured error hierarchy for the @suaveplan/streams package. Every runtime error emitted by stream operations is an instance of StreamError (which extends SuaveplanError) and carries a stable, dot-namespaced error code from the STREAM.* registry. This design means callers can narrow errors programmatically — error.code === STREAM_CODES.ABORTED — without string-matching unstructured messages, and ensures that OTel integrations, log pipelines, and dead-letter queues can route stream errors reliably.
The codes are registered once via defineCodes("STREAM", [...]) and reused across every throw site. Codes are intentionally coarse-grained: they represent the semantic category of failure, not the specific call stack location, so they remain stable across refactors.
Features
- Six concrete error subclasses covering all stream failure modes
- Stable
STREAM.*error code registry viadefineCodes - Exported
StreamErrorCodeunion type for exhaustive switch statements - Each subclass propagates
causewhen it is anErrorinstance - All classes extend
StreamErrorwhich extendsSuaveplanError, compatible with the project error wire format
Usage: Basic Example
import { StreamAbortedError, STREAM_CODES } from "@suaveplan/streams/errors/stream-error";
function checkAborted(signal?: AbortSignal): void {
if (signal?.aborted) {
throw new StreamAbortedError({ cause: signal.reason });
}
}
try {
checkAborted(AbortSignal.abort());
} catch (err) {
if (err instanceof StreamAbortedError) {
console.log(err.code); // "STREAM.ABORTED"
}
}
Usage: Advanced Example
import {
StreamTransformFailedError,
StreamClosedError,
StreamReadFailedError,
STREAM_CODES,
} from "@suaveplan/streams/errors/stream-error";
function handleStreamError(err: unknown): never {
if (!(err instanceof Error)) {
throw new StreamReadFailedError({ message: "Unknown read failure" });
}
switch ((err as { code?: string }).code) {
case STREAM_CODES.CLOSED:
throw new StreamClosedError({ message: "Cannot read from closed stream" });
case STREAM_CODES.TRANSFORM_FAILED:
throw new StreamTransformFailedError({ cause: err });
default:
throw new StreamReadFailedError({ cause: err });
}
}
API Reference
STREAM_CODES
Record<string, string> — the stable code map. Keys: ABORTED, BACKPRESSURE_EXCEEDED, CHUNK_INVALID, PIPE_FAILED, TRANSFORM_FAILED, SOURCE_EXHAUSTED, READ_FAILED, WRITE_FAILED, CLOSED. Values are dot-namespaced strings like "STREAM.ABORTED".
StreamError
Base class. Extends SuaveplanError<StreamErrorCode>. All concrete subclasses inherit from this.
StreamAbortedError
Thrown when an AbortSignal fires mid-operation. Constructor: { cause?: unknown; message?: string }.
StreamTransformFailedError
Thrown when a user-supplied transform function throws. Constructor: { cause?: unknown; message?: string }.
StreamClosedError
Thrown when an operation is attempted on a closed stream. Constructor: { message?: string }.
StreamReadFailedError
Thrown on unrecoverable read failures. Constructor: { cause?: unknown; message?: string }.
StreamWriteFailedError
Thrown on unrecoverable write failures. Constructor: { cause?: unknown; message?: string }.
StreamPipeFailedError
Thrown when a pipe or pipeline composition fails. Constructor: { cause?: unknown; message?: string }.
Implementation Notes
defineCodes generates stable string codes by joining the namespace prefix and the key with a dot. The generated values are frozen at module-init time and never change, which means error codes survive across serialization round-trips (they appear verbatim in JSON error envelopes). Cause propagation is conditional: we only set cause when the original value instanceof Error to avoid accidentally serializing non-Error objects into the error chain.