@firtoz/socka
Version:
Standard Schema–first WebSocket RPC for TypeScript — Bun, Hono, Node ws, Cloudflare Workers, Durable Objects
112 lines (106 loc) • 5.42 kB
TypeScript
import { DependencyList, RefObject, ReactNode, ReactElement } from 'react';
import { S as SockaContractBound, b as InferSockaPushHandlers, d as InferSockaSend, c as InferSockaPushPayload } from '../socka-report-error-BALZPjd1.js';
import { SockaSessionOptions, SockaSession, SockaConnectionStatus } from '../client/index.js';
import '@standard-schema/spec';
/** Options for {@link useSocka}. */
type UseSockaOptions<TContract extends SockaContractBound> = SockaSessionOptions<TContract>;
/**
* Connects a {@link SockaSession} in an effect: rejects all pending calls and closes
* the socket on cleanup or when `deps` change.
*/
declare function useSocka<TContract extends SockaContractBound>(options: UseSockaOptions<TContract>, deps: DependencyList): {
ready: boolean;
sessionRef: RefObject<SockaSession<TContract> | null>;
status: SockaConnectionStatus;
reconnecting: boolean;
reconnectAttempt: number;
};
type UseSockaSessionOptions<TContract extends SockaContractBound> = Omit<UseSockaOptions<TContract>, "contract" | "pushHandlers"> & {
pushHandlers?: Partial<InferSockaPushHandlers<TContract>>;
};
/**
* Builds the same typed `send` object as {@link useSockaSession} from a live session ref.
* Used by {@link useSockaSessionContext} so consumers do not open extra connections.
*/
declare function createSockaSendProxyFromSession<TContract extends SockaContractBound>(contract: TContract, sessionRef: RefObject<SockaSession<TContract> | null>): InferSockaSend<TContract>;
/**
* ```tsx
* const { ready, send } = useSockaSession(myContract, { url }, deps);
* await send.echo({ message: "hi" });
* ```
*/
declare function useSockaSession<TContract extends SockaContractBound>(contract: TContract, options: UseSockaSessionOptions<TContract>, deps: DependencyList): {
ready: boolean;
send: InferSockaSend<TContract>;
sessionRef: RefObject<SockaSession<TContract> | null>;
status: SockaConnectionStatus;
reconnecting: boolean;
reconnectAttempt: number;
};
type SockaPresenceOptions<TContract extends SockaContractBound, TUser extends {
userId: string;
}, KJoin extends keyof TContract["pushes"] & string, KLeave extends keyof TContract["pushes"] & string> = {
snapshot: () => Promise<{
selfUserId: string;
users: TUser[];
}>;
joinPush: KJoin;
leavePush: KLeave;
mapJoinUser: (p: InferSockaPushPayload<TContract, KJoin>) => TUser;
mapLeaveUserId: (p: InferSockaPushPayload<TContract, KLeave>) => string;
/** Optional display order after each update (e.g. by `displayName`). */
sortUsers?: (a: TUser, b: TUser) => number;
};
/**
* Loads a presence snapshot RPC once, then merges **`joinPush`** / **`leavePush`** deltas.
* Pass the same **`deps`** you use for {@link useSocka} when room identity changes.
* Options are read from a ref so you do not need to memoize the **`options`** object.
*/
declare function useSockaPresence<TContract extends SockaContractBound, TUser extends {
userId: string;
}, KJoin extends keyof TContract["pushes"] & string, KLeave extends keyof TContract["pushes"] & string>(sessionRef: RefObject<SockaSession<TContract> | null>, ready: boolean, options: SockaPresenceOptions<TContract, TUser, KJoin, KLeave>, deps: DependencyList): {
users: TUser[];
selfUserId: string | undefined;
loading: boolean;
};
type AnySockaContract = SockaContractBound;
/**
* Session slice stored on React context by {@link SockaSessionProvider}. The typed
* `send` object is built in {@link useSockaSessionContext} (same as {@link useSockaSession})
* so children do not open duplicate WebSockets.
*/
type SockaSessionContextValue<TContract extends SockaContractBound = AnySockaContract> = {
readonly contract: TContract;
readonly ready: boolean;
readonly sessionRef: RefObject<SockaSession<TContract> | null>;
readonly status: SockaConnectionStatus;
readonly reconnecting: boolean;
readonly reconnectAttempt: number;
};
type SockaSessionProviderProps<TContract extends SockaContractBound> = {
readonly contract: TContract;
readonly deps: DependencyList;
readonly children: ReactNode;
} & UseSockaSessionOptions<TContract>;
/**
* Owns a single {@link SockaSession} / WebSocket and exposes it to descendants via
* {@link useSockaSessionContext}. Mount once per connection (e.g. layout); avoid
* calling {@link useSockaSession} in every leaf—use the context hook instead.
*/
declare function SockaSessionProvider<TContract extends SockaContractBound>(props: SockaSessionProviderProps<TContract>): ReactElement;
declare namespace SockaSessionProvider {
var displayName: string;
}
/**
* Reads the socka session from the nearest {@link SockaSessionProvider}.
* Pass the **same** `contract` reference as the provider for typing and validation.
*/
declare function useSockaSessionContext<TContract extends SockaContractBound>(contract: TContract): {
ready: boolean;
send: InferSockaSend<TContract>;
sessionRef: RefObject<SockaSession<TContract> | null>;
status: SockaConnectionStatus;
reconnecting: boolean;
reconnectAttempt: number;
};
export { type SockaPresenceOptions, type SockaSessionContextValue, SockaSessionProvider, type SockaSessionProviderProps, type UseSockaOptions, type UseSockaSessionOptions, createSockaSendProxyFromSession, useSocka, useSockaPresence, useSockaSession, useSockaSessionContext };