aboutsummaryrefslogtreecommitdiff
path: root/src/server/session/utilities/ipc.ts
blob: fd8bf60754445614e029b135118a67be50320843 (plain)
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
67
68
69
70
71
72
73
74
import { isMaster } from "cluster";
import { Utils } from "../../../Utils";

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;

export interface Message {
    name: string;
    args?: any;
}
type InternalMessage = Message & { metadata: any };

export type MessageHandler<T extends Message = Message> = (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, awaitResponse = false): Promise<Error | undefined> => {
        if (!this.target.send) {
            return new Error("Cannot dispatch when send is undefined.");
        }
        if (awaitResponse) {
            return new Promise(resolve => {
                const messageId = Utils.GenerateGuid();
                const metadata: any = {};
                metadata[this.ipc_id] = messageId;
                const responseHandler: MessageHandler<InternalMessage> = ({ args, metadata }) => {
                    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 });
            });
        } else {
            this.target.send?.({ name, args });
        }
    }

    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
                    });
                }
            }
        });
    }

}

export function IPC(target: IPCTarget) {
    return new PromisifiedIPCManager(target);
}