@actdim/msgmesh 1.3.2 → 1.3.4

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.
Files changed (2) hide show
  1. package/README.md +79 -75
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -21,15 +21,17 @@
21
21
  - [Global vs Local Usage](#global-vs-local-usage)
22
22
  - [Creating a Message Bus](#creating-a-message-bus)
23
23
  - [Type Utilities](#type-utilities)
24
- - [API Reference](#api-reference)
25
- - [Configuration](#configuration)
26
- - [`send()`](#sending-messages-send)
27
- - [`on()`](#subscribing-to-messages-on)
28
- - [`once()`](#awaiting-a-single-message-once)
29
- - [`stream()`](#streaming-messages-stream)
30
- - [`provide()`](#providing-response-handlers-provide)
31
- - [`request()`](#request-response-pattern-request)
32
- - [Advanced Features](#advanced-features)
24
+ - [API Reference](#api-reference)
25
+ - [Configuration](#configuration)
26
+ - [`send()`](#sending-messages-send)
27
+ - [`on()`](#subscribing-to-messages-on)
28
+ - [`once()`](#awaiting-a-single-message-once)
29
+ - [`stream()`](#streaming-messages-stream)
30
+ - [`provide()`](#providing-response-handlers-provide)
31
+ - [Provider-Side Cancellation](#cancellation-handling)
32
+ - [`request()`](#request-response-pattern-request)
33
+ - [Request Cancellation](#cancellation)
34
+ - [Advanced Features](#advanced-features)
33
35
  - [Message Replay](#message-replay)
34
36
  - [Throttling and Debouncing](#throttling-and-debouncing)
35
37
  - [Error Handling](#error-handling)
@@ -43,13 +45,13 @@ Try @actdim/msgmesh instantly in your browser without any installation:
43
45
 
44
46
  [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/~/github.com/actdim/msgmesh)
45
47
 
46
- Once the project loads, run the tests to see the message bus in action:
47
-
48
- ```bash
49
- pnpm run test
50
- ```
51
-
52
- ## Installation
48
+ Once the project loads, run the tests to see the message bus in action:
49
+
50
+ ```bash
51
+ pnpm run test
52
+ ```
53
+
54
+ ## Installation
53
55
 
54
56
  ```bash
55
57
  npm install @actdim/msgmesh
@@ -193,19 +195,19 @@ Use generic `MsgStruct<...>` to define bus structures: it extends your declared
193
195
  import { MsgStruct } from '@actdim/msgmesh';
194
196
 
195
197
  export type MyBusStruct = MsgStruct<{
196
- 'Test.ComputeSum': {
198
+ 'TEST.COMPUTE_SUM': {
197
199
  in: { a: number; b: number };
198
200
  out: number;
199
201
  };
200
- 'Test.DoSomeWork': {
202
+ 'TEST.DO_SOME_WORK': {
201
203
  in: string;
202
204
  out: void;
203
205
  };
204
- 'Test.TestTaskWithRepeat': {
206
+ 'TEST.TEST_TASK_WITH_REPEAT': {
205
207
  in: string;
206
208
  out: void;
207
209
  };
208
- 'Test.Multiplexer': {
210
+ 'TEST.MULTIPLEXER': {
209
211
  in1: string;
210
212
  in2: number;
211
213
  out: number;
@@ -274,7 +276,7 @@ type MyMsgChannels<TChannel extends keyof MyBusStruct | Array<keyof MyBusStruct>
274
276
  // Helper types are necessary for IntelliSense with dynamic types
275
277
  // All API checks are enforced at compile time - you cannot violate defined contracts
276
278
  type Behavior = {
277
- messages: MyMsgChannels<'Test.ComputeSum' | 'Test.DoSomeWork'>;
279
+ messages: MyMsgChannels<'TEST.COMPUTE_SUM' | 'TEST.DO_SOME_WORK'>;
278
280
  };
279
281
  ```
280
282
 
@@ -297,7 +299,7 @@ You can configure channels with various options:
297
299
  import { MsgBusConfig } from '@actdim/msgmesh';
298
300
 
299
301
  const config: MsgBusConfig<MyBusStruct> = {
300
- 'Test.ComputeSum': {
302
+ 'TEST.COMPUTE_SUM': {
301
303
  replayBufferSize: 10, // Number of messages to buffer for replay
302
304
  replayWindowTime: 5000, // Time window for replay (ms)
303
305
  delay: 100, // Delay before processing (ms)
@@ -321,33 +323,33 @@ Send a message to the bus for a specific channel and group (default is `in`). Th
321
323
  ```typescript
322
324
  // Basic send
323
325
  await msgBus.send({
324
- channel: 'Test.ComputeSum',
326
+ channel: 'TEST.COMPUTE_SUM',
325
327
  payload: { a: 10, b: 20 }, // Typed and validated
326
328
  });
327
329
 
328
330
  // With group specification
329
331
  await msgBus.send({
330
- channel: 'Test.Multiplexer',
332
+ channel: 'TEST.MULTIPLEXER',
331
333
  group: 'in1',
332
334
  payload: 'hello', // Typed as string for 'in1' group
333
335
  });
334
336
 
335
337
  await msgBus.send({
336
- channel: 'Test.Multiplexer',
338
+ channel: 'TEST.MULTIPLEXER',
337
339
  group: 'in2',
338
340
  payload: 42, // Typed as number for 'in2' group
339
341
  });
340
342
 
341
343
  // With topic
342
344
  await msgBus.send({
343
- channel: 'Test.DoSomeWork',
345
+ channel: 'TEST.DO_SOME_WORK',
344
346
  topic: 'priority-high',
345
347
  payload: 'urgent task',
346
348
  });
347
349
 
348
350
  // With custom headers
349
351
  await msgBus.send({
350
- channel: 'Test.ComputeSum',
352
+ channel: 'TEST.COMPUTE_SUM',
351
353
  payload: { a: 5, b: 15 },
352
354
  headers: {
353
355
  correlationId: 'task-123',
@@ -365,7 +367,7 @@ Subscribe to messages on a specific channel and group with optional topic filter
365
367
  ```typescript
366
368
  // Basic subscription
367
369
  msgBus.on({
368
- channel: 'Test.ComputeSum',
370
+ channel: 'TEST.COMPUTE_SUM',
369
371
  callback: (msg) => {
370
372
  // msg.payload is typed as { a: number; b: number }
371
373
  console.log('Received:', msg.payload);
@@ -374,7 +376,7 @@ msgBus.on({
374
376
 
375
377
  // Subscribe to specific group
376
378
  msgBus.on({
377
- channel: 'Test.ComputeSum',
379
+ channel: 'TEST.COMPUTE_SUM',
378
380
  group: 'out', // Listen for responses
379
381
  callback: (msg) => {
380
382
  // msg.payload is typed as number
@@ -384,7 +386,7 @@ msgBus.on({
384
386
 
385
387
  // With topic filtering (regex pattern)
386
388
  msgBus.on({
387
- channel: 'Test.DoSomeWork',
389
+ channel: 'TEST.DO_SOME_WORK',
388
390
  topic: '/^task-.*/', // Match topics starting with "task-"
389
391
  callback: (msg) => {
390
392
  console.log('Task message:', msg.payload);
@@ -393,7 +395,7 @@ msgBus.on({
393
395
 
394
396
  // With options
395
397
  msgBus.on({
396
- channel: 'Test.ComputeSum',
398
+ channel: 'TEST.COMPUTE_SUM',
397
399
  callback: (msg) => {
398
400
  console.log('Message:', msg.payload);
399
401
  },
@@ -415,7 +417,7 @@ msgBus.on({
415
417
 
416
418
  ```typescript
417
419
  msgBus.on({
418
- channel: 'Test.ComputeSum',
420
+ channel: 'TEST.COMPUTE_SUM',
419
421
  callback: (msg) => {
420
422
  console.log(msg.payload);
421
423
  },
@@ -433,7 +435,7 @@ Use `AbortSignal` for controlled unsubscription. This allows combining abort sig
433
435
  const abortController = new AbortController();
434
436
 
435
437
  msgBus.on({
436
- channel: "Test.ComputeSum",
438
+ channel: "TEST.COMPUTE_SUM",
437
439
  callback: (msg) => {
438
440
  console.log(msg.payload);
439
441
  },
@@ -455,7 +457,7 @@ const combinedSignal = AbortSignal.any([
455
457
  ]);
456
458
 
457
459
  msgBus.on({
458
- channel: "Test.ComputeSum",
460
+ channel: "TEST.COMPUTE_SUM",
459
461
  options: {
460
462
  abortSignal: combinedSignal
461
463
  },
@@ -472,7 +474,7 @@ function MyComponent() {
472
474
  const controller = new AbortController();
473
475
 
474
476
  msgBus.on({
475
- channel: "Test.Events",
477
+ channel: "TEST.EVENTS",
476
478
  callback: handleEvent,
477
479
  options: {
478
480
  abortSignal: controller.signal
@@ -496,14 +498,14 @@ Subscribe and await the first (next) message on a specific channel and group, si
496
498
  ```typescript
497
499
  // Wait for one message
498
500
  const msg = await msgBus.once({
499
- channel: 'Test.ComputeSum',
501
+ channel: 'TEST.COMPUTE_SUM',
500
502
  });
501
503
 
502
504
  console.log('Received:', msg.payload); // Typed as { a: number; b: number }
503
505
 
504
506
  // With group specification
505
507
  const response = await msgBus.once({
506
- channel: 'Test.ComputeSum',
508
+ channel: 'TEST.COMPUTE_SUM',
507
509
  group: 'out',
508
510
  });
509
511
 
@@ -511,7 +513,7 @@ console.log('Result:', response.payload); // Typed as number
511
513
 
512
514
  // With topic filtering
513
515
  const taskMsg = await msgBus.once({
514
- channel: 'Test.DoSomeWork',
516
+ channel: 'TEST.DO_SOME_WORK',
515
517
  topic: '/^priority-.*/', // Match topics starting with "priority-"
516
518
  });
517
519
  ```
@@ -523,7 +525,7 @@ Configure timeout duration via the `timeout` option. The `abortSignal` option al
523
525
  ```typescript
524
526
  try {
525
527
  const msg = await msgBus.once({
526
- channel: 'Test.ComputeSum',
528
+ channel: 'TEST.COMPUTE_SUM',
527
529
  options: {
528
530
  timeout: 5000, // 5 second timeout
529
531
  },
@@ -539,7 +541,7 @@ try {
539
541
  const abortController = new AbortController();
540
542
 
541
543
  const messagePromise = msgBus.once({
542
- channel: 'Test.ComputeSum',
544
+ channel: 'TEST.COMPUTE_SUM',
543
545
  options: {
544
546
  timeout: 10000,
545
547
  abortSignal: abortController.signal,
@@ -565,7 +567,7 @@ Create an async iterable iterator for consuming messages as a stream.
565
567
  ```typescript
566
568
  // Basic streaming
567
569
  const messageStream = msgBus.stream({
568
- channel: 'Test.ComputeSum',
570
+ channel: 'TEST.COMPUTE_SUM',
569
571
  });
570
572
 
571
573
  for await (const msg of messageStream) {
@@ -575,7 +577,7 @@ for await (const msg of messageStream) {
575
577
 
576
578
  // With topic filtering
577
579
  const taskStream = msgBus.stream({
578
- channel: 'Test.DoSomeWork',
580
+ channel: 'TEST.DO_SOME_WORK',
579
581
  topic: '/^task-.*/',
580
582
  });
581
583
 
@@ -593,7 +595,7 @@ For a hard time limit on the stream's total duration, use `AbortSignal.timeout()
593
595
  ```typescript
594
596
  // Inactivity timeout: end stream if no messages for 5s
595
597
  const stream1 = msgBus.stream({
596
- channel: 'Test.Events',
598
+ channel: 'TEST.EVENTS',
597
599
  options: {
598
600
  timeout: 5000,
599
601
  },
@@ -601,7 +603,7 @@ const stream1 = msgBus.stream({
601
603
 
602
604
  // Total duration limit: end stream after 60s regardless of activity
603
605
  const stream2 = msgBus.stream({
604
- channel: 'Test.Events',
606
+ channel: 'TEST.EVENTS',
605
607
  options: {
606
608
  abortSignal: AbortSignal.timeout(60000),
607
609
  },
@@ -609,7 +611,7 @@ const stream2 = msgBus.stream({
609
611
 
610
612
  // Both: inactivity 5s + hard limit 60s
611
613
  const stream3 = msgBus.stream({
612
- channel: 'Test.Events',
614
+ channel: 'TEST.EVENTS',
613
615
  options: {
614
616
  timeout: 5000,
615
617
  abortSignal: AbortSignal.timeout(60000),
@@ -626,7 +628,7 @@ The callback can be asynchronous and its result is automatically used to form th
626
628
  ```typescript
627
629
  // Simple provider
628
630
  msgBus.provide({
629
- channel: 'Test.ComputeSum',
631
+ channel: 'TEST.COMPUTE_SUM',
630
632
  callback: (msg) => {
631
633
  // msg.payload is typed as { a: number; b: number }
632
634
  // Return type is inferred as number (from 'out' type)
@@ -636,7 +638,7 @@ msgBus.provide({
636
638
 
637
639
  // Async provider
638
640
  msgBus.provide({
639
- channel: 'Test.DoSomeWork',
641
+ channel: 'TEST.DO_SOME_WORK',
640
642
  callback: async (msg) => {
641
643
  // msg.payload is typed as string
642
644
  await performWork(msg.payload);
@@ -646,7 +648,7 @@ msgBus.provide({
646
648
 
647
649
  // With topic filtering
648
650
  msgBus.provide({
649
- channel: 'Test.ComputeSum',
651
+ channel: 'TEST.COMPUTE_SUM',
650
652
  topic: '/^calc-.*/',
651
653
  callback: (msg) => {
652
654
  return msg.payload.a + msg.payload.b;
@@ -655,7 +657,7 @@ msgBus.provide({
655
657
 
656
658
  // With options
657
659
  msgBus.provide({
658
- channel: 'Test.ComputeSum',
660
+ channel: 'TEST.COMPUTE_SUM',
659
661
  callback: (msg) => {
660
662
  return msg.payload.a + msg.payload.b;
661
663
  },
@@ -674,7 +676,7 @@ For providers that don't need cancellation support, simply check that `headers.s
674
676
 
675
677
  ```typescript
676
678
  msgBus.provide({
677
- channel: 'Test.ComputeSum',
679
+ channel: 'TEST.COMPUTE_SUM',
678
680
  callback: (msg, headers) => {
679
681
  if (headers.status !== 'ok') return;
680
682
  return msg.payload.a + msg.payload.b;
@@ -688,7 +690,7 @@ For providers with long-running or cancelable operations (e.g. `fetch`), track a
688
690
  const activeRequests = new Map<string, AbortController>();
689
691
 
690
692
  msgBus.provide({
691
- channel: 'Api.FetchData',
693
+ channel: 'API.FETCH_DATA',
692
694
  callback: async (msg, headers) => {
693
695
  const { requestId } = headers;
694
696
 
@@ -722,7 +724,7 @@ Send a message and automatically await a response from a handler (registered via
722
724
  ```typescript
723
725
  // Basic request
724
726
  const response = await msgBus.request({
725
- channel: 'Test.ComputeSum',
727
+ channel: 'TEST.COMPUTE_SUM',
726
728
  payload: { a: 10, b: 20 },
727
729
  });
728
730
 
@@ -730,13 +732,13 @@ console.log('Result:', response.payload); // Typed as number
730
732
 
731
733
  // With group overloading (using different input groups)
732
734
  const response1 = await msgBus.request({
733
- channel: 'Test.Multiplexer',
735
+ channel: 'TEST.MULTIPLEXER',
734
736
  group: 'in1',
735
737
  payload: 'hello',
736
738
  });
737
739
 
738
740
  const response2 = await msgBus.request({
739
- channel: 'Test.Multiplexer',
741
+ channel: 'TEST.MULTIPLEXER',
740
742
  group: 'in2',
741
743
  payload: 42,
742
744
  });
@@ -746,7 +748,7 @@ const response2 = await msgBus.request({
746
748
  // With timeout
747
749
  try {
748
750
  const response = await msgBus.request({
749
- channel: 'Test.ComputeSum',
751
+ channel: 'TEST.COMPUTE_SUM',
750
752
  payload: { a: 5, b: 15 },
751
753
  options: {
752
754
  timeout: 5000, // Overall timeout
@@ -760,7 +762,7 @@ try {
760
762
 
761
763
  // With separate send and response timeouts
762
764
  const response = await msgBus.request({
763
- channel: 'Test.ComputeSum',
765
+ channel: 'TEST.COMPUTE_SUM',
764
766
  payload: { a: 5, b: 15 },
765
767
  options: {
766
768
  sendTimeout: 1000, // Timeout for sending the message
@@ -770,7 +772,7 @@ const response = await msgBus.request({
770
772
 
771
773
  // With headers for correlation
772
774
  const response = await msgBus.request({
773
- channel: 'Test.ComputeSum',
775
+ channel: 'TEST.COMPUTE_SUM',
774
776
  payload: { a: 5, b: 15 },
775
777
  headers: {
776
778
  sourceId: 'component-123',
@@ -793,17 +795,19 @@ console.log(response.headers.correlationId); // Preserved correlation ID
793
795
 
794
796
  4. **Cancellation**: Cancel in-flight requests with `AbortSignal` (see below).
795
797
 
796
- #### Cancellation
797
-
798
- Cancel an in-flight request by passing an `AbortSignal` via `options.abortSignal`. When aborted, the bus sends a cancel message (with `headers.status === 'canceled'`) to the provider and rejects the returned Promise with an `OperationCanceledError`.
799
-
800
- On the provider side, the cancel message is delivered to the callback so it can clean up resources. See [`provide()` Cancellation Handling](#cancellation-handling) for details.
798
+ #### Cancellation
799
+
800
+ Request cancellation is cooperative: when `request()` is aborted, the provider callback receives a cancel message (`headers.status === 'canceled'`) so it can stop in-flight work and clean up resources.
801
+
802
+ Cancel an in-flight request by passing an `AbortSignal` via `options.abortSignal`. When aborted, the bus sends a cancel message to the provider and rejects the returned Promise with an `OperationCanceledError`.
803
+
804
+ See [`provide()` -> `Cancellation Handling`](#cancellation-handling) for provider-side handling details.
801
805
 
802
806
  ```typescript
803
807
  const abortController = new AbortController();
804
808
 
805
809
  const responsePromise = msgBus.request({
806
- channel: 'Api.FetchData',
810
+ channel: 'API.FETCH_DATA',
807
811
  payload: { url: 'https://api.example.com/data' },
808
812
  options: {
809
813
  abortSignal: abortController.signal,
@@ -829,7 +833,7 @@ Configure channels to buffer and replay messages for late subscribers.
829
833
 
830
834
  ```typescript
831
835
  const msgBus = createMsgBus<MyBusStruct>({
832
- 'Test.Events': {
836
+ 'TEST.EVENTS': {
833
837
  replayBufferSize: 50, // Keep last 50 messages
834
838
  replayWindowTime: 60000, // Keep messages for 60 seconds
835
839
  },
@@ -838,14 +842,14 @@ const msgBus = createMsgBus<MyBusStruct>({
838
842
  // Send messages
839
843
  for (let i = 0; i < 100; i++) {
840
844
  await msgBus.send({
841
- channel: 'Test.Events',
845
+ channel: 'TEST.EVENTS',
842
846
  payload: `Message ${i}`,
843
847
  });
844
848
  }
845
849
 
846
850
  // Late subscriber receives last 50 messages
847
851
  msgBus.on({
848
- channel: 'Test.Events',
852
+ channel: 'TEST.EVENTS',
849
853
  callback: (msg) => {
850
854
  console.log('Replayed:', msg.payload);
851
855
  },
@@ -859,7 +863,7 @@ Control message processing rate at both channel and subscription levels.
859
863
  ```typescript
860
864
  // Channel-level throttling
861
865
  const msgBus = createMsgBus<MyBusStruct>({
862
- 'Test.Updates': {
866
+ 'TEST.UPDATES': {
863
867
  throttle: {
864
868
  duration: 1000,
865
869
  leading: true,
@@ -870,7 +874,7 @@ const msgBus = createMsgBus<MyBusStruct>({
870
874
 
871
875
  // Subscription-level debouncing
872
876
  msgBus.on({
873
- channel: 'Test.Updates',
877
+ channel: 'TEST.UPDATES',
874
878
  callback: (msg) => {
875
879
  updateUI(msg.payload);
876
880
  },
@@ -887,7 +891,7 @@ The bus includes built-in error handling and a reserved error channel.
887
891
  ```typescript
888
892
  // Subscribe to errors for a specific channel
889
893
  msgBus.on({
890
- channel: 'Test.ComputeSum',
894
+ channel: 'TEST.COMPUTE_SUM',
891
895
  group: 'error',
892
896
  callback: (msg) => {
893
897
  console.error('Error in ComputeSum:', msg.payload.error);
@@ -904,7 +908,7 @@ msgBus.on({
904
908
 
905
909
  // Errors in providers are automatically caught and routed
906
910
  msgBus.provide({
907
- channel: 'Test.ComputeSum',
911
+ channel: 'TEST.COMPUTE_SUM',
908
912
  callback: (msg) => {
909
913
  if (msg.payload.a < 0) {
910
914
  throw new Error('Negative numbers not allowed');
@@ -946,7 +950,7 @@ type MyHeaders = MsgHeaders & {
946
950
  const msgBus = createMsgBus<MyBusStruct, MyHeaders>();
947
951
 
948
952
  await msgBus.send({
949
- channel: 'Test.ComputeSum',
953
+ channel: 'TEST.COMPUTE_SUM',
950
954
  payload: { a: 10, b: 20 },
951
955
  headers: {
952
956
  userId: 'user-123',
@@ -1119,7 +1123,7 @@ The message bus serves as a solid foundation for the @actdim/dynstruct architect
1119
1123
 
1120
1124
  ## Further Reading
1121
1125
 
1122
- - [GitHub Repository](https://github.com/actdim/msgmesh)
1123
- - [@actdim/dynstruct Documentation](https://github.com/actdim/dynstruct)
1124
- - [Type Safety Best Practices](https://www.typescriptlang.org/docs/handbook/2/types-from-types.html)
1125
- - [Message-Oriented Middleware Patterns](https://www.enterpriseintegrationpatterns.com/)
1126
+ - [GitHub Repository](https://github.com/actdim/msgmesh)
1127
+ - [@actdim/dynstruct Documentation](https://github.com/actdim/dynstruct)
1128
+ - [Type Safety Best Practices](https://www.typescriptlang.org/docs/handbook/2/types-from-types.html)
1129
+ - [Message-Oriented Middleware Patterns](https://www.enterpriseintegrationpatterns.com/)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actdim/msgmesh",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "A type-safe, modular message mesh for scalable async communication in TypeScript",
5
5
  "author": "Pavel Borodaev",
6
6
  "license": "Proprietary",
@@ -69,7 +69,7 @@
69
69
  "typecheck": "tsc -p tsconfig.json --noEmit"
70
70
  },
71
71
  "peerDependencies": {
72
- "@actdim/utico": "^1.1.2",
72
+ "@actdim/utico": "^1.1.5",
73
73
  "rxjs": "^7.8.2",
74
74
  "uuid": "^13.0.0"
75
75
  },