58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
import { assertRealtimeRoomName, type RealtimeMessage } from "./messages.ts";
|
|
|
|
export interface BrowserRoomConnection {
|
|
readonly name: string;
|
|
send(message: unknown): BrowserRoomConnection;
|
|
on(
|
|
type: string | ((message: unknown) => void),
|
|
callback?: (message: unknown) => void,
|
|
): BrowserRoomConnection;
|
|
close(): void;
|
|
}
|
|
|
|
export interface WrnexusRealtimeWindow extends Window {
|
|
wrn?: {
|
|
room?: (name: string, query?: string) => BrowserRoomConnection;
|
|
};
|
|
}
|
|
|
|
const QUERY_KEY = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
|
|
export function roomQuery(
|
|
params: Record<string, string | number | boolean | null | undefined>,
|
|
): string {
|
|
const query = new URLSearchParams();
|
|
for (const [key, value] of Object.entries(params)) {
|
|
if (!QUERY_KEY.test(key)) throw new TypeError(`Invalid realtime query key: ${key}`);
|
|
if (value !== undefined && value !== null) query.set(key, String(value));
|
|
}
|
|
const result = query.toString();
|
|
if (result.length > 2_048) throw new RangeError("Realtime room query exceeds 2048 characters.");
|
|
return result;
|
|
}
|
|
|
|
export function connectRoom(
|
|
name: string,
|
|
options: {
|
|
query?: Record<string, string | number | boolean | null | undefined>;
|
|
window?: WrnexusRealtimeWindow;
|
|
} = {},
|
|
): BrowserRoomConnection {
|
|
const roomName = assertRealtimeRoomName(name);
|
|
const target = options.window ?? (globalThis as unknown as WrnexusRealtimeWindow);
|
|
const factory = target.wrn?.room;
|
|
if (!factory) {
|
|
throw new Error(
|
|
"WRN-REALTIME-CLIENT-NOT-READY: add a RealtimeRoom/data-room component before calling connectRoom().",
|
|
);
|
|
}
|
|
return factory(roomName, options.query ? roomQuery(options.query) : undefined);
|
|
}
|
|
|
|
export function sendRoomMessage<T>(
|
|
room: BrowserRoomConnection,
|
|
message: RealtimeMessage<T> | T,
|
|
): BrowserRoomConnection {
|
|
return room.send(message);
|
|
}
|