Skip to content
Effect 4.0 is here. One ecosystem. Zero dependencies.

Socket unstable

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 Since v4.0.0 Source

Combinators

readerBytes

unstable
Since v4.0.0

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>
Since v4.0.0

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

unstable
Since v4.0.0

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>
Since v4.0.0

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>;
}
Since v4.0.0

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

unstable
Since v4.0.0

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

Since v4.0.0

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>>
Since v4.0.0

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

unstable
Since v4.0.0

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

unstable
Since v4.0.0

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>
Since v4.0.0

Creates a Socket backed by a WebSocketConstructor, dialing the WebSocket for each reader acquisition. Optional handshake headers require a Node or Bun constructor; without headers, protocols are passed directly for browser compatibility.

Signature

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

Creates a binary Channel backed by a WebSocket URL, requiring a WebSocketConstructor service. Supports subprotocols and, with a Node or Bun constructor, handshake headers.

Signature

declare function makeWebSocketChannel<IE = never>(url: string, options?: {
readonly headers?: Readonly<Record<string, string>>;
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

Since v4.0.0

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

unstable
Since v4.0.0

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;
}
Since v4.0.0

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 unstable
Since v4.0.0

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

Signature

type SocketErrorReason = SocketReadError | SocketWriteError | SocketOpenError | SocketUpgradeError | SocketCloseError
Since v4.0.0

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";
}
Since v4.0.0

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";
}
Since v4.0.0

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";
}
Since v4.0.0

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

Since v4.0.0

Returns true when a value is a CloseEvent.

Signature

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

isSocket

unstable
Since v4.0.0

Returns true when a value is a Socket.

Signature

declare function isSocket(u: unknown): u is Socket
Since v4.0.0

Returns true when a value is a SocketError.

Signature

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

Layers

Since v4.0.0

Layer that provides a Socket service backed by a WebSocket URL or URL effect. Supports subprotocols and, with a Node or Bun constructor, handshake headers.

Signature

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

Layer that provides WebSocketConstructor using globalThis.WebSocket. Rejects client options objects, including those with handshake headers.

Signature

declare const layerWebSocketConstructorGlobal: Layer.Layer<WebSocketConstructor>

Models

CloseEvent

unstable
Since v4.0.0

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 unstable
Since v4.0.0

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 unstable
Since v4.0.0

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 unstable
Since v4.0.0

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 unstable
Since v4.0.0

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;
readonly servername?: string;
}

WebSocketClientOptions

interface unstable
Since v4.0.0

Client options for Node and Bun WebSocket implementations, allowing handshake headers and subprotocols to be supplied together.

Signature

interface WebSocketClientOptions {
readonly headers?: Readonly<Record<string, string>>;
readonly protocols?: string | Array<string>;
}
Since v4.0.0

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 unstable
Since v4.0.0

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 unstable
Since v4.0.0

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 unstable
Since v4.0.0

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

unstable
Since v4.0.0

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

unstable
Since v4.0.0

Context service for the active WebSocket instance.

Signature

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

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

Since v4.0.0

Runtime type identifier attached to SocketError values.

Signature

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

SocketErrorTypeId

type unstable
Since v4.0.0

Type-level identifier used to mark SocketError values.

Signature

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

TypeId

unstable
Since v4.0.0

Runtime type identifier attached to Socket services.

Signature

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