@nsshunt/stsappframework 3.1.209 → 3.1.211
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build.sh +5 -0
- package/dist/commonTypes.js.map +1 -1
- package/dist/index.js +0 -5
- package/dist/index.js.map +1 -1
- package/dist/masterprocessbase.js +3 -53
- package/dist/masterprocessbase.js.map +1 -1
- package/dist/processbase.js +4 -17
- package/dist/processbase.js.map +1 -1
- package/dist/testing/app.js +0 -457
- package/dist/testing/app.js.map +1 -1
- package/dist/workerprocessbase.js +1 -36
- package/dist/workerprocessbase.js.map +1 -1
- package/package.json +14 -13
- package/src/commonTypes.ts +2 -33
- package/src/index.ts +0 -5
- package/src/masterprocessbase.ts +4 -18
- package/src/processbase.ts +7 -22
- package/src/testing/app.ts +1 -530
- package/src/workerprocessbase.ts +1 -40
- package/types/commonTypes.d.ts +0 -23
- package/types/commonTypes.d.ts.map +1 -1
- package/types/index.d.ts +0 -5
- package/types/index.d.ts.map +1 -1
- package/types/masterprocessbase.d.ts.map +1 -1
- package/types/processbase.d.ts +0 -2
- package/types/processbase.d.ts.map +1 -1
- package/types/workerprocessbase.d.ts.map +1 -1
- package/dist/ipcMessageHandler.js +0 -189
- package/dist/ipcMessageHandler.js.map +0 -1
- package/dist/ipcMessageManager.js +0 -146
- package/dist/ipcMessageManager.js.map +0 -1
- package/dist/ipcMessageProcessorPrimary.js +0 -65
- package/dist/ipcMessageProcessorPrimary.js.map +0 -1
- package/dist/ipcMessageProcessorWorker.js +0 -61
- package/dist/ipcMessageProcessorWorker.js.map +0 -1
- package/dist/messagehandling/webWorkerMessageHandler.js +0 -280
- package/dist/messagehandling/webWorkerMessageHandler.js.map +0 -1
- package/dist/messagehandling/webWorkerSupport.js +0 -62
- package/dist/messagehandling/webWorkerSupport.js.map +0 -1
- package/dist/redisMessageHandler.js +0 -305
- package/dist/redisMessageHandler.js.map +0 -1
- package/dist/redisMessageHandler.test.js +0 -129
- package/dist/redisMessageHandler.test.js.map +0 -1
- package/dist/testing/app_ipc_legacy.js +0 -84
- package/dist/testing/app_ipc_legacy.js.map +0 -1
- package/dist/testing/app_ipcex.js +0 -69
- package/dist/testing/app_ipcex.js.map +0 -1
- package/dist/testing/app_ww.js +0 -54
- package/dist/testing/app_ww.js.map +0 -1
- package/src/ipcMessageHandler.ts +0 -201
- package/src/ipcMessageManager.ts +0 -171
- package/src/ipcMessageProcessorPrimary.ts +0 -76
- package/src/ipcMessageProcessorWorker.ts +0 -70
- package/src/messagehandling/webWorkerMessageHandler.ts +0 -341
- package/src/messagehandling/webWorkerSupport.ts +0 -66
- package/src/redisMessageHandler.test.ts +0 -157
- package/src/redisMessageHandler.ts +0 -371
- package/src/testing/app_ipc_legacy.ts +0 -87
- package/src/testing/app_ipcex.ts +0 -68
- package/src/testing/app_ww.ts +0 -68
- package/types/ipcMessageHandler.d.ts +0 -30
- package/types/ipcMessageHandler.d.ts.map +0 -1
- package/types/ipcMessageManager.d.ts +0 -30
- package/types/ipcMessageManager.d.ts.map +0 -1
- package/types/ipcMessageProcessorPrimary.d.ts +0 -26
- package/types/ipcMessageProcessorPrimary.d.ts.map +0 -1
- package/types/ipcMessageProcessorWorker.d.ts +0 -25
- package/types/ipcMessageProcessorWorker.d.ts.map +0 -1
- package/types/messagehandling/webWorkerMessageHandler.d.ts +0 -52
- package/types/messagehandling/webWorkerMessageHandler.d.ts.map +0 -1
- package/types/messagehandling/webWorkerSupport.d.ts +0 -6
- package/types/messagehandling/webWorkerSupport.d.ts.map +0 -1
- package/types/redisMessageHandler.d.ts +0 -51
- package/types/redisMessageHandler.d.ts.map +0 -1
- package/types/redisMessageHandler.test.d.ts +0 -2
- package/types/redisMessageHandler.test.d.ts.map +0 -1
- package/types/testing/app_ipc_legacy.d.ts +0 -2
- package/types/testing/app_ipc_legacy.d.ts.map +0 -1
- package/types/testing/app_ipcex.d.ts +0 -2
- package/types/testing/app_ipcex.d.ts.map +0 -1
- package/types/testing/app_ww.d.ts +0 -2
- package/types/testing/app_ww.d.ts.map +0 -1
package/src/ipcMessageManager.ts
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
/* eslint @typescript-eslint/no-explicit-any: 0 */ // --> OFF
|
|
2
|
-
import { ISTSLogger, JSONObject } from '@nsshunt/stsutils'
|
|
3
|
-
|
|
4
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
-
|
|
6
|
-
import { IIPCMessageProcessorIPCPayload, IIPCMessageProcessorWorkerRecord } from './commonTypes'
|
|
7
|
-
|
|
8
|
-
export interface IPCMessageManagerOptions {
|
|
9
|
-
logger: ISTSLogger
|
|
10
|
-
requestResponseMessageTimeout: number
|
|
11
|
-
namespace: string
|
|
12
|
-
role: 'SERVER' | 'CLIENT'
|
|
13
|
-
groups: string[]
|
|
14
|
-
messageSender: (payload: IIPCMessageProcessorIPCPayload, options: any) => void
|
|
15
|
-
ProcessRequestMessage: (payload: IIPCMessageProcessorIPCPayload, options: any) => Promise<JSONObject>
|
|
16
|
-
ProcessResponseMessage?: (reesponses: Record<string, IIPCMessageProcessorIPCPayload>, options: any) => Promise<boolean>
|
|
17
|
-
messageReceiverStart: (options: any) => void
|
|
18
|
-
messageReceiverStop: (options: any) => void
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* todo
|
|
22
|
-
* @typedef {Object} options - todo
|
|
23
|
-
* @property {boolean} [wssServer=false] - Create a web socket server on this worker instance
|
|
24
|
-
*/
|
|
25
|
-
export class IPCMessageManager
|
|
26
|
-
{
|
|
27
|
-
#id: string;
|
|
28
|
-
#options: IPCMessageManagerOptions;
|
|
29
|
-
#inflightMessages: Record<string, IIPCMessageProcessorWorkerRecord> = { };
|
|
30
|
-
#messageHeader: string;
|
|
31
|
-
|
|
32
|
-
constructor(options: IPCMessageManagerOptions) {
|
|
33
|
-
this.#id = uuidv4();
|
|
34
|
-
this.#options = options;
|
|
35
|
-
this.#messageHeader = `__STS__${this.#options.namespace}__${uuidv4()}`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
get id() {
|
|
39
|
-
return this.#id;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
43
|
-
ReceivedMessageFromMaster(msg: any) {
|
|
44
|
-
// Override in subclass if required
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
SendMessage = (payload: JSONObject, options?: any): Promise<JSONObject> => {
|
|
48
|
-
return new Promise((resolve, reject) => {
|
|
49
|
-
this.#SendMessage(payload, options,
|
|
50
|
-
(payload: IIPCMessageProcessorIPCPayload) => {
|
|
51
|
-
resolve(payload.responsePayload);
|
|
52
|
-
},
|
|
53
|
-
(payload: IIPCMessageProcessorIPCPayload) => {
|
|
54
|
-
reject(payload.requestPayload);
|
|
55
|
-
})
|
|
56
|
-
})
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
#SendMessage = (payload: JSONObject, options: any,
|
|
60
|
-
callBack: (payload: IIPCMessageProcessorIPCPayload) => void,
|
|
61
|
-
errorCallBack: (payload: IIPCMessageProcessorIPCPayload) => void
|
|
62
|
-
): void => {
|
|
63
|
-
const messageId: string = uuidv4();
|
|
64
|
-
const requestPayload: IIPCMessageProcessorIPCPayload = {
|
|
65
|
-
header: this.#messageHeader,
|
|
66
|
-
messageId,
|
|
67
|
-
senderId: this.#id,
|
|
68
|
-
senderRole: this.#options.role,
|
|
69
|
-
requestPayload: payload,
|
|
70
|
-
responsePayload: { },
|
|
71
|
-
pid: process.pid.toString(),
|
|
72
|
-
messageType: 'REQUEST'
|
|
73
|
-
}
|
|
74
|
-
const messageRecord = {
|
|
75
|
-
messageId,
|
|
76
|
-
senderId: this.#id,
|
|
77
|
-
senderRole: this.#options.role,
|
|
78
|
-
requestPayload,
|
|
79
|
-
responses: { }, // record
|
|
80
|
-
startTime: performance.now(),
|
|
81
|
-
endTime: 0,
|
|
82
|
-
timeout: setTimeout(() => {
|
|
83
|
-
//this.#LogDebugMessage(chalk.red(`Timeout has occurred after: [${this.#options.requestResponseMessageTimeout}]ms with message id: [${messageRecord.messageId}]. Details: [${JSON.stringify(this.#inflightMessages[messageRecord.messageId].requestPayload)}]`));
|
|
84
|
-
setTimeout(() => {
|
|
85
|
-
delete this.#inflightMessages[messageRecord.messageId];
|
|
86
|
-
}, 0).unref();
|
|
87
|
-
errorCallBack(requestPayload);
|
|
88
|
-
}, this.#options.requestResponseMessageTimeout).unref(),// max message timeout allowed
|
|
89
|
-
callBack,
|
|
90
|
-
errorCallBack
|
|
91
|
-
}
|
|
92
|
-
this.#inflightMessages[messageRecord.messageId] = messageRecord;
|
|
93
|
-
//this.#LogDebugMessage(chalk.cyan(`sending: [${JSON.stringify(requestPayload)}]`));
|
|
94
|
-
this.#options.messageSender(requestPayload, options);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
#ProcessMessage = async (msg: any, options: any): Promise<void> => {
|
|
98
|
-
if (msg.header && msg.header.localeCompare(this.#messageHeader) === 0) {
|
|
99
|
-
const message = (msg as IIPCMessageProcessorIPCPayload);
|
|
100
|
-
if (this.#inflightMessages[message.messageId]) {
|
|
101
|
-
const inFlightMessageRecord: IIPCMessageProcessorWorkerRecord = this.#inflightMessages[message.messageId];
|
|
102
|
-
inFlightMessageRecord.responses[message.senderId ] = { ...message };
|
|
103
|
-
let completed = true; // Defaults to true
|
|
104
|
-
if (this.#options.ProcessResponseMessage) {
|
|
105
|
-
completed = await this.#options.ProcessResponseMessage(inFlightMessageRecord.responses, options);
|
|
106
|
-
if (completed) {
|
|
107
|
-
inFlightMessageRecord.endTime = performance.now();
|
|
108
|
-
clearTimeout(inFlightMessageRecord.timeout as NodeJS.Timeout);
|
|
109
|
-
inFlightMessageRecord.callBack({
|
|
110
|
-
responsePayload: Object.values(inFlightMessageRecord.responses).map(r => r.responsePayload)
|
|
111
|
-
} as any, options) //
|
|
112
|
-
delete this.#inflightMessages[message.messageId];
|
|
113
|
-
}
|
|
114
|
-
} else if (completed) {
|
|
115
|
-
inFlightMessageRecord.endTime = performance.now();
|
|
116
|
-
clearTimeout(inFlightMessageRecord.timeout as NodeJS.Timeout);
|
|
117
|
-
inFlightMessageRecord.callBack(message, options) // inFlightMessageRecord.responses
|
|
118
|
-
//@@inFlightMessageRecord.callBack(Object.values(inFlightMessageRecord.responses), options) //
|
|
119
|
-
delete this.#inflightMessages[message.messageId];
|
|
120
|
-
} else {
|
|
121
|
-
//console.log(chalk.grey(`#ProcessMessage:5`));
|
|
122
|
-
}
|
|
123
|
-
} else {
|
|
124
|
-
//throw new Error(`Could not find Request/Response message with id: [${message.messageId}]`); //@@
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
Start = (options?: any) => {
|
|
130
|
-
this.#messageHeader = `__STS__${this.#options.namespace}__${uuidv4()}`;
|
|
131
|
-
this.#options.messageReceiverStart(options);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
Stop = (options?: any) => {
|
|
135
|
-
// Kill in-flight messages
|
|
136
|
-
this.#options.messageReceiverStop(options);
|
|
137
|
-
|
|
138
|
-
for (const [, iPCMessageProcessorWorkerRecord] of Object.entries(this.#inflightMessages)) {
|
|
139
|
-
if (iPCMessageProcessorWorkerRecord.timeout) {
|
|
140
|
-
clearTimeout(iPCMessageProcessorWorkerRecord.timeout);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
this.#inflightMessages = { };
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Process a message recieved from a worker
|
|
147
|
-
ProcessMessage = async (msg: any, options: any) => {
|
|
148
|
-
if (msg.header) {
|
|
149
|
-
const checkName = `__STS__${this.#options.namespace}__`; //@@ this is a broadcast becuase the unique uuid is not part of the header test
|
|
150
|
-
if ((msg.header as string).includes(checkName)) {
|
|
151
|
-
const message = (msg as IIPCMessageProcessorIPCPayload);
|
|
152
|
-
if (msg.messageType.localeCompare('REQUEST') === 0) {
|
|
153
|
-
let processMessage = true;
|
|
154
|
-
if (message.requestPayload.args && message.requestPayload.args.length > 0 && message.requestPayload.args[0].group) {
|
|
155
|
-
const group = message.requestPayload.args[0].group;
|
|
156
|
-
processMessage = (this.#options.groups.indexOf(group) === -1) ? false : true;
|
|
157
|
-
}
|
|
158
|
-
if (processMessage) {
|
|
159
|
-
message.responsePayload = await this.#options.ProcessRequestMessage(message, options);
|
|
160
|
-
message.senderId = this.#id;
|
|
161
|
-
message.messageType = 'RESPONSE';
|
|
162
|
-
this.#options.messageSender(message, options);
|
|
163
|
-
}
|
|
164
|
-
} else {
|
|
165
|
-
// Received a response (to my request)
|
|
166
|
-
this.#ProcessMessage(msg, options);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/* eslint @typescript-eslint/no-explicit-any: 0 */ // --> OFF
|
|
2
|
-
import { ISTSLogger, JSONObject, defaultLogger } from '@nsshunt/stsutils'
|
|
3
|
-
|
|
4
|
-
import { Worker } from 'node:cluster';
|
|
5
|
-
|
|
6
|
-
import { IIPCMessageProcessorIPCPayload } from './commonTypes'
|
|
7
|
-
|
|
8
|
-
import { IPCMessageManager, IPCMessageManagerOptions } from './ipcMessageManager'
|
|
9
|
-
|
|
10
|
-
export interface IIPCMessageProcessorPrimary {
|
|
11
|
-
logger: ISTSLogger
|
|
12
|
-
namespace: string
|
|
13
|
-
processPayload?: (payload: IIPCMessageProcessorIPCPayload, options: any) => Promise<JSONObject>
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* IPC Message Handling.
|
|
18
|
-
*
|
|
19
|
-
* This class can be used to support messages between cluster.primary and cluster.worker instances using IPC.
|
|
20
|
-
* This class can be used for finer control for processing messages between the cluster primary and cluster workers.
|
|
21
|
-
* Use the ipcMessageHandler for a simpler implementation.
|
|
22
|
-
* This class is used for the cluster primary thread only.
|
|
23
|
-
* Note: Currently groups handling is not supported. Use the redis version for this capability.
|
|
24
|
-
*/
|
|
25
|
-
export class IPCMessageProcessorPrimary {
|
|
26
|
-
#ipcMessageManager: IPCMessageManager;
|
|
27
|
-
#worker: Worker | null = null;
|
|
28
|
-
|
|
29
|
-
constructor(classOptions: IIPCMessageProcessorPrimary) {
|
|
30
|
-
const ipcMessageManagerOptions: IPCMessageManagerOptions = {
|
|
31
|
-
logger: defaultLogger,
|
|
32
|
-
requestResponseMessageTimeout: 5000,
|
|
33
|
-
namespace: classOptions.namespace,
|
|
34
|
-
role: 'SERVER',
|
|
35
|
-
messageSender: (payload: IIPCMessageProcessorIPCPayload, options: any) => {
|
|
36
|
-
(options.worker as any).send(payload);
|
|
37
|
-
},
|
|
38
|
-
ProcessRequestMessage: async (payload: IIPCMessageProcessorIPCPayload, options: any): Promise<JSONObject> => {
|
|
39
|
-
if (classOptions.processPayload) {
|
|
40
|
-
return classOptions.processPayload(payload, options);
|
|
41
|
-
} else {
|
|
42
|
-
return { };
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
messageReceiverStart: (options: any) => {
|
|
46
|
-
// Receive a message to process from a worker
|
|
47
|
-
const worker = (options.worker as Worker);
|
|
48
|
-
worker.on('message', (payload) => this.#ipcMessageManager.ProcessMessage(payload, { worker }));
|
|
49
|
-
},
|
|
50
|
-
messageReceiverStop: (options: any) => {
|
|
51
|
-
const worker = (options.worker as Worker);
|
|
52
|
-
worker.off('message', (payload) => this.#ipcMessageManager.ProcessMessage(payload, { worker }));
|
|
53
|
-
},
|
|
54
|
-
groups: [ ]
|
|
55
|
-
}
|
|
56
|
-
this.#ipcMessageManager = new IPCMessageManager(ipcMessageManagerOptions);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
SendMessage = async (payload: JSONObject): Promise<JSONObject> => {
|
|
60
|
-
return this.#ipcMessageManager.SendMessage(payload, { worker: this.#worker });
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
Start = (worker: Worker) => {
|
|
64
|
-
this.#ipcMessageManager.Start({ worker });
|
|
65
|
-
this.#worker = worker;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
Stop = () => {
|
|
69
|
-
this.#ipcMessageManager.Stop({ worker: this.#worker });
|
|
70
|
-
this.#worker = null;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
get worker(): Worker | null {
|
|
74
|
-
return this.#worker;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/* eslint @typescript-eslint/no-explicit-any: 0 */ // --> OFF
|
|
2
|
-
import { ISTSLogger, JSONObject, defaultLogger } from '@nsshunt/stsutils'
|
|
3
|
-
|
|
4
|
-
import { IIPCMessageProcessorIPCPayload } from './commonTypes'
|
|
5
|
-
|
|
6
|
-
import { IPCMessageManager, IPCMessageManagerOptions } from './ipcMessageManager'
|
|
7
|
-
|
|
8
|
-
export interface IIPCMessageProcessorWorker {
|
|
9
|
-
logger: ISTSLogger
|
|
10
|
-
requestResponseMessageTimeout: number
|
|
11
|
-
namespace: string
|
|
12
|
-
processPayload?: (payload: IIPCMessageProcessorIPCPayload, options: any) => Promise<JSONObject>
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* IPC Message Handling.
|
|
17
|
-
*
|
|
18
|
-
* This class can be used to support messages between cluster.primary and cluster.worker instances using IPC.
|
|
19
|
-
* This class can be used for finer control for processing messages between the cluster primary and cluster workers.
|
|
20
|
-
* Use the ipcMessageHandler for a simpler implementation.
|
|
21
|
-
* This class is used for the cluster worker threads only.
|
|
22
|
-
* Note: Currently groups handling is not supported. Use the redis version for this capability.
|
|
23
|
-
*/
|
|
24
|
-
export class IPCMessageProcessorWorker {
|
|
25
|
-
#ipcMessageManager: IPCMessageManager;
|
|
26
|
-
|
|
27
|
-
constructor(classOptions: IIPCMessageProcessorWorker) {
|
|
28
|
-
const ipcMessageManagerOptions: IPCMessageManagerOptions = {
|
|
29
|
-
logger: defaultLogger,
|
|
30
|
-
requestResponseMessageTimeout: classOptions.requestResponseMessageTimeout,
|
|
31
|
-
namespace: classOptions.namespace,
|
|
32
|
-
role: 'CLIENT',
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
34
|
-
messageSender: (payload: IIPCMessageProcessorIPCPayload, options: any) => {
|
|
35
|
-
// Options not required for sending payloads to the master process
|
|
36
|
-
(process as any).send(payload);
|
|
37
|
-
},
|
|
38
|
-
ProcessRequestMessage: async (payload: IIPCMessageProcessorIPCPayload, options: any): Promise<JSONObject> => {
|
|
39
|
-
if (classOptions.processPayload) {
|
|
40
|
-
return classOptions.processPayload(payload, options);
|
|
41
|
-
} else {
|
|
42
|
-
return { };
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
messageReceiverStart: (options: any) => {
|
|
46
|
-
// Receive a message response back from the primary thread
|
|
47
|
-
process.on('message', (payload) => {
|
|
48
|
-
this.#ipcMessageManager.ProcessMessage(payload, options);
|
|
49
|
-
});
|
|
50
|
-
},
|
|
51
|
-
messageReceiverStop: (options: any) => {
|
|
52
|
-
process.off('message', (payload) => this.#ipcMessageManager.ProcessMessage(payload, options));
|
|
53
|
-
},
|
|
54
|
-
groups: [ ]
|
|
55
|
-
}
|
|
56
|
-
this.#ipcMessageManager = new IPCMessageManager(ipcMessageManagerOptions);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
SendMessage = async (payload: JSONObject): Promise<JSONObject> => {
|
|
60
|
-
return this.#ipcMessageManager.SendMessage(payload, { });
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
Start = () => {
|
|
64
|
-
this.#ipcMessageManager.Start({ });
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
Stop = () => {
|
|
68
|
-
this.#ipcMessageManager.Stop({ });
|
|
69
|
-
}
|
|
70
|
-
}
|
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
/* eslint @typescript-eslint/no-explicit-any: 0, @typescript-eslint/no-unused-vars: 0 */ // --> OFF */ // --> OFF
|
|
2
|
-
import { TinyEmitter } from "tiny-emitter";
|
|
3
|
-
import { ISTSLogger, JSONObject } from '@nsshunt/stsutils'
|
|
4
|
-
|
|
5
|
-
import { IIPCMessageProcessorIPCPayload, IServiceProcessContext, ProcessOptions } from '../commonTypes'
|
|
6
|
-
|
|
7
|
-
import { IPCMessageManager, IPCMessageManagerOptions } from '../ipcMessageManager'
|
|
8
|
-
|
|
9
|
-
import chalk from 'chalk';
|
|
10
|
-
|
|
11
|
-
import { MessagePort } from 'worker_threads';
|
|
12
|
-
|
|
13
|
-
export interface IWebWorderMessageHandlerOptions {
|
|
14
|
-
logger: ISTSLogger
|
|
15
|
-
role: 'SERVER' | 'CLIENT'
|
|
16
|
-
namespace: string
|
|
17
|
-
groups: string[]
|
|
18
|
-
ignoreEvents?: string[]
|
|
19
|
-
processOptions?: ProcessOptions
|
|
20
|
-
messagePort: MessagePort
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface IClientRecord {
|
|
24
|
-
id: string
|
|
25
|
-
clientConnected: Date
|
|
26
|
-
pingCount: number
|
|
27
|
-
timeout: NodeJS.Timeout
|
|
28
|
-
groups: string[]
|
|
29
|
-
serviceProcessContext?: IServiceProcessContext
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface IEventPayload {
|
|
33
|
-
__eventName: string
|
|
34
|
-
args: any[]
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface IEventRecord {
|
|
38
|
-
event: string
|
|
39
|
-
callback: any,
|
|
40
|
-
ctx?: any
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface IPingData {
|
|
44
|
-
id: string
|
|
45
|
-
groups: string[]
|
|
46
|
-
serviceProcessContext?: IServiceProcessContext
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export class WebWorkerMessageHandler extends TinyEmitter {
|
|
50
|
-
#ipcMessageManager: IPCMessageManager | null = null;
|
|
51
|
-
#options: IWebWorderMessageHandlerOptions;
|
|
52
|
-
#events: Record<string, IEventRecord> = { };
|
|
53
|
-
#clients: Record<string, IClientRecord> = { };
|
|
54
|
-
#pingTimeout: NodeJS.Timeout | null = null;
|
|
55
|
-
#messagePort: MessagePort;
|
|
56
|
-
|
|
57
|
-
constructor(options: IWebWorderMessageHandlerOptions) {
|
|
58
|
-
super();
|
|
59
|
-
this.#options = options;
|
|
60
|
-
this.#messagePort = options.messagePort;
|
|
61
|
-
|
|
62
|
-
this.SetupPrimary();
|
|
63
|
-
|
|
64
|
-
if (this.#options.role.localeCompare('CLIENT') === 0) {
|
|
65
|
-
const ping = () => {
|
|
66
|
-
this.#pingTimeout = setTimeout(() => {
|
|
67
|
-
const pingData: IPingData = {
|
|
68
|
-
id: (this.#ipcMessageManager as IPCMessageManager).id,
|
|
69
|
-
groups: this.#options.groups,
|
|
70
|
-
}
|
|
71
|
-
if (this.#options.processOptions) {
|
|
72
|
-
pingData.serviceProcessContext = this.#options.processOptions.serviceProcessContext;
|
|
73
|
-
}
|
|
74
|
-
this.emit('ping', pingData, (response: any) => { });
|
|
75
|
-
ping();
|
|
76
|
-
}, 1000).unref();
|
|
77
|
-
}
|
|
78
|
-
ping();
|
|
79
|
-
} else {
|
|
80
|
-
this.on('ping', (pingData: IPingData, callback: any) => {
|
|
81
|
-
const { id, groups, serviceProcessContext } = pingData;
|
|
82
|
-
if (this.#clients[id]) {
|
|
83
|
-
clearTimeout(this.#clients[id].timeout);
|
|
84
|
-
this.#clients[id].pingCount++;
|
|
85
|
-
this.#clients[id].timeout = setTimeout(() => {
|
|
86
|
-
delete this.#clients[id];
|
|
87
|
-
}, 2000);
|
|
88
|
-
this.#clients[id].groups = groups;
|
|
89
|
-
this.#clients[id].serviceProcessContext = serviceProcessContext;
|
|
90
|
-
} else {
|
|
91
|
-
this.#clients[id] = {
|
|
92
|
-
id,
|
|
93
|
-
clientConnected: new Date(),
|
|
94
|
-
pingCount: 0,
|
|
95
|
-
timeout: setTimeout(() => {
|
|
96
|
-
delete this.#clients[id];
|
|
97
|
-
}, 2000),
|
|
98
|
-
groups,
|
|
99
|
-
serviceProcessContext
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
callback('ok');
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
#processRawMessage = (rawmessage: string) => {
|
|
108
|
-
const message = JSON.parse(rawmessage);
|
|
109
|
-
this.#ipcMessageManager?.ProcessMessage(message, { });
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
get clients(): Record<string, IClientRecord> {
|
|
113
|
-
return this.#clients;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
get groups(): string[] {
|
|
117
|
-
return this.#options.groups;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
AddGroup = (group: string) => {
|
|
121
|
-
const index = this.#options.groups.indexOf(group);
|
|
122
|
-
if (index === -1) {
|
|
123
|
-
this.#options.groups.push(group);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
RemoveGroup = (group: string) => {
|
|
128
|
-
const removeIndex = this.#options.groups.indexOf(group);
|
|
129
|
-
if (removeIndex !== -1) {
|
|
130
|
-
this.#options.groups.splice(removeIndex, 1);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
SetupPrimary = () => {
|
|
135
|
-
const ipcMessageManagerOptions: IPCMessageManagerOptions = {
|
|
136
|
-
logger: this.#options.logger,
|
|
137
|
-
requestResponseMessageTimeout: 5000,
|
|
138
|
-
namespace: this.#options.namespace,
|
|
139
|
-
role: this.#options.role,
|
|
140
|
-
messageSender: this.#messageSender,
|
|
141
|
-
groups: this.#options.groups,
|
|
142
|
-
// This method is used to calculate if all responses have been received from multiple clients (broadcast)
|
|
143
|
-
// returns true/false.
|
|
144
|
-
ProcessResponseMessage: this.#ProcessResponseMessage,
|
|
145
|
-
// This gets called when an event is received from a message receiver (when ProcessMessage is invoked from the receiver event handler)
|
|
146
|
-
ProcessRequestMessage: this.#processPayload,
|
|
147
|
-
|
|
148
|
-
messageReceiverStart: (options: any) => {
|
|
149
|
-
this.#messagePort.on('message', this.#processRawMessage);
|
|
150
|
-
//this.#redisSubscriber.on("message", this.#processRawMessage);
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
messageReceiverStop: (options: any) => {
|
|
154
|
-
this.#messagePort.off("message", this.#processRawMessage);
|
|
155
|
-
//this.#redisSubscriber.off("message", this.#processRawMessage);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
this.#ipcMessageManager = new IPCMessageManager(ipcMessageManagerOptions);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
#messageSender = (payload: IIPCMessageProcessorIPCPayload, options: any) => {
|
|
163
|
-
if (payload.messageType.localeCompare('REQUEST') === 0) {
|
|
164
|
-
//this.#redisPublisher.publish(this.#requestChannel, JSON.stringify(payload));
|
|
165
|
-
this.#messagePort.postMessage(JSON.stringify(payload));
|
|
166
|
-
} else if (payload.messageType.localeCompare('RESPONSE') === 0) {
|
|
167
|
-
this.#messagePort.postMessage(JSON.stringify(payload));
|
|
168
|
-
//this.#redisPublisher.publish(this.#responseChannel, JSON.stringify(payload));
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
#ProcessResponseMessage = async (responses: Record<string, IIPCMessageProcessorIPCPayload>, options: any): Promise<boolean> => {
|
|
174
|
-
// Now check if we have all responses ...
|
|
175
|
-
let allFound = false;
|
|
176
|
-
|
|
177
|
-
for (const [responseId, response] of Object.entries(responses)) {
|
|
178
|
-
if (response.senderRole.localeCompare('CLIENT') === 0) {
|
|
179
|
-
allFound = true;
|
|
180
|
-
break;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
if (allFound) {
|
|
184
|
-
return allFound;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
let found = true;
|
|
188
|
-
|
|
189
|
-
// Sender role here is SERVER
|
|
190
|
-
let requestGroup = null;
|
|
191
|
-
for (const [responseId, response] of Object.entries(responses)) {
|
|
192
|
-
if (response.requestPayload.args.length > 0 && response.requestPayload.args[0].group) {
|
|
193
|
-
requestGroup = response.requestPayload.args[0].group;
|
|
194
|
-
break;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (requestGroup) {
|
|
199
|
-
const clientsInGroup = Object.values(this.#clients).filter(c => {
|
|
200
|
-
if (c.groups.indexOf(requestGroup) === -1) {
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
return true;
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
// Now make sure that all clients are in the responses
|
|
207
|
-
found = true;
|
|
208
|
-
clientsInGroup.forEach(c => {
|
|
209
|
-
if (!responses[c.id]) {
|
|
210
|
-
found = false;
|
|
211
|
-
}
|
|
212
|
-
})
|
|
213
|
-
} else {
|
|
214
|
-
const clientsInGroup = Object.values(this.#clients)
|
|
215
|
-
|
|
216
|
-
// Now make sure that all clients are in the responses
|
|
217
|
-
found = true;
|
|
218
|
-
clientsInGroup.forEach(c => {
|
|
219
|
-
if (!responses[c.id]) {
|
|
220
|
-
found = false;
|
|
221
|
-
}
|
|
222
|
-
})
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return found;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
#processPayload = (payload: IIPCMessageProcessorIPCPayload, options: any): Promise<JSONObject> => {
|
|
229
|
-
// check the event name from the collection and invoke that function
|
|
230
|
-
return new Promise<JSONObject>((resolve, reject) => {
|
|
231
|
-
if (payload.messageType.localeCompare('REQUEST') === 0) {
|
|
232
|
-
if (payload.requestPayload['__eventName']) {
|
|
233
|
-
const eventName = payload.requestPayload['__eventName'];
|
|
234
|
-
// Only process events that I have registered interest in (using .on)
|
|
235
|
-
if (this.#events[eventName]) {
|
|
236
|
-
try {
|
|
237
|
-
//const retVal = this.#events[eventName].callback(payload.requestPayload.args, payload, options, this.#events[eventName].ctx);
|
|
238
|
-
this.#events[eventName].callback(...payload.requestPayload.args, (responseMessage: any) => {
|
|
239
|
-
resolve(responseMessage);
|
|
240
|
-
});
|
|
241
|
-
} catch (error) {
|
|
242
|
-
reject(error);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// p.on('fromworker', (arg1: number, arg2: string, callback: any) => {
|
|
251
|
-
override on(event: string, callback: any, ctx?: any): this {
|
|
252
|
-
if (this.#events[event]) {
|
|
253
|
-
// Update the event with the same name
|
|
254
|
-
delete this.#events[event];
|
|
255
|
-
}
|
|
256
|
-
const eventObject: IEventRecord = {
|
|
257
|
-
event,
|
|
258
|
-
callback,
|
|
259
|
-
ctx
|
|
260
|
-
}
|
|
261
|
-
this.#events[eventObject.event] = eventObject;
|
|
262
|
-
return this;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
override off(event: string, callback?: any): this {
|
|
266
|
-
if (this.#events[event]) {
|
|
267
|
-
delete this.#events[event];
|
|
268
|
-
}
|
|
269
|
-
return this;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
Start = () => {
|
|
273
|
-
this.#ipcMessageManager?.Start();
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
Stop = () => {
|
|
277
|
-
if (this.#pingTimeout) {
|
|
278
|
-
clearTimeout(this.#pingTimeout);
|
|
279
|
-
this.#pingTimeout = null;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
this.#ipcMessageManager?.Stop();
|
|
283
|
-
|
|
284
|
-
/*
|
|
285
|
-
this.#redisSubscriber.quit();
|
|
286
|
-
this.#redisSubscriber.disconnect();
|
|
287
|
-
|
|
288
|
-
this.#redisPublisher.quit();
|
|
289
|
-
this.#redisPublisher.disconnect();
|
|
290
|
-
*/
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
override emit(event: string, ...args: any[]): this {
|
|
294
|
-
(async () => {
|
|
295
|
-
try {
|
|
296
|
-
const retVal = await this.#ipcMessageManager?.SendMessage({
|
|
297
|
-
__eventName: event,
|
|
298
|
-
args: args.slice(0, args.length-1)
|
|
299
|
-
} as IEventPayload);
|
|
300
|
-
// Invoke the response callback
|
|
301
|
-
args[args.length-1](retVal);
|
|
302
|
-
} catch (error) {
|
|
303
|
-
if (this.#options.ignoreEvents) {
|
|
304
|
-
if (this.#options.ignoreEvents.indexOf((error as any).__eventName) !== -1) {
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
this.#options.logger.error(chalk.red(`RedisMessageHandler:emit(): Error: [${JSON.stringify(error)}]`));
|
|
309
|
-
}
|
|
310
|
-
})();
|
|
311
|
-
return this;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
emitWithError(event: string, args: JSONObject, responseCb: (response: JSONObject | undefined) => void, errorCb: (error: any) => void): this {
|
|
315
|
-
(async () => {
|
|
316
|
-
try {
|
|
317
|
-
const retVal = await this.#ipcMessageManager?.SendMessage({
|
|
318
|
-
__eventName: event,
|
|
319
|
-
args: [ args ]
|
|
320
|
-
} as IEventPayload);
|
|
321
|
-
// Invoke the response callback
|
|
322
|
-
responseCb(retVal);
|
|
323
|
-
} catch (error) {
|
|
324
|
-
if (this.#options.ignoreEvents) {
|
|
325
|
-
if (this.#options.ignoreEvents.indexOf((error as any).__eventName) !== -1) {
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
errorCb(error);
|
|
330
|
-
}
|
|
331
|
-
})();
|
|
332
|
-
return this;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
emitex = async(event: string, ...args: any[]): Promise<JSONObject> => {
|
|
336
|
-
return (this.#ipcMessageManager as IPCMessageManager).SendMessage({
|
|
337
|
-
__eventName: event,
|
|
338
|
-
args
|
|
339
|
-
} as IEventPayload);
|
|
340
|
-
}
|
|
341
|
-
}
|