probably a working typecheck for callbacks
This commit is contained in:
@@ -3,3 +3,4 @@ export * from './src/Frontend';
|
|||||||
export * from './src/Interfaces'
|
export * from './src/Interfaces'
|
||||||
export * from './src/Types';
|
export * from './src/Types';
|
||||||
export * from './src/Utils'
|
export * from './src/Utils'
|
||||||
|
export * from './src/Decorator'
|
||||||
+3
-9
@@ -5,7 +5,7 @@ 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';
|
||||||
|
|
||||||
export class RPCServer<
|
export class RPCServer<
|
||||||
InterfaceT extends T.RPCInterface = T.RPCInterface,
|
InterfaceT extends T.RPCInterface = T.RPCInterface,
|
||||||
@@ -65,13 +65,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 {
|
||||||
@@ -114,7 +108,7 @@ 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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { CLASSNAME_ATTRIBUTE } from "./Strings"
|
||||||
|
|
||||||
|
export abstract class DeserializerFactory {
|
||||||
|
static entityClasses = {}
|
||||||
|
|
||||||
|
static from(object: any) {
|
||||||
|
if(!object){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof object !== 'object'){ //definitely not a class object
|
||||||
|
return object
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Array.isArray(object)){
|
||||||
|
return object.map(DeserializerFactory.from)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clazz = DeserializerFactory.entityClasses[object[CLASSNAME_ATTRIBUTE]]
|
||||||
|
delete object[CLASSNAME_ATTRIBUTE]
|
||||||
|
|
||||||
|
if(!clazz){ //anonymous object or class not registered as @Serializable
|
||||||
|
Object.keys(object).forEach(key => {
|
||||||
|
object[key] = DeserializerFactory.from(object[key])
|
||||||
|
})
|
||||||
|
return object
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = new clazz()
|
||||||
|
Object.keys(object).forEach(key => {
|
||||||
|
obj[key] = DeserializerFactory.from(object[key])
|
||||||
|
})
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static makeDeserializable(object: any){
|
||||||
|
if(!object)
|
||||||
|
return
|
||||||
|
|
||||||
|
if(typeof object !== 'object')
|
||||||
|
return object
|
||||||
|
|
||||||
|
if(object.constructor.name === 'Object'){
|
||||||
|
Object.keys(object).forEach(key => {
|
||||||
|
object[key] = DeserializerFactory.makeDeserializable(object[key])
|
||||||
|
})
|
||||||
|
return object
|
||||||
|
}
|
||||||
|
|
||||||
|
object[CLASSNAME_ATTRIBUTE] = object.constructor.name
|
||||||
|
Object.keys(object).forEach(key => {
|
||||||
|
object[key] = DeserializerFactory.makeDeserializable(object[key])
|
||||||
|
})
|
||||||
|
|
||||||
|
return object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Serializable(attr?: any) {
|
||||||
|
return function _Serializable<T extends { new(...args: any[]): {} }>(clazz: T) {
|
||||||
|
DeserializerFactory.entityClasses[clazz.name] = clazz
|
||||||
|
|
||||||
|
return clazz
|
||||||
|
/*
|
||||||
|
return class extends clazz{
|
||||||
|
constructor(...args: any[]) {
|
||||||
|
super(...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
-12
@@ -4,7 +4,9 @@ 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 { DESTROY_PREFIX, SOCKET_NOT_CONNECTED, UNKNOWN_RPC_IDENTIFIER, USER_DEFINED_TIMEOUT } from './Strings';
|
||||||
|
import { DeserializerFactory } from './Decorator';
|
||||||
|
DeserializerFactory
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,17 +119,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
|
||||||
@@ -203,9 +209,10 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
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)
|
||||||
|
const deserializer = DeserializerFactory
|
||||||
return eval(`async (${headerArgs}) => {
|
return eval(`async (${headerArgs}) => {
|
||||||
return await this.call("${fnName}", ${sesame} ${argParams})
|
const returnvalue = await this.call("${fnName}", ${sesame} ${argParams})
|
||||||
|
return deserializer.from(returnvalue)
|
||||||
}`)
|
}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,6 +230,8 @@ 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 deserializer = DeserializerFactory
|
||||||
|
const destroy_prefix = DESTROY_PREFIX
|
||||||
|
|
||||||
const frontendHookStr = `
|
const frontendHookStr = `
|
||||||
async (${headerArgs} $__callback__$) => {
|
async (${headerArgs} $__callback__$) => {
|
||||||
@@ -231,12 +240,12 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
if(r){
|
if(r){
|
||||||
if(r.uuid){
|
if(r.uuid){
|
||||||
$__callback__$['destroy'] = () => {
|
$__callback__$['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__$)
|
this.socket.hook(r.uuid, $__callback__$)
|
||||||
}
|
}
|
||||||
return r.return
|
return deserializer.from(r.return)
|
||||||
}else{
|
}else{
|
||||||
throw new Error("Empty response")
|
throw new Error("Empty response")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,3 +12,5 @@ RPC did not provide a name.
|
|||||||
\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__$_"
|
||||||
+53
-20
@@ -5,20 +5,20 @@ import { PromiseIO } from "./PromiseIO/Server";
|
|||||||
export type PioBindListener = (...args: any) => void
|
export type PioBindListener = (...args: any) => void
|
||||||
export type PioHookListener = AnyFunction
|
export type PioHookListener = AnyFunction
|
||||||
|
|
||||||
export type AnyFunction = (...args:any) => any
|
export type AnyFunction = (...args: any[]) => any
|
||||||
export type HookFunction = AnyFunction
|
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 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 = SocketIOClient.ConnectOpts & {
|
||||||
protocol?: 'http' | 'https',
|
protocol?: 'http' | 'https',
|
||||||
@@ -41,7 +41,7 @@ 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'
|
||||||
|
|
||||||
@@ -53,16 +53,16 @@ export type CallRPC<Name, Func extends AnyFunction> = {
|
|||||||
|
|
||||||
export type HookRPC<Name, Func extends AnyFunction> = {
|
export type HookRPC<Name, Func extends AnyFunction> = {
|
||||||
name: Name
|
name: Name
|
||||||
hook: Func
|
hook: AnyFunction
|
||||||
onCallback?: AnyFunction
|
onCallback?: AnyFunction
|
||||||
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 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]: {
|
||||||
[rpc in string] : AnyFunction
|
[rpc in string]: AnyFunction
|
||||||
}
|
}
|
||||||
} & Impl
|
} & Impl
|
||||||
|
|
||||||
@@ -71,7 +71,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 = {
|
||||||
@@ -82,7 +84,7 @@ export type BaseInfo = {
|
|||||||
|
|
||||||
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 & {
|
||||||
@@ -90,15 +92,46 @@ export type CallInfo = BaseInfo & {
|
|||||||
call: AnyFunction
|
call: AnyFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
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]]: AsyncAnyFunction<Ifc[grp][rpcname]> } }
|
||||||
|
|
||||||
export type AsyncAnyFunction<F extends AnyFunction = AnyFunction> = F extends (...args: Parameters<F>) => infer R
|
export type AsyncAnyFunction<F extends AnyFunction = AnyFunction> = 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 DYN_PARAM<
|
||||||
|
A = void,
|
||||||
|
B = void,
|
||||||
|
C = void,
|
||||||
|
D = void,
|
||||||
|
E = void,
|
||||||
|
F = void,
|
||||||
|
G = void,
|
||||||
|
H = void,
|
||||||
|
> = H extends void ?
|
||||||
|
G extends void ?
|
||||||
|
F extends void ?
|
||||||
|
E extends void ?
|
||||||
|
D extends void ?
|
||||||
|
C extends void ?
|
||||||
|
B extends void ?
|
||||||
|
A extends void ?
|
||||||
|
[]
|
||||||
|
: [A]
|
||||||
|
: [A,B]
|
||||||
|
:[A,B,C]
|
||||||
|
:[A,B,C,D]
|
||||||
|
:[A,B,C,D,E]
|
||||||
|
:[A,B,C,D,E,F]
|
||||||
|
:[A,B,C,D,E,F,G]
|
||||||
|
:[A,B,C,D,E,F,G,H]
|
||||||
|
|
||||||
|
type Destroyable = { destroy: () => void }
|
||||||
|
export type Callback<A0 = void, A1 = void, A2 = void, A3 = void, A4 = void, A5 = void, A6 = void, A7 = void> =
|
||||||
|
(this: Destroyable, ...args: DYN_PARAM<A0, A1, A2, A3, A4, A5, A6, A7>) => void
|
||||||
|
|||||||
+8
-5
@@ -3,7 +3,8 @@ 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";
|
||||||
|
import { DeserializerFactory } from "./Decorator";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate an RPC to RPCInfo for serialization.
|
* Translate an RPC to RPCInfo for serialization.
|
||||||
@@ -79,10 +80,12 @@ const callGenerator = (rpcName: string, $__socket__$: I.Socket, rpcFunction: T.A
|
|||||||
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 deserializer = DeserializerFactory
|
||||||
|
|
||||||
const callStr = `async (${args}) => {
|
const callStr = `async (${args}) => {
|
||||||
try{
|
try{
|
||||||
return await rpcFunction(${argsStr})
|
const res = await rpcFunction(${argsStr})
|
||||||
|
return deserializer.makeDeserializable(res)
|
||||||
}catch(e){
|
}catch(e){
|
||||||
errorHandler($__socket__$, e, rpcName, [${args}])
|
errorHandler($__socket__$, e, rpcName, [${args}])
|
||||||
}
|
}
|
||||||
@@ -113,7 +116,7 @@ const hookGenerator = (rpc: T.HookRPC<any, any>, errorHandler: T.ErrorHandler, s
|
|||||||
: 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{
|
||||||
@@ -123,7 +126,7 @@ const hookGenerator = (rpc: T.HookRPC<any, any>, errorHandler: T.ErrorHandler, s
|
|||||||
${rpc.onCallback ? `rpc.onCallback.apply({}, cbargs)` : ``}
|
${rpc.onCallback ? `rpc.onCallback.apply({}, cbargs)` : ``}
|
||||||
$__socket__$.call.apply($__socket__$, [uuid, ...cbargs])
|
$__socket__$.call.apply($__socket__$, [uuid, ...cbargs])
|
||||||
})
|
})
|
||||||
${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 +147,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(',') : []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+83
-5
@@ -1,7 +1,7 @@
|
|||||||
import { describe, it } from "mocha";
|
import { describe, it } from "mocha";
|
||||||
import { RPCServer, RPCSocket } from '../Index'
|
import { RPCServer, RPCSocket, Serializable } from '../Index'
|
||||||
import { RPCExporter, Socket } from "../src/Interfaces";
|
import { RPCExporter, Socket } from "../src/Interfaces";
|
||||||
import { ConnectedSocket } from "../src/Types";
|
import { ConnectedSocket, Callback } 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';
|
||||||
@@ -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]>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -688,7 +688,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()
|
||||||
}
|
}
|
||||||
@@ -1009,5 +1009,83 @@ describe("attaching handlers before connecting", () => {
|
|||||||
done(e)
|
done(e)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("class (de-)serialization", () => {
|
||||||
|
|
||||||
|
@Serializable()
|
||||||
|
class SubClass{
|
||||||
|
fString = "F"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable()
|
||||||
|
class TestClass{
|
||||||
|
aString = "A"
|
||||||
|
aNumber = 46
|
||||||
|
aObject = {
|
||||||
|
x: "x",
|
||||||
|
y: undefined,
|
||||||
|
sub: new SubClass()
|
||||||
|
}
|
||||||
|
aClassObject = new SubClass()
|
||||||
|
|
||||||
|
public returnOK(){
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let myServer: RPCServer;
|
||||||
|
let mySocket: RPCSocket;
|
||||||
|
|
||||||
|
before(function(done){
|
||||||
|
myServer = new RPCServer([{
|
||||||
|
name: "Test",
|
||||||
|
RPCs: [
|
||||||
|
function returnClass(){
|
||||||
|
return new TestClass()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}])
|
||||||
|
myServer.listen(8084)
|
||||||
|
|
||||||
|
mySocket = new RPCSocket(8084, 'localhost')
|
||||||
|
mySocket.connect().then(() => done())
|
||||||
|
})
|
||||||
|
|
||||||
|
after(function(done){
|
||||||
|
mySocket.close()
|
||||||
|
myServer.close()
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
it("receives class in call response", async () => {
|
||||||
|
const obj: TestClass = await mySocket['Test'].returnClass()
|
||||||
|
|
||||||
|
expect(obj).to.be.an.instanceOf(TestClass)
|
||||||
|
expect(obj.aString).to.be.a('string')
|
||||||
|
expect(obj.aNumber).to.be.a('number')
|
||||||
|
expect(obj.aObject).to.be.a('object')
|
||||||
|
expect(obj.aObject.x).to.be.a('string')
|
||||||
|
expect(obj.aObject.y).to.be.undefined
|
||||||
|
expect(obj.aObject.sub).to.be.an.instanceOf(SubClass)
|
||||||
|
expect(obj.aClassObject).to.be.an.instanceOf(SubClass)
|
||||||
|
|
||||||
|
expect(obj.returnOK()).to.be.equal('OK')
|
||||||
|
})
|
||||||
|
|
||||||
|
it("receives class in hook response", async () => {
|
||||||
|
const obj: TestClass = await mySocket['Test'].returnClass()
|
||||||
|
|
||||||
|
expect(obj).to.be.an.instanceOf(TestClass)
|
||||||
|
expect(obj.aString).to.be.a('string')
|
||||||
|
expect(obj.aNumber).to.be.a('number')
|
||||||
|
expect(obj.aObject).to.be.a('object')
|
||||||
|
expect(obj.aObject.x).to.be.a('string')
|
||||||
|
expect(obj.aObject.y).to.be.undefined
|
||||||
|
expect(obj.aObject.sub).to.be.an.instanceOf(SubClass)
|
||||||
|
expect(obj.aClassObject).to.be.an.instanceOf(SubClass)
|
||||||
|
|
||||||
|
expect(obj.returnOK()).to.be.equal('OK')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
+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", "demo.ts", "wat.ts"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user