container.ts
Version:
Modular application framework
153 lines • 6.15 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
/// <reference types="node" />
const process = require("process");
const Observable_1 = require("rxjs/Observable");
require("rxjs/add/observable/fromEvent");
require("rxjs/add/operator/map");
require("rxjs/add/operator/mergeMap");
require("rxjs/add/operator/filter");
require("rxjs/add/operator/timeout");
require("rxjs/add/operator/takeWhile");
const Process_1 = require("./Process");
/** Process message types. */
var EProcessMessageType;
(function (EProcessMessageType) {
EProcessMessageType[EProcessMessageType["Log"] = 0] = "Log";
EProcessMessageType[EProcessMessageType["Metric"] = 1] = "Metric";
EProcessMessageType[EProcessMessageType["CallRequest"] = 2] = "CallRequest";
EProcessMessageType[EProcessMessageType["CallResponse"] = 3] = "CallResponse";
EProcessMessageType[EProcessMessageType["User"] = 4] = "User";
})(EProcessMessageType = exports.EProcessMessageType || (exports.EProcessMessageType = {}));
/** Process error class. */
class ProcessError extends Error {
constructor(name, message, stack) {
const error = super(message);
this.name = error.name = name;
this.message = error.message = message;
this.stack = error.stack = stack;
}
}
exports.ProcessError = ProcessError;
class ChildProcess extends Process_1.Process {
constructor(name, opts) {
super(name, opts);
// Listen for and handle messages from parent process.
this._message = Observable_1.Observable.fromEvent(process, "message");
this._message
.subscribe((message) => this.handleMessage(message));
// Forward log and metric messages to parent process.
this.container.logs
.subscribe((log) => this.send(EProcessMessageType.Log, log));
this.container.metrics
.subscribe((metric) => this.send(EProcessMessageType.Metric, metric));
}
/** Extract serialisable error properties to object. */
static serialiseError(error) {
return {
name: error.name,
message: error.message,
stack: error.stack,
};
}
/** Convert serialised error to error instance. */
static deserialiseError(error) {
return new ProcessError(error.name || "", error.message || "", error.stack || "");
}
/** Handle method call requests. */
static handleCallRequest(emitter, container, data) {
const type = EProcessMessageType.CallResponse;
const responseData = { id: data.id };
try {
// Retrieve target module and make subscribe call to method.
const mod = container.resolve(data.target);
const method = mod[data.method].bind(mod);
method(...data.args)
.subscribe({
next: (value) => {
const nextData = Object.assign({ next: value }, responseData);
emitter.send(type, nextData);
},
error: (error) => {
error = ChildProcess.serialiseError(error);
const errorData = Object.assign({ error }, responseData);
emitter.send(type, errorData);
},
complete: () => {
const completeData = Object.assign({ complete: true }, responseData);
emitter.send(type, completeData);
},
});
}
catch (error) {
error = ChildProcess.serialiseError(error);
const errorData = Object.assign({ error }, responseData);
emitter.send(type, errorData);
}
}
/** Handle method call responses. */
static handleCallResponse(messageObservable, id, args, timeout) {
return messageObservable
.filter((message) => {
// Filter by message type and identifier.
if (message.type === EProcessMessageType.CallResponse) {
const data = message.data;
return data.id === id;
}
return false;
})
.map((message) => {
// Cast message data to type.
const data = message.data;
return data;
})
.timeout(timeout)
.takeWhile((data) => {
// Complete observable when complete message received.
return !data.complete;
})
.mergeMap((data) => {
// Throw error or emit next data.
if (data.error != null) {
const error = ChildProcess.deserialiseError(data.error);
return Observable_1.Observable.throw(error);
}
else {
return Observable_1.Observable.of(data.next);
}
});
}
/** Messages received from parent process. */
get message() { return this._message; }
/** Send message to parent process. */
send(type, data) {
if (process.send != null) {
process.send({ type, data });
}
}
/** Make call to module.method in parent process. */
call(target, method, options = {}) {
const timeout = options.timeout || ChildProcess.DEFAULT_TIMEOUT;
const args = options.args || [];
const id = this.identifier;
this.debug(`call '${target}.${method}' '${id}'`);
// Send call request to parent process.
const sendData = { id, target, method, args };
this.send(EProcessMessageType.CallRequest, sendData);
return ChildProcess.handleCallResponse(this.message, id, args, timeout);
}
/** Handle messages received from parent process. */
handleMessage(message) {
switch (message.type) {
// Call request received from parent.
case EProcessMessageType.CallRequest: {
ChildProcess.handleCallRequest(this, this.container, message.data);
break;
}
}
}
}
/** Default call method timeout. */
ChildProcess.DEFAULT_TIMEOUT = 10000;
exports.ChildProcess = ChildProcess;
//# sourceMappingURL=ChildProcess.js.map