- 六协议调试:HTTP/GraphQL/WebSocket/SSE/Socket.IO/MQTT
- 集合树/多标签/多环境变量/历史记录,localStorage 持久化
- 导入:OpenAPI 2/3/3.1、Postman、Apifox、Apipost、cURL、HAR、
Insomnia、Hoppscotch、Markdown(自动识别,21 个单测覆盖)
- 导出:OpenAPI 3.0 JSON/YAML、Postman v2.1、Markdown 文档、
全量备份;单请求生成 cURL/fetch/Python/Go 代码
- Gin 服务:HTTP 代理、NDJSON 流式代理(SSE 透传)、
WebSocket 隧道(浏览器自定义握手 Header)
- 内置产品官网 /home 与用户文档站 /docs(SSR,随二进制交付)
- lucide 图标体系 + 品牌标识(Logo/favicon),明暗双主题
- 环境变量:彩色徽标切换器、管理弹窗、{{ 自动补全
93 lines
2.7 KiB
TypeScript
93 lines
2.7 KiB
TypeScript
/**
|
||
* Socket.IO 客户端封装(动态加载 socket.io-client)。
|
||
*/
|
||
export type SocketIoStatus = "connecting" | "open" | "closed" | "error";
|
||
|
||
export type SocketIoSession = {
|
||
emit(event: string, args: unknown[]): void;
|
||
close(): void;
|
||
};
|
||
|
||
export type SocketIoOptions = {
|
||
url: string;
|
||
/** 附加选项 JSON:{ path?, transports?, auth?, ... } */
|
||
optionsJson?: string;
|
||
/** 监听事件列表;空数组 = onAny 全收 */
|
||
listenEvents: string[];
|
||
onEvent(event: string, args: unknown[]): void;
|
||
onStatus?(s: SocketIoStatus, detail?: string): void;
|
||
onError?(msg: string): void;
|
||
};
|
||
|
||
type IoSocket = {
|
||
emit(ev: string, ...args: unknown[]): void;
|
||
on(ev: string, cb: (...args: unknown[]) => void): void;
|
||
onAny(cb: (ev: string, ...args: unknown[]) => void): void;
|
||
disconnect(): void;
|
||
};
|
||
|
||
export function connectSocketIo(opts: SocketIoOptions): SocketIoSession {
|
||
let socket: IoSocket | null = null;
|
||
let disposed = false;
|
||
|
||
void (async () => {
|
||
try {
|
||
opts.onStatus?.("connecting");
|
||
const { io } = await import("socket.io-client");
|
||
const extra: Record<string, unknown> = {};
|
||
if (opts.optionsJson?.trim()) {
|
||
try {
|
||
Object.assign(extra, JSON.parse(opts.optionsJson));
|
||
} catch {
|
||
opts.onError?.("连接选项不是合法 JSON,已忽略");
|
||
}
|
||
}
|
||
const s: IoSocket = io(opts.url, {
|
||
transports: ["websocket", "polling"],
|
||
reconnection: false,
|
||
...extra,
|
||
}) as unknown as IoSocket;
|
||
socket = s;
|
||
|
||
s.on("connect", () => opts.onStatus?.("open"));
|
||
s.on("disconnect", (...args: unknown[]) =>
|
||
opts.onStatus?.("closed", String(args[0] ?? "")),
|
||
);
|
||
s.on("connect_error", (...args: unknown[]) => {
|
||
const err = args[0];
|
||
const msg = err instanceof Error ? err.message : String(err);
|
||
opts.onError?.(msg);
|
||
opts.onStatus?.("error", msg);
|
||
});
|
||
|
||
const events = opts.listenEvents.map((e) => e.trim()).filter(Boolean);
|
||
if (events.length === 0 || events.includes("*")) {
|
||
s.onAny((ev, ...args) => opts.onEvent(ev, args));
|
||
} else {
|
||
for (const ev of events) {
|
||
s.on(ev, (...args: unknown[]) => opts.onEvent(ev, args));
|
||
}
|
||
}
|
||
if (disposed) s.disconnect();
|
||
} catch (e) {
|
||
opts.onError?.(`socket.io-client 加载/连接失败:${e instanceof Error ? e.message : String(e)}`);
|
||
opts.onStatus?.("error");
|
||
}
|
||
})();
|
||
|
||
return {
|
||
emit(event, args) {
|
||
if (!socket) {
|
||
opts.onError?.("Socket.IO 尚未连接");
|
||
return;
|
||
}
|
||
socket.emit(event, ...args);
|
||
},
|
||
close() {
|
||
disposed = true;
|
||
socket?.disconnect();
|
||
opts.onStatus?.("closed");
|
||
},
|
||
};
|
||
}
|