Compare commits
5 Commits
e17a9591e1
...
809a3759d9
| Author | SHA1 | Date | |
|---|---|---|---|
| 809a3759d9 | |||
| bc1445d3a5 | |||
| 24a14a8e1c | |||
| 99c673a8dc | |||
| b0bcd64b41 |
43
__tests__/unit/core/RPCPlugin.test.ts
Normal file
43
__tests__/unit/core/RPCPlugin.test.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { createHookRunner, RPCPlugin } from "@/core/RPCPlugin"
|
||||
import type { CallIncomingCtx } from "@/core/RPCPlugin"
|
||||
|
||||
describe('RPCPlugin.test', () => {
|
||||
const plugin3 = {
|
||||
onCallIncoming(ctx: CallIncomingCtx) { throw new Error() }
|
||||
} as RPCPlugin;
|
||||
const plugin4 = {
|
||||
async onCallIncoming(ctx: CallIncomingCtx) { throw new Error() }
|
||||
} as RPCPlugin;
|
||||
|
||||
test('should be resolved', async () => {
|
||||
const plugin1 = {
|
||||
onCallIncoming: jest.fn(),
|
||||
} as RPCPlugin;
|
||||
const plugin2 = {
|
||||
async onCallIncoming(ctx: CallIncomingCtx) { }
|
||||
} as RPCPlugin;
|
||||
|
||||
const plugins = [plugin1, plugin2];
|
||||
const hookRunner = createHookRunner(plugins, 'onCallIncoming');
|
||||
await hookRunner({} as any);
|
||||
expect(plugin1.onCallIncoming).toHaveBeenCalled()
|
||||
})
|
||||
test('should be resolved2', async () => {
|
||||
const plugins = [] as RPCPlugin[];
|
||||
const hookRunner = createHookRunner(plugins, 'onCallIncoming');
|
||||
await hookRunner({} as any);
|
||||
expect.assertions(0);
|
||||
})
|
||||
|
||||
test('should be rejected1', async () => {
|
||||
const plugins = [plugin3];
|
||||
const hookRunner = createHookRunner(plugins, 'onCallIncoming');
|
||||
await expect(hookRunner({} as any)).rejects.toThrow(Error)
|
||||
})
|
||||
|
||||
test('should be rejected2', async () => {
|
||||
const plugins = [plugin4];
|
||||
const hookRunner = createHookRunner(plugins, 'onCallIncoming');
|
||||
await expect(hookRunner({} as any)).rejects.toThrow(Error)
|
||||
})
|
||||
})
|
||||
@@ -3,6 +3,7 @@ import { RPCClient } from "./RPCClient";
|
||||
import { RPCServer } from "./RPCServer";
|
||||
import { RPCProvider } from "./RPCProvider";
|
||||
import { RPCSession } from "./RPCSession";
|
||||
import { RPCPlugin } from "./RPCPlugin";
|
||||
|
||||
const DefaultListenOptions = {
|
||||
port: 5201,
|
||||
@@ -34,6 +35,7 @@ export class RPCHandler extends EventEmitter<RPCHandlerEvents> {
|
||||
private provider?: RPCProvider;
|
||||
private accessKey?: string;
|
||||
private config: RPCConfig;
|
||||
private plugins: RPCPlugin[] = [];
|
||||
|
||||
constructor(
|
||||
args?: {
|
||||
@@ -88,6 +90,29 @@ export class RPCHandler extends EventEmitter<RPCHandlerEvents> {
|
||||
}
|
||||
}
|
||||
|
||||
loadPlugin(plugin: RPCPlugin): boolean {
|
||||
const plugins = this.plugins;
|
||||
if (plugins.includes(plugin)) {
|
||||
return false;
|
||||
}
|
||||
plugins.push(plugin);
|
||||
return true;
|
||||
}
|
||||
|
||||
unloadPlugin(plugin: RPCPlugin): boolean {
|
||||
const plugins = this.plugins;
|
||||
const idx = plugins.indexOf(plugin);
|
||||
if (idx === -1) {
|
||||
return false;
|
||||
}
|
||||
plugins.splice(idx, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
getPlugins(): RPCPlugin[] {
|
||||
return [...this.plugins];
|
||||
}
|
||||
|
||||
async connect(options: {
|
||||
url?: string;
|
||||
accessKey?: string;
|
||||
|
||||
74
src/core/RPCPlugin.ts
Normal file
74
src/core/RPCPlugin.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { RPCSession } from "./RPCSession";
|
||||
|
||||
// interface BaseHookRuntimeCtx {
|
||||
// nexts: RPCPlugin[];
|
||||
// setNextPlugins: (plugins: RPCPlugin[]) => void
|
||||
// }
|
||||
|
||||
export interface BaseHookCtx {
|
||||
|
||||
}
|
||||
|
||||
export interface CallOutgoingBeforeCtx extends BaseHookCtx {
|
||||
session: RPCSession;
|
||||
options: {
|
||||
fnPath: string;
|
||||
args: any[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface CallOutgoingCtx extends CallOutgoingBeforeCtx {
|
||||
result: any;
|
||||
setResult: (data: any) => void;
|
||||
}
|
||||
|
||||
export interface CallIncomingBeforeCtx extends BaseHookCtx {
|
||||
|
||||
}
|
||||
|
||||
export interface CallIncomingCtx extends CallIncomingBeforeCtx {
|
||||
|
||||
}
|
||||
|
||||
export type NormalMethodReturn = Promise<void> | void;
|
||||
export type HookFn<Ctx> = (ctx: Ctx) => NormalMethodReturn;
|
||||
|
||||
export interface RPCPluginHooksCtx {
|
||||
onCallOutgoingBefore: CallOutgoingBeforeCtx;
|
||||
onCallOutgoing: CallOutgoingCtx;
|
||||
onCallIncomingBefore: CallIncomingBeforeCtx;
|
||||
onCallIncoming: CallIncomingCtx;
|
||||
}
|
||||
|
||||
export type RPCPluginHooks = {
|
||||
[K in keyof RPCPluginHooksCtx]?: HookFn<RPCPluginHooksCtx[K]>;
|
||||
};
|
||||
|
||||
|
||||
export interface RPCPlugin extends RPCPluginHooks {
|
||||
onInit?(): void;
|
||||
onDestroy?(): void;
|
||||
}
|
||||
|
||||
export abstract class AbstractRPCPlugin implements RPCPlugin {
|
||||
abstract onInit?(): void;
|
||||
abstract onDestroy?(): void;
|
||||
abstract onCallOutgoingBefore?(ctx: CallOutgoingBeforeCtx): NormalMethodReturn;
|
||||
abstract onCallOutgoing?(ctx: CallOutgoingCtx): NormalMethodReturn;
|
||||
abstract onCallIncomingBefore?(ctx: CallIncomingBeforeCtx): NormalMethodReturn;
|
||||
abstract onCallIncoming?(ctx: CallIncomingCtx): NormalMethodReturn;
|
||||
}
|
||||
|
||||
type HookName = keyof RPCPluginHooksCtx;
|
||||
|
||||
type HookRunner<Ctx> = (ctx: Ctx) => Promise<void>;
|
||||
export function createHookRunner<K extends HookName>(
|
||||
plugins: RPCPlugin[],
|
||||
hookName: K,
|
||||
): HookRunner<RPCPluginHooksCtx[K]> {
|
||||
return async (ctx: RPCPluginHooksCtx[K]) => {
|
||||
for (const plugin of plugins) {
|
||||
await (plugin[hookName] as HookFn<RPCPluginHooksCtx[K]> | undefined)?.(ctx);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -81,4 +81,20 @@ export function markAsPublicMethod<T extends Function | Record<any, unknown> | u
|
||||
|
||||
markAs(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
export function createDeferrablePromise<T = unknown>() {
|
||||
let resolve!: (value: T | PromiseLike<T>) => void;
|
||||
let reject!: (reason?: unknown) => void;
|
||||
|
||||
const promise = new Promise<T>((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
|
||||
return {
|
||||
promise,
|
||||
resolve,
|
||||
reject
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user