Skip to content
Effect Days 2026 Get your ticket

Socket

Models bidirectional socket connections in Effect.

A Socket exposes a pull-based reader and a scoped writer. Acquiring the reader dials the connection and returns an Effect that yields batches of incoming frames with end-to-end backpressure: nothing is read from the transport until the consumer pulls. Every termination, including clean closes, surfaces as a SocketError, so reconnecting is a plain Effect.retry around the scoped consume loop.

41 exports Added in v4.0.0 Source

Combinators

readerBytes

Added in v4.0.0 Source

Acquires the socket's binary pull, encoding any string frames as UTF-8 bytes.

Details

When a pulled batch contains no string frames it is returned as-is, so transports that only emit bytes (TCP) pay no per-chunk cost.

Signature

declare function readerBytes(self: Socket): Effect<Effect<readonly [Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>], SocketError, never>, SocketError, Scope>

readerString

Added in v4.0.0 Source

Acquires the socket's string pull, decoding binary frames with the optional text encoding.

Details

The TextDecoder is created once per acquisition.

Signature

declare function readerString(self: Socket, encoding?: string): Effect<Effect<readonly [string, string], SocketError, never>, SocketError, Scope>

toChannel

Added in v4.0.0 Source

Converts a Socket into a bidirectional binary Channel, encoding incoming string frames as UTF-8 bytes and writing outgoing frame batches to the socket.

Details

The read side is the socket's pull, so the channel is backpressured end-to-end.

Signature

declare function toChannel<IE = never>(self: Socket): Channel<readonly [Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>], SocketError | IE, void, readonly [string | Uint8Array<ArrayBufferLike> | CloseEvent, string | Uint8Array<ArrayBufferLike> | CloseEvent], IE>

Converts a Socket into a bidirectional string Channel, decoding binary frames with the optional text encoding.

Signature

declare const toChannelString: {
(encoding?: string): <IE>(self: Socket) => Channel<readonly [string, string], SocketError | IE, void, readonly [string | Uint8Array<ArrayBufferLike> | CloseEvent, string | Uint8Array<ArrayBufferLike> | CloseEvent], IE>;
<IE>(self: Socket, encoding?: string): Channel<readonly [string, string], SocketError | IE, void, readonly [string | Uint8Array<ArrayBufferLike> | CloseEvent, string | Uint8Array<ArrayBufferLike> | CloseEvent], IE>;
}

Creates a Socket to binary Channel adapter with a fixed upstream error type.

Signature

declare function toChannelWith<IE = never>(): (self: Socket) => Channel<readonly [Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>], SocketError | IE, void, readonly [string | Uint8Array<ArrayBufferLike> | CloseEvent, string | Uint8Array<ArrayBufferLike> | CloseEvent], IE>

toStream

Added in v4.0.0 Source

Converts a Socket into a read-only binary Stream backed by the socket's pull, so consumption is backpressured end-to-end.

Signature

declare function toStream(self: Socket): Stream<Uint8Array<ArrayBufferLike>, SocketError>

Constructors

Builds a Socket from a scoped InputTransformStream, pulling incoming chunks from the readable side and writing outgoing chunks to the writable stream, encoding strings as UTF-8.

Signature

declare function fromTransformStream<R>(acquire: Effect<InputTransformStream, SocketError, R>): Effect<Socket, never, Exclude<R, Scope>>

Builds a Socket from a scoped WebSocket acquisition effect.

Details

Reader acquisition runs acquire, attaches event listeners, then waits for the socket to open. Implementations exposing pause/resume (the ws package) remain flowing until buffered frames reach the configured highWaterMark (64 KiB by default). Draining the buffer resumes the transport. Incoming frames that arrive in the same tick are coalesced into one batch. Implementations without pause (browsers) buffer incoming frames, optionally failing the socket with a SocketReadError when highWaterMark bytes are exceeded (default unbounded). Message boundaries survive: each pulled batch contains one element per frame.

Signature

declare function fromWebSocket<RO, WS extends WebSocketLike>(acquire: Effect<WS, SocketError, RO>, options?: {
readonly highWaterMark?: number;
readonly openTimeout?: Input;
}): Effect<Socket, never, Exclude<RO, Scope>>

make

Added in v4.0.0 Source

Constructs a Socket from a reader acquisition and a scoped writer.

Details

The reader must fail a suspended pull when its acquisition scope closes; see Socket for why. A reader that leaves a pull blocked forever will hang any consumer that shuts the socket down by closing that scope.

Signature

declare function make(options: {
readonly reader: Effect<Reader<string | Uint8Array<ArrayBufferLike>>, SocketError, Scope>;
readonly writer: Effect<Writer, never, Scope>;
}): Socket

makeChannel

Added in v4.0.0 Source

Creates a binary socket Channel from the Socket service in the environment.

Signature

declare function makeChannel<IE = never>(): Channel<readonly [Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>], SocketError | IE, void, readonly [string | Uint8Array<ArrayBufferLike> | CloseEvent, string | Uint8Array<ArrayBufferLike> | CloseEvent], IE, unknown, Socket>

Creates a Socket backed by a WebSocketConstructor, dialing the WebSocket for each reader acquisition.

Signature

declare function makeWebSocket(url: string | Effect<string, never, never>, options?: {
readonly highWaterMark?: number;
readonly openTimeout?: Input;
readonly protocols?: string | Array<string>;
}): Effect<Socket, never, WebSocketConstructor>

Creates a binary Channel backed by a WebSocket URL, requiring a WebSocketConstructor service.

Signature

declare function makeWebSocketChannel<IE = never>(url: string, options?: {
readonly highWaterMark?: number;
readonly openTimeout?: Input;
readonly protocols?: string | Array<string>;
}): Channel<readonly [Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>], SocketError | IE, void, readonly [string | Uint8Array<ArrayBufferLike> | CloseEvent, string | Uint8Array<ArrayBufferLike> | CloseEvent], IE, unknown, WebSocketConstructor>

Errors

Typed error for a socket close, carrying the close code and optional close reason.

Details

Sockets never classify closes: any close, whatever the code, fails the reader with a SocketError wrapping this reason. Consumers that treat a close as normal catch it.

Signature

declare class SocketCloseError extends {
readonly _tag: "SocketCloseError";
readonly closeReason?: string;
readonly code: number;
} & YieldableError<this> {
constructor(...args: [props: {
readonly _tag?: "SocketCloseError";
readonly closeReason?: string;
readonly code: number;
}, options?: MakeOptions]);
message: string;
}

SocketError

Added in v4.0.0 Source

Tagged error that wraps socket read, write, open, upgrade, and close failures while preserving the underlying reason.

Signature

declare class SocketError extends {
readonly _tag: "SocketError";
readonly reason: SocketReadError | SocketWriteError | SocketOpenError | SocketUpgradeError | SocketCloseError;
} & YieldableError<this> {
constructor(props: {
readonly reason: SocketReadError | SocketWriteError | SocketOpenError | SocketUpgradeError | SocketCloseError;
});
readonly "~effect/socket/Socket/SocketError": "~effect/socket/Socket/SocketError";
readonly message: string;
static is(u: unknown): u is SocketError;
}

Schema for all socket-specific error reasons.

Signature

declare const SocketErrorReason: Union<readonly [typeof SocketReadError, typeof SocketWriteError, typeof SocketOpenError, typeof SocketUpgradeError, typeof SocketCloseError]>

SocketErrorReason type

Added in v4.0.0 Source

Union of socket-specific read, write, open, upgrade, and close error reasons.

Signature

type SocketErrorReason = SocketReadError | SocketWriteError | SocketOpenError | SocketUpgradeError | SocketCloseError

Typed error for failures that occur while opening a socket, including unknown open failures and open timeouts.

Signature

declare class SocketOpenError extends {
readonly _tag: "SocketOpenError";
readonly cause: unknown;
readonly kind: "Unknown" | "Timeout";
} & YieldableError<this> {
constructor(...args: [props: {
readonly _tag?: "SocketOpenError";
readonly cause: unknown;
readonly kind: "Unknown" | "Timeout";
}, options?: MakeOptions]);
message: "timeout waiting for \"open\"" | "An error occurred during Open";
}

Typed error for failures that occur while reading from a socket.

Signature

declare class SocketReadError extends {
readonly _tag: "SocketReadError";
readonly cause: unknown;
} & YieldableError<this> {
constructor(...args: [props: {
readonly _tag?: "SocketReadError";
readonly cause: unknown;
}, options?: MakeOptions]);
readonly message: "An error occurred during Read";
}

Typed error for an unsupported or failed in-place TLS upgrade.

Signature

declare class SocketUpgradeError extends {
readonly _tag: "SocketUpgradeError";
readonly cause?: unknown;
} & YieldableError<this> {
constructor(...args: [props?: {
readonly _tag?: "SocketUpgradeError";
readonly cause?: unknown;
}, options?: MakeOptions]);
static readonly unsupported: (options?: TlsUpgradeOptions) => Effect<void, SocketError>;
message: "Socket does not support TLS upgrade" | "An error occurred during TLS upgrade";
}

Typed error for failures that occur while writing to a socket.

Signature

declare class SocketWriteError extends {
readonly _tag: "SocketWriteError";
readonly cause: unknown;
} & YieldableError<this> {
constructor(...args: [props: {
readonly _tag?: "SocketWriteError";
readonly cause: unknown;
}, options?: MakeOptions]);
readonly message: "An error occurred during Write";
}

Guards

isCloseEvent

Added in v4.0.0 Source

Returns true when a value is a CloseEvent.

Signature

declare function isCloseEvent(u: unknown): u is CloseEvent

isSocket

Added in v4.0.0 Source

Returns true when a value is a Socket.

Signature

declare function isSocket(u: unknown): u is Socket

Returns true when a value is a SocketError.

Signature

declare function isSocketError(u: unknown): u is SocketError

Layers

Layer that provides a Socket service backed by a WebSocket URL or URL effect.

Signature

declare const layerWebSocket: (url: string | Effect.Effect<string>, options?: {
readonly highWaterMark?: number;
readonly openTimeout?: Duration.Input;
readonly protocols?: string | Array<string>;
}) => Layer.Layer<Socket, never, WebSocketConstructor>

Layer that provides WebSocketConstructor using globalThis.WebSocket.

Signature

declare const layerWebSocketConstructorGlobal: Layer.Layer<WebSocketConstructor>

Models

CloseEvent

Added in v4.0.0 Source

Represents a socket close event value carrying a close code and optional reason.

Signature

declare class CloseEvent {
constructor(code: number, reason?: string);
readonly "~effect/socket/Socket/CloseEvent": "~effect/socket/Socket/CloseEvent";
readonly code: number;
readonly reason?: string;
toString(): string;
}

InputTransformStream interface

Added in v4.0.0 Source

Readable and writable stream pair used to adapt transform-style streams into a Socket.

Signature

interface InputTransformStream {
readonly readable: ReadableStream<Uint8Array<ArrayBufferLike>> | ReadableStream<string> | ReadableStream<string | Uint8Array<ArrayBufferLike>>;
readonly writable: WritableStream<Uint8Array<ArrayBufferLike>>;
}

Reader interface

Added in v4.0.0 Source

The read side of a live Socket connection.

Details

pull reads the next non-empty batch. upgrade wraps this connection with TLS when the transport supports it. Unsupported readers fail with a SocketUpgradeError. The upgrade also fails with SocketUpgradeError when the selected TLS role requires an identity but key and cert are not both provided. Calling upgrade() without an options object uses the adapter's client defaults.

Signature

interface Reader<A extends Uint8Array | string = Uint8Array | string> {
readonly pull: Effect<readonly [A, A], SocketError>;
readonly upgrade: (options?: TlsUpgradeOptions) => Effect<void, SocketError>;
}

Socket interface

Added in v4.0.0 Source

Effect-based socket abstraction exposing a pull-based read side and a scoped writer.

Details

Acquiring reader establishes the connection; the scope of the acquisition owns the connection lifecycle. Its pull yields non-empty batches of incoming frames and never completes via Cause.Done: every termination, clean close included, fails with a SocketError wrapping the close reason. Code placed between the acquisition and the first pull runs exactly once per (re)connection, which makes handshakes plain code placement.

Closing the acquisition scope must fail a pull that is currently suspended, rather than leaving it blocked. Consumers such as toChannel rely on this to shut a connection down without racing every pull against a separate failure signal.

The writer is detached from any single connection: acquiring it cannot fail, and writes made while disconnected suspend until the next connection is established. Releasing the writer scope half-closes the write side where the transport supports it.

Signature

interface Socket {
readonly "~effect/socket/Socket": "~effect/socket/Socket";
readonly reader: Effect<Reader<string | Uint8Array<ArrayBufferLike>>, SocketError, Scope>;
readonly writer: Effect<Writer, never, Scope>;
}

Example

(Consuming with automatic reconnect)

Effect.gen(function*() {
const { pull } = yield* socket.reader
while (true) {
yield* handle(yield* pull)
}
}).pipe(
Effect.scoped,
Effect.retry({ schedule: Schedule.exponential(200) })
)

TlsUpgradeOptions interface

Added in v4.0.0 Source

TLS credentials and handshake settings used to upgrade a live socket.

Details

key and cert are optional for client upgrades that do not present a client certificate. Server upgrades require both, and providing only one is invalid. Missing or incomplete credentials fail the upgrade with a SocketUpgradeError when the adapter needs an identity.

Signature

interface TlsUpgradeOptions {
readonly alpnProtocols?: readonly Array<string>;
readonly ca?: string | Uint8Array<ArrayBufferLike> | readonly Array<string | Uint8Array<ArrayBufferLike>>;
readonly cert?: string | Uint8Array<ArrayBufferLike> | readonly Array<string | Uint8Array<ArrayBufferLike>>;
readonly key?: Redacted<string | Uint8Array<ArrayBufferLike>> | readonly Array<Redacted<string | Uint8Array<ArrayBufferLike>>>;
readonly passphrase?: Redacted<string>;
readonly rejectUnauthorized?: boolean;
readonly requestCert?: boolean;
}

WebSocketClientOptions interface

Added in v4.0.0 Source

Common options understood by a WebSocket client implementation.

Signature

interface WebSocketClientOptions {
readonly headers?: Readonly<Record<string, string>>;
}

Options accepted by a WebSocketConstructor.

Details

Browser-compatible constructors accept a protocol string or list. Node and Bun constructors additionally accept WebSocketClientOptions.

Signature

type WebSocketConstructorOptions = string | Array<string> | WebSocketClientOptions

WebSocketEvent interface

Added in v4.0.0 Source

Event payload exposed by a WebSocket implementation.

Details

The socket adapter only reads data, code, and reason; implementations may expose additional fields.

Signature

interface WebSocketEvent {
readonly code?: number;
readonly data?: unknown;
readonly reason?: string;
readonly type?: string;
}

WebSocketLike interface

Added in v4.0.0 Source

The subset of the WebSocket API required by Socket.

Details

This structural interface is intentionally independent of the DOM WebSocket type. Node implementations such as ws expose the same event-target methods, but are not assignable to globalThis.WebSocket because their event payload types are runtime-specific.

Signature

interface WebSocketLike {
readonly readyState: number;
addEventListener(type: "message" | "error" | "open" | "close", listener: (event: WebSocketEvent) => void, options?: {
readonly once?: boolean;
}): void;
close(code?: number, reason?: string): void;
removeEventListener(type: "message" | "error" | "open" | "close", listener: (event: WebSocketEvent) => void): void;
send(data: string | Uint8Array<ArrayBuffer>): void;
}

Writer interface

Added in v4.0.0 Source

The write side of a Socket.

Details

write sends a single frame or a CloseEvent; writeAll sends a batch of frames, allowing transports to coalesce them into a single flush. Both apply the transport's native backpressure before succeeding.

Signature

interface Writer {
readonly write: (chunk: string | Uint8Array<ArrayBufferLike> | CloseEvent) => Effect<void, SocketError>;
readonly writeAll: (chunks: readonly [string | Uint8Array<ArrayBufferLike>, string | Uint8Array<ArrayBufferLike>]) => Effect<void, SocketError>;
}

Services

Socket

Added in v4.0.0 Source

Service tag for bidirectional socket transports.

When to use

Use to access or provide the socket implementation used by programs that read and write frames through the Effect environment.

Signature

declare const Socket: Context.Service<Socket, Socket>

WebSocket

Added in v4.0.0 Source

Context service for the active WebSocket instance.

Signature

declare class WebSocket extends Shape<"~effect/socket/Socket/WebSocket", WebSocketLike, this> {
constructor(_: never);
}

Context service for constructing WebSocket instances from a URL and optional protocols or platform-specific options.

Signature

declare class WebSocketConstructor extends Shape<"@effect/platform/Socket/WebSocketConstructor", (url: string, options?: WebSocketConstructorOptions) => WebSocketLike, this> {
constructor(_: never);
}

Type IDs

Runtime type identifier attached to SocketError values.

Signature

declare const SocketErrorTypeId: "~effect/socket/Socket/SocketError"

SocketErrorTypeId type

Added in v4.0.0 Source

Type-level identifier used to mark SocketError values.

Signature

type SocketErrorTypeId = "~effect/socket/Socket/SocketError"

TypeId

Added in v4.0.0 Source

Runtime type identifier attached to Socket services.

Signature

declare const TypeId: "~effect/socket/Socket"