Compare commits
10
Commits
bf96f9007f
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
217bb4a549 | ||
|
|
cbe7f6eb1a | ||
|
|
dbed0081db | ||
|
|
c138a2b9af | ||
|
|
ec158e6090 | ||
|
|
c01a44ff3b | ||
|
|
928d2662fd | ||
|
|
b2ffbbfa48 | ||
|
|
a1f4055194 | ||
|
|
7e7ec0deb8 |
Generated
+493
-10727
File diff suppressed because it is too large
Load Diff
+15
-12
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "rpclibrary",
|
"name": "rpclibrary",
|
||||||
"version": "2.4.1",
|
"version": "2.5.1",
|
||||||
"description": "rpclibrary is a websocket RPC library",
|
"description": "rpclibrary is a websocket RPC library",
|
||||||
"main": "./js/Index.js",
|
"main": "./js/Index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -46,24 +46,27 @@
|
|||||||
"ts-mocha": "^6.0.0",
|
"ts-mocha": "^6.0.0",
|
||||||
"typedoc": "^0.15.0",
|
"typedoc": "^0.15.0",
|
||||||
"typedoc-plugin-markdown": "^2.2.6",
|
"typedoc-plugin-markdown": "^2.2.6",
|
||||||
"typescript": "^3.5.3",
|
"typescript": "^4.6.3",
|
||||||
"webpack": "^4.40.2",
|
"webpack": "^5.71.0",
|
||||||
"webpack-cli": "^3.3.9",
|
"webpack-cli": "^3.3.9",
|
||||||
"why-is-node-running": "^2.1.2"
|
"why-is-node-running": "^2.1.2",
|
||||||
|
"@types/chai": "^4.2.21",
|
||||||
|
"@types/socket.io": "^3.0.2",
|
||||||
|
"@types/socket.io-client": "^3.0.0",
|
||||||
|
"chai": "^4.3.4",
|
||||||
|
"chai-as-promised": "^7.1.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/chai": "^4.2.21",
|
|
||||||
"@types/socket.io": "^2.1.8",
|
|
||||||
"@types/socket.io-client": "^1.4.33",
|
|
||||||
"chai": "^4.3.4",
|
|
||||||
"chai-as-promised": "^7.1.1",
|
|
||||||
"http": "0.0.0",
|
"http": "0.0.0",
|
||||||
"socket.io": "^2.3.0",
|
"socket.io": "^4.5.4",
|
||||||
"socket.io-client": "^2.3.0",
|
"socket.io-client": "^4.5.4",
|
||||||
"socketio-wildcard": "^2.0.0",
|
"socketio-wildcard": "^2.0.0",
|
||||||
"uuid": "^3.3.3"
|
"uuid": "^3.3.3"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"js"
|
"js/browser",
|
||||||
|
"js/src",
|
||||||
|
"js/Index.js",
|
||||||
|
"js/Index.d.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-13
@@ -5,7 +5,8 @@ import { PromiseIO } from "./PromiseIO/Server";
|
|||||||
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 { BAD_CONFIG_PARAM, UNKNOWN_RPC_IDENTIFIER, UNKNOWN_RPC_SERVER } from './Strings';
|
import { BAD_CONFIG_PARAM, DESTROY_PREFIX, RPC_NO_NAME, UNKNOWN_RPC_IDENTIFIER, UNKNOWN_RPC_SERVER } from './Strings';
|
||||||
|
import { ServerOptions } from 'socket.io';
|
||||||
|
|
||||||
export class RPCServer<
|
export class RPCServer<
|
||||||
InterfaceT extends T.RPCInterface = T.RPCInterface,
|
InterfaceT extends T.RPCInterface = T.RPCInterface,
|
||||||
@@ -65,13 +66,7 @@ export class RPCServer<
|
|||||||
|
|
||||||
let badRPC = exporters.flatMap(ex => typeof ex.RPCs === "function" ? ex.RPCs() : (ex as any)).find(rpc => !rpc.name)
|
let badRPC = exporters.flatMap(ex => typeof ex.RPCs === "function" ? ex.RPCs() : (ex as any)).find(rpc => !rpc.name)
|
||||||
if (badRPC) {
|
if (badRPC) {
|
||||||
throw new Error(`
|
throw new Error(RPC_NO_NAME(badRPC.toString()))
|
||||||
RPC did not provide a name.
|
|
||||||
\nUse 'funtion name(..){ .. }' syntax instead.
|
|
||||||
\n
|
|
||||||
\n<------------OFFENDING RPC:
|
|
||||||
\n`+ badRPC.toString() + `
|
|
||||||
\n>------------OFFENDING RPC`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -86,13 +81,13 @@ export class RPCServer<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public attach = (httpServer = new http.Server(), options?: SocketIO.ServerOptions): RPCServer<InterfaceT> => {
|
public attach = (httpServer = new http.Server(), options?: T.SomeOf<ServerOptions>): RPCServer<InterfaceT> => {
|
||||||
this.pio.attach(httpServer, options)
|
this.pio.attach(httpServer, options)
|
||||||
this.attached = true
|
this.attached = true
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
public listen(port: number, options?: SocketIO.ServerOptions): RPCServer<InterfaceT> {
|
public listen(port: number, options?: T.SomeOf<ServerOptions>): RPCServer<InterfaceT> {
|
||||||
if (!this.attached) {
|
if (!this.attached) {
|
||||||
this.attach(undefined, options)
|
this.attach(undefined, options)
|
||||||
} else {
|
} else {
|
||||||
@@ -114,17 +109,19 @@ export class RPCServer<
|
|||||||
if (this.conf.throwOnUnknownRPC) {
|
if (this.conf.throwOnUnknownRPC) {
|
||||||
clientSocket.on("*", (packet) => {
|
clientSocket.on("*", (packet) => {
|
||||||
if (!infos.some(i => i.uniqueName === packet.data[0])) {
|
if (!infos.some(i => i.uniqueName === packet.data[0])) {
|
||||||
if (packet.data[0].startsWith('destroy_')) return
|
if (packet.data[0].startsWith(DESTROY_PREFIX)) return
|
||||||
this.errorHandler(clientSocket, new Error(UNKNOWN_RPC_SERVER(packet.data[0])), packet.data[0], [...packet.data].splice(1), true)
|
this.errorHandler(clientSocket, new Error(UNKNOWN_RPC_SERVER(packet.data[0])), packet.data[0], [...packet.data].splice(1), true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return infos
|
return infos
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getSocket(): PromiseIO{
|
||||||
|
return this.pio
|
||||||
|
}
|
||||||
|
|
||||||
close(): void {
|
close(): void {
|
||||||
this.pio.close()
|
this.pio.close()
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-20
@@ -4,7 +4,7 @@ import { PromiseIOClient, defaultClientConfig } from './PromiseIO/Client'
|
|||||||
import * as T from './Types';
|
import * as T from './Types';
|
||||||
import * as I from './Interfaces';
|
import * as I from './Interfaces';
|
||||||
import { stripAfterEquals, appendComma } from './Utils';
|
import { stripAfterEquals, appendComma } from './Utils';
|
||||||
import { SOCKET_NOT_CONNECTED, UNKNOWN_RPC_IDENTIFIER, USER_DEFINED_TIMEOUT } from './Strings';
|
import { CALLBACK_NAME, DESTROY_PREFIX, SOCKET_NOT_CONNECTED, UNKNOWN_RPC_IDENTIFIER, USER_DEFINED_TIMEOUT } from './Strings';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,12 +19,12 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
|
|
||||||
private socket: I.Socket
|
private socket: I.Socket
|
||||||
private handlers: {
|
private handlers: {
|
||||||
[name in string]: T.AnyFunction[]
|
[name in string]: T.GenericFunction[]
|
||||||
} = {
|
} = {
|
||||||
error: [],
|
error: [],
|
||||||
close: []
|
close: []
|
||||||
}
|
}
|
||||||
private hooks: { [name in string]: T.AnyFunction } = {}
|
private hooks: { [name in string]: T.GenericFunction } = {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -80,7 +80,7 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
* @param type 'error' or 'close'
|
* @param type 'error' or 'close'
|
||||||
* @param f The listener to attach
|
* @param f The listener to attach
|
||||||
*/
|
*/
|
||||||
public on(type: string, f: T.AnyFunction) {
|
public on(type: string, f: T.GenericFunction) {
|
||||||
if (!this.socket) {
|
if (!this.socket) {
|
||||||
if (!this.handlers[type])
|
if (!this.handlers[type])
|
||||||
this.handlers[type] = []
|
this.handlers[type] = []
|
||||||
@@ -117,17 +117,21 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
public async call(rpcname: string, ...args: any[]): Promise<any> {
|
public async call(rpcname: string, ...args: any[]): Promise<any> {
|
||||||
if (!this.socket) throw new Error(SOCKET_NOT_CONNECTED)
|
if (!this.socket) throw new Error(SOCKET_NOT_CONNECTED)
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(!this.conf.callTimeoutMs || this.conf.callTimeoutMs <= 0)
|
if(!this.conf.callTimeoutMs || this.conf.callTimeoutMs <= 0)
|
||||||
return await this.socket.call.apply(this.socket, [rpcname, ...args])
|
return await this.socket.call.apply(this.socket, [rpcname, ...args])
|
||||||
else
|
else
|
||||||
return await Promise.race([
|
if(this.conf.callTimeoutMs){
|
||||||
this.socket.call.apply(this.socket, [rpcname, ...args]),
|
return await Promise.race([
|
||||||
new Promise((_, rej) => {
|
this.socket.call.apply(this.socket, [rpcname, ...args]),
|
||||||
setTimeout(_ => rej(USER_DEFINED_TIMEOUT(this.conf.callTimeoutMs)), this.conf.callTimeoutMs)
|
new Promise((_, rej) => {
|
||||||
})
|
setTimeout(_ => rej(USER_DEFINED_TIMEOUT(this.conf.callTimeoutMs)), this.conf.callTimeoutMs)
|
||||||
])
|
})
|
||||||
|
])
|
||||||
|
}else{
|
||||||
|
return await this.socket.call.apply(this.socket, [rpcname, ...args])
|
||||||
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.emit('error', e)
|
this.emit('error', e)
|
||||||
throw e
|
throw e
|
||||||
@@ -160,7 +164,7 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
v.forEach(h => this.socket.on(k, h))
|
v.forEach(h => this.socket.on(k, h))
|
||||||
})
|
})
|
||||||
|
|
||||||
Object.entries(this.hooks).forEach((kv: [string, T.AnyFunction]) => {
|
Object.entries(this.hooks).forEach((kv: [string, T.GenericFunction]) => {
|
||||||
this.socket.hook(kv[0], kv[1])
|
this.socket.hook(kv[0], kv[1])
|
||||||
})
|
})
|
||||||
const info: T.ExtendedRpcInfo[] = await this.info(sesame)
|
const info: T.ExtendedRpcInfo[] = await this.info(sesame)
|
||||||
@@ -199,13 +203,13 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
* @param fnName The function name
|
* @param fnName The function name
|
||||||
* @param fnArgs A string-list of parameters
|
* @param fnArgs A string-list of parameters
|
||||||
*/
|
*/
|
||||||
private callGenerator(fnName: string, fnArgs: string[], sesame?: string): T.AnyFunction {
|
private callGenerator(fnName: string, fnArgs: string[], sesame?: string): T.GenericFunction {
|
||||||
const headerArgs = fnArgs.join(",")
|
const headerArgs = fnArgs.join(",")
|
||||||
const argParams = fnArgs.map(stripAfterEquals).join(",")
|
const argParams = fnArgs.map(stripAfterEquals).join(",")
|
||||||
sesame = appendComma(sesame)
|
sesame = appendComma(sesame)
|
||||||
|
|
||||||
return eval(`async (${headerArgs}) => {
|
return eval(`async (${headerArgs}) => {
|
||||||
return await this.call("${fnName}", ${sesame} ${argParams})
|
const returnvalue = await this.call("${fnName}", ${sesame} ${argParams})
|
||||||
|
return returnvalue
|
||||||
}`)
|
}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +218,7 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
* @param fnName The function name
|
* @param fnName The function name
|
||||||
* @param fnArgs A string-list of parameters
|
* @param fnArgs A string-list of parameters
|
||||||
*/
|
*/
|
||||||
private frontEndHookGenerator(fnName: string, fnArgs: string[], sesame?: string): T.HookFunction {
|
private frontEndHookGenerator(fnName: string, fnArgs: string[], sesame?: string): T.GenericFunction {
|
||||||
|
|
||||||
if (sesame)
|
if (sesame)
|
||||||
fnArgs.shift()
|
fnArgs.shift()
|
||||||
@@ -223,18 +227,25 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
const argParams = fnArgs.map(stripAfterEquals).join(",")
|
const argParams = fnArgs.map(stripAfterEquals).join(",")
|
||||||
sesame = appendComma(sesame, true)
|
sesame = appendComma(sesame, true)
|
||||||
headerArgs = fnArgs.length > 0 ? headerArgs + "," : headerArgs
|
headerArgs = fnArgs.length > 0 ? headerArgs + "," : headerArgs
|
||||||
|
const destroy_prefix = DESTROY_PREFIX
|
||||||
|
|
||||||
const frontendHookStr = `
|
const frontendHookStr = `
|
||||||
async (${headerArgs} $__callback__$) => {
|
async (${CALLBACK_NAME}, ${headerArgs}) => {
|
||||||
const r = await this.call("${fnName}", ${sesame} ${argParams})
|
const r = await this.call("${fnName}", ${sesame} ${argParams})
|
||||||
try{
|
try{
|
||||||
if(r){
|
if(r){
|
||||||
if(r.uuid){
|
if(r.uuid){
|
||||||
$__callback__$['destroy'] = () => {
|
${CALLBACK_NAME}['destroy'] = () => {
|
||||||
this.socket.fire('destroy_'+r.uuid)
|
this.socket.fire(destroy_prefix+r.uuid)
|
||||||
this.socket.unhook(r.uuid)
|
this.socket.unhook(r.uuid)
|
||||||
}
|
}
|
||||||
this.socket.hook(r.uuid, $__callback__$)
|
${CALLBACK_NAME} = ${CALLBACK_NAME}.bind({
|
||||||
|
destroy: ${CALLBACK_NAME}['destroy']
|
||||||
|
})
|
||||||
|
|
||||||
|
this.socket.hook(r.uuid, (...args) => {
|
||||||
|
${CALLBACK_NAME}.apply(${CALLBACK_NAME}, args)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return r.return
|
return r.return
|
||||||
}else{
|
}else{
|
||||||
|
|||||||
+2
-2
@@ -15,10 +15,10 @@ export interface Socket {
|
|||||||
id?: string
|
id?: string
|
||||||
bind: (name: string, listener: T.PioBindListener) => void
|
bind: (name: string, listener: T.PioBindListener) => void
|
||||||
hook: (rpcname: string, handler: T.PioHookListener) => void
|
hook: (rpcname: string, handler: T.PioHookListener) => void
|
||||||
unhook: (rpcname: string, listener?:T.AnyFunction) => void
|
unhook: (rpcname: string, listener?:T.GenericFunction) => void
|
||||||
call: (rpcname: string, ...args: any[]) => Promise<any>
|
call: (rpcname: string, ...args: any[]) => Promise<any>
|
||||||
fire: (rpcname: string, ...args: any[]) => Promise<any>
|
fire: (rpcname: string, ...args: any[]) => Promise<any>
|
||||||
on: (type: string, f: T.AnyFunction)=>any
|
on: (type: string, f: T.GenericFunction)=>any
|
||||||
emit: (eventName: string, ...args: any[]) => void
|
emit: (eventName: string, ...args: any[]) => void
|
||||||
close(): void
|
close(): void
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ export class PromiseIOClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const address = `${host}:${port}`
|
const address = `${host}:${port}`
|
||||||
const socket = socketio(`${options.protocol?options.protocol:'http'}://${address}`, options)
|
const socket = socketio.io(`${options.protocol?options.protocol:'http'}://${address}`, options)
|
||||||
|
|
||||||
socket.on('connect_error', e => {
|
socket.on('connect_error', e => {
|
||||||
sock.emit('error', e)
|
sock.emit('error', e)
|
||||||
|
|||||||
@@ -5,10 +5,8 @@ import * as T from '../Types'
|
|||||||
import socketio = require('socket.io')
|
import socketio = require('socket.io')
|
||||||
import middleware = require('socketio-wildcard');
|
import middleware = require('socketio-wildcard');
|
||||||
|
|
||||||
const defaultConfig : socketio.ServerOptions = {
|
|
||||||
cookie: false,
|
const defaultConfig : T.SomeOf<socketio.ServerOptions> = {}
|
||||||
path: '/socket.io',
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PromiseIO {
|
export class PromiseIO {
|
||||||
io?: Server
|
io?: Server
|
||||||
@@ -18,12 +16,12 @@ export class PromiseIO {
|
|||||||
connect: []
|
connect: []
|
||||||
}
|
}
|
||||||
|
|
||||||
attach(httpServer: httpServer, options: socketio.ServerOptions = defaultConfig) {
|
attach(httpServer: httpServer, options: T.SomeOf<socketio.ServerOptions> = defaultConfig) {
|
||||||
if(options.path && !options.path.startsWith('/')){
|
if(options.path && !options.path.startsWith('/')){
|
||||||
options.path = "/"+options.path
|
options.path = "/"+options.path
|
||||||
}
|
}
|
||||||
this.httpServer = httpServer
|
this.httpServer = httpServer
|
||||||
this.io = socketio(httpServer, options)
|
this.io = new Server(httpServer, options)
|
||||||
this.io!.use(middleware())
|
this.io!.use(middleware())
|
||||||
this.io!.on('connection', (clientSocket: Socket) => {
|
this.io!.on('connection', (clientSocket: Socket) => {
|
||||||
|
|
||||||
@@ -50,7 +48,7 @@ export class PromiseIO {
|
|||||||
this.httpServer!.listen(port)
|
this.httpServer!.listen(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
on(eventName: string, listener: T.AnyFunction) {
|
on(eventName: string, listener: T.GenericFunction) {
|
||||||
if (this.listeners[eventName] == null) {
|
if (this.listeners[eventName] == null) {
|
||||||
this.listeners[eventName] = []
|
this.listeners[eventName] = []
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -11,4 +11,7 @@ RPC did not provide a name.
|
|||||||
\n
|
\n
|
||||||
\n<------------OFFENDING RPC:
|
\n<------------OFFENDING RPC:
|
||||||
\n${name}
|
\n${name}
|
||||||
\n>------------OFFENDING RPC`
|
\n>------------OFFENDING RPC`
|
||||||
|
export const CLASSNAME_ATTRIBUTE = "$__CLASSNAME__$"
|
||||||
|
export const DESTROY_PREFIX = "$__DESTROY__$_"
|
||||||
|
export const CALLBACK_NAME = "$__CALLBACK__$"
|
||||||
+78
-35
@@ -1,29 +1,51 @@
|
|||||||
import * as I from "./Interfaces";
|
import * as I from "./Interfaces";
|
||||||
import { RPCSocket } from "./Frontend";
|
import { RPCSocket } from "./Frontend";
|
||||||
import { PromiseIO } from "./PromiseIO/Server";
|
import { ManagerOptions, SocketOptions } from 'socket.io-client'
|
||||||
|
|
||||||
export type PioBindListener = (...args: any) => void
|
export type PioBindListener = (...args: any) => void
|
||||||
export type PioHookListener = AnyFunction
|
export type PioHookListener = GenericFunction
|
||||||
|
|
||||||
export type AnyFunction = (...args:any) => any
|
export type GenericFunction<Parameters extends any[] = any[], Result = any> = {(...args: Parameters): Result}
|
||||||
export type HookFunction = AnyFunction
|
|
||||||
export type AccessFilter<InterfaceT extends RPCInterface = RPCInterface> = (sesame:string|undefined, exporter: I.RPCExporter<InterfaceT, keyof InterfaceT>) => Promise<boolean> | boolean
|
export type BackendHook<Func extends GenericFunction> =
|
||||||
|
GenericFunction<
|
||||||
|
[
|
||||||
|
GenericFunction<
|
||||||
|
Parameters<
|
||||||
|
AsFunction<
|
||||||
|
Parameters<Func>[0]
|
||||||
|
>
|
||||||
|
>,
|
||||||
|
void
|
||||||
|
>,
|
||||||
|
...Tail<Parameters<Func>>
|
||||||
|
],
|
||||||
|
ReturnType<Func>
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
export type AccessFilter<InterfaceT extends RPCInterface = RPCInterface> = (sesame: string | undefined, exporter: I.RPCExporter<InterfaceT, keyof InterfaceT>) => Promise<boolean> | 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
|
||||||
export type CloseHandler = (socket:I.Socket) => void
|
export type CloseHandler = (socket: I.Socket) => void
|
||||||
export type SesameFunction = (sesame : string) => boolean
|
export type SesameFunction = (sesame: string) => boolean
|
||||||
export type SesameConf = {
|
export type SesameConf = {
|
||||||
sesame?: string | SesameFunction
|
sesame?: string | SesameFunction
|
||||||
}
|
}
|
||||||
export type FrontEndHandlerType = {
|
export type FrontEndHandlerType = {
|
||||||
'error' : (e: any) => void
|
'error': (e: any) => void
|
||||||
'close' : () => void
|
'close': () => void
|
||||||
}
|
}
|
||||||
export type ClientConfig = SocketIOClient.ConnectOpts & {
|
export type ClientConfig = Partial<ManagerOptions & SocketOptions> & {
|
||||||
protocol?: 'http' | 'https',
|
protocol?: 'http' | 'https',
|
||||||
callTimeoutMs?: number
|
callTimeoutMs?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SomeOf<T> = {
|
||||||
|
[key in keyof T]?: T[key]
|
||||||
|
}
|
||||||
|
|
||||||
export type ExporterArray<InterfaceT extends RPCInterface = RPCInterface> = I.RPCExporter<RPCInterface<InterfaceT>, keyof InterfaceT>[]
|
export type ExporterArray<InterfaceT extends RPCInterface = RPCInterface> = I.RPCExporter<RPCInterface<InterfaceT>, keyof InterfaceT>[]
|
||||||
|
|
||||||
export type ConnectedSocket<T extends RPCInterface = RPCInterface> = RPCSocket & AsyncIfc<T>
|
export type ConnectedSocket<T extends RPCInterface = RPCInterface> = RPCSocket & AsyncIfc<T>
|
||||||
@@ -40,30 +62,29 @@ export type ResponseType = "Subscribe" | "Success" | "Error"
|
|||||||
export type Outcome = "Success" | "Error"
|
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 RPCType = 'Hook' | 'Unhook' | 'Call'
|
export type RPCType = 'Hook' | 'Unhook' | 'Call'
|
||||||
|
|
||||||
export type CallRPC<Name, Func extends AnyFunction> = {
|
export type CallRPC<Name, Func extends GenericFunction> = {
|
||||||
name: Name
|
name: Name
|
||||||
call: Func
|
call: Func
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type HookRPC<Name, Func extends GenericFunction> = {
|
||||||
export type HookRPC<Name, Func extends AnyFunction> = {
|
|
||||||
name: Name
|
name: Name
|
||||||
hook: Func
|
hook: BackendHook<Func>
|
||||||
onCallback?: AnyFunction
|
onCallback?: GenericFunction
|
||||||
onDestroy?: HookCloseFunction<ReturnType<Func> extends Promise<infer T> ? T : ReturnType<Func>>
|
onDestroy?: HookCloseFunction<ReturnType<Func> extends Promise<infer T> ? T : ReturnType<Func>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RPC<Name, Func extends AnyFunction> = HookRPC<Name, Func> | CallRPC<Name,Func> | Func
|
export type RPC<Name, Func extends GenericFunction> = 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]: {
|
||||||
[rpc in string] : AnyFunction
|
[rpc in string]: GenericFunction
|
||||||
}
|
}
|
||||||
} & Impl
|
} & Impl
|
||||||
|
|
||||||
export type exportT = {
|
export type exportT = {
|
||||||
@@ -71,7 +92,9 @@ export type exportT = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type RPCDefinitions<Ifc extends RPCInterface> = {
|
export type RPCDefinitions<Ifc extends RPCInterface> = {
|
||||||
[grp in keyof Ifc]:( { [rpc in keyof Ifc[grp]]: RPC<rpc, Ifc[grp][rpc]> }[keyof Ifc[grp]] )[]
|
[grp in keyof Ifc]: ({
|
||||||
|
[rpc in keyof Ifc[grp]]: RPC<rpc, Ifc[grp][rpc]>
|
||||||
|
}[keyof Ifc[grp]])[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BaseInfo = {
|
export type BaseInfo = {
|
||||||
@@ -80,25 +103,45 @@ export type BaseInfo = {
|
|||||||
argNames: string[],
|
argNames: string[],
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HookInfo<SubresT = {}> = BaseInfo & {
|
export type HookInfo<SubresT = {}> = BaseInfo & {
|
||||||
type: 'Hook',
|
type: 'Hook',
|
||||||
generator: (socket?:I.Socket) => (...args:any[]) => SubresT
|
generator: (socket?: I.Socket) => (...args: any[]) => SubresT
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CallInfo = BaseInfo & {
|
export type CallInfo = BaseInfo & {
|
||||||
type: 'Call',
|
type: 'Call',
|
||||||
call: AnyFunction
|
call: GenericFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RpcInfo = HookInfo | CallInfo
|
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: T, rpc:HookRPC<any, any>) => any
|
export type HookCloseFunction<T> = (res: T, rpc: HookRPC<any, any>) => any
|
||||||
|
|
||||||
|
|
||||||
export type AsyncIfc<Ifc extends RPCInterface> = { [grp in keyof Ifc]: {[rpcname in keyof Ifc[grp]] : AsyncAnyFunction<Ifc[grp][rpcname]> } }
|
export type AsyncIfc<Ifc extends RPCInterface> = { [grp in keyof Ifc]: { [rpcname in keyof Ifc[grp]]: AsyncGenericFunction<Ifc[grp][rpcname]> } }
|
||||||
|
|
||||||
export type AsyncAnyFunction<F extends AnyFunction = AnyFunction> = F extends (...args: Parameters<F>) => infer R
|
export type AsyncGenericFunction<F extends GenericFunction = GenericFunction> = F extends (...args: Parameters<F>) => infer R
|
||||||
? ((...args: Parameters<F>) => R extends Promise<any> ? R : Promise<R> )
|
? ((...args: Parameters<F>) => R extends Promise<any> ? R : Promise<R>)
|
||||||
: Promise<any>
|
: Promise<any>
|
||||||
|
|
||||||
|
type Destroyable = { destroy: () => void }
|
||||||
|
export type Callback<Params extends any[] = []> =
|
||||||
|
(this: Destroyable, ...args: Params) => void
|
||||||
|
|
||||||
|
type AsFunction<F> = F extends GenericFunction ? F : GenericFunction
|
||||||
|
|
||||||
|
type Last<Tuple extends any[]> = Tuple[ Subtract<Length<Tuple>, 1> ]
|
||||||
|
type Tail<T extends any[]> = T extends [any, ...infer R] ? R : any[]
|
||||||
|
|
||||||
|
type Length<T extends any[]> =
|
||||||
|
T extends { length: infer L } ? L : never;
|
||||||
|
|
||||||
|
type BuildTuple<L extends number, T extends any[] = []> =
|
||||||
|
T extends { length: L } ? T : BuildTuple<L, [...T, any]>;
|
||||||
|
|
||||||
|
type Subtract<A extends number, B extends number> =
|
||||||
|
BuildTuple<A> extends [...(infer U), ...BuildTuple<B>]
|
||||||
|
? Length<U>
|
||||||
|
: never;
|
||||||
+11
-10
@@ -3,7 +3,7 @@ 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 { Socket } from "socket.io"
|
import { Socket } from "socket.io"
|
||||||
import { CALL_NOT_FOUND, RPC_BAD_TYPE, RPC_NO_NAME } from "./Strings";
|
import { CALL_NOT_FOUND, DESTROY_PREFIX, RPC_BAD_TYPE, RPC_NO_NAME } from "./Strings";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate an RPC to RPCInfo for serialization.
|
* Translate an RPC to RPCInfo for serialization.
|
||||||
@@ -75,14 +75,15 @@ export function rpcHooker(socket: I.Socket, exporter: I.RPCExporter<any, any>, e
|
|||||||
* Decorate an RPC with the error handler
|
* Decorate an RPC with the error handler
|
||||||
* @param rpcFunction the function to decorate
|
* @param rpcFunction the function to decorate
|
||||||
*/
|
*/
|
||||||
const callGenerator = (rpcName: string, $__socket__$: I.Socket, rpcFunction: T.AnyFunction, errorHandler: T.ErrorHandler): T.AnyFunction => {
|
const callGenerator = (rpcName: string, $__socket__$: I.Socket, rpcFunction: T.GenericFunction, errorHandler: T.ErrorHandler): T.GenericFunction => {
|
||||||
const argsArr = extractArgs(rpcFunction)
|
const argsArr = extractArgs(rpcFunction)
|
||||||
const args = argsArr.join(',')
|
const args = argsArr.join(',')
|
||||||
const argsStr = argsArr.map(stripAfterEquals).join(',')
|
const argsStr = argsArr.map(stripAfterEquals).join(',')
|
||||||
|
|
||||||
const callStr = `async (${args}) => {
|
const callStr = `async (${args}) => {
|
||||||
try{
|
try{
|
||||||
return await rpcFunction(${argsStr})
|
const res = await rpcFunction(${argsStr})
|
||||||
|
return res
|
||||||
}catch(e){
|
}catch(e){
|
||||||
errorHandler($__socket__$, e, rpcName, [${args}])
|
errorHandler($__socket__$, e, rpcName, [${args}])
|
||||||
}
|
}
|
||||||
@@ -106,24 +107,24 @@ export function stripAfterEquals(str: string): string {
|
|||||||
*/
|
*/
|
||||||
const hookGenerator = (rpc: T.HookRPC<any, any>, errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction, injectSocket?: boolean): T.HookInfo['generator'] => {
|
const hookGenerator = (rpc: T.HookRPC<any, any>, errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction, injectSocket?: boolean): T.HookInfo['generator'] => {
|
||||||
let argsArr = extractArgs(rpc.hook)
|
let argsArr = extractArgs(rpc.hook)
|
||||||
argsArr.pop() //remove callback param
|
argsArr.shift()//remove callback param
|
||||||
|
|
||||||
let callArgs = argsArr.join(',')
|
let callArgs = argsArr.join(',')
|
||||||
const args = sesameFn ? (['sesame', ...argsArr].join(','))
|
const args = sesameFn ? (['sesame', ...argsArr].join(','))
|
||||||
: callArgs
|
: callArgs
|
||||||
|
|
||||||
callArgs = appendComma(callArgs, false)
|
callArgs = appendComma(callArgs, false)
|
||||||
|
const destroy_prefix = DESTROY_PREFIX
|
||||||
const hookStr = `
|
const hookStr = `
|
||||||
($__socket__$) => async (${args}) => {
|
($__socket__$) => async (${args}) => {
|
||||||
try{
|
try{
|
||||||
if(sesameFn && !sesameFn(sesame)) return
|
if(sesameFn && !sesameFn(sesame)) return
|
||||||
const uuid = uuidv4()
|
const uuid = uuidv4()
|
||||||
const res = await rpc.hook(${callArgs} (...cbargs) => {
|
const res = await rpc.hook((...cbargs) => {
|
||||||
${rpc.onCallback ? `rpc.onCallback.apply({}, cbargs)` : ``}
|
${rpc.onCallback ? `rpc.onCallback.apply({}, cbargs)` : ``}
|
||||||
$__socket__$.call.apply($__socket__$, [uuid, ...cbargs])
|
$__socket__$.call.apply($__socket__$, [uuid, ...cbargs])
|
||||||
})
|
},${callArgs})
|
||||||
${rpc.onDestroy ? `$__socket__$.bind('destroy_'+uuid, () => {
|
${rpc.onDestroy ? `$__socket__$.bind(destroy_prefix+uuid, () => {
|
||||||
rpc.onDestroy(res, rpc)
|
rpc.onDestroy(res, rpc)
|
||||||
})` : ``}
|
})` : ``}
|
||||||
return {'uuid': uuid, 'return': res}
|
return {'uuid': uuid, 'return': res}
|
||||||
@@ -144,7 +145,7 @@ const makeError = (callName: string) => new Error(CALL_NOT_FOUND(callName))
|
|||||||
*/
|
*/
|
||||||
const extractArgs = (f: Function): string[] => {
|
const extractArgs = (f: Function): string[] => {
|
||||||
let fn: string
|
let fn: string
|
||||||
fn = (fn = String(f)).substr(0, fn.indexOf(")")).substr(fn.indexOf("(") + 1)
|
fn = (fn = String(f)).substring(0, fn.indexOf(")")).substring(fn.indexOf("(") + 1)
|
||||||
return fn !== "" ? fn.split(',') : []
|
return fn !== "" ? fn.split(',') : []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +245,7 @@ export const makePioSocket = (socket: any): I.Socket => {
|
|||||||
res(undefined)
|
res(undefined)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
unhook: (name: string, listener?: T.AnyFunction) => {
|
unhook: (name: string, listener?: T.GenericFunction) => {
|
||||||
if (listener) {
|
if (listener) {
|
||||||
socket.removeListener(name, listener)
|
socket.removeListener(name, listener)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+31
-31
@@ -1,7 +1,6 @@
|
|||||||
import { describe, it } from "mocha";
|
import { describe, it } from "mocha";
|
||||||
import { RPCServer, RPCSocket } from '../Index'
|
|
||||||
import { RPCExporter, Socket } from "../src/Interfaces";
|
import { RPCExporter, Socket } from "../src/Interfaces";
|
||||||
import { ConnectedSocket } from "../src/Types";
|
import { ConnectedSocket, Callback, GenericFunction } from "../src/Types";
|
||||||
import * as log from 'why-is-node-running';
|
import * as log from 'why-is-node-running';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
@@ -9,17 +8,18 @@ import * as fetch from 'node-fetch';
|
|||||||
import { PromiseIO } from "../src/PromiseIO/Server";
|
import { PromiseIO } from "../src/PromiseIO/Server";
|
||||||
import { PromiseIOClient } from "../src/PromiseIO/Client";
|
import { PromiseIOClient } from "../src/PromiseIO/Client";
|
||||||
import { assert, expect } from 'chai';
|
import { assert, expect } from 'chai';
|
||||||
import { USER_DEFINED_TIMEOUT } from "../src/Strings";
|
import { CLASSNAME_ATTRIBUTE, USER_DEFINED_TIMEOUT } from "../src/Strings";
|
||||||
|
import { RPCServer, RPCSocket } from "../Index";
|
||||||
var should = require('chai').should();
|
var should = require('chai').should();
|
||||||
var chai = require("chai");
|
var chai = require("chai");
|
||||||
var chaiAsPromised = require("chai-as-promised");
|
var chaiAsPromised = require("chai-as-promised");
|
||||||
|
|
||||||
chai.use(chaiAsPromised);
|
chai.use(chaiAsPromised);
|
||||||
const noop = (...args) => { }
|
const noop = (...args: any[]) => { }
|
||||||
|
|
||||||
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(onCallback = noop, connectionHandler = noop, hookCloseHandler = noop, closeHandler = noop, errorHandler = noop) {
|
function makeServer(onCallback = noop, connectionHandler = noop, hookCloseHandler = noop, closeHandler = noop, errorHandler = noop) {
|
||||||
let subcallback
|
let subcallback: GenericFunction
|
||||||
const serv = new RPCServer([{
|
const serv = new RPCServer([{
|
||||||
name: 'test',
|
name: 'test',
|
||||||
RPCs: [
|
RPCs: [
|
||||||
@@ -68,7 +68,7 @@ describe('PromiseIO', () => {
|
|||||||
const server = new PromiseIO()
|
const server = new PromiseIO()
|
||||||
server.attach(new http.Server())
|
server.attach(new http.Server())
|
||||||
server.on("socket", clientSocket => {
|
server.on("socket", clientSocket => {
|
||||||
clientSocket.bind("test123", (p1, p2) => {
|
clientSocket.bind("test123", (p1: string, p2:string) => {
|
||||||
server.close()
|
server.close()
|
||||||
if (p1 === "p1" && p2 === "p2")
|
if (p1 === "p1" && p2 === "p2")
|
||||||
done()
|
done()
|
||||||
@@ -489,12 +489,12 @@ describe('RPCSocket', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
it('should have rpc echo', async() => {
|
it('should have rpc echo', async () => {
|
||||||
const x = await client['test'].echo("x")
|
const x = await client['test'].echo("x")
|
||||||
expect(x).to.be.equal('x')
|
expect(x).to.be.equal('x')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should add up to 6', async() => {
|
it('should add up to 6', async () => {
|
||||||
const sum = await client['test'].add(1, 2, 3)
|
const sum = await client['test'].add(1, 2, 3)
|
||||||
expect(sum).to.be.equal(6)
|
expect(sum).to.be.equal(6)
|
||||||
})
|
})
|
||||||
@@ -549,7 +549,7 @@ describe('It should do unhook', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'subscribeWithParam',
|
name: 'subscribeWithParam',
|
||||||
hook: async (param, callback): Promise<{ uuid: string }> => {
|
hook: async (callback, param): Promise<{ uuid: string }> => {
|
||||||
|
|
||||||
if (param != "OK") {
|
if (param != "OK") {
|
||||||
console.log("param was" + param);
|
console.log("param was" + param);
|
||||||
@@ -583,14 +583,14 @@ describe('It should do unhook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('Subscribe with param', async () => {
|
it('Subscribe with param', async () => {
|
||||||
const res = await client['test'].subscribeWithParam("OK", noop)
|
const res = await client['test'].subscribeWithParam(noop, "OK")
|
||||||
expect(res.uuid).to.be.equal(candy)
|
expect(res.uuid).to.be.equal(candy)
|
||||||
})
|
})
|
||||||
|
|
||||||
let run = 0
|
let run = 0
|
||||||
const expected = [yesCandy, noCandy, noCandy, noCandy]
|
const expected = [yesCandy, noCandy, noCandy, noCandy]
|
||||||
|
|
||||||
it('Unhook+unsubscribe should stop callbacks', async() => {
|
it('Unhook+unsubscribe should stop callbacks', async () => {
|
||||||
await client['test'].subscribe(function myCallback(c) {
|
await client['test'].subscribe(function myCallback(c) {
|
||||||
if (run == 1)
|
if (run == 1)
|
||||||
(myCallback as any).destroy()
|
(myCallback as any).destroy()
|
||||||
@@ -614,7 +614,7 @@ type topicDTO = { topic: string; }
|
|||||||
type SesameTestIfc = {
|
type SesameTestIfc = {
|
||||||
test: {
|
test: {
|
||||||
checkCandy: () => Promise<string>
|
checkCandy: () => Promise<string>
|
||||||
subscribe: (callback: Function) => Promise<topicDTO>
|
subscribe: (callback: Callback<[string]>) => Promise<topicDTO>
|
||||||
manyParams: <A = string, B = number, C = boolean, D = Object>(a: A, b: B, c: C, d: D) => Promise<[A, B, C, D]>
|
manyParams: <A = string, B = number, C = boolean, D = Object>(a: A, b: B, c: C, d: D) => Promise<[A, B, C, D]>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -625,6 +625,7 @@ describe('Sesame should unlock the socket', () => {
|
|||||||
let server: RPCServer<SesameTestIfc>
|
let server: RPCServer<SesameTestIfc>
|
||||||
let cb: Function = (...args) => { }
|
let cb: Function = (...args) => { }
|
||||||
|
|
||||||
|
|
||||||
before((done) => {
|
before((done) => {
|
||||||
server = new RPCServer<SesameTestIfc>([{
|
server = new RPCServer<SesameTestIfc>([{
|
||||||
name: "test",
|
name: "test",
|
||||||
@@ -688,7 +689,7 @@ describe('Sesame should unlock the socket', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('callback should work with sesame', (done) => {
|
it('callback should work with sesame', (done) => {
|
||||||
client.test.subscribe((c) => {
|
client.test.subscribe(function (c) {
|
||||||
if (c === candy) {
|
if (c === candy) {
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
@@ -725,21 +726,21 @@ describe('Error handling', () => {
|
|||||||
a: 'a',
|
a: 'a',
|
||||||
b: 'b'
|
b: 'b'
|
||||||
})
|
})
|
||||||
.then(r => {
|
.then(r => {
|
||||||
if (r != null)
|
if (r != null)
|
||||||
done(new Error("UNEXPECTED RESULT " + r))
|
done(new Error("UNEXPECTED RESULT " + r))
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
if (e.message === errtxt)
|
if (e.message === errtxt)
|
||||||
done()
|
done()
|
||||||
else
|
else
|
||||||
done(e)
|
done(e)
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
cli.close()
|
cli.close()
|
||||||
sock.close()
|
sock.close()
|
||||||
server.close()
|
server.close()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1009,5 +1010,4 @@ describe("attaching handlers before connecting", () => {
|
|||||||
done(e)
|
done(e)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
})
|
|
||||||
+1
-1
@@ -9,6 +9,6 @@
|
|||||||
"strict": true,
|
"strict": true,
|
||||||
"experimentalDecorators": true
|
"experimentalDecorators": true
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "test/**/*.ts", "Index.ts", "demo.ts", "scratchpad.ts"],
|
"include": ["src/**/*.ts", "test/**/*.ts", "Index.ts"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user