import type { Socket } from 'node:net'; import { type Message } from './io.common.ts'; import { Lock, TimeoutError } from './utils.common.ts'; declare global { interface WebSocket { _socket?: Socket; } } /** 对于 request() 函数来说无意义的 headers,会自动过滤掉 */ export declare const drop_request_headers: Set; export interface BasicAuth { type: 'basic'; username: string; password: string; } export interface BearerAuth { type: 'bearer'; token: string; } export declare const WebSocketConnecting = 0; export declare const WebSocketOpen = 1; export declare const WebSocketClosing = 2; export declare const WebSocketClosed = 3; export declare const websocket_states: readonly ['connecting', 'open', 'closing', 'closed']; export declare class WebSocketConnectionError extends Error { name: 'WebSocketConnectionError'; url: string; protocols?: string[]; type?: 'close' | 'error'; /** close 事件时为 close code (非 1000 的 number), error 事件为 error code (可能是 string) */ code?: string | number; reason?: string; event?: CloseEvent | ErrorEvent; address?: string; errno?: number; port?: number; syscall?: string; static get_reason_string(code: number | string, reason?: string): string | number; constructor(url: string, protocols: string[] | undefined, event?: CloseEvent | ErrorEvent, message?: string); } export interface ConnectWebSocketPrint { connect: boolean; error: boolean; close: boolean; } export interface ConnectWebsocketOptions { print?: boolean | ConnectWebSocketPrint; protocols?: string[]; proxy?: string; keep_alive_duration?: number; on_message(data: ArrayBuffer | string, websocket: WebSocket): any; on_error?(error: WebSocketConnectionError, websocket: WebSocket): any; on_close?(event: CloseEvent, websocket: WebSocket): any; } /** 连接 websocket url, 设置各种事件监听器。在 open 事件后 resolve, 返回 websocket 遇到 error, close 事件时会创建 WebSocketConnectionError: - reject 掉返回的 promise (若此时未 settle) - 作为参数调用 on_error (已 settle 且有 on_error 回调) 可以用 WebSocket.bufferedAmount 来查询大消息的发送进度 https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/bufferedAmount - url - options: - print?: boolean 或者 ConnectWebSocketPrint,可以控制是否打印连接、正常断开、关闭信息 - connect: `true` 连接建立 - error: `true` 出错 - protocols? - proxy?: string (仅 nodejs 环境有效) - keep_alive_duration?: 毫秒数,设置后启用操作系统级的 keep alive (仅 nodejs 环境可以有效,浏览器端默认已启用) - on_message: 根据 websocket frame 的 opcode 不同 (text frame 或 binary frame),event 中的 data 对应为 ArrayBuffer 或者 string https://datatracker.ietf.org/doc/html/rfc6455#section-5.2 - on_error?: 在 websocket 出错和非正常关闭 (close, error 事件) 时都调用,可以根据 error.type 来区分,error 的类型是 WebSocketConnectionError, type 为 'close' 时有 code 和 reason 属性 - on_close?: 和 websocket 的 'close' 事件不相同,只在正常关闭 (close code 为 1000) 时才调用,否则都会调用 on_error https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes - connect?: nodejs 环境下需要指定 ip, port 手动连接 tls server 时使用,有 agent 时不生效 */ export declare function connect_websocket(url: string | URL, { print, protocols, proxy, keep_alive_duration, on_message, on_error, on_close, }: ConnectWebsocketOptions): Promise; /** 接收到消息后的处理函数 返回值会自动被 await,然后可能被封装为 message 回传 */ export type MessageHandler = (message: Message, remote: Remote) => void | any | Promise; export interface RemoteOptions { /** 对端名称,方便接收端提供更明确的报错信息 */ name?: string; /** 作为 websocket 连接发起方,对端的 url 地址 */ url?: string; /** 作为 websocket 连接接收方,将已有的 websocket 连接转为 rpc 通道 */ websocket?: WebSocket; /** 能被对端调用的函数 */ funcs?: Remote['funcs']; /** 设置后启用操作系统级的 keep alive (仅 nodejs 环境可设置) - 浏览器端有默认 tcp keep alive 心跳机制,禁止设置这个参数 - nodejs 端 - 作为连接发起方: 在 websocket 连接建立时启用 - 作为连接接收方: 在根据传入 websocket 创建 Remote 时启用 */ alive?: boolean; /** websocket 连接建立成功时调用对端 register 函数的参数 (仅发起方有效) */ args?: any[]; /** `true` 是否打印连接信息、错误信息 */ print?: boolean; /** `false` 打印所有交互的 rpc messages */ verbose?: boolean; proxy?: string; /** 使用者自定义的在 websocket 连接出错时,或者 handlers 出错时的处理 用户设置后会覆盖默认的 print 错误功能 */ on_error?(error: WebSocketConnectionError | Error, remote: Remote): void; /** 使用者自定义的,在重连之前调用的函数,比如可以修改 url,仅连接发起方有效 */ on_reconnect?(this: Remote): Promise; /** 使用者自定义的,在连接建立,包括首次连接和重连 之后调用的函数, 比如可以重新调用一些函数,仅连接发起方有效 */ on_connected?(this: Remote): void; } /** 通过创建 remote 对象对 websocket rpc 进行抽象 使用 remote.call() 进行一元 rpc 使用 remote.send() 结合 message.id 进行复杂 rpc 可传入 funcs 注册函数,使得对端能调用,传入后, 连接发起方在检测到连接断开后间隔 2s (首次) / 10s 尝试重连 可启用 probe 选项启用主动探测连接,进一步确保连接可靠性 有两种创建方法: - 传入 url 主动创建 websocket 连接 创建后等到首个 remote.call 或 remote.send 时建立实际 websocket 连接 - 传入 websocket,监听事件,将已有的 websocket 连接转为 rpc 通道 rpc 状态与底层连接的状态无关,如果是发起方,remote.send 时检测到断线会自动尝试建立新的 websocket 连接 */ export declare class Remote { /** 对端名称,方便接收端提供更明确的报错信息 */ name?: string; /** 作为 websocket 连接发起方,对端的 url 地址 */ url?: string; /** 能被对端调用的函数 */ funcs: Record; /** tcp keep alive */ alive: boolean; /** websocket 连接建立成功时调用对端 register 函数的参数 (仅发起方有效) */ args?: any[]; /** `true` 是否打印连接信息、错误信息 */ print: boolean; /** `false` 打印所有交互的 rpc messages */ verbose: boolean; proxy?: string; /** 防止作为 websocket 连接发起方时并发创建 websocket 连接 */ lwebsocket: Lock; /** map: 通过 (rpc message).id 找到对应的 handler 一元 rpc 接收方不需要设置 handlers, 发送方需要 */ handlers: Map>; /** websocket 检测到错误,_on_error 被调用,此时会尝试一段时间后重连 */ reconnecting: NodeJS.Timeout; /** 用户是否手动调用了 disconnect() 主动断开了连接 (仅发起方有效) */ disconnected: boolean; /** on_error 接收到的,正常 websocket 连接发生的首个非 TimeoutError 错误 */ error: WebSocketConnectionError | Error; /** 作为 websocket 连接发起方,传入 url 作为 websocket 连接接收方,传入 websocket + name */ constructor({ name, url, websocket, funcs, print, verbose, alive, args, proxy, on_error, on_reconnect, on_connected }?: RemoteOptions); /** 统一处理首次连接和连接后的 websocket 错误 */ _on_error: (error: WebSocketConnectionError | Error) => void; on_reconnect?: (this: Remote) => Promise; on_connected?: (this: Remote) => void; reconnect: () => Promise; _on_message: (data: ArrayBuffer) => void; /** 使用者自定义的在 websocket 连接出错时,或者 handlers 出错时的处理 用户设置后会覆盖默认的 print 错误功能 */ on_error(error: WebSocketConnectionError | Error, remote: Remote): void; print_error(error: WebSocketConnectionError | Error): void; /** 检查是否已经有 error, websocket 状态是否正常 (open | connecting) 返回 true: 连接正常,false: 连接异常 */ check_connection(): boolean; /** 幂等,保证 websocket 已连接,否则抛出异常 通常用于手动建立 websocket 连接并确保成功 连接断开后,作为发起方会自动创建新的 websocket 连接; 作为接收方只会检查连接状态,在断开时抛出异常 保存的 rpc 状态在 this.handlers, 与 websocket 无关,因此即使断开重连也不影响 rpc 的运行,即 底层连接断开后自动重连对上层应该是无感知的,除非再次连接时失败 */ connect(): Promise; _connect: () => Promise; /** 尝试建立连接,通常用于开始保活 重连错误会在 this.connect 里面调用 this._on_error 处理 */ try_connect(): Promise; /** 检测连接,返回连接错误,出错时触发 on_error 打印错误信息 返回值: - 连接正常返回: undefined - 连接错误返回: 实际的连接错误,通常是 WebSocketConnectionError | TimeoutError */ test(print_timeout?: boolean): Promise; /** node.js 下连接接收方在 Remote 创建后手动启用 tcp keep alive */ keep_alive(): void; /** 手动关闭到对端的 websocket 连接 */ disconnect(): void; /** 发送 message 到对端 remote 发送或连接出错时自动清理 message.id 对应的 handler */ send(message: Message): Promise; /** 能发就发一下,忽略发送失败, best effort */ try_send(message: Message): void; /** 处理接收到的 websocket message 并解析, 根据 message.id 或 message.func 分发到对应的 handler 进行处理, handler 处理完成后: - 传了 func: 调用函数的情况下 (通常是一元 rpc,也有可能是仅 send),如果有 id,则总是将非 undefined 返回值包装为 message 回传 - 未传 func: 通过 id 调用,如果 handler 返回非 undefined 的值,也包装为 message 回传 如果 message.done == true 则对端指示当前 remote 可以清理 handler 使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 这个方法一般不会抛出错误,也不需要 await,一般在 websocket on_message 时使用 */ handle(data: Uint8Array): Promise; /** 向对端 remote 中的 func 推送数据,没有期望任何返回信息 */ push(func: string, data?: any): Promise; /** 调用对端 remote 中的 func, 只适用于最简单的一元 rpc (请求, 响应) */ call(func: string, data?: any): Promise; /** 调用对端 remote 中的 func, 开始订阅并接收连续的消息 (订阅流) - func: 订阅处理函数 - on_data: 接收开始订阅后的数据 - options?: - on_error?: 处理开始订阅后的错误 */ subscribe(func: string, on_data: (data: TData) => void, on_error?: Remote['on_error']): Promise<{ id: number; data: TSubscribed; }>; }