useSocketChannel
Opens a channel by name from a Socket contract registry, bound to the component; this is the function used to generate the useSocket auto-import.
import { useSocketChannel } from "@meekojs/meeko/framework";useSocketChannel(contratos, nombre, opciones?) constructs the URL for channel nombre (/_meeko/socket/<nombre>, with ?room= if there is a roomId) and opens a component-bound client with that channel's contract, like useSocketClient. Generated clients use it to define the useSocket auto-import:
export const useSocket = (name, options) => useSocketChannel(socketContracts, name, options);In a framework application, use useSocket. useSocketChannel is for a custom host or package with its own contract registry (socketContracts) that wants the same behavior. Import it from @meekojs/meeko/framework.
Usage#
// src/socket.ts, in a custom host
import { useSocketChannel } from "@meekojs/meeko/framework";
import type { SocketRegistryOptions } from "@meekojs/meeko/framework";
import { socketContracts } from "./socket-contracts.js";
export const useChannel = <K extends keyof typeof socketContracts & string>(name: K, options?: SocketRegistryOptions) =>
useSocketChannel(socketContracts, name, options);<script lang="ts">
import { useChannel } from "../socket";
const prices = useChannel("prices", { roomId: "eur" });
prices.on("price", (price) => console.log(price.symbol, price.value));
</script>Type#
function useSocketChannel<
C extends Record<string, SocketContract>,
K extends keyof C & string,
>(contracts: C, name: K, options?: SocketRegistryOptions): SocketClient<C[K]>interface SocketClient<C extends SocketContract> {
readonly state: ReadonlySignal<SocketClientState>;
emit<K extends keyof C["clientToServer"] & string>(
event: K,
data: SchemaInput<C["clientToServer"][K]>,
): Promise<void>;
on<K extends keyof C["serverToClient"] & string>(
event: K,
listener: (data: SchemaOutput<C["serverToClient"][K]>) => void,
): () => void;
close(): void;
}interface SocketRegistryOptions extends SocketClientOptions {
readonly baseUrl?: string
readonly pathnamePrefix?: string
readonly roomId?: string
}Parameters#
| Parameter | Type | Default | Description |
|---|---|---|---|
contracts |
Record<string, SocketContract> |
required | Public contracts by channel name, like the socketContracts generated by Meeko in .meeko/generated/socket-contracts.ts. |
name |
key of contracts |
required | Channel to open. A name not in the registry is a type error and, at runtime, TypeError: Unknown socket channel: <nombre>. |
options |
SocketRegistryOptions |
{} |
roomId, baseUrl (the browser origin by default), pathnamePrefix ("/_meeko/socket"), retryMs, maxRetryMs, maxMessageBytes, webSocketFactory and onError. |
Return value#
The SocketClient for contract contracts[name]: state, emit, on and close, closed when the component unmounts. In SSR and HTML-only rendering, it returns an inert client that does not connect: it stays in "connecting", on registers nothing and emit rejects. Through 1.6, SSR threw Error: useSocketClient() is only available in the browser.
Examples#
A package's channel registry#
// packages/live/src/index.ts
import { useSocketChannel } from "@meekojs/meeko/framework";
import type { SocketRegistryOptions } from "@meekojs/meeko/framework";
import { liveContracts } from "./contracts.js";
/** Open a live service channel with its server URL. */
export function useLive<K extends keyof typeof liveContracts & string>(name: K, options: SocketRegistryOptions = {}) {
return useSocketChannel(liveContracts, name, { baseUrl: "wss://live.example.com", ...options });
}Choosing the channel at runtime#
With a name received as text, check it first: TypeScript no longer can.
import { useSocketChannel } from "@meekojs/meeko/framework";
declare const socketContracts: Record<"chat" | "presence", Parameters<typeof useSocketChannel>[0][string]>;
function isChannel(name: string): name is "chat" | "presence" {
return Object.hasOwn(socketContracts, name);
}
export function openChannel(name: string) {
if (!isChannel(name)) throw new TypeError(`Canal desconocido: ${name}`);
return useSocketChannel(socketContracts, name);
}Common mistakes#
A channel not in the registry#
const feed = useSocketChannel(socketContracts, "feeds" as never);At runtime, it throws TypeError: Unknown socket channel: feeds. With a literal name, it is a type error; do not hide it with a cast:
const feed = useSocketChannel(socketContracts, "feed");Waiting for the connection with await#
// In the <script> of a component
const chat = await useSocketChannel(socketContracts, "chat");The compiler only splits setup at an await of useElementRef, useSocket or useSocketClient: this one is MEEK055. Without await, the call also works in SSR (since 1.7, it returns an inert client there; through 1.6, it threw useSocketClient() is only available in the browser). To continue setup with the socket open, use the useSocket auto-import with await:
const chat = await useSocket("chat");Using it outside a component#
// app/utils/live.ts, evaluated on import
export const chat = useSocketChannel(socketContracts, "chat");The client needs a component root to close on unmount: outside one, it closes immediately and throws Error: onCleanup() requires an active component root. For a standalone client you close yourself, use createSocketClient or the application's socket registry:
import { createSocketClient } from "@meekojs/meeko/framework";
export const chat = createSocketClient("wss://example.com/_meeko/socket/chat", socketContracts.chat);Awaiting it with await#
const chat = await useSocketChannel(socketContracts, "chat");meeko check reports MEEK055: El await de nivel superior sólo se admite sobre useElementRef, useSocket, useSocketClient, useApi, useRpcQuery; mueve el resto del trabajo asíncrono a una función o a onMounted. In an application, use the useSocket auto-import, which supports await:
const chat = await useSocket("chat");Notes#
- Like
useSocket, except forawait. Same lifecycle (bound to the component, closed on unmount) and behavior in SSR and dry runs. The compiler only splits setup at anawaitofuseElementRef,useSocketoruseSocketClient; any other top-levelawait, includinguseSocketChannelor a custom wrapper, isMEEK055. - URL.
baseUrlacceptshttp(s):andws(s):; it defaults to the browser origin, which is what the server checks inOrigin. - Public contracts. Contracts contain only schemas from
shared/: they never import server handlers.