/** * 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. * * @stability unstable * @since 4.0.0 */ import type { NonEmptyReadonlyArray } from "../Array.ts"; import type * as Cause from "../Cause.ts"; import * as Channel from "../Channel.ts"; import * as Context from "../Context.ts"; import type * as Duration from "../Duration.ts"; import * as Effect from "../Effect.ts"; import * as Layer from "../Layer.ts"; import type * as Redacted from "../Redacted.ts"; import * as Schema from "../Schema.ts"; import * as Scope from "../Scope.ts"; import * as Stream from "../Stream.ts"; /** * Runtime type identifier attached to `Socket` services. * * @stability unstable * @category type IDs * @since 4.0.0 */ export declare const TypeId = "~effect/socket/Socket"; /** * Returns `true` when a value is a `Socket`. * * @stability unstable * @category guards * @since 4.0.0 */ export declare const isSocket: (u: unknown) => u is Socket; /** * 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. * * @stability unstable * @category services * @since 4.0.0 */ export declare const Socket: Context.Service; /** * 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. * * **Example** (Consuming with automatic reconnect) * * ```ts skip-type-checking * Effect.gen(function*() { * const { pull } = yield* socket.reader * while (true) { * yield* handle(yield* pull) * } * }).pipe( * Effect.scoped, * Effect.retry({ schedule: Schedule.exponential(200) }) * ) * ``` * * @stability unstable * @category models * @since 4.0.0 */ export interface Socket { readonly [TypeId]: typeof TypeId; readonly reader: Effect.Effect; readonly writer: Effect.Effect; } /** * 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. * * @stability unstable * @category models * @since 4.0.0 */ export interface Reader { readonly pull: Effect.Effect, SocketError>; readonly upgrade: (options?: TlsUpgradeOptions) => Effect.Effect; } /** * 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. * * @stability unstable * @category models * @since 4.0.0 */ export interface Writer { readonly write: (chunk: Uint8Array | string | CloseEvent) => Effect.Effect; readonly writeAll: (chunks: NonEmptyReadonlyArray) => Effect.Effect; } /** * 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. * * @stability unstable * @category models * @since 4.0.0 */ export interface TlsUpgradeOptions { /** * DNS hostname used for Server Name Indication (SNI) and certificate identity * verification during client upgrades. Server upgrades ignore this option. * When omitted, the adapter retains its existing hostname behavior. */ readonly servername?: string | undefined; readonly key?: Redacted.Redacted | ReadonlyArray> | undefined; readonly cert?: string | Uint8Array | ReadonlyArray | undefined; readonly ca?: string | Uint8Array | ReadonlyArray | undefined; readonly passphrase?: Redacted.Redacted | undefined; readonly alpnProtocols?: ReadonlyArray | undefined; readonly requestCert?: boolean | undefined; readonly rejectUnauthorized?: boolean | undefined; } /** * 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. * * @stability unstable * @category constructors * @since 4.0.0 */ export declare const make: (options: { readonly reader: Socket["reader"]; readonly writer: Socket["writer"]; }) => Socket; /** * 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. * * @stability unstable * @category combinators * @since 4.0.0 */ export declare const readerBytes: (self: Socket) => Effect.Effect, SocketError>, SocketError, Scope.Scope>; /** * Acquires the socket's string `pull`, decoding binary frames with the optional * text encoding. * * **Details** * * Each acquisition has its own streaming decoder. Characters may span binary * frames and pulls; each frame produces one string, possibly empty. A string * frame flushes incomplete characters as U+FFFD before its text. Close code 1000 * flushes remaining text once before propagating the close error; other failures * do not flush. * * **Gotchas** * * Binary WebSocket messages share decoder state: incomplete characters carry * into the next binary frame, and BOM handling resets only after a flush. Use * `readerBytes` and decode each frame separately for per-message decoding. * * @stability unstable * @category combinators * @since 4.0.0 */ export declare const readerString: (self: Socket, encoding?: string | undefined) => Effect.Effect, SocketError>, SocketError, Scope.Scope>; declare const CloseEventTypeId = "~effect/socket/Socket/CloseEvent"; /** * Represents a socket close event value carrying a close code and optional * reason. * * @stability unstable * @category models * @since 4.0.0 */ export declare class CloseEvent { /** * Marks this value as a socket close event for runtime guards. * * @stability unstable * @since 4.0.0 */ readonly [CloseEventTypeId]: typeof CloseEventTypeId; readonly code: number; readonly reason?: string | undefined; constructor(code?: number, reason?: string); /** * Formats the close code and optional reason for display. * * @stability unstable * @since 4.0.0 */ toString(): string; } /** * Returns `true` when a value is a `CloseEvent`. * * @stability unstable * @category guards * @since 4.0.0 */ export declare const isCloseEvent: (u: unknown) => u is CloseEvent; /** * Type-level identifier used to mark `SocketError` values. * * @stability unstable * @category type IDs * @since 4.0.0 */ export type SocketErrorTypeId = "~effect/socket/Socket/SocketError"; /** * Runtime type identifier attached to `SocketError` values. * * @stability unstable * @category type IDs * @since 4.0.0 */ export declare const SocketErrorTypeId: SocketErrorTypeId; /** * Returns `true` when a value is a `SocketError`. * * @stability unstable * @category guards * @since 4.0.0 */ export declare const isSocketError: (u: unknown) => u is SocketError; declare const SocketReadError_base: Schema.Class; readonly cause: Schema.Defect; }>, Cause.YieldableError>; /** * Typed error for failures that occur while reading from a socket. * * @stability unstable * @category errors * @since 4.0.0 */ export declare class SocketReadError extends SocketReadError_base { /** * Default message used for socket read failures. * * @stability unstable * @since 4.0.0 */ readonly message = "An error occurred during Read"; } declare const SocketWriteError_base: Schema.Class; readonly cause: Schema.Defect; }>, Cause.YieldableError>; /** * Typed error for failures that occur while writing to a socket. * * @stability unstable * @category errors * @since 4.0.0 */ export declare class SocketWriteError extends SocketWriteError_base { /** * Default message used for socket write failures. * * @stability unstable * @since 4.0.0 */ readonly message = "An error occurred during Write"; } declare const SocketOpenError_base: Schema.Class; readonly kind: Schema.Literals; readonly cause: Schema.Defect; }>, Cause.YieldableError>; /** * Typed error for failures that occur while opening a socket, including * unknown open failures and open timeouts. * * @stability unstable * @category errors * @since 4.0.0 */ export declare class SocketOpenError extends SocketOpenError_base { /** * Formats timeout and unknown open failures for display. * * @stability unstable * @since 4.0.0 */ get message(): "An error occurred during Open" | "timeout waiting for \"open\""; } declare const SocketUpgradeError_base: Schema.Class; readonly cause: Schema.optional; }>, Cause.YieldableError>; /** * Typed error for an unsupported or failed in-place TLS upgrade. * * @stability unstable * @category errors * @since 4.0.0 */ export declare class SocketUpgradeError extends SocketUpgradeError_base { /** * An upgrade implementation for transports that cannot wrap the connection * with TLS. * * @stability unstable * @since 4.0.0 */ static readonly unsupported: Reader["upgrade"]; get message(): "An error occurred during TLS upgrade" | "Socket does not support TLS upgrade"; } declare const SocketCloseError_base: Schema.Class; readonly code: Schema.Int; readonly closeReason: Schema.optional; }>, Cause.YieldableError>; /** * 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. * * @stability unstable * @category errors * @since 4.0.0 */ export declare class SocketCloseError extends SocketCloseError_base { get message(): string; } /** * Schema for all socket-specific error reasons. * * @stability unstable * @category errors * @since 4.0.0 */ export declare const SocketErrorReason: Schema.Union; /** * Union of socket-specific read, write, open, upgrade, and close error reasons. * * @stability unstable * @category errors * @since 4.0.0 */ export type SocketErrorReason = SocketReadError | SocketWriteError | SocketOpenError | SocketUpgradeError | SocketCloseError; declare const SocketError_base: Schema.Class; readonly reason: Schema.Union; }>, Cause.YieldableError>; /** * Tagged error that wraps socket read, write, open, upgrade, and close failures * while preserving the underlying reason. * * @stability unstable * @category errors * @since 4.0.0 */ export declare class SocketError extends SocketError_base { constructor(props: { readonly reason: SocketReadError | SocketWriteError | SocketOpenError | SocketUpgradeError | SocketCloseError; }); /** * Marks this value as a socket error wrapper for runtime guards. * * @stability unstable * @since 4.0.0 */ readonly [SocketErrorTypeId]: SocketErrorTypeId; /** * Returns `true` when the value is a `SocketError`. * * @stability unstable * @since 4.0.0 */ static is(u: unknown): u is SocketError; readonly message: string; } /** * 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. * * @stability unstable * @category combinators * @since 4.0.0 */ export declare const toChannel: (self: Socket) => Channel.Channel, SocketError | IE, void, NonEmptyReadonlyArray, IE>; /** * Converts a `Socket` into a bidirectional string `Channel`, decoding binary * frames with the optional text encoding. * * **Details** * * Uses `readerString` for decoding and close handling. * * @stability unstable * @category combinators * @since 4.0.0 */ export declare const toChannelString: { /** * Converts a `Socket` into a bidirectional string `Channel`, decoding binary * frames with the optional text encoding. * * **Details** * * Uses `readerString` for decoding and close handling. * * @stability unstable * @category combinators * @since 4.0.0 */ (encoding?: string | undefined): (self: Socket) => Channel.Channel, SocketError | IE, void, NonEmptyReadonlyArray, IE>; /** * Converts a `Socket` into a bidirectional string `Channel`, decoding binary * frames with the optional text encoding. * * **Details** * * Uses `readerString` for decoding and close handling. * * @stability unstable * @category combinators * @since 4.0.0 */ (self: Socket, encoding?: string | undefined): Channel.Channel, SocketError | IE, void, NonEmptyReadonlyArray, IE>; }; /** * Creates a `Socket` to binary `Channel` adapter with a fixed upstream error * type. * * @stability unstable * @category combinators * @since 4.0.0 */ export declare const toChannelWith: () => (self: Socket) => Channel.Channel, SocketError | IE, void, NonEmptyReadonlyArray, IE>; /** * Converts a `Socket` into a read-only binary `Stream` backed by the socket's * pull, so consumption is backpressured end-to-end. * * @stability unstable * @category combinators * @since 4.0.0 */ export declare const toStream: (self: Socket) => Stream.Stream; /** * Creates a binary socket `Channel` from the `Socket` service in the * environment. * * @stability unstable * @category constructors * @since 4.0.0 */ export declare const makeChannel: () => Channel.Channel, SocketError | IE, void, NonEmptyReadonlyArray, IE, unknown, Socket>; /** * Event payload exposed by a WebSocket implementation. * * **Details** * * The socket adapter only reads `data`, `code`, and `reason`; implementations * may expose additional fields. * * @stability unstable * @category models * @since 4.0.0 */ export interface WebSocketEvent { readonly type?: string; readonly data?: unknown; readonly code?: number; readonly reason?: string; } /** * 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. * * @stability unstable * @category models * @since 4.0.0 */ export interface WebSocketLike { readonly readyState: number; addEventListener(type: "open" | "message" | "error" | "close", listener: (event: WebSocketEvent) => void, options?: { readonly once?: boolean; }): void; removeEventListener(type: "open" | "message" | "error" | "close", listener: (event: WebSocketEvent) => void): void; close(code?: number, reason?: string): void; send(data: string | Uint8Array): void; } /** * Client options for Node and Bun WebSocket implementations, allowing * handshake headers and subprotocols to be supplied together. * * @stability unstable * @category models * @since 4.0.0 */ export interface WebSocketClientOptions { /** * Subprotocols to offer in the opening handshake. */ readonly protocols?: string | Array | undefined; /** * Headers to include in the opening handshake. * * **Details** * * This is supported by Node and Bun WebSocket clients. Browser constructors * cannot set arbitrary handshake headers and must reject this option. */ readonly headers?: Readonly> | undefined; } /** * Options accepted by a `WebSocketConstructor`. * * **Details** * * Browser-compatible constructors accept a protocol string or list. Node and * Bun constructors additionally accept `WebSocketClientOptions`. * * @stability unstable * @category models * @since 4.0.0 */ export type WebSocketConstructorOptions = string | Array | WebSocketClientOptions; declare const WebSocket_base: Context.ServiceClass; /** * Context service for the active `WebSocket` instance. * * @stability unstable * @category services * @since 4.0.0 */ export declare class WebSocket extends WebSocket_base { } declare const WebSocketConstructor_base: Context.ServiceClass WebSocketLike>; /** * Context service for constructing `WebSocket` instances from a URL and * optional protocols or platform-specific options. * * @stability unstable * @category services * @since 4.0.0 */ export declare class WebSocketConstructor extends WebSocketConstructor_base { } /** * Layer that provides `WebSocketConstructor` using `globalThis.WebSocket`. * Rejects client options objects, including those with handshake headers. * * @stability unstable * @category layers * @since 4.0.0 */ export declare const layerWebSocketConstructorGlobal: Layer.Layer; /** * 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. * * @stability unstable * @category constructors * @since 4.0.0 */ export declare const makeWebSocket: (url: string | Effect.Effect, options?: { readonly openTimeout?: Duration.Input | undefined; readonly protocols?: string | Array | undefined; readonly headers?: Readonly> | undefined; readonly highWaterMark?: number | undefined; }) => Effect.Effect; /** * 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. * * @stability unstable * @category constructors * @since 4.0.0 */ export declare const fromWebSocket: (acquire: Effect.Effect, options?: { readonly openTimeout?: Duration.Input | undefined; readonly highWaterMark?: number | undefined; } | undefined) => Effect.Effect>; /** * Creates a binary `Channel` backed by a WebSocket URL, requiring a * `WebSocketConstructor` service. * Supports subprotocols and, with a Node or Bun constructor, handshake headers. * * @stability unstable * @category constructors * @since 4.0.0 */ export declare const makeWebSocketChannel: (url: string, options?: { readonly openTimeout?: Duration.Input | undefined; readonly protocols?: string | Array | undefined; readonly headers?: Readonly> | undefined; readonly highWaterMark?: number | undefined; }) => Channel.Channel, SocketError | IE, void, NonEmptyReadonlyArray, IE, unknown, WebSocketConstructor>; /** * 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. * * @stability unstable * @category layers * @since 4.0.0 */ export declare const layerWebSocket: (url: string | Effect.Effect, options?: { readonly openTimeout?: Duration.Input | undefined; readonly protocols?: string | Array | undefined; readonly headers?: Readonly> | undefined; readonly highWaterMark?: number | undefined; } | undefined) => Layer.Layer; /** * Readable and writable stream pair used to adapt transform-style streams into * a `Socket`. * * @stability unstable * @category models * @since 4.0.0 */ export interface InputTransformStream { readonly readable: ReadableStream | ReadableStream | ReadableStream; readonly writable: WritableStream; } /** * 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. * * @stability unstable * @category constructors * @since 4.0.0 */ export declare const fromTransformStream: (acquire: Effect.Effect) => Effect.Effect>; export {}; //# sourceMappingURL=Socket.d.ts.map