@nsshunt/stsmessaging 1.0.78 → 1.0.79

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.
@@ -0,0 +1,624 @@
1
+ # IPCMessageHandler
2
+
3
+ `IPCMessageHandler` provides IPC-based request/response and event messaging between a Node.js cluster primary process and **multiple worker processes**.
4
+
5
+ It is built on top of `MessagingManager` and exposes an event-oriented API using `on()`, `off()`, `emit()`, and `emitNoResponse()`.
6
+
7
+ ---
8
+
9
+ ## Purpose
10
+
11
+ Use `IPCMessageHandler` when one server/primary process needs to communicate with a **collection of worker processes**.
12
+
13
+ Typical topology:
14
+
15
+ ```text
16
+ Worker 1
17
+ /
18
+ Primary / Server --- Worker 2
19
+ \
20
+ Worker 3
21
+ ```
22
+
23
+ The primary process maintains the worker collection inside the message handler and can broadcast messages or events to all registered workers.
24
+
25
+ ---
26
+
27
+ ## Key Characteristics
28
+
29
+ - Supports `SERVER` and `CLIENT` roles.
30
+ - Designed for **one primary process to many workers**.
31
+ - Maintains an internal collection of workers/clients.
32
+ - Workers are registered with `AddClient()`.
33
+ - Workers are removed with `RemoveClient()`.
34
+ - Server-side `SendMessage()` sends the message to **all registered workers**.
35
+ - Server-side `emitNoResponse()` sends the event to **all registered workers**.
36
+ - Request/response sends are aggregated with `Promise.all()`.
37
+ - Uses `MessagingManager` for the underlying request/response message protocol.
38
+ - Supports event-style request handling through `on()` and `off()`.
39
+ - Supports request messages that expect a response.
40
+ - Supports fire-and-forget request messages through `emitNoResponse()`.
41
+
42
+ ---
43
+
44
+ ## Constructor
45
+
46
+ ```ts
47
+ new IPCMessageHandler(options);
48
+ ```
49
+
50
+ ### Options
51
+
52
+ ```ts
53
+ export interface IPCMessageHandlerOptions {
54
+ logger: ISTSLogger;
55
+ requestResponseMessageTimeout: number;
56
+ namespace: string;
57
+ role: "SERVER" | "CLIENT";
58
+ ignoreEvents?: string[];
59
+ }
60
+ ```
61
+
62
+ ### `logger`
63
+
64
+ Logger used when message processing or event emission fails.
65
+
66
+ ### `requestResponseMessageTimeout`
67
+
68
+ Maximum time allowed for request/response messaging.
69
+
70
+ On the client side this value is passed directly to `MessagingManager`.
71
+
72
+ ### `namespace`
73
+
74
+ Namespace used by the underlying messaging layer.
75
+
76
+ ### `role`
77
+
78
+ Determines how the handler is configured.
79
+
80
+ ```ts
81
+ role: "SERVER";
82
+ ```
83
+
84
+ Configures the handler for the cluster primary/server process.
85
+
86
+ ```ts
87
+ role: "CLIENT";
88
+ ```
89
+
90
+ Configures the handler for a worker/client process.
91
+
92
+ ### `ignoreEvents`
93
+
94
+ Optional collection of event names whose errors should be ignored by `emit()`.
95
+
96
+ ---
97
+
98
+ # Server Role
99
+
100
+ When configured as:
101
+
102
+ ```ts
103
+ role: "SERVER";
104
+ ```
105
+
106
+ the handler is designed to manage **multiple workers**.
107
+
108
+ The server-side handler keeps an internal client collection:
109
+
110
+ ```ts
111
+ #clients: Record<string, IClientRecord> = {};
112
+ ```
113
+
114
+ Each registered worker has:
115
+
116
+ - the worker/client object;
117
+ - its associated message listener.
118
+
119
+ ---
120
+
121
+ ## AddClient()
122
+
123
+ ```ts
124
+ AddClient(client): string
125
+ ```
126
+
127
+ Registers a worker with the handler.
128
+
129
+ Example:
130
+
131
+ ```ts
132
+ const workerId = ipcHandler.AddClient(worker);
133
+ ```
134
+
135
+ `AddClient()`:
136
+
137
+ 1. Creates a unique identifier.
138
+ 2. Registers a `"message"` listener on the worker.
139
+ 3. Routes incoming messages into `MessagingManager.ProcessMessage()`.
140
+ 4. Stores the worker and listener in the internal client collection.
141
+ 5. Returns the generated client ID.
142
+
143
+ Conceptually:
144
+
145
+ ```text
146
+ IPCMessageHandler
147
+
148
+ ├── client-id-1 -> Worker 1
149
+ ├── client-id-2 -> Worker 2
150
+ └── client-id-3 -> Worker 3
151
+ ```
152
+
153
+ ---
154
+
155
+ ## RemoveClient()
156
+
157
+ ```ts
158
+ RemoveClient(id);
159
+ ```
160
+
161
+ Removes a previously registered worker.
162
+
163
+ Example:
164
+
165
+ ```ts
166
+ ipcHandler.RemoveClient(workerId);
167
+ ```
168
+
169
+ The method:
170
+
171
+ 1. Finds the worker record.
172
+ 2. Removes its `"message"` listener.
173
+ 3. Removes the worker from the internal collection.
174
+
175
+ ---
176
+
177
+ ## clients
178
+
179
+ ```ts
180
+ ipcHandler.clients;
181
+ ```
182
+
183
+ Returns the current internal worker/client collection.
184
+
185
+ ---
186
+
187
+ # Client Role
188
+
189
+ When configured as:
190
+
191
+ ```ts
192
+ role: "CLIENT";
193
+ ```
194
+
195
+ the handler communicates with the parent process using Node.js process IPC.
196
+
197
+ Outgoing messages use:
198
+
199
+ ```ts
200
+ process.send(...)
201
+ ```
202
+
203
+ Incoming messages are received with:
204
+
205
+ ```ts
206
+ process.on("message", ...)
207
+ ```
208
+
209
+ The worker does not maintain a collection of other workers.
210
+
211
+ ---
212
+
213
+ # Start()
214
+
215
+ ```ts
216
+ ipcHandler.Start();
217
+ ```
218
+
219
+ Starts the underlying `MessagingManager`.
220
+
221
+ No worker argument is required.
222
+
223
+ On the server side, workers are managed separately through `AddClient()` and `RemoveClient()`.
224
+
225
+ ---
226
+
227
+ # Stop()
228
+
229
+ ```ts
230
+ ipcHandler.Stop();
231
+ ```
232
+
233
+ Stops the underlying `MessagingManager`.
234
+
235
+ Worker message listeners registered by `AddClient()` should be managed through `RemoveClient()`.
236
+
237
+ ---
238
+
239
+ # SendMessage()
240
+
241
+ ```ts
242
+ await ipcHandler.SendMessage(payload);
243
+ ```
244
+
245
+ Sends a request message.
246
+
247
+ ## Client behavior
248
+
249
+ When running as a client:
250
+
251
+ ```text
252
+ Worker -> Primary
253
+ ```
254
+
255
+ a single message is sent to the parent process.
256
+
257
+ ## Server behavior
258
+
259
+ When running as a server:
260
+
261
+ ```text
262
+ Primary -> Worker 1
263
+ -> Worker 2
264
+ -> Worker 3
265
+ ```
266
+
267
+ the message is sent to **every registered worker**.
268
+
269
+ The individual requests are collected into an array and executed using:
270
+
271
+ ```ts
272
+ Promise.all(...)
273
+ ```
274
+
275
+ The returned value therefore represents the results from all workers.
276
+
277
+ Conceptually:
278
+
279
+ ```ts
280
+ {
281
+ result: [worker1Response, worker2Response, worker3Response];
282
+ }
283
+ ```
284
+
285
+ This broadcast behavior is one of the most important characteristics of `IPCMessageHandler`.
286
+
287
+ ---
288
+
289
+ # Event Registration
290
+
291
+ ## on()
292
+
293
+ ```ts
294
+ ipcHandler.on(eventName, callback);
295
+ ```
296
+
297
+ Registers a callback for an incoming IPC event.
298
+
299
+ Example:
300
+
301
+ ```ts
302
+ ipcHandler.on("get-status", (callback) => {
303
+ callback({
304
+ status: "OK",
305
+ pid: process.pid,
306
+ });
307
+ });
308
+ ```
309
+
310
+ Only one callback is stored for a given event name.
311
+
312
+ Registering the same event again replaces the previous callback.
313
+
314
+ ---
315
+
316
+ ## off()
317
+
318
+ ```ts
319
+ ipcHandler.off(eventName);
320
+ ```
321
+
322
+ Removes the registered callback for an event.
323
+
324
+ ---
325
+
326
+ # emit()
327
+
328
+ ```ts
329
+ ipcHandler.emit(eventName, ...args, callback);
330
+ ```
331
+
332
+ Sends an event that expects a response.
333
+
334
+ Example:
335
+
336
+ ```ts
337
+ ipcHandler.emit("get-status", (response) => {
338
+ console.log(response);
339
+ });
340
+ ```
341
+
342
+ Internally the event is converted into a messaging payload containing:
343
+
344
+ ```ts
345
+ {
346
+ __eventName: eventName,
347
+ args: [...]
348
+ }
349
+ ```
350
+
351
+ The final argument is treated as the response callback.
352
+
353
+ ---
354
+
355
+ ## Server-side emit behavior
356
+
357
+ Because `emit()` ultimately calls `SendMessage()`, a server-side `emit()` is broadcast to **all registered workers**.
358
+
359
+ Therefore:
360
+
361
+ ```ts
362
+ ipcHandler.emit("get-status", callback);
363
+ ```
364
+
365
+ can produce responses from multiple workers.
366
+
367
+ Conceptually:
368
+
369
+ ```text
370
+ Primary
371
+ |
372
+ +----> Worker 1
373
+ |
374
+ +----> Worker 2
375
+ |
376
+ +----> Worker 3
377
+ ```
378
+
379
+ ---
380
+
381
+ # emitNoResponse()
382
+
383
+ ```ts
384
+ await ipcHandler.emitNoResponse(eventName, ...args);
385
+ ```
386
+
387
+ Sends a fire-and-forget event.
388
+
389
+ No response callback is expected.
390
+
391
+ ## Client behavior
392
+
393
+ ```text
394
+ Worker -> Primary
395
+ ```
396
+
397
+ ## Server behavior
398
+
399
+ The event is sent to **every registered worker**.
400
+
401
+ ```text
402
+ Primary
403
+ |
404
+ +----> Worker 1
405
+ +----> Worker 2
406
+ +----> Worker 3
407
+ ```
408
+
409
+ ---
410
+
411
+ # Message Processing
412
+
413
+ Incoming request messages are processed by `#processPayload()`.
414
+
415
+ The payload must contain:
416
+
417
+ ```ts
418
+ requestPayload.__eventName;
419
+ ```
420
+
421
+ The corresponding event callback is located in the internal event collection.
422
+
423
+ Two request types are handled:
424
+
425
+ ```text
426
+ REQUEST
427
+ REQUEST_NO_RESPONSE
428
+ ```
429
+
430
+ ## REQUEST
431
+
432
+ The registered callback receives the request arguments followed by a response callback.
433
+
434
+ Conceptually:
435
+
436
+ ```ts
437
+ handler.on("event-name", (...args, callback) => {
438
+ callback(response);
439
+ });
440
+ ```
441
+
442
+ ## REQUEST_NO_RESPONSE
443
+
444
+ The registered event callback is invoked without a response callback.
445
+
446
+ ---
447
+
448
+ # Typical Server Usage
449
+
450
+ ```ts
451
+ const ipcHandler = new IPCMessageHandler({
452
+ logger,
453
+ requestResponseMessageTimeout: 5000,
454
+ namespace: "workers",
455
+ role: "SERVER",
456
+ });
457
+
458
+ ipcHandler.Start();
459
+
460
+ const worker1Id = ipcHandler.AddClient(worker1);
461
+ const worker2Id = ipcHandler.AddClient(worker2);
462
+ const worker3Id = ipcHandler.AddClient(worker3);
463
+
464
+ ipcHandler.emit("refresh-config", (responses) => {
465
+ console.log(responses);
466
+ });
467
+ ```
468
+
469
+ The event is sent to all three workers.
470
+
471
+ ---
472
+
473
+ # Typical Worker Usage
474
+
475
+ ```ts
476
+ const ipcHandler = new IPCMessageHandler({
477
+ logger,
478
+ requestResponseMessageTimeout: 5000,
479
+ namespace: "workers",
480
+ role: "CLIENT",
481
+ });
482
+
483
+ ipcHandler.on("refresh-config", (callback) => {
484
+ // Refresh configuration.
485
+
486
+ callback({
487
+ pid: process.pid,
488
+ status: "OK",
489
+ });
490
+ });
491
+
492
+ ipcHandler.Start();
493
+ ```
494
+
495
+ ---
496
+
497
+ # Difference from IPCMessageHandlerPair
498
+
499
+ This is the most important distinction.
500
+
501
+ ## IPCMessageHandler is a one-to-many handler
502
+
503
+ ```text
504
+ IPCMessageHandler
505
+
506
+ Worker 1
507
+ /
508
+ Primary ---------- Worker 2
509
+ \
510
+ Worker 3
511
+ ```
512
+
513
+ The class itself owns the worker collection.
514
+
515
+ ```ts
516
+ #clients: Record<string, IClientRecord>
517
+ ```
518
+
519
+ Workers are managed with:
520
+
521
+ ```ts
522
+ AddClient(worker);
523
+ RemoveClient(id);
524
+ ```
525
+
526
+ Server-side messages are broadcast to every registered worker.
527
+
528
+ ---
529
+
530
+ ## IPCMessageHandlerPair is a one-to-one handler
531
+
532
+ `IPCMessageHandlerPair` represents one primary/worker relationship:
533
+
534
+ ```text
535
+ Primary <----------> Worker 1
536
+ ```
537
+
538
+ It stores one worker:
539
+
540
+ ```ts
541
+ #worker;
542
+ ```
543
+
544
+ and starts that relationship using:
545
+
546
+ ```ts
547
+ pair.Start(worker);
548
+ ```
549
+
550
+ To manage three workers using `IPCMessageHandlerPair`, the surrounding application would normally maintain three handler instances:
551
+
552
+ ```text
553
+ Worker Manager
554
+
555
+ ├── IPCMessageHandlerPair -> Worker 1
556
+ ├── IPCMessageHandlerPair -> Worker 2
557
+ └── IPCMessageHandlerPair -> Worker 3
558
+ ```
559
+
560
+ ---
561
+
562
+ ## Responsibility Difference
563
+
564
+ ### IPCMessageHandler
565
+
566
+ The handler is responsible for:
567
+
568
+ ```text
569
+ IPC messaging
570
+ +
571
+ worker collection management
572
+ +
573
+ broadcasting
574
+ ```
575
+
576
+ ### IPCMessageHandlerPair
577
+
578
+ The handler is responsible for:
579
+
580
+ ```text
581
+ IPC messaging
582
+ +
583
+ one worker connection
584
+ ```
585
+
586
+ Worker collection management and broadcasting are expected to happen outside the pair.
587
+
588
+ ---
589
+
590
+ ## Comparison
591
+
592
+ | Feature | IPCMessageHandler | IPCMessageHandlerPair |
593
+ | ------------------------------ | ------------------------------- | --------------------------- |
594
+ | Relationship | One-to-many | One-to-one |
595
+ | Server owns worker collection | Yes | No |
596
+ | Worker storage | `#clients` collection | Single `#worker` |
597
+ | Add worker | `AddClient(worker)` | `Start(worker)` |
598
+ | Remove worker | `RemoveClient(id)` | `Stop()` |
599
+ | Server `SendMessage()` | Sends to all workers | Sends to one worker |
600
+ | Server `emit()` | Broadcasts | Sends to one worker |
601
+ | Server `emitNoResponse()` | Broadcasts | Sends to one worker |
602
+ | Multiple responses | Aggregated with `Promise.all()` | Single response |
603
+ | Broadcast built into transport | Yes | No |
604
+ | Best suited for | Cluster-wide communication | Dedicated worker connection |
605
+
606
+ ---
607
+
608
+ # When to Use IPCMessageHandler
609
+
610
+ Use `IPCMessageHandler` when the message handler itself should own and manage the worker collection.
611
+
612
+ It is particularly appropriate when the required abstraction is:
613
+
614
+ ```text
615
+ "Send this message to my cluster workers."
616
+ ```
617
+
618
+ rather than:
619
+
620
+ ```text
621
+ "Send this message to this specific worker."
622
+ ```
623
+
624
+ If the surrounding process manager already owns the worker collection, `IPCMessageHandlerPair` may provide a cleaner separation of responsibilities.