From cc78248e44a41495030c0353679dbe5bde8c676d Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 30 Sep 2019 23:48:05 +0200 Subject: [PATCH] v 1.3.0 improve pseudointerfaces --- README.md | 4 ++-- package.json | 2 +- src/Backend.ts | 2 +- src/Frontend.ts | 2 +- src/Interfaces.ts | 10 ++++---- src/Types.ts | 46 +++++++++++++++++++---------------- test/Test.ts | 13 +++------- test/devtest.ts | 61 ++++++++++++++++------------------------------- 8 files changed, 58 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index d9a4ea4..0fe9045 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ It is possible to declare pseudo-interfaces for servers and clients by using ser This feature is currently still in development and considered **unstable and untested**. Use with caution. ```typescript -type MyInterface = RPCInterface<{ +type MyInterface = { Group1: { triggerCallbacks: (...args:any[]) => Promise, subscribe: (param:string, callback:Function) => Promise>, @@ -116,7 +116,7 @@ type MyInterface = RPCInterface<{ Group2: { echo: (x:string) => Promise } -}> +} ``` Create a client using ```typescript diff --git a/package.json b/package.json index 7cb4194..a9bcc38 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rpclibrary", - "version": "1.2.6", + "version": "1.3.0", "description": "rpclibrary is a websocket on steroids!", "main": "./js/Index.js", "repository": { diff --git a/src/Backend.ts b/src/Backend.ts index bf7a7ed..4b41cb1 100644 --- a/src/Backend.ts +++ b/src/Backend.ts @@ -29,7 +29,7 @@ export class RPCServer< */ constructor( private port:number, - private exporters: I.RPCExporter, keyof T.RPCInterface, SubResType>[] = [], + private exporters: I.RPCExporter, keyof InterfaceT, SubResType>[] = [], conf: T.SocketConf = {} ){ diff --git a/src/Frontend.ts b/src/Frontend.ts index 612d5c6..2c86e6e 100644 --- a/src/Frontend.ts +++ b/src/Frontend.ts @@ -129,7 +129,7 @@ export class RPCSocket implements I.Socket{ * @param fnName The function name * @param fnArgs A string-list of parameters */ - private callGenerator(fnName: string, fnArgs:string[]): T.AsyncFunction{ + private callGenerator(fnName: string, fnArgs:string[]): T.AnyFunction{ const headerArgs = fnArgs.join(",") const argParams = fnArgs.map(stripAfterEquals).join(",") return eval( '( () => async ('+headerArgs+') => { return await this.socket.call("'+fnName+'", '+argParams+')} )()' ) diff --git a/src/Interfaces.ts b/src/Interfaces.ts index 3950345..af4a8f7 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -2,15 +2,15 @@ import * as T from "./Types"; import * as I from "./Interfaces" /** - * Interface for all classes that may export RPCs + * Interface for all classes that export RPCs */ export interface RPCExporter< - Ifc extends T.RPCInterface, - K extends keyof Ifc, + Ifc extends T.RPCInterface, + Name extends keyof Ifc, SubresT = {} >{ - name: K - exportRPCs() : T.RPC[] + name: Name + exportRPCs() : T.RPCInterfaceArray[Name] } /** diff --git a/src/Types.ts b/src/Types.ts index 77b8d3c..23e8802 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -1,9 +1,7 @@ import * as I from "./Interfaces"; -export type AnyFunction = (...any:any)=>any -export type AsyncFunction = ReturnType extends Promise ? Fn : (...any:Parameters) => Promise> -export type RPCGroup = { [name in string] : AsyncFunction } -export type RPCInterface = { [groupName in string]: RPCGroup } & Impl +export type AnyFunction = (...args:any) => any +export type HookFunction = (...args: Parameters) => Promise | ErrorResponse> export type Visibility = "127.0.0.1" | "0.0.0.0" export type ConnectionHandler = (socket:I.Socket) => void @@ -19,7 +17,6 @@ export type SocketConf = { export type ResponseType = "Subscribe" | "Success" | "Error" export type Outcome = "Success" | "Error" - export type Respose = T & { result: Outcome } export type SuccessResponse = Respose & { result: "Success" } export type ErrorResponse = Respose & { result: "Error", message?:string } @@ -27,22 +24,31 @@ export type SubscriptionResponse = Respose & { result: "Success"; uui export type RPCType = 'Hook' | 'Unhook' | 'Call' -export type HookT = AsyncFunction> -export type CallT = AsyncFunction +export type CallRPC = { + name: N + call: F +} -export type HookRPC = { - name: K - hook: HookT - onCallback?: CallbackFunction, +export type HookRPC = { + name: N + hook: HookFunction + onCallback?: AnyFunction onClose?: HookCloseFunction } -export type CallRPC = { - name: K - call: CallT -} +export type RPC = HookRPC | CallRPC | F -export type RPC = CallRPC | HookRPC +export type RPCInterface = { + [grp in string] : { + [rpc in string] : AnyFunction + } +} & Impl + +export type RPCInterfaceArray = { + [grp in keyof Itfc]: Array< + { [rpc in keyof Itfc[grp]]: RPC }[keyof Itfc[grp]] + > +} export type BaseInfo = { name: string, @@ -50,14 +56,14 @@ export type BaseInfo = { argNames: string[], } -export type HookInfo = BaseInfo & { +export type HookInfo = BaseInfo & { type: 'Hook', - generator: (socket?:I.Socket) => HookFunction + generator: (socket?:I.Socket) => (...args:any) => SubresT } export type CallInfo = BaseInfo & { type: 'Call', - call: AsyncFunction + call: AnyFunction } export type RpcInfo = HookInfo | CallInfo @@ -65,5 +71,3 @@ export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } export type OnFunction = (type: 'error' | 'close', f: (e?:any)=>void) => I.Socket export type HookCloseFunction = (res:SubscriptionResponse, rpc:HookRPC) => any -export type HookFunction = AsyncFunction<(...args:Parameters) => SubscriptionResponse | ErrorResponse> -export type CallbackFunction = (callback:AnyFunction, ...args:any) => any \ No newline at end of file diff --git a/test/Test.ts b/test/Test.ts index f5066a0..30c07aa 100644 --- a/test/Test.ts +++ b/test/Test.ts @@ -4,6 +4,7 @@ import { RPCServer } from '../src/Backend' import * as uuidv4 from "uuid/v4" import { RPCSocket } from "../src/Frontend"; import { SubscriptionResponse } from "../src/Types"; +import { makeSubResponse } from "../src/Utils"; const add = (...args:number[]) => {return args.reduce((a,b)=>a+b, 0)} function makeServer(){ @@ -18,21 +19,13 @@ function makeServer(){ name: 'simpleSubscribe', hook: async(callback) => { subcallback = callback - return { - result: "Success", - uuid: uuidv4(), - topic: "test" - } + return makeSubResponse<{topic: string}>({topic: "test"}) } },{ name: 'subscribe', hook: async (callback) => { subcallback = callback - return { - result: "Success", - uuid: uuidv4(), - topic: "test" - } + return makeSubResponse<{topic: string}>({topic: "test"}) }, onClose: (res, rpc) => { console.log("onClose", rpc.name === 'subscribe' && res?"OK":"") diff --git a/test/devtest.ts b/test/devtest.ts index c3972fb..821864a 100644 --- a/test/devtest.ts +++ b/test/devtest.ts @@ -1,55 +1,40 @@ import { RPCServer } from "../src/Backend"; -import { SubscriptionResponse, CallRPC, HookRPC, RPCInterface } from "../src/Types"; +import { SubscriptionResponse, RPCInterface } from "../src/Types"; import { RPCSocket } from "../src/Frontend"; +import { makeSubResponse } from "../src/Utils"; -type MyInterface = RPCInterface<{ +type SubresExtension = {a:string} + +type MyInterface = { Group1: { triggerCallbacks: (...args:any[]) => Promise, - subscribe: (param:string, callback:Function) => Promise>, + subscribe: (param:string, callback:Function) => Promise>, unsubscribe: (uuid:string) => Promise }, Group2: { echo: (x:string) => Promise } -}> +} -const callbacks:Map = new Map() - -new RPCServer<{a:string}, MyInterface>(20000, +new RPCServer(20000, [{ - name: "Group1", - exportRPCs: () => [ - >{ - name: 'subscribe', - hook: async (param, callback) => { const _uuid = ""+Math.random(); console.log(param); callbacks.set(_uuid, callback); return { result: 'Success', a: '3', uuid: _uuid} } - }, - >{ - name: 'unsubscribe', - call: async (uuid) => { callbacks.delete(uuid) } - }, - >{ + name: 'Group1', + exportRPCs: () => [{ name: 'triggerCallbacks', - call: async (...args) => { callbacks.forEach(cb => cb.apply({}, args)) } - }] - },{ - name: 'Group2', - exportRPCs: () => [ - >{ - name: 'echo', - call: async (x) => x - }] - }] -) - -new RPCServer<{a:string}, MyInterface>(20001, - [{ - name: "Group1", - exportRPCs: () => [] + call: async () => { /*...*/ } + },{ + name: 'subscribe', + hook: async (param, callback) => { return makeSubResponse({a: "test"}) } + },{ + name: 'unsubscribe', + call: async(uuid) => { } + } + ] },{ name: 'Group2', exportRPCs: () => [{ name: 'echo', - call: async (x) => x+" lol" + call: async (x) => "..." }] }] ) @@ -65,10 +50,4 @@ RPCSocket.makeSocket(20000, 'localhost').then((async (client) => { }) await client.Group1.triggerCallbacks("Hello", "World", "Callbacks") -})) - -RPCSocket.makeSocket(20001, 'localhost').then((async (client) => { - console.log(client) - const r = await client.Group2.echo("hee") - console.log(r) })) \ No newline at end of file