0.4.0 • Published 2 days ago
cpcall v0.4.0
CPCALL
与协议无关的为 JavaScript 设计的远程过程调用的库
目前版本不稳定,不遵循 semver 语义,可能会有较大的破坏性变更
特性
- 与协议无关,可用于基于 TCP、IPC、WebSocket 等
- 类型安全
- 数据传输采用 JBOD 编码,相比与 JSON 拥有更多的数据类型,更小的数据帧大小
- 无需定义数据结构,非常适合动态类型语言
使用
首先定义服务
service_server.ts (运行在服务端,提供给客户端调用)
class SubService {
mul(a: number, b: number) {
return a * b;
}
}
export class ServerService {
sub = new SubService();
calc(a: number, b: number) {
return a + b;
}
getData() {
return {
regExp: /abc/,
map: new Map([
[1, 2],
[3, 4],
]),
set: new Set([1, 2, 3, 4]),
};
}
}
service_client.ts (运行在客户端,提供给服务端调用)
export class ClientService {
getData(id: number) {
return "ok" + id;
}
}
基于 TCP 的示例
Node
server.ts
import { createServer } from "node:net";
import { createSocketCpc } from "cpcall/node";
import { ServerService } from "./service_server.js";
import type { ClientService } from "./service_client.js"; //仅导入类型
const server = createServer(async (socket) => {
const serverCpc = createSocketCpc(socket);
serverCpc.setObject(new ServerService());
const caller = serverCpc.genCaller<ClientService>(); //配置类型,获取客户端完整的类型提示
const msg = await caller.getData(8);
console.log("server", msg);
await serverCpc.caller.end(); //结束调用
});
server.listen(8888);
client.ts
import { connect } from "node:net";
import { createSocketCpc } from "cpcall/node";
import type { ServerService } from "./service_server.js"; //仅导入类型
import { ClientService } from "./service_client.js";
const clientSocket = connect({ port: 8888, host: "127.0.0.1" });
clientSocket.on("connect", async () => {
const clientCpc = createSocketCpc(clientSocket);
clientCpc.setObject(new ClientService()); //客户端设置服务,可由服务端主动调用
const caller = clientCpc.genCaller<ServerService>(); //配置类型,获取服务端完整的类型提示
const data1 = await caller.getData();
console.log("client", data1);
const data2 = await caller.sub.mul(3, 5); //15
console.log("client", data2);
await clientCpc.caller.end(); //结束调用
});
clientSocket.on("close", () => console.log("client close"));
Deno
server
import { createWebStreamCpc } from "npm:cpcall/web";
const server = Deno.listen({ port: 8888 });
for await (const conn of server) {
const serverCpc = createWebStreamCpc(conn);
// ...
}
client
import { createWebStreamCpc } from "npm:cpcall/web";
const conn = await Deno.connect({ port: 8888 });
const clientCpc = createWebStreamCpc(conn);
// ...
基于 WebSocket 的实例
浏览器客户端:
import { createWebSocketCpc } from "https://esm.sh/cpcall@latest/web";
function connectWs() {
return new Promise<WebSocket>(function (resolve, reject) {
const ws = new WebSocket("ws://localhost:7770");
ws.onopen = () => resolve(ws);
});
}
const ws = await connectWs();
const clientCpc = createWebSocketCpc(ws);
// ...
API
cpcall/node
export function createSocketCpc(duplex: Duplex): CpCall;
cpcall/web
export function createWebSocketCpc(ws: WebSocket): CpCall;
export function createWebStreamCpc(stream: {
readable: ReadableStream<Uint8Array>;
writable: WritableStream<Uint8Array>;
}): CpCall;
类型
/** @internal 提供最基础的命令调用 */
interface CpCall {
/**
* @remarks 设置函数服务,设置后,可由对方调用
* @param cmd 方法名称
* @param opts.this 函数执行时的 this 指向
*/
setFn(cmd: any, fn: CmdFn, opts?: FnOpts): void;
/** @remarks 删除函数服务 */
removeFn(cmd: any): void;
/** @remarks 获取所有已设置的函数 */
getAllFn(): IterableIterator<string>;
/** @remarks 清空所有已设置的函数 */
clearFn(): void;
/** @remarks CpCaller 对象**/
caller: CpCaller;
/** @remarks CpCall 关闭事件. */
readonly closeEvent: OnceEventTrigger<void>;
/** @remarks 向对方发送 disable 帧。调用后,对方如果继续发起远程调用,将会响应给对方异常 */
disable(): Promise<void>;
/**
* @remarks 销毁连接
* @returns 返回完全关闭后解决的 Promise
*/
dispose(reason?: any): Promise<void>;
/**
* @remarks 根据对象设置调用服务。遍历对象自身和原型上值为function 类型的键,将其添加为函数服务*
* @param obj 需要添加为服务的对象。
* @param cmd 前缀
*/
setObject(obj: object, cmd?: string): void;
/**
* @remarks 生成一个代理对象。
* @returns 一个代理对象,其本质仍然是 caller.call()
* @example
* ```ts
* const service=cpcall.genCaller("pre")
* service.s1.get(1,2) //等价于 cpcall.caller.call("pre.s1.get",1,2)
* ```
*/
genCaller(prefix?: string, opts?: GenCallerOpts): AnyCaller;
}
interface CpCaller {
/** @remarks 调用远程设置的函数 */
call(...args: any[]): Promise<any>;
/** @remarks 调用远程设置的函数。与call不同的是,它没有返回值 */
exec(...args: any[]): void;
/**
* @remarks 结束远程调用。
* @param abort - 如果为true, 这将直接拒绝所有等待返回队列, 并将 ended 置为 3
* @returns 当 ended 状态变为 3后解决的 Promise
* */
end(abort?: boolean): Promise<void>;
/**
* @remarks
* 3: 表示已调用 end() 或已收到 disable 帧并且所有等待队列已清空
* 2: 已收到 disable 帧. 后续不再会收到任何返回帧, 当前非异步返回的等待队列会被全部拒绝 (如果错误的收到了对方的返回帧, 会被丢弃)
* 1: 已调用 end(). 当前不能再执行 exec() 或 call() 方法
* 0: 当前可调用 */
ended: 0 | 1 | 2 | 3;
/**
* @remarks ended 变为 2 时触发
*/
disableEvent: OnceEventTrigger<void>;
/**
* @remarks ended 变为 3 时触发
*/
finishEvent: OnceEventTrigger<void>;
}
/** @public */
type RpcFrameCtrl<T = RpcFrame> = {
frameIter: AsyncIterable<T>;
sendFrame(frame: T): void;
/**
* @remarks 在 closeEvent 触发前调用
*/
close?(): Promise<void> | void;
/**
* @remarks 当用户手动调用 dispose() 时或迭代器抛出异常时调用
*/
dispose?(reason?: any): Promise<void> | void;
};
其他
0.4.0
2 days ago
0.3.3
10 days ago
0.3.2
23 days ago
0.3.1
24 days ago
0.3.0
26 days ago
0.2.9
2 months ago
0.2.7
2 months ago
0.2.6
2 months ago
0.2.8
2 months ago
0.2.3
3 months ago
0.2.5
2 months ago
0.2.4
2 months ago
0.2.1
3 months ago
0.2.2
3 months ago
0.2.0
3 months ago
0.1.1
3 months ago
0.1.0
3 months ago
0.0.6
4 months ago
0.0.5
4 months ago
0.0.4
4 months ago
0.0.3
5 months ago
0.0.2
6 months ago
0.0.1-6
7 months ago
0.0.1-5
7 months ago
0.0.1-4
8 months ago
0.0.1-3
9 months ago
0.0.1-2
9 months ago
0.0.1-1
9 months ago
0.0.1-0
10 months ago