diff --git a/src/core/RPCSession.ts b/src/core/RPCSession.ts index d76e799..5dced55 100644 --- a/src/core/RPCSession.ts +++ b/src/core/RPCSession.ts @@ -1,4 +1,4 @@ -import { isPublicMethod, ToDeepPromise } from "@/utils/utils"; +import { isArray, isObject, isPublicMethod, ToDeepPromise } from "@/utils/utils"; import { RPCConnection } from "./RPCConnection"; import { RPCHandler } from "./RPCHandler"; import { RPCProvider } from "./RPCProvider"; @@ -8,6 +8,7 @@ import { RPCError, RPCErrorCode } from "./RPCError"; import { makeCallPacket, makeCallResponsePacket, parseCallPacket, parseCallResponsePacket } from "./RPCCommon"; import { RPCPacket } from "./RPCPacket"; import { EventEmitter } from "@/utils/EventEmitter"; +import { createHookRunner } from "./RPCPlugin"; function getProviderFunction(provider: RPCProvider, fnPath: string): [(...args: any[]) => Promise, object] | null { @@ -111,6 +112,45 @@ export class RPCSession { }); } + function setOptions(opt: unknown) { + if (!isObject(opt)) { + return; + } + + if ('fnPath' in opt && 'args' in opt && 'timeout' in opt) { + const { fnPath, args, timeout } = opt; + if (typeof fnPath !== 'string') { + return; + } + if (!isArray(args)) { + return; + } + if (typeof timeout !== 'number') { + return; + } + options = { + ...options, + fnPath, + args, + timeout, + } + } + } + + const hookRunner = createHookRunner(this.rpcHandler.getPlugins(), 'onCallOutgoingBefore'); + await hookRunner({ + session: this, + options: { ...options }, + setOptions, + }); + + /** due to `await hookRunner` */ + if (this.connection.closed) { + throw new RPCError({ + errorCode: RPCErrorCode.CONNECTION_DISCONNECTED, + }); + } + const { fnPath, args } = options; const packet = makeCallPacket({ fnPath, @@ -135,28 +175,75 @@ export class RPCSession { })(); const handleCallResponsePacket = (packet: RPCPacket) => { - const result = parseCallResponsePacket(packet); - if (result === null) { - return reject(new RPCError({ + let result = parseCallResponsePacket(packet); + function setResult(res: unknown) { + if (!isObject(res)) { + return; + } + + const { success, error } = res; + + if (typeof success === 'object' && typeof error === 'object') { + if (success && !error) { + if ('data' in success) { + result = { + success: { data: success.data }, + error: null, + } + } + } else if (!success && error) { + const { errorCode, reason } = error; + if (typeof errorCode === 'number' && typeof reason === 'string') { + result = { + success: null, + error: { + errorCode, + reason, + }, + } + } + } + } + } + + const hookRunner = createHookRunner(this.rpcHandler.getPlugins(), 'onCallOutgoing'); + hookRunner({ + session: this, + options: { ...options }, + setOptions, + result, + setResult, + }).then(() => { + if (result === null) { + return reject(new RPCError({ + errorCode: RPCErrorCode.UNKNOWN_ERROR, + }));; + } + + const { success, error } = result; + if (success) { + return resolve(success.data); + } + + if (error) { + return reject(new RPCError({ + errorCode: error.errorCode, + reason: error.reason + })); + } + + reject(new RPCError({ errorCode: RPCErrorCode.UNKNOWN_ERROR, }));; - } + }).catch((e) => { + if (e instanceof RPCError) { + return reject(e); + } - const { success, error } = result; - if (success) { - return resolve(success.data); - } - - if (error) { - return reject(new RPCError({ - errorCode: error.errorCode, - reason: error.reason - })); - } - - return reject(new RPCError({ - errorCode: RPCErrorCode.UNKNOWN_ERROR, - }));; + reject(new RPCError({ + errorCode: RPCErrorCode.UNKNOWN_ERROR, + })) + }) } this.callResponseEmitter.once(packet.id, handleCallResponsePacket);