feat: OpsxAPI v1.0 全协议接口工作台
- 六协议调试: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),明暗双主题
- 环境变量:彩色徽标切换器、管理弹窗、{{ 自动补全
This commit is contained in:
commit
dec66db5aa
182 files changed
+23377
No files matched your search
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* 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");
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user