1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
import { isMaster } from "cluster";
import { Utils } from "../../../Utils";
export function IPC(target: IPCTarget) {
return new PromisifiedIPCManager(target);
}
export type IPCTarget = NodeJS.EventEmitter & { send?: Function };
export type Router = (message: Message) => void | Promise<void>;
export const suffix = isMaster ? Utils.GenerateGuid() : process.env.ipc_suffix;
type InternalMessage<T = any> = Message<T> & { metadata: any };
export interface Message<T = any> {
name: string;
args: T;
}
export type MessageHandler<T = any> = (message: T) => any | Promise<any>;
export class PromisifiedIPCManager {
private readonly target: IPCTarget;
private readonly ipc_id = `ipc_id_${suffix}`;
private readonly is_response = `is_response_${suffix}`;
constructor(target: IPCTarget) {
this.target = target;
}
public emit = async (name: string, args?: any) => this.target.send?.({ name, args });
public emitPromise = async (name: string, args?: any) => {
return new Promise(resolve => {
const messageId = Utils.GenerateGuid();
const metadata: any = {};
metadata[this.ipc_id] = messageId;
const responseHandler: MessageHandler<any> = ({ metadata, args }) => {
if (metadata[this.is_response] && metadata[this.ipc_id] === messageId) {
this.target.removeListener("message", responseHandler);
resolve(args?.error as Error | undefined);
}
};
this.target.addListener("message", responseHandler);
this.target.send?.({ name, args, metadata });
});
}
public setRouter = (router: Router) => {
this.target.addListener("message", async ({ name, args, metadata }: InternalMessage) => {
if (name && (!metadata || !metadata[this.is_response])) {
let error: Error | undefined;
try {
await router({ name, args });
} catch (e) {
error = e;
}
if (metadata && this.target.send) {
metadata[this.is_response] = true;
this.target.send({ name, args: { error }, metadata });
}
}
});
}
}
|