2019-01-12 02:33:44 +07:00
|
|
|
import * as vm from "vm";
|
2019-01-30 07:48:02 +07:00
|
|
|
import { NewEvalMessage, TypedValue, EvalFailedMessage, EvalDoneMessage, ServerMessage, EvalEventMessage, ClientMessage } from "../proto";
|
2019-01-12 02:33:44 +07:00
|
|
|
import { SendableConnection } from "../common/connection";
|
2019-01-30 07:48:02 +07:00
|
|
|
import { EventEmitter } from "events";
|
|
|
|
|
|
|
|
export interface ActiveEvaluation {
|
|
|
|
onEvent(msg: EvalEventMessage): void;
|
|
|
|
}
|
2019-01-12 02:33:44 +07:00
|
|
|
|
2019-01-16 01:36:09 +07:00
|
|
|
declare var __non_webpack_require__: typeof require;
|
2019-01-30 07:48:02 +07:00
|
|
|
export const evaluate = (connection: SendableConnection, message: NewEvalMessage, onDispose: () => void): ActiveEvaluation | void => {
|
2019-01-12 02:58:31 +07:00
|
|
|
const argStr: string[] = [];
|
|
|
|
message.getArgsList().forEach((value) => {
|
|
|
|
argStr.push(value);
|
|
|
|
});
|
|
|
|
const sendResp = (resp: any): void => {
|
|
|
|
const evalDone = new EvalDoneMessage();
|
|
|
|
evalDone.setId(message.getId());
|
|
|
|
const tof = typeof resp;
|
|
|
|
if (tof !== "undefined") {
|
|
|
|
const tv = new TypedValue();
|
|
|
|
let t: TypedValue.Type;
|
|
|
|
switch (tof) {
|
|
|
|
case "string":
|
|
|
|
t = TypedValue.Type.STRING;
|
|
|
|
break;
|
|
|
|
case "boolean":
|
|
|
|
t = TypedValue.Type.BOOLEAN;
|
|
|
|
break;
|
|
|
|
case "object":
|
|
|
|
t = TypedValue.Type.OBJECT;
|
|
|
|
break;
|
|
|
|
case "number":
|
|
|
|
t = TypedValue.Type.NUMBER;
|
|
|
|
break;
|
|
|
|
default:
|
2019-01-19 04:46:40 +07:00
|
|
|
return sendErr(EvalFailedMessage.Reason.EXCEPTION, `unsupported response type ${tof}`);
|
2019-01-12 02:58:31 +07:00
|
|
|
}
|
|
|
|
tv.setValue(tof === "string" ? resp : JSON.stringify(resp));
|
|
|
|
tv.setType(t);
|
|
|
|
evalDone.setResponse(tv);
|
|
|
|
}
|
2019-01-12 02:33:44 +07:00
|
|
|
|
2019-01-12 02:58:31 +07:00
|
|
|
const serverMsg = new ServerMessage();
|
|
|
|
serverMsg.setEvalDone(evalDone);
|
|
|
|
connection.send(serverMsg.serializeBinary());
|
|
|
|
};
|
|
|
|
const sendErr = (reason: EvalFailedMessage.Reason, msg: string): void => {
|
|
|
|
const evalFailed = new EvalFailedMessage();
|
|
|
|
evalFailed.setId(message.getId());
|
|
|
|
evalFailed.setReason(reason);
|
|
|
|
evalFailed.setMessage(msg);
|
2019-01-12 02:33:44 +07:00
|
|
|
|
2019-01-12 02:58:31 +07:00
|
|
|
const serverMsg = new ServerMessage();
|
|
|
|
serverMsg.setEvalFailed(evalFailed);
|
|
|
|
connection.send(serverMsg.serializeBinary());
|
|
|
|
};
|
2019-01-30 07:48:02 +07:00
|
|
|
let eventEmitter: EventEmitter | undefined;
|
2019-01-12 02:58:31 +07:00
|
|
|
try {
|
2019-01-30 07:48:02 +07:00
|
|
|
if (message.getActive()) {
|
|
|
|
eventEmitter = new EventEmitter();
|
|
|
|
}
|
|
|
|
|
|
|
|
const value = vm.runInNewContext(`(${message.getFunction()})(${eventEmitter ? `eventEmitter, ` : ""}${argStr.join(",")})`, {
|
|
|
|
eventEmitter: eventEmitter ? {
|
|
|
|
on: (event: string, cb: (...args: any[]) => void): void => {
|
|
|
|
eventEmitter!.on(event, cb);
|
|
|
|
},
|
|
|
|
emit: (event: string, ...args: any[]): void => {
|
|
|
|
const eventMsg = new EvalEventMessage();
|
|
|
|
eventMsg.setEvent(event);
|
|
|
|
eventMsg.setArgsList(args.filter(a => a).map(a => JSON.stringify(a)));
|
|
|
|
eventMsg.setId(message.getId());
|
|
|
|
const serverMsg = new ServerMessage();
|
|
|
|
serverMsg.setEvalEvent(eventMsg);
|
|
|
|
connection.send(serverMsg.serializeBinary());
|
|
|
|
},
|
|
|
|
} : undefined,
|
2019-01-19 04:46:40 +07:00
|
|
|
Buffer,
|
|
|
|
require: typeof __non_webpack_require__ !== "undefined" ? __non_webpack_require__ : require,
|
|
|
|
_require: typeof __non_webpack_require__ !== "undefined" ? __non_webpack_require__ : require,
|
|
|
|
tslib_1: require("tslib"), // TODO: is there a better way to do this?
|
|
|
|
setTimeout,
|
|
|
|
}, {
|
2019-01-30 07:48:02 +07:00
|
|
|
timeout: message.getTimeout() || 15000,
|
|
|
|
});
|
|
|
|
if (eventEmitter) {
|
|
|
|
// Is an active evaluation and should NOT be ended
|
|
|
|
eventEmitter.on("close", () => onDispose());
|
|
|
|
eventEmitter.on("error", () => onDispose());
|
|
|
|
} else {
|
|
|
|
if ((value as Promise<void>).then) {
|
|
|
|
// Is promise
|
|
|
|
(value as Promise<void>).then(r => sendResp(r)).catch(ex => sendErr(EvalFailedMessage.Reason.EXCEPTION, ex.toString()));
|
|
|
|
} else {
|
|
|
|
sendResp(value);
|
|
|
|
}
|
|
|
|
onDispose();
|
|
|
|
}
|
2019-01-12 02:58:31 +07:00
|
|
|
} catch (ex) {
|
|
|
|
sendErr(EvalFailedMessage.Reason.EXCEPTION, ex.toString());
|
|
|
|
}
|
2019-01-30 07:48:02 +07:00
|
|
|
|
|
|
|
return eventEmitter ? {
|
|
|
|
onEvent: (eventMsg: EvalEventMessage): void => {
|
|
|
|
eventEmitter!.emit(eventMsg.getEvent(), ...eventMsg.getArgsList().map(a => JSON.parse(a)));
|
|
|
|
},
|
|
|
|
} : undefined;
|
2019-01-19 04:46:40 +07:00
|
|
|
};
|