diff --git a/lib/src/Client.d.ts b/lib/src/Client.d.ts deleted file mode 100644 index 01a3bb8..0000000 --- a/lib/src/Client.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Socket } from './interfaces/Socket'; -export declare class Client implements Socket { - port: number; - private server; - private tls; - private socket; - constructor(port: number, server: string, tls?: boolean); - hook(name: any, args: any): Socket; - unhook(name: any): Socket; - on(type: "error" | "close", f: (e?: any) => void): Socket; - destroy(): void; - close(): void; - call(rpcname: string, ...args: any[]): Promise; - fire(rpcname: string, ...args: any[]): Promise; - connect(): Promise; - info(): Promise; - private callGenerator; - private hookGenerator; - private unhookGenerator; -} diff --git a/lib/src/Client.js b/lib/src/Client.js deleted file mode 100644 index c890c61..0000000 --- a/lib/src/Client.js +++ /dev/null @@ -1,88 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var bsock = require('bsock'); -//fix args with defaults like "force = true" -> "force" -function stripAfterEquals(str) { - return str.split("=")[0]; -} -class Client { - constructor(port, server, tls = false) { - this.port = port; - this.server = server; - this.tls = tls; - } - hook(name, args) { - return this.socket.hook(name, args); - } - unhook(name) { - return this.socket.unhook(name); - } - on(type, f) { - return this.socket.on(type, name); - } - destroy() { - return this.socket.destroy(); - } - close() { - return this.socket.close(); - } - async call(rpcname, ...args) { - return await this.socket.call.apply(this.socket, [rpcname, ...args]); - } - async fire(rpcname, ...args) { - return await this.socket.fire.apply(this.socket, [rpcname, ...args]); - } - async connect() { - this.socket = await bsock.connect(this.port, this.server, this.tls); - const info = await this.info(); - info.forEach(i => { - let f; - switch (i.type) { - case 'Call': - f = this.callGenerator(i.uniqueName, i.argNames); - break; - case 'Hook': - f = this.hookGenerator(i.uniqueName, i.argNames); - break; - case 'Unhook': - f = this.unhookGenerator(i.uniqueName, i.argNames); - break; - } - if (this[i.owner] == null) - this[i.owner] = {}; - this[i.owner][i.name] = f; - this[i.owner][i.name].bind(this); - }); - } - async info() { - return await this.socket.call('info'); - } - callGenerator(fnName, fnArgs) { - const headerArgs = fnArgs.join(","); - const argParams = fnArgs.map(stripAfterEquals).join(","); - return eval('( () => async (' + headerArgs + ') => { return await this.socket.call("' + fnName + '", ' + argParams + ')} )()'); - } - hookGenerator(fnName, fnArgs) { - const headerArgs = fnArgs.join(","); - const argParams = fnArgs.map(stripAfterEquals).join(","); - return eval(`( () => async (` + headerArgs + (headerArgs.length !== 0 ? "," : "") + ` callback) => { - const r = await this.socket.call("` + fnName + `", ` + argParams + `) - if(r.uid != null){ - this.socket.hook(res.uid, callback) - } - return res - } )()`); - } - unhookGenerator(fnName, fnArgs) { - const headerArgs = fnArgs.join(","); - const argParams = fnArgs.map(stripAfterEquals).join(","); - if (fnArgs.length != 1) - console.error("UnhookFunction", fnName, "specified more than one argument: (" + headerArgs + ")"); - return eval(`( () => async (` + headerArgs + `) => { - const r = await this.socket.call("` + fnName + `", ` + argParams + `) - this.socket.unhook(` + argParams + `) - return res - } )()`); - } -} -exports.Client = Client; diff --git a/lib/src/Response.d.ts b/lib/src/Response.d.ts deleted file mode 100644 index ee1072b..0000000 --- a/lib/src/Response.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -export declare type Outcome = "Success" | "Error"; -export declare class Response { - message?: string | undefined; - constructor(message?: string | undefined); -} -export declare class SuccessResponse extends Response { - result: Outcome; - constructor(message?: string); -} -export declare class ErrorResponse extends Response { - result: Outcome; - constructor(message?: string); -} -export declare class SubscriptionResponse extends SuccessResponse { - uid: string; - constructor(uid: string, message?: string); -} diff --git a/lib/src/Response.js b/lib/src/Response.js deleted file mode 100644 index 9acd085..0000000 --- a/lib/src/Response.js +++ /dev/null @@ -1,30 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -/* Responses */ -class Response { - constructor(message) { - this.message = message; - } -} -exports.Response = Response; -class SuccessResponse extends Response { - constructor(message) { - super(message); - this.result = "Success"; - } -} -exports.SuccessResponse = SuccessResponse; -class ErrorResponse extends Response { - constructor(message = "Unknown error") { - super(message); - this.result = "Error"; - } -} -exports.ErrorResponse = ErrorResponse; -class SubscriptionResponse extends SuccessResponse { - constructor(uid, message) { - super(message); - this.uid = uid; - } -} -exports.SubscriptionResponse = SubscriptionResponse; diff --git a/lib/src/Server.d.ts b/lib/src/Server.d.ts deleted file mode 100644 index 89e01c0..0000000 --- a/lib/src/Server.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { SocketConf } from './Types'; -import { Exporter } from './interfaces/Exporter'; -import { Socket } from "./interfaces/Socket"; -export declare class Server { - private port; - private rpcExporters; - private conf; - private io; - private wsServer; - constructor(port: number, rpcExporters?: Exporter[], conf?: SocketConf); - private startWebsocket; - protected initRPCs(socket: Socket): void; - protected initPublicRPCs(socket: Socket): void; -} diff --git a/lib/src/Server.js b/lib/src/Server.js deleted file mode 100644 index 32c8911..0000000 --- a/lib/src/Server.js +++ /dev/null @@ -1,58 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const http = require("http"); -const bsock = require("bsock"); -const Util_1 = require("./Util"); -class Server { - constructor(port, rpcExporters = [], conf = { - errorHandler: (socket) => (error) => { socket.destroy(); console.error(error); }, - closeHandler: (socket) => () => { console.log("Socket closing"); }, - connectionHandler: (socket) => { console.log("New websocket connection in port " + socket.port); }, - visibility: "127.0.0.1" - }) { - this.port = port; - this.rpcExporters = rpcExporters; - this.conf = conf; - this.io = bsock.createServer(); - this.wsServer = http.createServer(); - this.startWebsocket(); - } - startWebsocket() { - try { - this.io.attach(this.wsServer); - this.io.on('socket', (socket) => { - socket.on('error', this.conf.errorHandler(socket)); - socket.on('close', this.conf.closeHandler(socket)); - if (this.conf.visibility === "127.0.0.1") - this.initRPCs(socket); - else - this.initPublicRPCs(socket); - }); - this.wsServer.listen(this.port, this.conf.visibility); - } - catch (e) { - //@ts-ignore - this.errorHandler(undefined)("Unable to connect to socket"); - } - } - initRPCs(socket) { - const infoRPC = [ - { - name: 'info', - type: 'Call', - func: async () => rpcInfos - } - ]; - const rpcInfos = [ - ...Util_1.rpcHooker(socket, "RPC", infoRPC, false), - ...this.rpcExporters.flatMap(exporter => Util_1.rpcHooker(socket, exporter.name, [...exporter.exportPublicRPCs(), ...exporter.exportRPCs()])) - ]; - } - initPublicRPCs(socket) { - const rpcInfos = [ - ...Util_1.rpcHooker(socket, "Admin", adminRPCs, false), - ...this.rpcExporters.flatMap(exporter => Util_1.rpcHooker(socket, exporter.name, exporter.exportPublicRPCs())) - ]; - } -} -exports.Server = Server; diff --git a/lib/src/Types.d.ts b/lib/src/Types.d.ts deleted file mode 100644 index 2d87875..0000000 --- a/lib/src/Types.d.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { SuccessResponse, ErrorResponse, SubscriptionResponse } from "./Response"; -import { Socket } from "./interfaces/Socket"; -export declare type Visibility = "127.0.0.1" | "0.0.0.0"; -export declare type Name = string; -export declare type SocketConf = { - connectionHandler: (socket: Socket) => void; - errorHandler: (socket: Socket) => (error: any) => void; - closeHandler: (socket: Socket) => () => void; - visibility: Visibility; -}; -export declare type rpcType = 'Hook' | 'Unhook' | 'Call'; -export declare type BaseRPC = { - type: rpcType; - name: string; -}; -export declare type HookRPC = BaseRPC & { - type: 'Hook'; - func: CallbackFunction; - unhook: UnhookFunction; -}; -export declare type UnhookRPC = BaseRPC & { - type: 'Unhook'; - func: UnhookFunction; -}; -export declare type CallRPC = BaseRPC & { - type: 'Call'; - func: (...args: any[]) => Promise; -}; -export declare type SocketioRPC = CallRPC | UnhookRPC | HookRPC; -export declare type BaseInfo = { - owner: string; - argNames: string[]; -}; -export declare type HookInfo = BaseRPC & BaseInfo & { - type: 'Hook'; - generator: (socket: any) => CallbackFunction; - unhook: UnhookFunction; -}; -export declare type UnhookInfo = BaseRPC & BaseInfo & { - type: 'Unhook'; - func: UnhookFunction; -}; -export declare type CallInfo = BaseRPC & BaseInfo & { - type: 'Call'; - func: AsyncFunction; -}; -export declare type RpcInfo = HookInfo | UnhookInfo | CallInfo; -export declare type ExtendedRpcInfo = RpcInfo & { - uniqueName: string; -}; -export declare type OnFunction = (type: 'error' | 'close', f: (e?: any) => void) => Socket; -export declare type UnhookFunction = (uid: string) => Promise; -export declare type CallbackFunction = (...args: any[]) => Promise; -export declare type AsyncFunction = (...args: any[]) => Promise; diff --git a/lib/src/Types.js b/lib/src/Types.js deleted file mode 100644 index c8ad2e5..0000000 --- a/lib/src/Types.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/src/Util.d.ts b/lib/src/Util.d.ts deleted file mode 100644 index 0763df1..0000000 --- a/lib/src/Util.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { SocketioRPC, RpcInfo, ExtendedRpcInfo } from "./Types"; -import { Socket } from "./interfaces/Socket"; -export declare const rpcToRpcinfo: (rpc: SocketioRPC, owner: string) => RpcInfo; -export declare const rpcHooker: (socket: Socket, owner: string, RPCs: SocketioRPC[], makeUnique?: boolean) => ExtendedRpcInfo[]; diff --git a/lib/src/Util.js b/lib/src/Util.js deleted file mode 100644 index 74dd8fb..0000000 --- a/lib/src/Util.js +++ /dev/null @@ -1,75 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const uuid = require("uuid/v4"); -exports.rpcToRpcinfo = (rpc, owner) => { - switch (rpc.type) { - case "Call": - return { - owner: owner, - argNames: extractArgs(rpc.func), - type: rpc.type, - name: rpc.name, - func: rpc.func, - }; - case "Unhook": - return { - owner: owner, - argNames: extractArgs(rpc.func), - type: rpc.type, - name: rpc.name, - func: rpc.func, - }; - case "Hook": - const generator = hookGenerator(rpc); - return { - owner: owner, - argNames: extractArgs(generator(undefined)), - type: rpc.type, - name: rpc.name, - unhook: rpc.unhook, - generator: generator, - }; - } -}; -exports.rpcHooker = (socket, owner, RPCs, makeUnique = true) => { - const suffix = makeUnique ? "-" + uuid().substr(0, 4) : ""; - return RPCs.map(rpc => exports.rpcToRpcinfo(rpc, owner)) - .map(info => { - const ret = info; - ret.uniqueName = info.name + suffix; - switch (info.type) { - case "Hook": - socket.hook(ret.uniqueName, info.generator(socket)); - break; - default: - socket.hook(ret.uniqueName, info.func); - } - socket.on('close', () => socket.unhook(info.name)); - return ret; - }); -}; -const hookGenerator = (rpc) => { - const argsArr = extractArgs(rpc.func); - argsArr.pop(); - const args = argsArr.join(','); - return eval(`(socket) => async (` + args + `) => { - const res = await rpc.func(` + args + (args.length !== 0 ? ',' : '') + ` (x) => { - socket.call(res.uid, x) - }) - if(res.result == 'Success'){ - socket.on('close', async () => { - const unhookRes = await rpc.unhook(res.uid) - console.log("Specific close handler for", rpc.name, res.uid, unhookRes) - }) - - } - return res - }`); -}; -const extractArgs = (f) => { - let fn = String(f); - let args = fn.substr(0, fn.indexOf(")")); - args = args.substr(fn.indexOf("(") + 1); - let ret = args.split(","); - return ret; -}; diff --git a/lib/src/backend/RPCSocketServer.d.ts b/lib/src/backend/RPCSocketServer.d.ts deleted file mode 100644 index 70b2fa1..0000000 --- a/lib/src/backend/RPCSocketServer.d.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { Socket } from "./RPCSocketServer"; -declare type rpcType = 'hook' | 'unhook' | 'call'; -export declare type Outcome = "Success" | "Error"; -export declare type Visibility = "127.0.0.1" | "0.0.0.0"; -export declare class Response { - message?: string | undefined; - constructor(message?: string | undefined); -} -export declare class SuccessResponse extends Response { - result: Outcome; - constructor(message?: string); -} -export declare class ErrorResponse extends Response { - result: Outcome; - constructor(message?: string); -} -export declare class SubscriptionResponse extends SuccessResponse { - uid: string; - constructor(uid: string, message?: string); -} -export declare type UnhookFunction = (uid: string) => Promise; -export declare type callbackFunction = (...args: any[]) => Promise; -export declare type AsyncFunction = (...args: any[]) => Promise; -export interface RPCExporter { - name: string; - exportRPCs(): socketioRPC[]; - exportPublicRPCs(): socketioRPC[]; -} -declare type baseRPC = { - type: rpcType; - name: string; -}; -declare type hookRPC = baseRPC & { - type: 'hook'; - func: callbackFunction; - unhook: UnhookFunction; -}; -declare type unhookRPC = baseRPC & { - type: 'unhook'; - func: UnhookFunction; -}; -declare type callRPC = baseRPC & { - type: 'call'; - func: (...args: any[]) => Promise; -}; -export declare type socketioRPC = callRPC | unhookRPC | hookRPC; -export declare type baseInfo = { - owner: string; - argNames: string[]; -}; -declare type HookInfo = baseRPC & baseInfo & { - type: 'hook'; - generator: (socket: any) => callbackFunction; - unhook: UnhookFunction; -}; -declare type UnhookInfo = baseRPC & baseInfo & { - type: 'unhook'; - func: UnhookFunction; -}; -declare type CallInfo = baseRPC & baseInfo & { - type: 'call'; - func: AsyncFunction; -}; -declare type RpcInfo = HookInfo | UnhookInfo | CallInfo; -export declare type ExtendedRpcInfo = RpcInfo & { - uniqueName: string; -}; -export declare const rpcToRpcinfo: (rpc: socketioRPC, owner: string) => RpcInfo; -declare type OnFunction = (type: 'error' | 'close', f: (e?: any) => void) => Socket; -export interface Socket { - port: number; - hook: (rpcname: string, ...args: any[]) => Socket; - unhook: (rpcname: string) => Socket; - call: (rpcname: string, ...args: any[]) => Promise; - fire: (rpcname: string, ...args: any[]) => Promise; - on: OnFunction; - destroy: () => void; - close: () => void; -} -export declare type RPCSocketConf = { - connectionHandler: (socket: Socket) => void; - errorHandler: (socket: Socket) => (error: any) => void; - closeHandler: (socket: Socket) => () => void; -}; -export declare class RPCSocketServer { - private port; - private rpcExporters; - private visibility; - private conf; - private io; - private wsServer; - constructor(port: number, rpcExporters?: RPCExporter[], visibility?: Visibility, conf?: RPCSocketConf); - private startWebsocket; - protected initRPCs(socket: Socket): void; - protected initPublicRPCs(socket: Socket): void; -} -export {}; diff --git a/lib/src/backend/RPCSocketServer.js b/lib/src/backend/RPCSocketServer.js deleted file mode 100644 index a7db1e2..0000000 --- a/lib/src/backend/RPCSocketServer.js +++ /dev/null @@ -1,153 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const http = require("http"); -const bsock = require("bsock"); -const uuid = require("uuid/v4"); -/* Responses */ -class Response { - constructor(message) { - this.message = message; - } -} -exports.Response = Response; -class SuccessResponse extends Response { - constructor(message) { - super(message); - this.result = "Success"; - } -} -exports.SuccessResponse = SuccessResponse; -class ErrorResponse extends Response { - constructor(message = "Unknown error") { - super(message); - this.result = "Error"; - } -} -exports.ErrorResponse = ErrorResponse; -class SubscriptionResponse extends SuccessResponse { - constructor(uid, message) { - super(message); - this.uid = uid; - } -} -exports.SubscriptionResponse = SubscriptionResponse; -exports.rpcToRpcinfo = (rpc, owner) => { - switch (rpc.type) { - case "call": - return { - owner: owner, - argNames: extractArgs(rpc.func), - type: rpc.type, - name: rpc.name, - func: rpc.func, - }; - case "unhook": - return { - owner: owner, - argNames: extractArgs(rpc.func), - type: rpc.type, - name: rpc.name, - func: rpc.func, - }; - case "hook": - const generator = hookGenerator(rpc); - return { - owner: owner, - argNames: extractArgs(generator(undefined)), - type: rpc.type, - name: rpc.name, - unhook: rpc.unhook, - generator: generator, - }; - } -}; -function rpcHooker(socket, exporter, makeUnique = true) { - const owner = exporter.name; - const RPCs = [...exporter.exportPublicRPCs(), ...exporter.exportRPCs()]; - const suffix = makeUnique ? "-" + uuid().substr(0, 4) : ""; - return RPCs.map(rpc => exports.rpcToRpcinfo(rpc, owner)) - .map(info => { - const ret = info; - ret.uniqueName = info.name + suffix; - switch (info.type) { - case "hook": - socket.hook(ret.uniqueName, info.generator(socket)); - break; - default: - socket.hook(ret.uniqueName, info.func); - } - socket.on('close', () => socket.unhook(info.name)); - return ret; - }); -} -const hookGenerator = (rpc) => { - const argsArr = extractArgs(rpc.func); - argsArr.pop(); - const args = argsArr.join(','); - return eval(`(socket) => async (` + args + `) => { - const res = await rpc.func(` + args + (args.length !== 0 ? ',' : '') + ` (x) => { - socket.call(res.uid, x) - }) - if(res.result == 'Success'){ - socket.on('close', async () => { - const unhookRes = await rpc.unhook(res.uid) - console.log("Specific close handler for", rpc.name, res.uid, unhookRes) - }) - - } - return res - }`); -}; -const extractArgs = (f) => { - let fn = String(f); - let args = fn.substr(0, fn.indexOf(")")); - args = args.substr(fn.indexOf("(") + 1); - let ret = args.split(","); - return ret; -}; -class RPCSocketServer { - constructor(port, rpcExporters = [], visibility = "127.0.0.1", conf = { - errorHandler: (socket) => (error) => { socket.destroy(); console.error(error); }, - closeHandler: (socket) => () => { console.log("Socket closing"); }, - connectionHandler: (socket) => { console.log("New websocket connection in port " + socket.port); } - }) { - this.port = port; - this.rpcExporters = rpcExporters; - this.visibility = visibility; - this.conf = conf; - this.io = bsock.createServer(); - this.wsServer = http.createServer(); - this.startWebsocket(); - } - startWebsocket() { - try { - this.io.attach(this.wsServer); - this.io.on('socket', (socket) => { - socket.on('error', this.conf.errorHandler(socket)); - socket.on('close', this.conf.closeHandler(socket)); - if (this.visibility === "127.0.0.1") - this.initRPCs(socket); - else - this.initPublicRPCs(socket); - }); - this.wsServer.listen(this.port, this.visibility); - } - catch (e) { - //@ts-ignore - this.errorHandler(undefined)("Unable to connect to socket"); - } - } - initRPCs(socket) { - socket.hook('info', () => rpcInfos); - const rpcInfos = [ - ...this.rpcExporters.flatMap(exporter => rpcHooker(socket, exporter)) - ]; - } - initPublicRPCs(socket) { - socket.hook('info', () => rpcInfos); - const rpcInfos = [ - ...this.rpcExporters.flatMap(exporter => rpcHooker(socket, exporter)) - ]; - } -} -exports.RPCSocketServer = RPCSocketServer; diff --git a/lib/src/interfaces/Exporter.d.ts b/lib/src/interfaces/Exporter.d.ts deleted file mode 100644 index b61cd0b..0000000 --- a/lib/src/interfaces/Exporter.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { SocketioRPC, Name } from "../Types"; -export interface Exporter { - name: Name; - exportRPCs(): SocketioRPC[]; - exportPublicRPCs(): SocketioRPC[]; -} diff --git a/lib/src/interfaces/Exporter.js b/lib/src/interfaces/Exporter.js deleted file mode 100644 index c8ad2e5..0000000 --- a/lib/src/interfaces/Exporter.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/src/interfaces/Socket.d.ts b/lib/src/interfaces/Socket.d.ts deleted file mode 100644 index 276a1c2..0000000 --- a/lib/src/interfaces/Socket.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { OnFunction } from "../Types"; -export interface Socket { - port: number; - hook: (rpcname: string, ...args: any[]) => Socket; - unhook: (rpcname: string) => Socket; - call: (rpcname: string, ...args: any[]) => Promise; - fire: (rpcname: string, ...args: any[]) => Promise; - on: OnFunction; - destroy: () => void; - close: () => void; -} diff --git a/lib/src/interfaces/Socket.js b/lib/src/interfaces/Socket.js deleted file mode 100644 index c8ad2e5..0000000 --- a/lib/src/interfaces/Socket.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/test/test.d.ts b/lib/test/test.d.ts deleted file mode 100644 index cb0ff5c..0000000 --- a/lib/test/test.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/lib/test/test.js b/lib/test/test.js deleted file mode 100644 index 2e353e6..0000000 --- a/lib/test/test.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const Server_1 = require("../src/Server"); -//@ts-ignore -const Client_1 = require("../src/Client"); -new Server_1.Server(20000, [{ - name: "HelloWorldRPCGroup", - exportPublicRPCs: () => [], - exportRPCs: () => [{ - type: 'call', - name: 'echo', - func: async (s) => s, - }], - }]); -<<<<<<< HEAD -const caller = new Client_1.Client(20000, 'localhost'); -======= -const caller = new RPCSocket_1.RPCSocket(20000, 'localhost'); ->>>>>>> 17dc58c5b3fd3c76113d592d895400498578affa -caller.connect().then(_ => { - caller.info().then(console.log); - caller["HelloWorldRPCGroup"].echo("x").then(console.log); -}); diff --git a/package.json b/package.json index efbdfe7..8d25962 100644 --- a/package.json +++ b/package.json @@ -23,5 +23,6 @@ "dependencies": { "bsock": "^0.1.9", "uuid": "^3.3.3" - } + }, + "files": [] } diff --git a/src/Client.ts b/src/Client.ts index b2fc995..fd8e91c 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -1,50 +1,56 @@ +'use strict' + var bsock = require('bsock') -import { ExtendedRpcInfo, UnhookFunction, CallbackFunction, AsyncFunction } from "./Types"; -import { Socket } from './interfaces/Socket' +import * as I from './Interfaces'; +import * as T from './Types'; + //fix args with defaults like "force = true" -> "force" function stripAfterEquals(str:string){ return str.split("=")[0] } -export class Client implements Socket{ - private socket: Socket +export class Client implements I.Socket{ + + private socket: I.Socket constructor(public port:number, private server: string, private tls: boolean = false){ } - hook(name, args){ + + + public hook(name: T.Name, args: T.Arg){ return this.socket.hook(name, args) } - unhook(name){ + public unhook(name: T.Name){ return this.socket.unhook(name) } - on(type: "error" | "close", f: (e?: any) => void){ - return this.socket.on(type, name) + public on(type: "error" | "close", f: (e?: any) => void){ + return this.socket.on(type, f) } - destroy(){ + public destroy(){ return this.socket.destroy() } - close(){ + public close(){ return this.socket.close() } - async call (rpcname: string, ...args: any[]) : Promise{ + public async call (rpcname: T.Name, ...args: T.Any[]) : Promise{ return await this.socket.call.apply(this.socket, [rpcname, ...args]) } - async fire(rpcname: string, ...args: any[]) : Promise{ + public async fire(rpcname: T.Name, ...args: T.Any[]) : Promise{ return await this.socket.fire.apply(this.socket, [rpcname, ...args]) } - async connect(){ + public async connect(){ this.socket = await bsock.connect(this.port, this.server, this.tls) - const info:ExtendedRpcInfo[] = await this.info() + const info:T.ExtendedRpcInfo[] = await this.info() info.forEach(i => { let f: any switch (i.type) { @@ -65,17 +71,17 @@ export class Client implements Socket{ }) } - async info(){ + public async info(){ return await this.socket.call('info') } - private callGenerator(fnName, fnArgs:string[]): AsyncFunction{ + private callGenerator(fnName: T.Name, fnArgs:T.Arg[]): T.AsyncFunction{ const headerArgs = fnArgs.join(",") const argParams = fnArgs.map(stripAfterEquals).join(",") return eval( '( () => async ('+headerArgs+') => { return await this.socket.call("'+fnName+'", '+argParams+')} )()' ) } - private hookGenerator(fnName, fnArgs:string[]): CallbackFunction{ + private hookGenerator(fnName: T.Name, fnArgs:T.Arg[]): T.CallbackFunction{ const headerArgs = fnArgs.join(",") const argParams = fnArgs.map(stripAfterEquals).join(",") return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => { @@ -87,7 +93,7 @@ export class Client implements Socket{ } )()` ) } - private unhookGenerator(fnName, fnArgs:string[]): UnhookFunction{ + private unhookGenerator(fnName: T.Name, fnArgs:T.Arg[]): T.UnhookFunction{ const headerArgs = fnArgs.join(",") const argParams = fnArgs.map(stripAfterEquals).join(",") if(fnArgs.length != 1) diff --git a/src/Interfaces.ts b/src/Interfaces.ts new file mode 100644 index 0000000..b64fbff --- /dev/null +++ b/src/Interfaces.ts @@ -0,0 +1,19 @@ +import * as T from "./Types"; +import * as I from "./Interfaces" + +export interface Exporter{ + name: T.Name + localRPCs() : T.RPC[] + publicRPCs() : T.RPC[] +} + +export interface Socket { + port: number + hook: (rpcname: T.Name, ...args: T.Any[]) => I.Socket + unhook: (rpcname:T.Name) => I.Socket + call: (rpcname:T.Name, ...args: T.Any[]) => Promise + fire: (rpcname:T.Name, ...args: T.Any[]) => Promise + on: T.OnFunction + destroy: ()=>void + close: ()=>void +} \ No newline at end of file diff --git a/src/Response.ts b/src/Responses.ts similarity index 100% rename from src/Response.ts rename to src/Responses.ts diff --git a/src/Server.ts b/src/Server.ts index eedba7f..79fc8f9 100644 --- a/src/Server.ts +++ b/src/Server.ts @@ -1,23 +1,23 @@ +'use strict' + import http = require('http'); import bsock = require('bsock'); -import { ExtendedRpcInfo, SocketConf, SocketioRPC } from './Types'; -import { rpcHooker } from './Util'; -import { Exporter } from './interfaces/Exporter'; -import { Socket } from "./interfaces/Socket"; +import * as T from './Types'; +import * as U from './Utils'; +import * as I from './Interfaces'; export class Server{ - + private ws = http.createServer() private io = bsock.createServer() - private wsServer = http.createServer() constructor( private port:number, - private rpcExporters: Exporter[] = [], - private conf: SocketConf = { - errorHandler: (socket:Socket) => (error:any) => { socket.destroy(); console.error(error) }, - closeHandler: (socket:Socket) => () => { console.log("Socket closing") }, - connectionHandler: (socket:Socket) => { console.log("New websocket connection in port "+socket.port)}, + private exporters: I.Exporter[] = [], + private conf: T.SocketConf = { + errorHandler: (socket:I.Socket) => (error:any) => { socket.destroy(); console.error(error) }, + closeHandler: (socket:I.Socket) => () => { console.log("Socket closing") }, + connectionHandler: (socket:I.Socket) => { console.log("New websocket connection in port "+socket.port)}, visibility: "127.0.0.1" } ){ @@ -26,8 +26,8 @@ export class Server{ private startWebsocket(){ try{ - this.io.attach(this.wsServer) - this.io.on('socket', (socket:Socket) => { + this.io.attach(this.ws) + this.io.on('socket', (socket:I.Socket) => { socket.on('error', this.conf.errorHandler(socket)) socket.on('close', this.conf.closeHandler(socket)) if(this.conf.visibility === "127.0.0.1") @@ -35,26 +35,26 @@ export class Server{ else this.initPublicRPCs(socket) }) - this.wsServer.listen(this.port, this.conf.visibility) + this.ws.listen(this.port, this.conf.visibility) }catch(e){ //@ts-ignore this.errorHandler(undefined)("Unable to connect to socket") } } - protected initRPCs(socket:Socket){ - const infoRPC:SocketioRPC[] = [ - { - name: 'info', - type: 'Call', - func: async () => rpcInfos - } - ] - const rpcInfos:ExtendedRpcInfo[] = [ - ...rpcHooker(socket, "RPC", infoRPC, false), - ...this.rpcExporters.flatMap(exporter => rpcHooker(socket, exporter.name, [...exporter.exportPublicRPCs(), ...exporter.exportRPCs()])) + protected initRPCs(socket:I.Socket){ + socket.hook('info', () => rpcInfos) + + const rpcInfos:T.ExtendedRpcInfo[] = [ + ...this.exporters.flatMap(exporter => U.rpcHooker(socket, exporter)) ] } - protected initPublicRPCs(socket:Socket){} + protected initPublicRPCs(socket:I.Socket){ + socket.hook('info', () => rpcInfos) + + const rpcInfos:T.ExtendedRpcInfo[] = [ + ...this.exporters.flatMap(exporter => U.rpcHooker(socket, exporter)) + ] + } } \ No newline at end of file diff --git a/src/Types.ts b/src/Types.ts index cddbf3f..63dcdae 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -1,44 +1,47 @@ -import { SuccessResponse, ErrorResponse, SubscriptionResponse } from "./Response"; -import { Socket } from "./interfaces/Socket"; +import * as R from "./Responses"; +import * as I from "./Interfaces"; export type Visibility = "127.0.0.1" | "0.0.0.0" -export type Name = string +export type Any = any +export type Arg = string +export type Name = Arg +export type Owner = Name export type SocketConf = { - connectionHandler: (socket:Socket) => void - errorHandler: (socket:Socket) => (error:any) => void - closeHandler: (socket:Socket) => () => void + connectionHandler: (socket:I.Socket) => void + errorHandler: (socket:I.Socket) => (error:any) => void + closeHandler: (socket:I.Socket) => () => void visibility: Visibility } -export type rpcType = 'Hook' | 'Unhook' | 'Call' +export type RPCType = 'Hook' | 'Unhook' | 'Call' export type BaseRPC = { - type: rpcType - name: string + type: RPCType + name: Name } export type HookRPC = BaseRPC & { type: 'Hook' - func: CallbackFunction + clbk: CallbackFunction unhook: UnhookFunction } export type UnhookRPC = BaseRPC & { type: 'Unhook' - func: UnhookFunction + unhook: UnhookFunction } export type CallRPC = BaseRPC & { type: 'Call' - func: (...args) => Promise + call: AsyncFunction } -export type SocketioRPC = CallRPC | UnhookRPC | HookRPC +export type RPC = CallRPC | UnhookRPC | HookRPC export type BaseInfo = { - owner: string, - argNames: string[], + owner: Name, + argNames: Name[], } export type HookInfo = BaseRPC & BaseInfo & { @@ -49,18 +52,18 @@ export type HookInfo = BaseRPC & BaseInfo & { export type UnhookInfo = BaseRPC & BaseInfo & { type: 'Unhook', - func: UnhookFunction + unhook: UnhookFunction } export type CallInfo = BaseRPC & BaseInfo & { type: 'Call', - func: AsyncFunction + call: AsyncFunction } export type RpcInfo = HookInfo | UnhookInfo | CallInfo export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } -export type OnFunction = (type: 'error' | 'close', f: (e?:any)=>void) => Socket -export type UnhookFunction = (uid:string) => Promise -export type CallbackFunction = (...args) => Promise +export type OnFunction = (type: 'error' | 'close', f: (e?:any)=>void) => I.Socket +export type UnhookFunction = (uid:string) => Promise +export type CallbackFunction = (...args) => Promise export type AsyncFunction = (...args) => Promise diff --git a/src/Util.ts b/src/Utils.ts similarity index 65% rename from src/Util.ts rename to src/Utils.ts index 18d2bdd..d288c61 100644 --- a/src/Util.ts +++ b/src/Utils.ts @@ -1,25 +1,25 @@ import * as uuid from "uuid/v4" -import { HookRPC, HookInfo, SocketioRPC, RpcInfo, ExtendedRpcInfo } from "./Types"; -import { Socket } from "./interfaces/Socket"; +import * as T from "./Types"; +import * as I from "./Interfaces"; -export const rpcToRpcinfo = (rpc : SocketioRPC, owner: string):RpcInfo => { +export const rpcToRpcinfo = (rpc : T.RPC, owner: T.Owner):T.RpcInfo => { switch(rpc.type){ case "Call" : return { owner: owner, - argNames: extractArgs(rpc.func), + argNames: extractArgs(rpc.call), type: rpc.type, name: rpc.name, - func: rpc.func, + call: rpc.call, } case "Unhook" : return { owner: owner, - argNames: extractArgs(rpc.func), + argNames: extractArgs(rpc.unhook), type: rpc.type, name: rpc.name, - func: rpc.func, + unhook: rpc.unhook, } case "Hook" : const generator = hookGenerator(rpc) @@ -33,8 +33,9 @@ export const rpcToRpcinfo = (rpc : SocketioRPC, owner: string):RpcInfo => { } } } - -export const rpcHooker = (socket: Socket, owner:string, RPCs: SocketioRPC[], makeUnique = true):ExtendedRpcInfo[] => { +export function rpcHooker(socket: I.Socket, exporter:I.Exporter, makeUnique = true):T.ExtendedRpcInfo[]{ + const owner = exporter.name + const RPCs = [...exporter.publicRPCs(), ...exporter.localRPCs()] const suffix = makeUnique?"-"+uuid().substr(0,4):"" return RPCs.map(rpc => rpcToRpcinfo(rpc, owner)) .map(info => { @@ -45,21 +46,25 @@ export const rpcHooker = (socket: Socket, owner:string, RPCs: SocketioRPC[], mak case "Hook": socket.hook(ret.uniqueName, info.generator(socket)) break; - default: - socket.hook(ret.uniqueName, info.func) + case "Unhook": + socket.hook(ret.uniqueName, info.unhook) + break; + case "Call": + socket.hook(ret.uniqueName, info.call) + break; } socket.on('close', () => socket.unhook(info.name)) return ret }) } -const hookGenerator = (rpc:HookRPC): HookInfo['generator'] => { - const argsArr = extractArgs(rpc.func) +const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => { + const argsArr = extractArgs(rpc.clbk) argsArr.pop() const args = argsArr.join(',') return eval(`(socket) => async (`+args+`) => { - const res = await rpc.func(`+args+(args.length!==0?',':'')+` (x) => { + const res = await rpc.clbk(`+args+(args.length!==0?',':'')+` (x) => { socket.call(res.uid, x) }) if(res.result == 'Success'){ @@ -73,7 +78,7 @@ const hookGenerator = (rpc:HookRPC): HookInfo['generator'] => { }`) } -const extractArgs = (f:Function):string[] => { +const extractArgs = (f:Function):T.Arg[] => { let fn = String(f) let args = fn.substr(0, fn.indexOf(")")) args = args.substr(fn.indexOf("(")+1) diff --git a/src/backend/RPCSocketServer.ts b/src/backend/RPCSocketServer.ts deleted file mode 100644 index 71ca88e..0000000 --- a/src/backend/RPCSocketServer.ts +++ /dev/null @@ -1,256 +0,0 @@ -import http = require('http'); -import bsock = require('bsock'); - -import * as uuid from "uuid/v4" -import { Socket } from "./RPCSocketServer" - -type rpcType = 'hook' | 'unhook' | 'call' -export type Outcome = "Success" | "Error" -export type Visibility = "127.0.0.1" | "0.0.0.0" - -/* Responses */ -export class Response{ - constructor( - public message?:string - ){} -} - -export class SuccessResponse extends Response{ - result:Outcome = "Success" - - constructor( - message?:string - ){ - super(message) - } -} - -export class ErrorResponse extends Response{ - result:Outcome = "Error" - - constructor( - message: string = "Unknown error" - ){ - super(message) - } -} - -export class SubscriptionResponse extends SuccessResponse{ - constructor( - public uid: string, - message?:string - ){ - super(message) - } -} -export type UnhookFunction = (uid:string) => Promise -export type callbackFunction = (...args) => Promise -export type AsyncFunction = (...args) => Promise - -export interface RPCExporter{ - name: string - exportRPCs() : socketioRPC[] - exportPublicRPCs() : socketioRPC[] -} - -type baseRPC = { - type: rpcType - name: string -} - -type hookRPC = baseRPC & { - type: 'hook' - func: callbackFunction - unhook: UnhookFunction -} - -type unhookRPC = baseRPC & { - type: 'unhook' - func: UnhookFunction -} - -type callRPC = baseRPC & { - type: 'call' - func: (...args) => Promise -} - -export type socketioRPC = callRPC | unhookRPC | hookRPC - -export type baseInfo = { - owner: string, - argNames: string[], -} - -type HookInfo = baseRPC & baseInfo & { - type: 'hook', - generator: (socket) => callbackFunction - unhook: UnhookFunction -} - -type UnhookInfo = baseRPC & baseInfo & { - type: 'unhook', - func: UnhookFunction -} - -type CallInfo = baseRPC & baseInfo & { - type: 'call', - func: AsyncFunction -} - -type RpcInfo = HookInfo | UnhookInfo | CallInfo - -export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } - -export const rpcToRpcinfo = (rpc : socketioRPC, owner: string):RpcInfo => { - switch(rpc.type){ - case "call" : - return { - owner: owner, - argNames: extractArgs(rpc.func), - type: rpc.type, - name: rpc.name, - func: rpc.func, - } - case "unhook" : - return { - owner: owner, - argNames: extractArgs(rpc.func), - type: rpc.type, - name: rpc.name, - func: rpc.func, - } - case "hook" : - const generator = hookGenerator(rpc) - return { - owner: owner, - argNames: extractArgs(generator(undefined)), - type: rpc.type, - name: rpc.name, - unhook: rpc.unhook, - generator: generator, - } - } -} - - -function rpcHooker(socket: Socket, exporter:RPCExporter, makeUnique = true):ExtendedRpcInfo[]{ - const owner = exporter.name - const RPCs = [...exporter.exportPublicRPCs(), ...exporter.exportRPCs()] - const suffix = makeUnique?"-"+uuid().substr(0,4):"" - return RPCs.map(rpc => rpcToRpcinfo(rpc, owner)) - .map(info => { - const ret:any = info - ret.uniqueName = info.name+suffix - - switch(info.type){ - case "hook": - socket.hook(ret.uniqueName, info.generator(socket)) - break; - default: - socket.hook(ret.uniqueName, info.func) - } - socket.on('close', () => socket.unhook(info.name)) - return ret - }) -} - -const hookGenerator = (rpc:hookRPC): HookInfo['generator'] => { - const argsArr = extractArgs(rpc.func) - argsArr.pop() - const args = argsArr.join(',') - - return eval(`(socket) => async (`+args+`) => { - const res = await rpc.func(`+args+(args.length!==0?',':'')+` (x) => { - socket.call(res.uid, x) - }) - if(res.result == 'Success'){ - socket.on('close', async () => { - const unhookRes = await rpc.unhook(res.uid) - console.log("Specific close handler for", rpc.name, res.uid, unhookRes) - }) - - } - return res - }`) -} - -const extractArgs = (f:Function):string[] => { - let fn = String(f) - let args = fn.substr(0, fn.indexOf(")")) - args = args.substr(fn.indexOf("(")+1) - let ret = args.split(",") - return ret -} - - -type OnFunction = (type: 'error' | 'close', f: (e?:any)=>void) => Socket - -export interface Socket { - port: number - hook: (rpcname: string, ...args: any[]) => Socket - unhook: (rpcname:string) => Socket - call: (rpcname:string, ...args: any[]) => Promise - fire: (rpcname:string, ...args: any[]) => Promise - on: OnFunction - destroy: ()=>void - close: ()=>void -} - -export type RPCSocketConf = { - connectionHandler: (socket:Socket) => void - errorHandler: (socket:Socket) => (error:any) => void - closeHandler: (socket:Socket) => () => void -} - -export class RPCSocketServer{ - - private io = bsock.createServer() - private wsServer = http.createServer() - - constructor( - private port:number, - private rpcExporters: RPCExporter[] = [], - private visibility: Visibility = "127.0.0.1", - private conf: RPCSocketConf = { - errorHandler: (socket:Socket) => (error:any) => { socket.destroy(); console.error(error) }, - closeHandler: (socket:Socket) => () => { console.log("Socket closing") }, - connectionHandler: (socket:Socket) => { console.log("New websocket connection in port "+socket.port) } - } - ){ - this.startWebsocket() - } - - private startWebsocket(){ - try{ - this.io.attach(this.wsServer) - this.io.on('socket', (socket:Socket) => { - socket.on('error', this.conf.errorHandler(socket)) - socket.on('close', this.conf.closeHandler(socket)) - if(this.visibility === "127.0.0.1") - this.initRPCs(socket) - else - this.initPublicRPCs(socket) - }) - this.wsServer.listen(this.port, this.visibility) - }catch(e){ - //@ts-ignore - this.errorHandler(undefined)("Unable to connect to socket") - } - } - - protected initRPCs(socket:Socket){ - socket.hook('info', () => rpcInfos) - - const rpcInfos:ExtendedRpcInfo[] = [ - ...this.rpcExporters.flatMap(exporter => rpcHooker(socket, exporter)) - ] - } - - protected initPublicRPCs(socket:Socket){ - socket.hook('info', () => rpcInfos) - - const rpcInfos:ExtendedRpcInfo[] = [ - ...this.rpcExporters.flatMap(exporter => rpcHooker(socket, exporter)) - ] - } -} \ No newline at end of file diff --git a/src/interfaces/Exporter.ts b/src/interfaces/Exporter.ts deleted file mode 100644 index 047ddcb..0000000 --- a/src/interfaces/Exporter.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { SocketioRPC, Name } from "../Types"; - -export interface Exporter{ - name: Name - exportRPCs() : SocketioRPC[] - exportPublicRPCs() : SocketioRPC[] -} \ No newline at end of file diff --git a/src/interfaces/Socket.ts b/src/interfaces/Socket.ts deleted file mode 100644 index bc1b6ac..0000000 --- a/src/interfaces/Socket.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { OnFunction } from "../Types"; - -export interface Socket { - port: number - hook: (rpcname: string, ...args: any[]) => Socket - unhook: (rpcname:string) => Socket - call: (rpcname:string, ...args: any[]) => Promise - fire: (rpcname:string, ...args: any[]) => Promise - on: OnFunction - destroy: ()=>void - close: ()=>void -} \ No newline at end of file diff --git a/src/webpack.prod.js b/src/webpack.prod.js index 18530dc..dba9774 100644 --- a/src/webpack.prod.js +++ b/src/webpack.prod.js @@ -1,27 +1,59 @@ const path = require('path'); const TerserPlugin = require('terser-webpack-plugin'); -module.exports = { +const frontendConf = { + mode: 'production', + target: "web", + entry: path.resolve(__dirname, 'Client.ts'), + output: { + path: path.resolve(__dirname, '../lib'), + filename: 'Frontend.js', + libraryTarget: 'commonjs', + }, + module: { + rules: [ + { test: /\.ts?$/, loader: "ts-loader" } + ] + }, + resolve: { + extensions: [".ts", ".tsx", ".js"] + }, + optimization: { + minimize: true, + minimizer: [ + new TerserPlugin({ + parallel: true, + exclude: [ + /\.\/(.*)\/.ts/, + /\.\/(.*).ts/, + ], + }), + ], + }, +} + +const backendConf = { mode: 'production', - target: "web", - entry: path.resolve(__dirname, 'Client.ts'), + target: "node", + entry: path.resolve(__dirname, 'Server.ts'), output: { - path: path.resolve(__dirname, '../../lib'), - filename: 'Frontend.min.js', + path: path.resolve(__dirname, '../lib'), + filename: 'Backend.js', libraryTarget: 'commonjs', }, - resolve: { - // Add `.ts` and `.tsx` as a resolvable extension. - extensions: [".ts", ".tsx", ".js"] - }, module: { rules: [ { test: /\.ts?$/, loader: "ts-loader" } ] }, + resolve: { + extensions: [".ts", ".tsx", ".js"] + }, optimization: { + minimize: true, minimizer: [ new TerserPlugin({ + parallel: true, exclude: [ /\.\/(.*)\/.ts/, /\.\/(.*).ts/, @@ -29,6 +61,6 @@ module.exports = { }), ], }, - externals: { - } } + +module.exports = [frontendConf, backendConf] diff --git a/test/test.ts b/test/test.ts index da259d4..2c12800 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,15 +1,14 @@ import { Server } from '../src/Server' -//@ts-ignore import {Client} from '../src/Client' new Server(20000, [{ name: "HelloWorldRPCGroup", - exportPublicRPCs: () => [], - exportRPCs: () => [{ - type: 'call', + publicRPCs: () => [], + localRPCs: () => [{ + type: 'Call', name: 'echo', - func: async (s:string) => s, - }], + call: async (s:string) => s, + }] }]) const caller = new Client(20000, 'localhost') diff --git a/tsconfig.json b/tsconfig.json index ae05096..abe5456 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,14 @@ { - "compilerOptions": { - "strictPropertyInitialization": false, - "noImplicitAny": false, - "target": "ESnext", - "module": "commonjs", - "declaration": true, - "outDir": "./lib", - "strict": true, - "experimentalDecorators": true - }, - "include": ["src/backend/**/*", "test/**/*"], - "exclude": ["node_modules", "**/__tests__/*", "src/frontend/**/*"] + "compilerOptions": { + "strictPropertyInitialization": false, + "noImplicitAny": false, + "target": "ESnext", + "module": "commonjs", + "declaration": true, + "outDir": "./js", + "strict": true, + "experimentalDecorators": true + }, + "include": ["src/**/*.ts", "test/**/*.ts"], + "exclude": ["node_modules"], } \ No newline at end of file