Raw sockets
serveSocket serves the router over TCP, TLS or Unix sockets, with experimental_socketLink on the client.
Between services, or where there is no HTTP stack, serveSocket(handler) from @trpc12/trpc/socket serves the router on Effect's SocketServer. The platform layer picks the socket: NodeSocketServer.layer({ port }) for TCP, NodeSocketServer.layerTls for TLS, or { path } for a Unix socket.
import { import NodeRuntimeNodeRuntime, import NodeSocketServerNodeSocketServer } from '@effect/platform-node';
import { import LayerLayer } from 'effect';
import { function serveSocket(handler: SocketHandlerLike, opts?: SocketOptions): Layer.Layer<never, SocketServerError, SocketServer>serveSocket } from '@trpc12/trpc/socket';
function serveSocket(handler: SocketHandlerLike, opts?: SocketOptions): Layer.Layer<never, SocketServerError, SocketServer>serveSocket(const handler: FetchHandler<undefined>handler).Pipeable.pipe<Layer.Layer<never, SocketServerError, SocketServer>, Layer.Layer<never, SocketServerError, never>, Effect<never, SocketServerError, never>, void>(this: Layer.Layer<...>, ab: (_: Layer.Layer<never, SocketServerError, SocketServer>) => Layer.Layer<never, SocketServerError, never>, bc: (_: Layer.Layer<never, SocketServerError, never>) => Effect<never, SocketServerError, never>, cd: (_: Effect<...>) => void): void (+21 overloads)pipe(
import LayerLayer.const provide: <never, SocketServerError, SocketServer>(that: Layer.Layer<SocketServer, SocketServerError, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ROut2, SocketServerError | E2, Exclude<RIn2, SocketServer>> (+3 overloads)provide(import NodeSocketServerNodeSocketServer.const layer: (options: ServerOpts & ListenOptions) => Layer.Layer<SocketServer, SocketServerError>layer({ ListenOptions.port?: number | undefinedport: 4000 })),
import LayerLayer.const launch: <RIn, E, ROut>(self: Layer.Layer<ROut, E, RIn>) => Effect<never, E, RIn>launch,
import NodeRuntimeNodeRuntime.const runMain: {
(options?: {
readonly disableErrorReporting?: boolean | undefined;
readonly teardown?: Teardown | undefined;
}): <E, A>(effect: Effect<A, E>) => void;
<E, A>(effect: Effect<A, E>, options?: {
readonly disableErrorReporting?: boolean | undefined;
readonly teardown?: Teardown | undefined;
}): void;
}
runMain,
);The client is experimental_socketLink with an Effect Socket:
import { import NodeSocketNodeSocket } from '@effect/platform-node';
import { function experimental_socketLink(opts: RawSocketLinkOptions): SocketLinkexperimental_socketLink } from '@trpc12/trpc/client';
const const link: SocketLink<{}>link = function experimental_socketLink(opts: RawSocketLinkOptions): SocketLinkexperimental_socketLink({
RawSocketLinkOptions.socket: Effect<Socket, unknown, never>socket: import NodeSocketNodeSocket.const makeNet: (options: NetConnectOpts & {
readonly openTimeout?: Input | undefined;
}) => Effect<Socket>
makeNet({ TcpSocketConnectOpts.host?: string | undefinedhost: 'localhost', TcpSocketConnectOpts.port: numberport: 4000 }),
SocketLinkOptions.connectionParams?: Record<string, unknown> | (() => MaybePromise<Record<string, unknown> | undefined>) | undefinedconnectionParams: { token: stringtoken },
});A connection speaks the WebSocket protocol, one JSON message per line, so everything that works over wsLink works here: connectionParams, subscriptions with resume, deferred values, aborts, keep-alive pings and reconnects. createContext gets info.transport === 'socket'. There are no headers, so authenticate with connectionParams. A connection that sends nothing for 60 s is closed (serveSocket(handler, { idleTimeoutMs }), or false).
WebTransport isn't supported: no server runtime that Effect has a platform for serves it yet.