11/**
2- * MessageBusBridge - Bridges parent and child message buses
3- *
4- * Forwards specified topics between parent and child clusters while maintaining
5- * isolation and preventing message loops.
6- *
7- * Features:
8- * - Forward specified parent topics to child (contextStrategy.parentTopics)
9- * - Forward child completion events to parent
10- * - Namespace child topics to avoid collisions
11- * - Prevent message loops via forwarding flags
2+ * Bridges parent and child message buses while preserving cluster isolation and
3+ * preventing forwarding loops.
124 */
135
6+ interface BridgeMetadata extends Record < string , unknown > {
7+ forwarded ?: unknown ;
8+ }
9+
10+ interface BridgeMessage extends Record < string , unknown > {
11+ cluster_id : string ;
12+ topic : string ;
13+ metadata ?: BridgeMetadata ;
14+ }
15+
16+ interface BridgeMessageBus {
17+ subscribe ( handler : ( message : BridgeMessage ) => void ) : ( ) => void ;
18+ publish ( message : BridgeMessage ) : unknown ;
19+ }
20+
21+ interface MessageBusBridgeConfig {
22+ parentClusterId : string ;
23+ childClusterId : string ;
24+ parentTopics ?: unknown [ ] ;
25+ }
26+
27+ function parentTopicName ( entry : unknown ) : unknown {
28+ if ( typeof entry === 'string' ) return entry ;
29+ if ( typeof entry !== 'object' || entry === null ) return undefined ;
30+ return 'topic' in entry ? entry . topic : undefined ;
31+ }
32+
33+ function isNonEmptyString ( value : unknown ) : value is string {
34+ return typeof value === 'string' && value . length > 0 ;
35+ }
36+
1437class MessageBusBridge {
15- constructor ( parentBus , childBus , config ) {
38+ parentBus : BridgeMessageBus ;
39+ childBus : BridgeMessageBus ;
40+ config : MessageBusBridgeConfig ;
41+ parentTopicNames : Set < string > ;
42+ parentUnsubscribe : ( ( ) => void ) | null ;
43+ childUnsubscribe : ( ( ) => void ) | null ;
44+ active : boolean ;
45+
46+ constructor (
47+ parentBus : BridgeMessageBus ,
48+ childBus : BridgeMessageBus ,
49+ config : MessageBusBridgeConfig
50+ ) {
1651 this . parentBus = parentBus ;
1752 this . childBus = childBus ;
1853 this . config = config ;
1954 this . parentTopicNames = new Set (
20- ( config . parentTopics || [ ] )
21- . map ( ( entry ) => ( typeof entry === 'string' ? entry : entry ?. topic ) )
22- . filter ( ( topic ) => typeof topic === 'string' && topic . length > 0 )
55+ ( config . parentTopics || [ ] ) . map ( parentTopicName ) . filter ( isNonEmptyString )
2356 ) ;
2457
2558 this . parentUnsubscribe = null ;
@@ -29,47 +62,33 @@ class MessageBusBridge {
2962 this . _setupBridge ( ) ;
3063 }
3164
32- /**
33- * Set up bidirectional message forwarding
34- * @private
35- */
36- _setupBridge ( ) {
37- // Forward specified parent topics to child
65+ _setupBridge ( ) : void {
3866 if ( this . parentTopicNames . size > 0 ) {
39- this . parentUnsubscribe = this . parentBus . subscribe ( ( message ) => {
67+ this . parentUnsubscribe = this . parentBus . subscribe ( ( message : BridgeMessage ) => {
4068 this . _forwardParentToChild ( message ) ;
4169 } ) ;
4270 }
4371
44- // Forward child completion/failure events to parent
45- this . childUnsubscribe = this . childBus . subscribe ( ( message ) => {
72+ this . childUnsubscribe = this . childBus . subscribe ( ( message : BridgeMessage ) => {
4673 this . _forwardChildToParent ( message ) ;
4774 } ) ;
4875
4976 this . active = true ;
5077 }
5178
52- /**
53- * Forward parent message to child cluster
54- * @private
55- */
56- _forwardParentToChild ( message ) {
57- // Only forward messages from parent cluster
79+ _forwardParentToChild ( message : BridgeMessage ) : void {
5880 if ( message . cluster_id !== this . config . parentClusterId ) {
5981 return ;
6082 }
6183
62- // Only forward topics specified in config
6384 if ( ! this . parentTopicNames . has ( message . topic ) ) {
6485 return ;
6586 }
6687
67- // Skip already-forwarded messages (prevent loops)
6888 if ( message . metadata ?. forwarded ) {
6989 return ;
7090 }
7191
72- // Forward to child with metadata flag
7392 this . childBus . publish ( {
7493 ...message ,
7594 cluster_id : this . config . childClusterId ,
@@ -81,28 +100,20 @@ class MessageBusBridge {
81100 } ) ;
82101 }
83102
84- /**
85- * Forward child message to parent cluster
86- * @private
87- */
88- _forwardChildToParent ( message ) {
89- // Only forward messages from child cluster
103+ _forwardChildToParent ( message : BridgeMessage ) : void {
90104 if ( message . cluster_id !== this . config . childClusterId ) {
91105 return ;
92106 }
93107
94- // Only forward completion/failure events
95108 const forwardTopics = [ 'CLUSTER_COMPLETE' , 'CLUSTER_FAILED' , 'AGENT_ERROR' ] ;
96109 if ( ! forwardTopics . includes ( message . topic ) ) {
97110 return ;
98111 }
99112
100- // Skip already-forwarded messages (prevent loops)
101113 if ( message . metadata ?. forwarded ) {
102114 return ;
103115 }
104116
105- // Forward to parent with namespaced topic and metadata flag
106117 this . parentBus . publish ( {
107118 ...message ,
108119 cluster_id : this . config . parentClusterId ,
@@ -116,10 +127,7 @@ class MessageBusBridge {
116127 } ) ;
117128 }
118129
119- /**
120- * Close the bridge and stop forwarding
121- */
122- close ( ) {
130+ close ( ) : void {
123131 if ( this . parentUnsubscribe ) {
124132 this . parentUnsubscribe ( ) ;
125133 this . parentUnsubscribe = null ;
@@ -133,12 +141,9 @@ class MessageBusBridge {
133141 this . active = false ;
134142 }
135143
136- /**
137- * Check if bridge is active
138- */
139- isActive ( ) {
144+ isActive ( ) : boolean {
140145 return this . active ;
141146 }
142147}
143148
144- module . exports = MessageBusBridge ;
149+ export = MessageBusBridge ;
0 commit comments