clean up type system, remove subres

This commit is contained in:
2020-03-17 23:35:47 +01:00
parent bb94b1c405
commit 7d580c4c23
9 changed files with 459 additions and 462 deletions
+55 -35
View File
@@ -1,6 +1,6 @@
{ {
"name": "rpclibrary", "name": "rpclibrary",
"version": "1.6.1", "version": "1.9.2",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -467,9 +467,9 @@
"dev": true "dev": true
}, },
"acorn": { "acorn": {
"version": "6.3.0", "version": "6.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
"integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
"dev": true "dev": true
}, },
"aggregate-error": { "aggregate-error": {
@@ -753,9 +753,9 @@
"dev": true "dev": true
}, },
"bluebird": { "bluebird": {
"version": "3.7.1", "version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"dev": true "dev": true
}, },
"bn.js": { "bn.js": {
@@ -952,9 +952,9 @@
}, },
"dependencies": { "dependencies": {
"glob": { "glob": {
"version": "7.1.5", "version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dev": true, "dev": true,
"requires": { "requires": {
"fs.realpath": "^1.0.0", "fs.realpath": "^1.0.0",
@@ -1062,9 +1062,9 @@
} }
}, },
"chownr": { "chownr": {
"version": "1.1.3", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
"dev": true "dev": true
}, },
"chrome-trace-event": { "chrome-trace-event": {
@@ -1331,6 +1331,11 @@
"randomfill": "^1.0.3" "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": { "cyclist": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
@@ -2661,9 +2666,9 @@
"dev": true "dev": true
}, },
"handlebars": { "handlebars": {
"version": "4.5.1", "version": "4.7.3",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.3.tgz",
"integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==", "integrity": "sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg==",
"dev": true, "dev": true,
"requires": { "requires": {
"neo-async": "^2.6.0", "neo-async": "^2.6.0",
@@ -3388,9 +3393,9 @@
} }
}, },
"kind-of": { "kind-of": {
"version": "6.0.2", "version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"dev": true "dev": true
}, },
"lcid": { "lcid": {
@@ -4707,9 +4712,9 @@
"dev": true "dev": true
}, },
"serialize-javascript": { "serialize-javascript": {
"version": "1.9.1", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
"integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
"dev": true "dev": true
}, },
"set-blocking": { "set-blocking": {
@@ -5039,6 +5044,12 @@
"figgy-pudding": "^3.5.1" "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": { "static-extend": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
@@ -5094,9 +5105,9 @@
} }
}, },
"stream-shift": { "stream-shift": {
"version": "1.0.0", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
"dev": true "dev": true
}, },
"string-width": { "string-width": {
@@ -5182,9 +5193,9 @@
"dev": true "dev": true
}, },
"terser": { "terser": {
"version": "4.3.9", "version": "4.6.6",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.3.9.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.6.tgz",
"integrity": "sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA==", "integrity": "sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g==",
"dev": true, "dev": true,
"requires": { "requires": {
"commander": "^2.20.0", "commander": "^2.20.0",
@@ -5201,16 +5212,16 @@
} }
}, },
"terser-webpack-plugin": { "terser-webpack-plugin": {
"version": "1.4.1", "version": "1.4.3",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
"integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==", "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
"dev": true, "dev": true,
"requires": { "requires": {
"cacache": "^12.0.2", "cacache": "^12.0.2",
"find-cache-dir": "^2.1.0", "find-cache-dir": "^2.1.0",
"is-wsl": "^1.1.0", "is-wsl": "^1.1.0",
"schema-utils": "^1.0.0", "schema-utils": "^1.0.0",
"serialize-javascript": "^1.7.0", "serialize-javascript": "^2.1.2",
"source-map": "^0.6.1", "source-map": "^0.6.1",
"terser": "^4.1.2", "terser": "^4.1.2",
"webpack-sources": "^1.4.0", "webpack-sources": "^1.4.0",
@@ -5488,9 +5499,9 @@
"dev": true "dev": true
}, },
"uglify-js": { "uglify-js": {
"version": "3.6.7", "version": "3.8.0",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.7.tgz", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.0.tgz",
"integrity": "sha512-4sXQDzmdnoXiO+xvmTzQsfIiwrjUCSA95rSP4SEd8tDb51W2TiDOlL76Hl+Kw0Ie42PSItCW8/t6pBNCF2R48A==", "integrity": "sha512-ugNSTT8ierCsDHso2jkBHXYrU8Y5/fY2ZUprfrJUiD7YpuFvV4jODLFmb3h4btQjqr5Nh4TX4XtgDfCU1WdioQ==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@@ -5857,6 +5868,15 @@
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
"dev": true "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": { "wide-align": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+4 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "rpclibrary", "name": "rpclibrary",
"version": "1.9.2", "version": "1.10.0",
"description": "rpclibrary is a websocket on steroids!", "description": "rpclibrary is a websocket on steroids!",
"main": "./js/Index.js", "main": "./js/Index.js",
"repository": { "repository": {
@@ -46,10 +46,12 @@
"typedoc-plugin-markdown": "^2.2.6", "typedoc-plugin-markdown": "^2.2.6",
"typescript": "^3.5.3", "typescript": "^3.5.3",
"webpack": "^4.40.2", "webpack": "^4.40.2",
"webpack-cli": "^3.3.9" "webpack-cli": "^3.3.9",
"why-is-node-running": "^2.1.2"
}, },
"dependencies": { "dependencies": {
"bsock": "^0.1.9", "bsock": "^0.1.9",
"crypto-js": "^4.0.0",
"http": "0.0.0", "http": "0.0.0",
"uuid": "^3.3.3" "uuid": "^3.3.3"
}, },
+7 -11
View File
@@ -2,19 +2,15 @@
import http = require('http'); import http = require('http');
import bsock = require('bsock'); import bsock = require('bsock');
import * as T from './Types'; import * as T from './Types';
import * as U from './Utils'; import * as U from './Utils';
import * as I from './Interfaces'; import * as I from './Interfaces';
import { Socket } from 'dgram';
type Exporters<InterfaceT extends T.RPCInterface = T.RPCInterface, SubResType = {}> = I.RPCExporter<T.RPCInterface<InterfaceT>, keyof InterfaceT, SubResType>[]
/** /**
* A Websocket-server-on-steroids with built-in RPC capabilities * A Websocket-server-on-steroids with built-in RPC capabilities
*/ */
export class RPCServer< export class RPCServer<
SubResType = {},
InterfaceT extends T.RPCInterface = T.RPCInterface, InterfaceT extends T.RPCInterface = T.RPCInterface,
> implements I.Destroyable { > implements I.Destroyable {
@@ -25,7 +21,7 @@ export class RPCServer<
private errorHandler: T.ErrorHandler private errorHandler: T.ErrorHandler
private connectionHandler: T.ConnectionHandler private connectionHandler: T.ConnectionHandler
private sesame?: T.SesameFunction private sesame?: T.SesameFunction
private accessFilter: T.AccessFilter<InterfaceT, SubResType> private accessFilter: T.AccessFilter<InterfaceT>
/** /**
* @throws On RPC with no name * @throws On RPC with no name
@@ -35,12 +31,11 @@ export class RPCServer<
*/ */
constructor( constructor(
private port: number, private port: number,
private exporters: Exporters<InterfaceT, SubResType> = [], private exporters: T.ExporterArray<InterfaceT> = [],
conf: T.ServerConf<InterfaceT, SubResType> = {} conf: T.ServerConf<InterfaceT> = {}
) { ) {
if (!conf.visibility) this.visibility = "0.0.0.0" if (!conf.visibility) this.visibility = "0.0.0.0"
if (conf.sesame) { if (conf.sesame) {
this.sesame = U.makeSesameFunction(conf.sesame) this.sesame = U.makeSesameFunction(conf.sesame)
} }
@@ -88,7 +83,7 @@ export class RPCServer<
this.connectionHandler(socket) this.connectionHandler(socket)
this.initRPCs(socket) this.initRPCs(socket)
}) })
this.ws.listen(this.port, this.visibility) this.ws = this.ws.listen(this.port, this.visibility)
} catch (e) { } catch (e) {
this.errorHandler(this.io, e, 'system', []) this.errorHandler(this.io, e, 'system', [])
} }
@@ -108,8 +103,7 @@ export class RPCServer<
/** /**
* Publishes a new list of Exporters. This destroys and restarts the socket * Publishes a new list of Exporters. This destroys and restarts the socket
* @param exporters the exporters to publish * @param exporters the exporters to publish
*/ public setExporters(exporters: T.ExporterArray<InterfaceT>): any {
public setExporters(exporters: Exporters<InterfaceT, SubResType>):any{
exporters.forEach(U.fixNames) exporters.forEach(U.fixNames)
this.destroy() this.destroy()
this.ws = http.createServer() this.ws = http.createServer()
@@ -117,6 +111,8 @@ export class RPCServer<
this.exporters = exporters this.exporters = exporters
this.startWebsocket() this.startWebsocket()
} }
*/
destroy(): void { destroy(): void {
this.io.close() this.io.close()
+15 -6
View File
@@ -7,12 +7,12 @@ import * as I from './Interfaces';
import { stripAfterEquals, appendComma } from './Utils'; import { stripAfterEquals, appendComma } from './Utils';
/** /**
* A websocket-on-steroids with built-in RPC capabilities * A websocket-on-steroids with built-in RPC capabilities
*/ */
export class RPCSocket implements I.Socket{ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I.Socket{
static async makeSocket<T extends T.RPCInterface = T.RPCInterface>(port:number, server: string, sesame?:string, conf?:T.SocketConf): Promise<RPCSocket & T> {
static async makeSocket<T extends T.RPCInterface = T.RPCInterface>(port:number, server: string, sesame?:string, conf?:T.SocketConf): Promise<T.ConnectedSocket<T>> {
const socket = new RPCSocket(port, server, conf) const socket = new RPCSocket(port, server, conf)
return await socket.connect<T>(sesame) return await socket.connect<T>(sesame)
} }
@@ -128,7 +128,7 @@ export class RPCSocket implements I.Socket{
/** /**
* Connects to the server and attaches available RPCs to this object * Connects to the server and attaches available RPCs to this object
*/ */
public async connect<T extends T.RPCInterface= T.RPCInterface>( sesame?: string ) : Promise<RPCSocket & T>{ public async connect<T extends T.RPCInterface= Ifc>( sesame?: string ) : Promise<T.ConnectedSocket<T>> {
this.socket = await bsock.connect(this.port, this.server, this.conf.tls?this.conf.tls:false) 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.errorHandlers.forEach(h => this.socket.on('error', h))
this.closeHandlers.forEach(h => this.socket.on('close', h)) this.closeHandlers.forEach(h => this.socket.on('close', h))
@@ -197,10 +197,19 @@ export class RPCSocket implements I.Socket{
return eval( ` return eval( `
async (${headerArgs} callback) => { async (${headerArgs} callback) => {
const r = await this.call("${fnName}", ${sesame} ${argParams}) const r = await this.call("${fnName}", ${sesame} ${argParams})
if(r && r.result === 'Success'){ try{
if(r){
if(r.uuid){
callback['destroy'] = () => { this.socket.unhook(r.uuid) }
this.socket.hook(r.uuid, callback) this.socket.hook(r.uuid, callback)
} }
return r return r.return
}else{
throw new Error("Empty response")
}
}catch(e){
throw e
}
}`) }`)
} }
} }
+4 -5
View File
@@ -4,13 +4,12 @@ import * as I from "./Interfaces"
/** /**
* Interface for all classes that export RPCs * Interface for all classes that export RPCs
*/ */
export interface RPCExporter< export type RPCExporter<
Ifc extends T.RPCInterface = T.RPCInterface, Ifc extends T.RPCInterface = T.RPCInterface,
Name extends keyof Ifc = keyof Ifc, Name extends keyof Ifc = string,
SubresT = {} > = {
>{
name: Name name: Name
exportRPCs() : T.RPCInterfaceArray<Ifc, SubresT>[Name] exportRPCs() : T.RPCDefinitions<Ifc>[Name]
} }
/** /**
+26 -18
View File
@@ -1,8 +1,9 @@
import * as I from "./Interfaces"; import * as I from "./Interfaces";
import { RPCSocket } from "./Frontend";
export type AnyFunction = (...args:any) => any export type AnyFunction = (...args:any) => any
export type HookFunction<F extends AnyFunction = AnyFunction, SubresT = {}> = (...args: Parameters<F>) => Promise<SubscriptionResponse<SubresT> | ErrorResponse> export type HookFunction = AnyFunction
export type AccessFilter<InterfaceT extends RPCInterface, SubresT> = (sesame:string|undefined, exporter: I.RPCExporter<RPCInterface<InterfaceT>, keyof InterfaceT, SubresT>) => Promise<boolean> export type AccessFilter<InterfaceT extends RPCInterface = RPCInterface> = (sesame:string|undefined, exporter: I.RPCExporter<InterfaceT, keyof InterfaceT>) => Promise<boolean>
export type Visibility = "127.0.0.1" | "0.0.0.0" export type Visibility = "127.0.0.1" | "0.0.0.0"
export type ConnectionHandler = (socket:I.Socket) => void export type ConnectionHandler = (socket:I.Socket) => void
export type ErrorHandler = (socket:I.Socket, error:any, rpcName: string, args: any[]) => void export type ErrorHandler = (socket:I.Socket, error:any, rpcName: string, args: any[]) => void
@@ -17,8 +18,12 @@ export type FrontEndHandlerType = {
'close' : () => void 'close' : () => void
} }
export type ServerConf<InterfaceT extends RPCInterface, SubresT> = { export type ExporterArray<InterfaceT extends RPCInterface = RPCInterface> = I.RPCExporter<RPCInterface<InterfaceT>, keyof InterfaceT>[]
accessFilter?: AccessFilter<InterfaceT, SubresT>
export type ConnectedSocket<T extends RPCInterface = RPCInterface> = RPCSocket & T
export type ServerConf<InterfaceT extends RPCInterface> = {
accessFilter?: AccessFilter<InterfaceT>
connectionHandler?: ConnectionHandler connectionHandler?: ConnectionHandler
errorHandler?: ErrorHandler errorHandler?: ErrorHandler
closeHandler?: CloseHandler closeHandler?: CloseHandler
@@ -36,23 +41,23 @@ export type Outcome = "Success" | "Error"
export type Respose<T> = T & { result: Outcome } export type Respose<T> = T & { result: Outcome }
export type SuccessResponse<T = {}> = Respose<T> & { result: "Success" } export type SuccessResponse<T = {}> = Respose<T> & { result: "Success" }
export type ErrorResponse<T = {}> = Respose<T> & { result: "Error", message?:string } export type ErrorResponse<T = {}> = Respose<T> & { result: "Error", message?:string }
export type SubscriptionResponse<T = {}> = Respose<T> & { result: "Success"; uuid: string }
export type RPCType = 'Hook' | 'Unhook' | 'Call' export type RPCType = 'Hook' | 'Unhook' | 'Call'
export type CallRPC<N, F> = { export type CallRPC<Name, Func extends AnyFunction> = {
name: N name: Name
call: F call: Func
} }
export type HookRPC<N, F extends AnyFunction, SubresT = {}> = {
name: N export type HookRPC<Name, Func extends AnyFunction> = {
hook: HookFunction<F, SubresT> name: Name
hook: Func
onCallback?: AnyFunction onCallback?: AnyFunction
onClose?: HookCloseFunction<SubresT> onClose?: HookCloseFunction<ReturnType<Func> extends Promise<infer T> ? T : ReturnType<Func>>
} }
export type RPC<N, F extends AnyFunction, SubresT = {}> = HookRPC<N, F, SubresT> | CallRPC<N,F> | F export type RPC<Name, Func extends AnyFunction> = HookRPC<Name, Func> | CallRPC<Name,Func> | Func
export type RPCInterface<Impl extends RPCInterface = {}> = { export type RPCInterface<Impl extends RPCInterface = {}> = {
[grp in string] : { [grp in string] : {
@@ -60,10 +65,13 @@ export type RPCInterface<Impl extends RPCInterface = {}> = {
} }
} & Impl } & Impl
export type RPCInterfaceArray<Itfc extends RPCInterface, SubresT = {}> = { export type exportT = {
[grp in keyof Itfc]: Array< [group in string]: {}
{ [rpc in keyof Itfc[grp]]: RPC<rpc, Itfc[grp][rpc], SubresT> }[keyof Itfc[grp]] }
>
//This probably has lots of issues
export type RPCDefinitions<Ifc extends RPCInterface> = {
[grp in keyof Ifc]:( { [rpc in keyof Ifc[grp]]: RPC<rpc, Ifc[grp][rpc]> }[keyof Ifc[grp]] )[]
} }
export type BaseInfo = { export type BaseInfo = {
@@ -86,4 +94,4 @@ export type RpcInfo = HookInfo | CallInfo
export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } export type ExtendedRpcInfo = RpcInfo & { uniqueName: string }
export type OnFunction = <T extends "error" | "close">(type: T, f: FrontEndHandlerType[T]) => void export type OnFunction = <T extends "error" | "close">(type: T, f: FrontEndHandlerType[T]) => void
export type HookCloseFunction<T = {}> = (res:SubscriptionResponse<T>, rpc:HookRPC<any, any, T>) => any export type HookCloseFunction<T> = (res: T, rpc:HookRPC<any, any>) => any
+17 -24
View File
@@ -2,7 +2,6 @@ import * as uuidv4 from "uuid/v4"
import * as T from "./Types"; import * as T from "./Types";
import * as I from "./Interfaces"; import * as I from "./Interfaces";
import { SubscriptionResponse } from "./Types";
/** /**
* Translate an RPC to RPCInfo for serialization. * 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 * @param sesame optional sesame phrase to prepend before all RPC arguments
* @throws Error on RPC without name property * @throws Error on RPC without name property
*/ */
export const rpcToRpcinfo = <SubResT = {}>(socket: I.Socket, rpc : T.RPC<any, any, SubResT>, owner: string, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction):T.RpcInfo => { export const rpcToRpcinfo = (socket: I.Socket, rpc : T.RPC<any, any>, owner: string, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction):T.RpcInfo => {
switch (typeof rpc){ switch (typeof rpc){
case "object": case "object":
if(rpc['call']){ if(rpc['call']){
@@ -24,7 +23,7 @@ export const rpcToRpcinfo = <SubResT = {}>(socket: I.Socket, rpc : T.RPC<any, an
call: sesame?async (_sesame, ...args) => {if(sesame(_sesame)) return await rpc['call'].apply({}, args); socket.destroy()}:rpc['call'], // check & remove sesame call: sesame?async (_sesame, ...args) => {if(sesame(_sesame)) return await rpc['call'].apply({}, args); socket.destroy()}:rpc['call'], // check & remove sesame
} }
}else{ }else{
const generator = hookGenerator(<T.HookRPC<any, any, any>>rpc, errorHandler, sesame) const generator = hookGenerator(<T.HookRPC<any, any>>rpc, errorHandler, sesame)
return { return {
owner: owner, owner: owner,
argNames: extractArgs(generator(undefined)), argNames: extractArgs(generator(undefined)),
@@ -58,10 +57,9 @@ RPC did not provide a name.
* @param exporter The exporter * @param exporter The exporter
* @param makeUnique @default true Attach a suffix to RPC names * @param makeUnique @default true Attach a suffix to RPC names
*/ */
export function rpcHooker<SubResT = {}>(socket: I.Socket, exporter:I.RPCExporter<any, any, SubResT>, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction, makeUnique = true):T.ExtendedRpcInfo[]{ export function rpcHooker(socket: I.Socket, exporter:I.RPCExporter<any, any>, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction, makeUnique = true):T.ExtendedRpcInfo[]{
const owner = exporter.name const owner = exporter.name
const RPCs = [...exporter.exportRPCs()] const RPCs = exporter.exportRPCs()
return RPCs.map(rpc => rpcToRpcinfo(socket, rpc, owner, errorHandler, sesame)) return RPCs.map(rpc => rpcToRpcinfo(socket, rpc, owner, errorHandler, sesame))
.map(info => { .map(info => {
@@ -107,7 +105,7 @@ export function stripAfterEquals(str:string):string{
* @param rpc The RPC to transform * @param rpc The RPC to transform
* @returns A {@link HookFunction} * @returns A {@link HookFunction}
*/ */
const hookGenerator = (rpc:T.HookRPC<any, any, any>, errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction): T.HookInfo['generator'] => { const hookGenerator = (rpc:T.HookRPC<any, any>, /*not unused!*/ errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction): T.HookInfo['generator'] => {
let argsArr = extractArgs(rpc.hook) let argsArr = extractArgs(rpc.hook)
argsArr.pop() //remove 'callback' from the end argsArr.pop() //remove 'callback' from the end
let callArgs = argsArr.join(',') let callArgs = argsArr.join(',')
@@ -119,16 +117,23 @@ const hookGenerator = (rpc:T.HookRPC<any, any, any>, errorHandler: T.ErrorHandle
//note rpc.hook is the associated RPC, not a socket.hook //note rpc.hook is the associated RPC, not a socket.hook
return eval(` return eval(`
(socket) => async (${args}) => { (clientSocket) => async (${args}) => {
try{ try{
if(sesameFn && !sesameFn(sesame)) return if(sesameFn && !sesameFn(sesame)) return
const uuid = uuidv4()
const res = await rpc.hook(${callArgs} (...cbargs) => { const res = await rpc.hook(${callArgs} (...cbargs) => {
if(rpc.onCallback) rpc.onCallback.apply({}, cbargs) if(rpc.onCallback){
socket.call.apply(socket, [res.uuid, ...cbargs]) 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){ }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(',') : [] 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<T extends {} = {}>(extension:T):SubscriptionResponse & T{
return {
result: "Success",
uuid: uuidv4(),
...extension
}
}
export function makeSesameFunction (sesame : T.SesameFunction | string) : T.SesameFunction { export function makeSesameFunction (sesame : T.SesameFunction | string) : T.SesameFunction {
if(typeof sesame === 'function'){ if(typeof sesame === 'function'){
+142 -99
View File
@@ -1,16 +1,14 @@
import { describe, it, Func } from "mocha"; import { describe, it } from "mocha";
import { RPCServer, RPCSocket } from '../Index'
import { RPCServer, RPCSocket, SubscriptionResponse, makeSubResponse } from '../Index'
import * as uuidv4 from "uuid/v4"
import { doesNotReject } from "assert";
import { Socket } from "dgram";
import { RPCExporter } from "../src/Interfaces"; 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) } const add = (...args: number[]) => { return args.reduce((a, b) => a + b, 0) }
function makeServer() { function makeServer() {
let subcallback let subcallback
return new RPCServer<{ topic: string }>(21010, [{ return new RPCServer(21010, [{
name: "test", name: 'test',
exportRPCs: () => [ exportRPCs: () => [
{ {
name: 'echo', name: 'echo',
@@ -19,13 +17,14 @@ function makeServer(){
name: 'simpleSubscribe', name: 'simpleSubscribe',
hook: async (callback) => { hook: async (callback) => {
subcallback = callback subcallback = callback
return makeSubResponse<{topic: string}>({topic: "test"}) return { topic: "test" }
} },
onClose: (res) => { }
}, { }, {
name: 'subscribe', name: 'subscribe',
hook: async (callback) => { hook: async (callback) => {
subcallback = callback subcallback = callback
return makeSubResponse<{topic: string}>({topic: "test"}) return { topic: "test" }
}, },
onClose: (res, rpc) => { onClose: (res, rpc) => {
console.log("onClose", rpc.name === 'subscribe' && res ? "OK" : "") console.log("onClose", rpc.name === 'subscribe' && res ? "OK" : "")
@@ -47,21 +46,11 @@ function makeServer(){
describe('RPCServer', () => { describe('RPCServer', () => {
let server: RPCServer<{ topic: string }, any> let client, server
before(() => {
server = makeServer()
})
after(() => {
server.destroy()
})
it('should be able to use all kinds of RPC definitions', (done) => {
const echo = (x) => x const echo = (x) => x
const server = new RPCServer(21003, [{ before(done => {
server = new RPCServer(21003, [{
name: 'HelloWorldRPCGroup', name: 'HelloWorldRPCGroup',
exportRPCs: () => [ exportRPCs: () => [
echo, //named function variable echo, //named function variable
@@ -73,17 +62,28 @@ describe('RPCServer', () => {
] ]
}]) }])
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 () => { client.connect().then(async () => {
const r0 = await client['HelloWorldRPCGroup'].echo('Hello') const r0 = await client['HelloWorldRPCGroup'].echo('Hello')
const r1 = await client['HelloWorldRPCGroup'].echof('World') 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!') { if (r0 === 'Hello' && r1 === 'World' && r2.join('') === 'RPC!') {
client.destroy()
server.destroy()
done() done()
}else{
done(new Error("Bad response"))
} }
}) })
}) })
@@ -91,7 +91,7 @@ describe('RPCServer', () => {
it('new RPCServer() should fail on bad RPC', (done) => { it('new RPCServer() should fail on bad RPC', (done) => {
try { try {
new RPCServer(20001, [{ new RPCServer(20001, [{
name: "bad", name: 'bad',
exportRPCs: () => [ exportRPCs: () => [
(aaa, bbb, ccc) => { return aaa + bbb + ccc } (aaa, bbb, ccc) => { return aaa + bbb + ccc }
] ]
@@ -106,7 +106,7 @@ describe('RPCServer', () => {
describe('RPCSocket', () => { describe('RPCSocket', () => {
let client: RPCSocket let client: RPCSocket
let server: RPCServer<{topic: string}> let server: RPCServer
before(async () => { before(async () => {
server = makeServer() server = makeServer()
@@ -140,7 +140,7 @@ describe('RPCSocket', () => {
it('should subscribe with success', (done) => { it('should subscribe with success', (done) => {
client['test'].simpleSubscribe(console.log).then(res => { client['test'].simpleSubscribe(console.log).then(res => {
if(res.result === 'Success'){ if (res.topic === 'test') {
done() done()
} else { } else {
console.error(res) console.error(res)
@@ -173,48 +173,42 @@ describe('RPCSocket', () => {
}) })
describe('It should do unhook', () => { describe('It should do unhook', () => {
let candy = "OK" const yesCandy = "OK"
const noCandy = "stolen"
let candy = yesCandy
let cb: Function let cb: Function
let cb2: Function let cb2: Function
let client: RPCSocket let client: RPCSocket
let server: RPCServer<{topic: string}> let server: RPCServer
before(async () => { before(async () => {
server = new RPCServer<{ topic: string }>(21010, [{ server = new RPCServer(21010, [{
name: "test", name: "test",
exportRPCs: () => [{ exportRPCs: () => [{
name: 'subscribe', name: 'subscribe',
hook: async(callback):Promise<SubscriptionResponse<{topic:string}>> => { hook: async (callback): Promise<void> => {
cb = <Function>callback cb = <Function>callback
return { return
result: "Success",
uuid: uuidv4(),
topic: "test"
}
} }
}, },
{ {
name: 'subscribeWithParam', name: 'subscribeWithParam',
hook: async(param, callback):Promise<SubscriptionResponse<{topic:string}>> => { hook: async (param, callback): Promise<{ uuid: string }> => {
if (param != "OK") { if (param != "OK") {
console.log("param was" + param); console.log("param was" + param);
return { return {
result: "Success",
uuid: "no", uuid: "no",
topic: "test"
} }
} }
cb2 = <Function>callback cb2 = <Function>callback
return { return {
result: "Success",
uuid: "OK", uuid: "OK",
topic: "test"
} }
} }
}, },
function checkCandy():string { cb(candy); return candy }, function publish(): string { cb(candy); return candy },
function stealCandy():string { candy = "_OK"; cb(candy); cb = () => {}; return candy } function unsubscribe(): string { candy = noCandy; cb(candy); cb = () => { }; return candy }
] ]
}], { }], {
connectionHandler: (socket) => { }, connectionHandler: (socket) => { },
@@ -231,23 +225,33 @@ describe('It should do unhook', () => {
}) })
it('Subscribe with param', (done) => { it('Subscribe with param', (done) => {
client['test'].subscribeWithParam("OK", c => {}).then( async (res: SubscriptionResponse) => { client['test'].subscribeWithParam("OK", c => { }).then(async (res) => {
if(res.uuid === "OK"){ if (res.uuid === candy) {
done() done()
} else } else
done(new Error("Results did not match " + res.uuid)) done(new Error("Results did not match " + res.uuid))
}) })
}) })
it('Unhook+unsubscribe should stop callbacks', (done) => { let run = 0
client['test'].subscribe(c => {}).then( async (res: SubscriptionResponse) => { const expected = [yesCandy, noCandy, noCandy, noCandy]
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()
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() done()
else else
done(new Error("Results did not match: " + [r1, r2, r3, r4])) done(new Error("Results did not match: " + [r1, r2, r3, r4]))
@@ -255,43 +259,54 @@ describe('It should do unhook', () => {
}) })
}) })
type topicDTO = { topic: string; }
type SesameTestIfc = { type SesameTestIfc = {
test: { test: {
checkCandy: () => Promise<string> checkCandy: () => Promise<string>
subscribe: (callback) => Promise<SubscriptionResponse<{ topic: string; }>> subscribe: (callback: Function) => Promise<topicDTO>
manyParams: <A=string,B=number,C=boolean,D=Object>(a:A, b:B, c:C, d:D) => Promise<[A, B, C, D]>
}
other: {
echo: (x:any) => Promise<any>
} }
} }
describe('Sesame should unlock the socket', () => { describe('Sesame should unlock the socket', () => {
let candy = "OK" let candy = "OK"
let client: RPCSocket & SesameTestIfc let client: ConnectedSocket<SesameTestIfc>
let server: RPCServer let server: RPCServer<SesameTestIfc>
let cb = (...args) => {} let cb: Function = (...args) => { }
before((done) => { before((done) => {
server = new RPCServer(21004, [{ server = new RPCServer<SesameTestIfc>(21004, [{
name: "test", name: "test",
exportRPCs: () => [ exportRPCs: () => [
{ {
name: 'subscribe', name: 'subscribe',
hook: async (callback) => { hook: async (callback) => {
cb = callback cb = callback
return <SubscriptionResponse>{ return {
result: "Success",
uuid: uuidv4(),
topic: 'test' topic: 'test'
} }
}
}, },
async function checkCandy():Promise<string> { cb(candy); cb=()=>{}; return candy }, onClose: (a) => { }
},
async function checkCandy() { cb(candy); cb = () => { }; return candy },
async function manyParams(a, b, c, d) { return [a, b, c, d] } 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!' sesame: (_sesame) => _sesame === 'sesame!'
}) })
const sock = new RPCSocket(21004, "localhost") const sock = new RPCSocket<SesameTestIfc>(21004, "localhost")
sock.connect<SesameTestIfc>('sesame!').then(cli => { sock.connect('sesame!').then(cli => {
client = cli client = cli
done() done()
}) })
@@ -345,8 +360,8 @@ describe('Sesame should unlock the socket', () => {
done() done()
} }
}).then(d => { }).then(d => {
if(d.result !== 'Success') if (d.topic !== 'test')
done('unexpected valid response') done('unexpected invalid response')
client.test.checkCandy() client.test.checkCandy()
}) })
@@ -355,18 +370,20 @@ describe('Sesame should unlock the socket', () => {
describe('Error handling', () => { describe('Error handling', () => {
const errtxt = "BAD BAD BAD"
let createUser = async (user: { a: any, b: any }) => { let createUser = async (user: { a: any, b: any }) => {
throw new Error("BAD BAD BAD") throw new Error(errtxt)
} }
it("RPC throws on client without handler", (done) => { it("RPC throws on client without handler", (done) => {
let server = new RPCServer(21004, [{ let server = new RPCServer(21004, [{
name: 'createUser' as 'createUser', name: "createUser",
exportRPCs: () => [{ exportRPCs: () => [{
name: 'createUser' as 'createUser', name: 'createUser' as 'createUser',
call: createUser call: createUser
}]}], { }]
}], {
}) })
@@ -378,11 +395,13 @@ describe('Error handling', ()=>{
}) })
.then(r => { .then(r => {
if (r != null) if (r != null)
done("UNEXPECTED RESULT " + r) done(new Error("UNEXPECTED RESULT " + r))
}) })
.catch((e) => { .catch((e) => {
//console.log("EXPECTED CLIENT EXCEPTION", String(e)); if (e.message === errtxt)
done() done()
else
done(e)
}) })
.finally(() => { .finally(() => {
cli.destroy() cli.destroy()
@@ -394,11 +413,12 @@ describe('Error handling', ()=>{
it("RPC throws on server with handler", (done) => { it("RPC throws on server with handler", (done) => {
let server = new RPCServer(21004, [{ let server = new RPCServer(21004, [{
name: 'createUser' as 'createUser', name: "createUser",
exportRPCs: () => [{ exportRPCs: () => [{
name: 'createUser' as 'createUser', name: 'createUser' as 'createUser',
call: createUser call: createUser
}]}], { }]
}], {
errorHandler: (socket, e, rpcName, args) => { errorHandler: (socket, e, rpcName, args) => {
done() done()
} }
@@ -429,19 +449,22 @@ describe('Error handling', ()=>{
describe("Errorhandler functionality", () => { describe("Errorhandler functionality", () => {
const errtxt = "BAD BAD BAD"
let createUser = async (user: { a: any, b: any }) => { let createUser = async (user: { a: any, b: any }) => {
throw new Error("BAD BAD BAD") throw new Error(errtxt)
} }
it("correct values are passed to the handler", (done) => { it("correct values are passed to the handler", (done) => {
let server = new RPCServer(21004, [{ let server = new RPCServer(21004, [{
name: 'createUser' as 'createUser', name: "createUser",
exportRPCs: () => [{ exportRPCs: () => [{
name: 'createUser' as 'createUser', name: 'createUser' as 'createUser',
call: createUser call: createUser
}]}], { }]
}], {
errorHandler: (socket, e, rpcName, args) => { 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() done()
} }
}) })
@@ -457,8 +480,7 @@ describe("Errorhandler functionality", ()=>{
done("UNEXPECTED RESULT " + r) done("UNEXPECTED RESULT " + r)
}) })
.catch((e) => { .catch((e) => {
done("UNEXPECTED CLIENT ERROR " + e) done(new Error("UNEXPECTED CLIENT ERROR " + e.message))
done(e)
}) })
.finally(() => { .finally(() => {
cli.destroy() cli.destroy()
@@ -471,14 +493,15 @@ describe("Errorhandler functionality", ()=>{
it("handler sees sesame", (done) => { it("handler sees sesame", (done) => {
let sesame = "AAAAAAAAAAAAAAA" let sesame = "AAAAAAAAAAAAAAA"
let server = new RPCServer(21004, [{ let server = new RPCServer(21004, [{
name: 'createUser' as 'createUser', name: "createUser" as "createUser",
exportRPCs: () => [{ exportRPCs: () => [{
name: 'createUser' as 'createUser', name: 'createUser' as 'createUser',
call: createUser call: createUser
}]}], { }]
}], {
sesame: sesame, sesame: sesame,
errorHandler: (socket, e, rpcName, args) => { 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() done()
} }
@@ -517,24 +540,24 @@ type myExporterIfc = {
describe("Class binding", () => { describe("Class binding", () => {
let exporter1: MyExporter let exporter1: MyExporter
let serv : RPCServer<{}, myExporterIfc> let serv: RPCServer<myExporterIfc>
let sock: RPCSocket & myExporterIfc let sock: RPCSocket & myExporterIfc
let allowed = true let allowed = true
class MyExporter implements RPCExporter<myExporterIfc>{ class MyExporter implements RPCExporter<myExporterIfc>{
name = "MyExporter" as "MyExporter"; name = "MyExporter" as "MyExporter"
exportRPCs = () => [ exportRPCs = () => [
this.myRPC this.myRPC
] ]
myRPC = async () => { myRPC = async () => {
serv.setExporters([new MyOtherExporter]) //serv.setExporters([new MyOtherExporter])
return "Hello World" return "Hello World"
} }
} }
class MyOtherExporter implements RPCExporter<myExporterIfc>{ class MyOtherExporter implements RPCExporter<myExporterIfc>{
name = "MyExporter" as "MyExporter"; name = "MyExporter" as "MyExporter"
exportRPCs = () => [ exportRPCs = () => [
this.myRPC this.myRPC
] ]
@@ -547,14 +570,13 @@ describe("Class binding", ()=>{
before(done => { before(done => {
exporter1 = new MyExporter() exporter1 = new MyExporter()
serv = new RPCServer<{}, myExporterIfc>(21004, [exporter1], { serv = new RPCServer<myExporterIfc>(21004, [exporter1], {
accessFilter: async (sesame, exporter) => { accessFilter: async (sesame, exporter) => {
switch(exporter.name){ if(exporter.name === 'MyExporter'){
case "MyExporter":
if (!allowed) return false if (!allowed) return false
allowed = false allowed = false
return sesame === 'xxx'; return sesame === 'xxx';
default: }else{
return false return false
} }
}, },
@@ -564,7 +586,6 @@ describe("Class binding", ()=>{
}) })
beforeEach((done) => { beforeEach((done) => {
const s = new RPCSocket(21004, 'localhost') const s = new RPCSocket(21004, 'localhost')
s.connect<myExporterIfc>("xxx").then(conn => { s.connect<myExporterIfc>("xxx").then(conn => {
sock = conn sock = conn
@@ -572,7 +593,7 @@ describe("Class binding", ()=>{
}) })
}) })
afterEach(done => { afterEach((done) => {
sock.destroy() sock.destroy()
done() done()
}) })
@@ -581,7 +602,11 @@ describe("Class binding", ()=>{
serv.destroy() 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) => { sock['MyExporter'].myRPC().then((res) => {
done(new Error(res)) done(new Error(res))
}).catch(e => { }).catch(e => {
@@ -592,6 +617,7 @@ describe("Class binding", ()=>{
}) })
it("changes exporters", (done) => { it("changes exporters", (done) => {
sock['MyExporter'].myRPC().then((res) => { sock['MyExporter'].myRPC().then((res) => {
if (res === "Hello Borld") if (res === "Hello Borld")
done() done()
@@ -599,6 +625,16 @@ describe("Class binding", ()=>{
done(new Error(res)) 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) => { it("use sesameFilter", (done) => {
if (!sock['MyExporter']) done() if (!sock['MyExporter']) done()
@@ -667,4 +703,11 @@ describe("attaching handlers before connecting", ()=>{
done(e) done(e)
}) })
}) })
})
describe('finally', () => {
it('print open handles (Ignore `DNSCHANNEL` and `Immediate`)', () => {
log()
})
}) })
-73
View File
@@ -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<void>,
subscribe: (param:string, callback:Function) => Promise<SubscriptionResponse<SubresExtension>>,
unsubscribe: (uuid:string) => Promise<void>
},
Group2: {
echo: (x:string) => Promise<string>
}
}
new RPCServer<SubresExtension, MyInterface>(20000,
[{
name: 'Group1',
exportRPCs: () => [{
name: 'triggerCallbacks',
call: async () => { /*...*/ }
},{
name: 'subscribe',
hook: async (param, callback) => { return makeSubResponse<SubresExtension>({a: "test"}) }
},{
name: 'unsubscribe',
call: async(uuid) => { }
}
]
},{
name: 'Group2',
exportRPCs: () => [{
name: 'echo',
call: async (x) => "..."
}]
}]
)
RPCSocket.makeSocket<MyInterface>(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)
})