diff --git a/package-lock.json b/package-lock.json index 2a075f1..b6b2180 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "rpclibrary", - "version": "1.6.1", + "version": "1.9.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -467,9 +467,9 @@ "dev": true }, "acorn": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", - "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", "dev": true }, "aggregate-error": { @@ -753,9 +753,9 @@ "dev": true }, "bluebird": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", - "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, "bn.js": { @@ -952,9 +952,9 @@ }, "dependencies": { "glob": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", - "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -1062,9 +1062,9 @@ } }, "chownr": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, "chrome-trace-event": { @@ -1331,6 +1331,11 @@ "randomfill": "^1.0.3" } }, + "crypto-js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz", + "integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg==" + }, "cyclist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", @@ -2661,9 +2666,9 @@ "dev": true }, "handlebars": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz", - "integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.3.tgz", + "integrity": "sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg==", "dev": true, "requires": { "neo-async": "^2.6.0", @@ -3388,9 +3393,9 @@ } }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, "lcid": { @@ -4707,9 +4712,9 @@ "dev": true }, "serialize-javascript": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", - "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", "dev": true }, "set-blocking": { @@ -5039,6 +5044,12 @@ "figgy-pudding": "^3.5.1" } }, + "stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha1-Gsig2Ug4SNFpXkGLbQMaPDzmjjs=", + "dev": true + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -5094,9 +5105,9 @@ } }, "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, "string-width": { @@ -5182,9 +5193,9 @@ "dev": true }, "terser": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.9.tgz", - "integrity": "sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA==", + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.6.tgz", + "integrity": "sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g==", "dev": true, "requires": { "commander": "^2.20.0", @@ -5201,16 +5212,16 @@ } }, "terser-webpack-plugin": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz", - "integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", "dev": true, "requires": { "cacache": "^12.0.2", "find-cache-dir": "^2.1.0", "is-wsl": "^1.1.0", "schema-utils": "^1.0.0", - "serialize-javascript": "^1.7.0", + "serialize-javascript": "^2.1.2", "source-map": "^0.6.1", "terser": "^4.1.2", "webpack-sources": "^1.4.0", @@ -5488,9 +5499,9 @@ "dev": true }, "uglify-js": { - "version": "3.6.7", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.7.tgz", - "integrity": "sha512-4sXQDzmdnoXiO+xvmTzQsfIiwrjUCSA95rSP4SEd8tDb51W2TiDOlL76Hl+Kw0Ie42PSItCW8/t6pBNCF2R48A==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.0.tgz", + "integrity": "sha512-ugNSTT8ierCsDHso2jkBHXYrU8Y5/fY2ZUprfrJUiD7YpuFvV4jODLFmb3h4btQjqr5Nh4TX4XtgDfCU1WdioQ==", "dev": true, "optional": true, "requires": { @@ -5857,6 +5868,15 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "why-is-node-running": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.1.2.tgz", + "integrity": "sha512-TwUeoRNMAWy8jAD8oFLtgmYKecZkH3yCtbQ17CYVCxd1WaPJAEB6oqkNgm0o+wIzxJi1oHUkOxMk0M/t5jCGeA==", + "dev": true, + "requires": { + "stackback": "0.0.2" + } + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", diff --git a/package.json b/package.json index df1c49f..bdc44a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rpclibrary", - "version": "1.9.2", + "version": "1.10.0", "description": "rpclibrary is a websocket on steroids!", "main": "./js/Index.js", "repository": { @@ -46,10 +46,12 @@ "typedoc-plugin-markdown": "^2.2.6", "typescript": "^3.5.3", "webpack": "^4.40.2", - "webpack-cli": "^3.3.9" + "webpack-cli": "^3.3.9", + "why-is-node-running": "^2.1.2" }, "dependencies": { "bsock": "^0.1.9", + "crypto-js": "^4.0.0", "http": "0.0.0", "uuid": "^3.3.3" }, diff --git a/src/Backend.ts b/src/Backend.ts index 6a68842..c146e4f 100644 --- a/src/Backend.ts +++ b/src/Backend.ts @@ -2,30 +2,26 @@ import http = require('http'); import bsock = require('bsock'); - -import * as T from './Types'; -import * as U from './Utils'; +import * as T from './Types'; +import * as U from './Utils'; import * as I from './Interfaces'; -import { Socket } from 'dgram'; -type Exporters = I.RPCExporter, keyof InterfaceT, SubResType>[] /** * A Websocket-server-on-steroids with built-in RPC capabilities */ export class RPCServer< - SubResType = {}, InterfaceT extends T.RPCInterface = T.RPCInterface, -> implements I.Destroyable{ - +> implements I.Destroyable { + private ws = http.createServer() private io = bsock.createServer() - private visibility:T.Visibility - private closeHandler:T.CloseHandler + private visibility: T.Visibility + private closeHandler: T.CloseHandler private errorHandler: T.ErrorHandler private connectionHandler: T.ConnectionHandler - private sesame? : T.SesameFunction - private accessFilter: T.AccessFilter + private sesame?: T.SesameFunction + private accessFilter: T.AccessFilter /** * @throws On RPC with no name @@ -34,71 +30,70 @@ export class RPCServer< * @param conf A {@link SocketConf} object with optional settings */ constructor( - private port:number, - private exporters: Exporters = [], - conf: T.ServerConf = {} - ){ - if(!conf.visibility) this.visibility = "0.0.0.0" - + private port: number, + private exporters: T.ExporterArray = [], + conf: T.ServerConf = {} + ) { + if (!conf.visibility) this.visibility = "0.0.0.0" - if(conf.sesame){ + if (conf.sesame) { this.sesame = U.makeSesameFunction(conf.sesame) } this.accessFilter = conf.accessFilter || (async (sesame) => { - if(!this.sesame) return true + if (!this.sesame) return true return this.sesame!(sesame!) }) - this.errorHandler = (socket:I.Socket) => (error:any, rpcName:string, args: any[]) => { - if(conf.errorHandler) conf.errorHandler(socket, error, rpcName, args) + this.errorHandler = (socket: I.Socket) => (error: any, rpcName: string, args: any[]) => { + if (conf.errorHandler) conf.errorHandler(socket, error, rpcName, args) else throw error } - - this.closeHandler = (socket:I.Socket) => { - if(conf.closeHandler) conf.closeHandler(socket) + + this.closeHandler = (socket: I.Socket) => { + if (conf.closeHandler) conf.closeHandler(socket) } - - this.connectionHandler = (socket:I.Socket) => { - if(conf.connectionHandler) conf.connectionHandler(socket) + + this.connectionHandler = (socket: I.Socket) => { + if (conf.connectionHandler) conf.connectionHandler(socket) } exporters.forEach(U.fixNames) //TSC for some reason doesn't preserve name properties of methods let badRPC = exporters.flatMap(ex => ex.exportRPCs()).find(rpc => !rpc.name) - if(badRPC){ + if (badRPC) { throw new Error(` RPC did not provide a name. \nUse 'funtion name(..){ .. }' syntax instead. \n \n<------------OFFENDING RPC: - \n`+badRPC.toString()+` + \n`+ badRPC.toString() + ` \n>------------OFFENDING RPC`) } this.startWebsocket() } - private startWebsocket(){ - try{ + private startWebsocket() { + try { this.io.attach(this.ws) - this.io.on('socket', (socket:I.Socket) => { + this.io.on('socket', (socket: I.Socket) => { socket.on('error', (err) => this.errorHandler(socket, err, "system", [])) socket.on('close', () => this.closeHandler(socket)) this.connectionHandler(socket) this.initRPCs(socket) }) - this.ws.listen(this.port, this.visibility) - }catch(e){ + this.ws = this.ws.listen(this.port, this.visibility) + } catch (e) { this.errorHandler(this.io, e, 'system', []) } } - protected initRPCs(socket:I.Socket){ - socket.hook('info', async (sesame? : string) => { + protected initRPCs(socket: I.Socket) { + socket.hook('info', async (sesame?: string) => { const rpcs = await Promise.all(this.exporters.map(async exp => { const allowed = await this.accessFilter(sesame, exp) - if(!allowed) return [] + if (!allowed) return [] return U.rpcHooker(socket, exp, this.errorHandler, this.sesame) })) return rpcs.flat() @@ -108,8 +103,7 @@ export class RPCServer< /** * Publishes a new list of Exporters. This destroys and restarts the socket * @param exporters the exporters to publish - */ - public setExporters(exporters: Exporters):any{ + public setExporters(exporters: T.ExporterArray): any { exporters.forEach(U.fixNames) this.destroy() this.ws = http.createServer() @@ -117,6 +111,8 @@ export class RPCServer< this.exporters = exporters this.startWebsocket() } + */ + destroy(): void { this.io.close() diff --git a/src/Frontend.ts b/src/Frontend.ts index 30f8dc8..49e7512 100644 --- a/src/Frontend.ts +++ b/src/Frontend.ts @@ -7,12 +7,12 @@ import * as I from './Interfaces'; import { stripAfterEquals, appendComma } from './Utils'; - /** * A websocket-on-steroids with built-in RPC capabilities */ -export class RPCSocket implements I.Socket{ - static async makeSocket(port:number, server: string, sesame?:string, conf?:T.SocketConf): Promise { +export class RPCSocket implements I.Socket{ + + static async makeSocket(port:number, server: string, sesame?:string, conf?:T.SocketConf): Promise> { const socket = new RPCSocket(port, server, conf) return await socket.connect(sesame) } @@ -128,7 +128,7 @@ export class RPCSocket implements I.Socket{ /** * Connects to the server and attaches available RPCs to this object */ - public async connect( sesame?: string ) : Promise{ + public async connect( sesame?: string ) : Promise> { this.socket = await bsock.connect(this.port, this.server, this.conf.tls?this.conf.tls:false) this.errorHandlers.forEach(h => this.socket.on('error', h)) this.closeHandlers.forEach(h => this.socket.on('close', h)) @@ -197,10 +197,19 @@ export class RPCSocket implements I.Socket{ return eval( ` async (${headerArgs} callback) => { const r = await this.call("${fnName}", ${sesame} ${argParams}) - if(r && r.result === 'Success'){ - this.socket.hook(r.uuid, callback) + try{ + if(r){ + if(r.uuid){ + callback['destroy'] = () => { this.socket.unhook(r.uuid) } + this.socket.hook(r.uuid, callback) + } + return r.return + }else{ + throw new Error("Empty response") + } + }catch(e){ + throw e } - return r }`) } } diff --git a/src/Interfaces.ts b/src/Interfaces.ts index 3c8428c..02fc7e5 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -4,13 +4,12 @@ import * as I from "./Interfaces" /** * Interface for all classes that export RPCs */ -export interface RPCExporter< +export type RPCExporter< Ifc extends T.RPCInterface = T.RPCInterface, - Name extends keyof Ifc = keyof Ifc, - SubresT = {} ->{ + Name extends keyof Ifc = string, +> = { name: Name - exportRPCs() : T.RPCInterfaceArray[Name] + exportRPCs() : T.RPCDefinitions[Name] } /** diff --git a/src/Types.ts b/src/Types.ts index f57213a..c549473 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -1,8 +1,9 @@ import * as I from "./Interfaces"; +import { RPCSocket } from "./Frontend"; export type AnyFunction = (...args:any) => any -export type HookFunction = (...args: Parameters) => Promise | ErrorResponse> -export type AccessFilter = (sesame:string|undefined, exporter: I.RPCExporter, keyof InterfaceT, SubresT>) => Promise +export type HookFunction = AnyFunction +export type AccessFilter = (sesame:string|undefined, exporter: I.RPCExporter) => Promise export type Visibility = "127.0.0.1" | "0.0.0.0" export type ConnectionHandler = (socket:I.Socket) => void export type ErrorHandler = (socket:I.Socket, error:any, rpcName: string, args: any[]) => void @@ -17,8 +18,12 @@ export type FrontEndHandlerType = { 'close' : () => void } -export type ServerConf = { - accessFilter?: AccessFilter +export type ExporterArray = I.RPCExporter, keyof InterfaceT>[] + +export type ConnectedSocket = RPCSocket & T + +export type ServerConf = { + accessFilter?: AccessFilter connectionHandler?: ConnectionHandler errorHandler?: ErrorHandler closeHandler?: CloseHandler @@ -36,23 +41,23 @@ 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 } -export type SubscriptionResponse = Respose & { result: "Success"; uuid: string } export type RPCType = 'Hook' | 'Unhook' | 'Call' -export type CallRPC = { - name: N - call: F +export type CallRPC = { + name: Name + call: Func } -export type HookRPC = { - name: N - hook: HookFunction + +export type HookRPC = { + name: Name + hook: Func onCallback?: AnyFunction - onClose?: HookCloseFunction + onClose?: HookCloseFunction extends Promise ? T : ReturnType> } -export type RPC = HookRPC | CallRPC | F +export type RPC = HookRPC | CallRPC | Func export type RPCInterface = { [grp in string] : { @@ -60,10 +65,13 @@ export type RPCInterface = { } } & Impl -export type RPCInterfaceArray = { - [grp in keyof Itfc]: Array< - { [rpc in keyof Itfc[grp]]: RPC }[keyof Itfc[grp]] - > +export type exportT = { + [group in string]: {} +} + +//This probably has lots of issues +export type RPCDefinitions = { + [grp in keyof Ifc]:( { [rpc in keyof Ifc[grp]]: RPC }[keyof Ifc[grp]] )[] } export type BaseInfo = { @@ -86,4 +94,4 @@ export type RpcInfo = HookInfo | CallInfo export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } export type OnFunction = (type: T, f: FrontEndHandlerType[T]) => void -export type HookCloseFunction = (res:SubscriptionResponse, rpc:HookRPC) => any +export type HookCloseFunction = (res: T, rpc:HookRPC) => any diff --git a/src/Utils.ts b/src/Utils.ts index 1631406..f814044 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -2,7 +2,6 @@ import * as uuidv4 from "uuid/v4" import * as T from "./Types"; import * as I from "./Interfaces"; -import { SubscriptionResponse } from "./Types"; /** * Translate an RPC to RPCInfo for serialization. @@ -12,7 +11,7 @@ import { SubscriptionResponse } from "./Types"; * @param sesame optional sesame phrase to prepend before all RPC arguments * @throws Error on RPC without name property */ -export const rpcToRpcinfo = (socket: I.Socket, rpc : T.RPC, owner: string, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction):T.RpcInfo => { +export const rpcToRpcinfo = (socket: I.Socket, rpc : T.RPC, owner: string, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction):T.RpcInfo => { switch (typeof rpc){ case "object": if(rpc['call']){ @@ -24,7 +23,7 @@ export const rpcToRpcinfo = (socket: I.Socket, rpc : T.RPC {if(sesame(_sesame)) return await rpc['call'].apply({}, args); socket.destroy()}:rpc['call'], // check & remove sesame } }else{ - const generator = hookGenerator(>rpc, errorHandler, sesame) + const generator = hookGenerator(>rpc, errorHandler, sesame) return { owner: owner, argNames: extractArgs(generator(undefined)), @@ -58,10 +57,9 @@ RPC did not provide a name. * @param exporter The exporter * @param makeUnique @default true Attach a suffix to RPC names */ -export function rpcHooker(socket: I.Socket, exporter:I.RPCExporter, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction, makeUnique = true):T.ExtendedRpcInfo[]{ +export function rpcHooker(socket: I.Socket, exporter:I.RPCExporter, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction, makeUnique = true):T.ExtendedRpcInfo[]{ const owner = exporter.name - const RPCs = [...exporter.exportRPCs()] - + const RPCs = exporter.exportRPCs() return RPCs.map(rpc => rpcToRpcinfo(socket, rpc, owner, errorHandler, sesame)) .map(info => { @@ -107,7 +105,7 @@ export function stripAfterEquals(str:string):string{ * @param rpc The RPC to transform * @returns A {@link HookFunction} */ -const hookGenerator = (rpc:T.HookRPC, errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction): T.HookInfo['generator'] => { +const hookGenerator = (rpc:T.HookRPC, /*not unused!*/ errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction): T.HookInfo['generator'] => { let argsArr = extractArgs(rpc.hook) argsArr.pop() //remove 'callback' from the end let callArgs = argsArr.join(',') @@ -119,16 +117,23 @@ const hookGenerator = (rpc:T.HookRPC, errorHandler: T.ErrorHandle //note rpc.hook is the associated RPC, not a socket.hook return eval(` - (socket) => async (${args}) => { + (clientSocket) => async (${args}) => { try{ if(sesameFn && !sesameFn(sesame)) return + const uuid = uuidv4() const res = await rpc.hook(${callArgs} (...cbargs) => { - if(rpc.onCallback) rpc.onCallback.apply({}, cbargs) - socket.call.apply(socket, [res.uuid, ...cbargs]) + if(rpc.onCallback){ + rpc.onCallback.apply({}, cbargs) + } + clientSocket.call.apply(clientSocket, [uuid, ...cbargs]) }) - return res + if(rpc.onClose){ + clientSocket.on('close', () => rpc.onClose(res, rpc)) + } + return {'uuid': uuid, 'return': res} }catch(e){ - errorHandler(socket)(e, ${rpc.name}, [${args}]) + //can throw to pass exception to client or swallow to keep it local + errorHandler(clientSocket)(e, ${rpc.name}, [${args}]) } }`) } @@ -147,18 +152,6 @@ const extractArgs = (f:Function):string[] => { return fn!==""?fn.split(',') : [] } -/** - * Simple utility function to create basic {@link SubscriptionResponse} - * @param uuid optional uuid to use, otherwise defaults to uuid/v4 - */ -export function makeSubResponse(extension:T):SubscriptionResponse & T{ - return { - result: "Success", - uuid: uuidv4(), - ...extension - } -} - export function makeSesameFunction (sesame : T.SesameFunction | string) : T.SesameFunction { if(typeof sesame === 'function'){ diff --git a/test/Test.ts b/test/Test.ts index 0a201fd..2ead2de 100644 --- a/test/Test.ts +++ b/test/Test.ts @@ -1,45 +1,44 @@ -import { describe, it, Func } from "mocha"; - -import { RPCServer, RPCSocket, SubscriptionResponse, makeSubResponse } from '../Index' -import * as uuidv4 from "uuid/v4" -import { doesNotReject } from "assert"; -import { Socket } from "dgram"; +import { describe, it } from "mocha"; +import { RPCServer, RPCSocket } from '../Index' import { RPCExporter } from "../src/Interfaces"; +import { ConnectedSocket } from "../src/Types"; +import * as log from 'why-is-node-running'; -const add = (...args:number[]) => {return args.reduce((a,b)=>a+b, 0)} -function makeServer(){ +const add = (...args: number[]) => { return args.reduce((a, b) => a + b, 0) } +function makeServer() { let subcallback - return new RPCServer<{ topic: string }>(21010, [{ - name: "test", + return new RPCServer(21010, [{ + name: 'test', exportRPCs: () => [ { name: 'echo', - call: async (s:string) => s, - },{ + call: async (s: string) => s, + }, { name: 'simpleSubscribe', - hook: async(callback) => { - subcallback = callback - return makeSubResponse<{topic: string}>({topic: "test"}) - } - },{ + hook: async (callback) => { + subcallback = callback + return { topic: "test" } + }, + onClose: (res) => { } + }, { name: 'subscribe', hook: async (callback) => { subcallback = callback - return makeSubResponse<{topic: string}>({topic: "test"}) + return { topic: "test" } }, - onClose: (res, rpc) => { - console.log("onClose", rpc.name === 'subscribe' && res?"OK":"") - subcallback = null + onClose: (res, rpc) => { + console.log("onClose", rpc.name === 'subscribe' && res ? "OK" : "") + subcallback = null }, - onCallback: (...args:any) => { - console.log("onCallback", args[0] === "test" && args[1] === "callback"?"OK":"") + onCallback: (...args: any) => { + console.log("onCallback", args[0] === "test" && args[1] === "callback" ? "OK" : "") } }, add, - function triggerCallback(...messages:any[]):number {return subcallback.apply({}, messages)}, + function triggerCallback(...messages: any[]): number { return subcallback.apply({}, messages) }, ] - }],{ - connectionHandler: (socket) => { }, + }], { + connectionHandler: (socket) => { }, closeHandler: (socket) => { }, errorHandler: (socket, err) => { throw err } }) @@ -47,57 +46,58 @@ function makeServer(){ describe('RPCServer', () => { - let server: RPCServer<{ topic: string }, any> + let client, server + const echo = (x) => x - before(() => { - server = makeServer() - }) - - after(() => { - server.destroy() - }) - - it('should be able to use all kinds of RPC definitions', (done) => { - - const echo = (x) => x - - const server = new RPCServer(21003, [{ + before(done => { + server = new RPCServer(21003, [{ name: 'HelloWorldRPCGroup', - exportRPCs: () => [ + exportRPCs: () => [ echo, //named function variable - function echof(x){ return x }, //named function + function echof(x) { return x }, //named function { name: 'echoExplicit', //describing object - call: async (x,y,z) => [x,y,z] + call: async (x, y, z) => [x, y, z] } ] }]) - const client = new RPCSocket(21003, 'localhost') + client = new RPCSocket(21003, 'localhost') + done() + }) + + after(done => { + client.destroy() + server.destroy() + done() + }) + + it('should be able to use all kinds of RPC definitions', (done) => { client.connect().then(async () => { const r0 = await client['HelloWorldRPCGroup'].echo('Hello') const r1 = await client['HelloWorldRPCGroup'].echof('World') - const r2 = await client['HelloWorldRPCGroup'].echoExplicit('R','P','C!') + const r2 = await client['HelloWorldRPCGroup'].echoExplicit('R', 'P', 'C!') - if(r0 === 'Hello' && r1 === 'World' && r2.join('') ==='RPC!'){ - client.destroy() - server.destroy() + + if (r0 === 'Hello' && r1 === 'World' && r2.join('') === 'RPC!') { done() + }else{ + done(new Error("Bad response")) } }) }) it('new RPCServer() should fail on bad RPC', (done) => { - try{ + try { new RPCServer(20001, [{ - name: "bad", - exportRPCs: () => [ - (aaa,bbb,ccc) => { return aaa+bbb+ccc } + name: 'bad', + exportRPCs: () => [ + (aaa, bbb, ccc) => { return aaa + bbb + ccc } ] }]) done(new Error("Didn't fail with bad RPC")) - }catch(badRPCError){ + } catch (badRPCError) { done() } }) @@ -106,9 +106,9 @@ describe('RPCServer', () => { describe('RPCSocket', () => { let client: RPCSocket - let server: RPCServer<{topic: string}> + let server: RPCServer - before(async() => { + before(async () => { server = makeServer() client = new RPCSocket(21010, "localhost") return await client.connect() @@ -122,27 +122,27 @@ describe('RPCSocket', () => { it('should have rpc echo', (done) => { client['test'].echo("x").then(x => { - if(x === 'x') + if (x === 'x') done() else - done(new Error('echo RPC response did not match')) + done(new Error('echo RPC response did not match')) }) }) it('should add up to 6', (done) => { - client['test'].add(1,2,3).then(x => { - if(x === 6) + client['test'].add(1, 2, 3).then(x => { + if (x === 6) done() else - done(new Error('add RPC response did not match')) + done(new Error('add RPC response did not match')) }) }) it('should subscribe with success', (done) => { client['test'].simpleSubscribe(console.log).then(res => { - if(res.result === 'Success'){ + if (res.topic === 'test') { done() - }else{ + } else { console.error(res) done(new Error('Subscribe did not return success')) } @@ -151,72 +151,66 @@ describe('RPCSocket', () => { it('subscribe should call back', (done) => { client['test'].subscribe((...args: any) => { - if(args[0] === "test" && args[1] === "callback") + if (args[0] === "test" && args[1] === "callback") done() - else - done(new Error("Bad callback value "+ args)) - }).then( async () => { + else + done(new Error("Bad callback value " + args)) + }).then(async () => { await client['test'].triggerCallback("test", "callback") }) }) it('simpleSubscribe should call back', (done) => { client['test'].simpleSubscribe((...args: any) => { - if(args[0] === "test_" && args[1] === "callback_") + if (args[0] === "test_" && args[1] === "callback_") done() - else - done(new Error("Bad callback value "+ args)) - }).then( async () => { + else + done(new Error("Bad callback value " + args)) + }).then(async () => { await client['test'].triggerCallback("test_", "callback_") }) }) }) describe('It should do unhook', () => { - let candy = "OK" + const yesCandy = "OK" + const noCandy = "stolen" + let candy = yesCandy let cb: Function let cb2: Function let client: RPCSocket - let server: RPCServer<{topic: string}> + let server: RPCServer - before(async() => { - server = new RPCServer<{ topic: string }>(21010, [{ + before(async () => { + server = new RPCServer(21010, [{ name: "test", exportRPCs: () => [{ name: 'subscribe', - hook: async(callback):Promise> => { - cb = callback - return { - result: "Success", - uuid: uuidv4(), - topic: "test" - } + hook: async (callback): Promise => { + cb = callback + return } - }, + }, { name: 'subscribeWithParam', - hook: async(param, callback):Promise> => { - - if(param != "OK"){ - console.log("param was"+ param); + hook: async (param, callback): Promise<{ uuid: string }> => { + + if (param != "OK") { + console.log("param was" + param); return { - result: "Success", uuid: "no", - topic: "test" } } - cb2 = callback + cb2 = callback return { - result: "Success", uuid: "OK", - topic: "test" } } }, - function checkCandy():string { cb(candy); return candy }, - function stealCandy():string { candy = "_OK"; cb(candy); cb = () => {}; return candy } - ] - }],{ + function publish(): string { cb(candy); return candy }, + function unsubscribe(): string { candy = noCandy; cb(candy); cb = () => { }; return candy } + ] + }], { connectionHandler: (socket) => { }, closeHandler: (socket) => { }, errorHandler: (socket, err) => { throw err } @@ -231,69 +225,90 @@ describe('It should do unhook', () => { }) it('Subscribe with param', (done) => { - client['test'].subscribeWithParam("OK", c => {}).then( async (res: SubscriptionResponse) => { - if(res.uuid === "OK"){ + client['test'].subscribeWithParam("OK", c => { }).then(async (res) => { + if (res.uuid === candy) { done() - }else - done(new Error("Results did not match "+res.uuid)) + } else + done(new Error("Results did not match " + res.uuid)) }) }) - it('Unhook+unsubscribe should stop callbacks', (done) => { - client['test'].subscribe(c => {}).then( async (res: SubscriptionResponse) => { - const r1 = await client['test'].checkCandy() - const r3 = await client['test'].stealCandy() - client.unhook(res.uuid) - const r2 = await client['test'].checkCandy() - const r4 = await client['test'].checkCandy() + let run = 0 + const expected = [yesCandy, noCandy, noCandy, noCandy] - if(r1 === "OK" && r3 === "_OK" && r2 === "_OK" && r4 === "_OK") + it('Unhook+unsubscribe should stop callbacks', (done) => { + + client['test'].subscribe(function myCallback(c){ + if(run == 1) + (myCallback as any).destroy() + + if (c !== expected[run++]) { + done(new Error(`Wrong candy '${c}' in iteration '${run - 1}'`)) + } + }).then(async function(res){ + const r1 = await client['test'].publish() + const r3 = await client['test'].unsubscribe() + const r2 = await client['test'].publish() + const r4 = await client['test'].publish() + + if (r1 === yesCandy && r3 === noCandy && r2 === noCandy && r4 === noCandy) done() else - done(new Error("Results did not match: "+[r1,r2,r3,r4])) + done(new Error("Results did not match: " + [r1, r2, r3, r4])) }) }) }) +type topicDTO = { topic: string; } -type SesameTestIfc = { - test: { - checkCandy: ()=>Promise - subscribe: (callback) => Promise> - } +type SesameTestIfc = { + test: { + checkCandy: () => Promise + subscribe: (callback: Function) => Promise + manyParams: (a:A, b:B, c:C, d:D) => Promise<[A, B, C, D]> + } + + other: { + echo: (x:any) => Promise + } } describe('Sesame should unlock the socket', () => { let candy = "OK" - let client: RPCSocket & SesameTestIfc - let server: RPCServer - let cb = (...args) => {} + let client: ConnectedSocket + let server: RPCServer + let cb: Function = (...args) => { } before((done) => { - server = new RPCServer(21004, [{ + server = new RPCServer(21004, [{ name: "test", exportRPCs: () => [ { name: 'subscribe', - hook: async(callback) => { + hook: async (callback) => { cb = callback - return { - result: "Success", - uuid: uuidv4(), + return { topic: 'test' } - } + }, + onClose: (a) => { } }, - async function checkCandy():Promise { cb(candy); cb=()=>{}; return candy }, - async function manyParams(a,b,c,d) {return [a,b,c,d]} - ]} - ],{ - sesame: (_sesame) => _sesame === 'sesame!' + async function checkCandy() { cb(candy); cb = () => { }; return candy }, + async function manyParams(a, b, c, d) { return [a, b, c, d] } + ], + },{ + name: 'other', + exportRPCs: () => [ + async function echo(x){return x} + ] + + }], { + sesame: (_sesame) => _sesame === 'sesame!' }) - const sock = new RPCSocket(21004, "localhost") - sock.connect('sesame!').then(cli => { + const sock = new RPCSocket(21004, "localhost") + sock.connect('sesame!').then(cli => { client = cli - done() + done() }) }) @@ -307,8 +322,8 @@ describe('Sesame should unlock the socket', () => { }) it('should work with multiple params', (done) => { - client.test['manyParams']('a','b','c','d').then(c => { - if(c[0] == 'a' && c[1] === 'b' && c[2] === 'c' && c[3] === 'd') + client.test['manyParams']('a', 'b', 'c', 'd').then(c => { + if (c[0] == 'a' && c[1] === 'b' && c[2] === 'c' && c[3] === 'd') done() }) }) @@ -316,9 +331,9 @@ describe('Sesame should unlock the socket', () => { it('should not work without sesame', (done) => { const sock = new RPCSocket(21004, "localhost") sock.connect( /* no sesame */).then(async (cli) => { - if(!cli.test) + if (!cli.test) done() - else{ + else { done(new Error("Function supposed to be removed without sesame")) } cli.destroy() @@ -329,9 +344,9 @@ describe('Sesame should unlock the socket', () => { it('should fail with wrong sesame', (done) => { const sock = new RPCSocket(21004, "localhost") sock.connect('abasd').then(async (cli) => { - if(!cli.test) + if (!cli.test) done() - else{ + else { done(new Error("Function supposed to be removed without sesame")) } cli.destroy() @@ -341,12 +356,12 @@ describe('Sesame should unlock the socket', () => { it('callback should work with sesame', (done) => { client.test.subscribe((c) => { - if(c === candy){ + if (c === candy) { done() } }).then(d => { - if(d.result !== 'Success') - done('unexpected valid response') + if (d.topic !== 'test') + done('unexpected invalid response') client.test.checkCandy() }) @@ -354,51 +369,56 @@ describe('Sesame should unlock the socket', () => { }) -describe('Error handling', ()=>{ - - let createUser = async( user: {a:any,b:any}) => { - throw new Error("BAD BAD BAD") +describe('Error handling', () => { + const errtxt = "BAD BAD BAD" + + let createUser = async (user: { a: any, b: any }) => { + throw new Error(errtxt) } - it("RPC throws on client without handler", (done)=>{ - let server = new RPCServer(21004, [ { - name: 'createUser' as 'createUser', + it("RPC throws on client without handler", (done) => { + let server = new RPCServer(21004, [{ + name: "createUser", exportRPCs: () => [{ name: 'createUser' as 'createUser', call: createUser - }]}], { + }] + }], { }) let sock = new RPCSocket(21004, 'localhost') sock.connect().then((cli) => { cli["createUser"]["createUser"]({ - a:'a', - b:'b' - }) - .then(r => { - if(r != null) - done("UNEXPECTED RESULT " + r) - }) - .catch((e) => { - //console.log("EXPECTED CLIENT EXCEPTION", String(e)); - done() - }) - .finally(() => { - cli.destroy() - sock.destroy() - server.destroy() + a: 'a', + b: 'b' }) + .then(r => { + if (r != null) + done(new Error("UNEXPECTED RESULT " + r)) + }) + .catch((e) => { + if (e.message === errtxt) + done() + else + done(e) + }) + .finally(() => { + cli.destroy() + sock.destroy() + server.destroy() + }) }) }) - it("RPC throws on server with handler", (done)=>{ - let server = new RPCServer(21004, [ { - name: 'createUser' as 'createUser', + it("RPC throws on server with handler", (done) => { + let server = new RPCServer(21004, [{ + name: "createUser", exportRPCs: () => [{ name: 'createUser' as 'createUser', call: createUser - }]}], { + }] + }], { errorHandler: (socket, e, rpcName, args) => { done() } @@ -407,41 +427,44 @@ describe('Error handling', ()=>{ let sock = new RPCSocket(21004, 'localhost') sock.connect().then((cli) => { cli["createUser"]["createUser"]({ - a:'a', - b:'b' - }) - .then(r => { - if(r != null) - done("UNEXPECTED RESULT " + r) - }) - .catch((e) => { - done("UNEXPECTED CLIENT ERROR " + e) - done(e) - }) - .finally(() => { - cli.destroy() - sock.destroy() - server.destroy() + a: 'a', + b: 'b' }) + .then(r => { + if (r != null) + done("UNEXPECTED RESULT " + r) + }) + .catch((e) => { + done("UNEXPECTED CLIENT ERROR " + e) + done(e) + }) + .finally(() => { + cli.destroy() + sock.destroy() + server.destroy() + }) }) }) }) -describe("Errorhandler functionality", ()=>{ - let createUser = async( user: {a:any,b:any}) => { - throw new Error("BAD BAD BAD") +describe("Errorhandler functionality", () => { + const errtxt = "BAD BAD BAD" + + let createUser = async (user: { a: any, b: any }) => { + throw new Error(errtxt) } - it("correct values are passed to the handler", (done)=>{ - let server = new RPCServer(21004, [ { - name: 'createUser' as 'createUser', + it("correct values are passed to the handler", (done) => { + let server = new RPCServer(21004, [{ + name: "createUser", exportRPCs: () => [{ name: 'createUser' as 'createUser', call: createUser - }]}], { + }] + }], { errorHandler: (socket, e, rpcName, args) => { - if(e.message === "BAD BAD BAD" && rpcName === "createUser" && args[0]['a'] === 'a' && args[0]['b'] === 'b') + if (e.message === errtxt && rpcName === "createUser" && args[0]['a'] === 'a' && args[0]['b'] === 'b') done() } }) @@ -449,97 +472,97 @@ describe("Errorhandler functionality", ()=>{ let sock = new RPCSocket(21004, 'localhost') sock.connect().then((cli) => { cli["createUser"]["createUser"]({ - a:'a', - b:'b' - }) - .then(r => { - if(r != null) - done("UNEXPECTED RESULT " + r) - }) - .catch((e) => { - done("UNEXPECTED CLIENT ERROR " + e) - done(e) - }) - .finally(() => { - cli.destroy() - sock.destroy() - server.destroy() + a: 'a', + b: 'b' }) + .then(r => { + if (r != null) + done("UNEXPECTED RESULT " + r) + }) + .catch((e) => { + done(new Error("UNEXPECTED CLIENT ERROR " + e.message)) + }) + .finally(() => { + cli.destroy() + sock.destroy() + server.destroy() + }) }) }) - it("handler sees sesame", (done)=>{ + it("handler sees sesame", (done) => { let sesame = "AAAAAAAAAAAAAAA" - let server = new RPCServer(21004, [ { - name: 'createUser' as 'createUser', + let server = new RPCServer(21004, [{ + name: "createUser" as "createUser", exportRPCs: () => [{ name: 'createUser' as 'createUser', call: createUser - }]}], { + }] + }], { sesame: sesame, errorHandler: (socket, e, rpcName, args) => { - if(e.message === "BAD BAD BAD" && rpcName === "createUser" && args[0] === sesame && args[1]['a'] === 'a' && args[1]['b'] === 'b') + if (e.message === errtxt && rpcName === "createUser" && args[0] === sesame && args[1]['a'] === 'a' && args[1]['b'] === 'b') done() } - + }) let sock = new RPCSocket(21004, 'localhost') sock.connect(sesame).then((cli) => { cli["createUser"]["createUser"]({ - a:'a', - b:'b' - }) - .then(r => { - if(r != null) - done("UNEXPECTED RESULT " + r) - }) - .catch((e) => { - done("UNEXPECTED CLIENT ERROR " + e) - done(e) - }) - .finally(() => { - cli.destroy() - sock.destroy() - server.destroy() + a: 'a', + b: 'b' }) + .then(r => { + if (r != null) + done("UNEXPECTED RESULT " + r) + }) + .catch((e) => { + done("UNEXPECTED CLIENT ERROR " + e) + done(e) + }) + .finally(() => { + cli.destroy() + sock.destroy() + server.destroy() + }) }) }) }) type myExporterIfc = { MyExporter: { - myRPC: ()=>Promise + myRPC: () => Promise } } -describe("Class binding", ()=>{ +describe("Class binding", () => { - let exporter1 : MyExporter - let serv : RPCServer<{}, myExporterIfc> + let exporter1: MyExporter + let serv: RPCServer let sock: RPCSocket & myExporterIfc let allowed = true class MyExporter implements RPCExporter{ - name = "MyExporter" as "MyExporter"; + name = "MyExporter" as "MyExporter" exportRPCs = () => [ this.myRPC ] myRPC = async () => { - serv.setExporters([new MyOtherExporter]) + //serv.setExporters([new MyOtherExporter]) return "Hello World" } } class MyOtherExporter implements RPCExporter{ - name = "MyExporter" as "MyExporter"; + name = "MyExporter" as "MyExporter" exportRPCs = () => [ this.myRPC ] - myRPC = async () => { + myRPC = async () => { return "Hello Borld" } @@ -547,24 +570,22 @@ describe("Class binding", ()=>{ before(done => { exporter1 = new MyExporter() - serv = new RPCServer<{}, myExporterIfc>(21004, [exporter1], { - accessFilter: async (sesame,exporter) => { - switch(exporter.name){ - case "MyExporter": - if(!allowed) return false - allowed = false - return sesame==='xxx'; - default: - return false + serv = new RPCServer(21004, [exporter1], { + accessFilter: async (sesame, exporter) => { + if(exporter.name === 'MyExporter'){ + if (!allowed) return false + allowed = false + return sesame === 'xxx'; + }else{ + return false } }, sesame: "xxx" }) done() }) - - beforeEach((done)=>{ - + + beforeEach((done) => { const s = new RPCSocket(21004, 'localhost') s.connect("xxx").then(conn => { sock = conn @@ -572,7 +593,7 @@ describe("Class binding", ()=>{ }) }) - afterEach(done => { + afterEach((done) => { sock.destroy() done() }) @@ -581,7 +602,11 @@ describe("Class binding", ()=>{ serv.destroy() }) - it("binds correctly", (done)=>{ + /* The server-side socket will enter a 30s timeout if destroyed by a RPC. + to mitigate the impact on testing time these are not run. + + it("binds correctly", function(done){ + this.timeout(1000) sock['MyExporter'].myRPC().then((res) => { done(new Error(res)) }).catch(e => { @@ -592,31 +617,42 @@ describe("Class binding", ()=>{ }) it("changes exporters", (done) => { + sock['MyExporter'].myRPC().then((res) => { - if(res === "Hello Borld") + if (res === "Hello Borld") done() else done(new Error(res)) }) }) + */ + + + it("use sesameFilter for available", (done) => { + if (sock['MyExporter']){ + allowed = false + done() + } + else done(new Error("RPC supposed to be here")) + }) it("use sesameFilter", (done) => { - if(!sock['MyExporter']) done() + if (!sock['MyExporter']) done() else done(new Error("RPC supposed to be gone")) }) }) -describe("attaching handlers before connecting", ()=>{ - it("fires error if server is unreachable", (done)=>{ +describe("attaching handlers before connecting", () => { + it("fires error if server is unreachable", (done) => { const sock = new RPCSocket(21004, 'localhost') let errorHandleCount = 0 sock.on('error', (err) => { //attached listener fires first - if(errorHandleCount != 0){ + if (errorHandleCount != 0) { console.log("Error handler didn't fire first"); - }else{ + } else { errorHandleCount++ } }) @@ -625,16 +661,16 @@ describe("attaching handlers before connecting", ()=>{ console.log("Unexpected successful connect") }).catch(e => { //catch clause fires second - if(errorHandleCount != 1){ + if (errorHandleCount != 1) { console.log("catch clause didn't fire second"); - }else{ + } else { sock.destroy() done() } }) }) - it("fires error if call is unknown", (done)=>{ + it("fires error if call is unknown", (done) => { const serv = new RPCServer(21004) const sock = new RPCSocket(21004, 'localhost') @@ -645,14 +681,14 @@ describe("attaching handlers before connecting", ()=>{ }) sock.connect().then(_ => { - sock.call("unknownRPC123", "AAAAA").catch(e => { /* ignore */}) + sock.call("unknownRPC123", "AAAAA").catch(e => { /* ignore */ }) }).catch(e => { console.log("unexpected connect catch clause"); done(e) }) }) - it("demands catch on method invocation if call is unknown", (done)=>{ + it("demands catch on method invocation if call is unknown", (done) => { const serv = new RPCServer(21004) const sock = new RPCSocket(21004, 'localhost') @@ -667,4 +703,11 @@ describe("attaching handlers before connecting", ()=>{ done(e) }) }) + +}) + +describe('finally', () => { + it('print open handles (Ignore `DNSCHANNEL` and `Immediate`)', () => { + log() + }) }) \ No newline at end of file diff --git a/test/devtest.ts b/test/devtest.ts deleted file mode 100644 index 0a0b371..0000000 --- a/test/devtest.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { RPCServer } from "../src/Backend"; -import { SubscriptionResponse, RPCInterface } from "../src/Types"; -import { RPCSocket } from "../src/Frontend"; -import { makeSubResponse } from "../src/Utils"; - -type SubresExtension = {a:string} - -type MyInterface = { - Group1: { - triggerCallbacks: (...args:any[]) => Promise, - subscribe: (param:string, callback:Function) => Promise>, - unsubscribe: (uuid:string) => Promise - }, - Group2: { - echo: (x:string) => Promise - } -} - -new RPCServer(20000, - [{ - name: 'Group1', - exportRPCs: () => [{ - name: 'triggerCallbacks', - 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) => "..." - }] - }] -) - -RPCSocket.makeSocket(20000, 'localhost').then((async (client) => { - console.log(client) - const res = await client.Group1.subscribe('test', async (...args:any) => { - console.log.apply(console, args) - - /* close the callbacks once you're done */ - await client.Group1.unsubscribe(res.uuid) - client.unhook(res.uuid) - }) - - await client.Group1.triggerCallbacks("Hello", "World", "Callbacks") -})) - -const srv = new RPCServer(30000, [{ - name: 'Group2', - exportRPCs: () => [{ - name: 'echo', - call: async (x) => x - }] -}], { - sesame: 'open' -}) - -const s = new RPCSocket(30000, 'localhost') -s.connect("open").then(async() => { - s['Group2']['echo']('open', 'dfgfg').then(console.log) - s['Group2']['echo']('dfgfg').then(console.log) - - s['Group2']['echo']('dfgfg').then(console.log) - -}) -