@@ -40,6 +40,9 @@ import { StoreOffsetRequest } from "./requests/store_offset_request"
4040import { QueryOffsetResponse } from "./responses/query_offset_response"
4141import { QueryOffsetRequest } from "./requests/query_offset_request"
4242import { coerce , lt } from "semver"
43+ import EventEmitter from "events"
44+ import { MetadataUpdateResponse } from "./responses/metadata_update_response"
45+ import { MetadataInfo } from "./responses/raw_response"
4346
4447export type ConnectionClosedListener = ( hadError : boolean ) => void
4548
@@ -68,6 +71,11 @@ function extractHeartbeatInterval(heartbeatInterval: number, tuneResponse: TuneR
6871 return heartbeatInterval === 0 ? tuneResponse . heartbeat : Math . min ( heartbeatInterval , tuneResponse . heartbeat )
6972}
7073
74+ type ListenerEntry = {
75+ extendedId : string
76+ stream : string
77+ }
78+
7179export class Connection {
7280 public readonly hostname : string
7381 public readonly leader : boolean
@@ -92,6 +100,9 @@ export class Connection {
92100 private setupCompleted : boolean = false
93101 publisherId = 0
94102 consumerId = 0
103+ private consumerListeners : ListenerEntry [ ] = [ ]
104+ private publisherListeners : ListenerEntry [ ] = [ ]
105+ private closeEventsEmitter = new EventEmitter ( )
95106
96107 constructor (
97108 private readonly params : ConnectionParams ,
@@ -249,7 +260,22 @@ export class Connection {
249260 )
250261 }
251262
263+ public onPublisherClosed ( publisherExtendedId : string , streamName : string , callback : ( ) => void | Promise < void > ) {
264+ this . publisherListeners . push ( { extendedId : publisherExtendedId , stream : streamName } )
265+ this . closeEventsEmitter . once ( `close_publisher_${ publisherExtendedId } ` , callback )
266+ }
267+
268+ public onConsumerClosed ( consumerExtendedId : string , streamName : string , callback : ( ) => void | Promise < void > ) {
269+ this . consumerListeners . push ( { extendedId : consumerExtendedId , stream : streamName } )
270+ this . closeEventsEmitter . once ( `close_consumer_${ consumerExtendedId } ` , callback )
271+ }
272+
252273 private registerListeners ( listeners ?: ConnectionListenersParams ) {
274+ this . decoder . on ( "metadata_update" , ( metadata ) => {
275+ this . publisherListeners = notifyOnceClose ( this . publisherListeners , metadata , this . closeEventsEmitter , "publisher" )
276+ this . consumerListeners = notifyOnceClose ( this . consumerListeners , metadata , this . closeEventsEmitter , "consumer" )
277+ } )
278+
253279 if ( listeners ?. metadata_update ) this . decoder . on ( "metadata_update" , listeners . metadata_update )
254280 if ( listeners ?. publish_confirm ) this . decoder . on ( "publish_confirm" , listeners . publish_confirm )
255281 if ( listeners ?. publish_error ) this . decoder . on ( "publish_error" , listeners . publish_error )
@@ -548,3 +574,29 @@ export function connect(logger: Logger, params: ConnectionParams) {
548574export function create ( logger : Logger , params : ConnectionParams ) {
549575 return Connection . create ( params , logger )
550576}
577+
578+ function notifyOnceClose (
579+ listeners : ListenerEntry [ ] ,
580+ metadata : MetadataUpdateResponse ,
581+ closeEventsEmitter : EventEmitter ,
582+ eventName : "publisher" | "consumer"
583+ ) : ListenerEntry [ ] {
584+ const [ toNotify , toKeep ] = partition ( listeners , isSameStream ( metadata ) )
585+ toNotify . forEach ( ( l ) => closeEventsEmitter . emit ( `close_${ eventName } _${ l . extendedId } ` ) )
586+ return toKeep
587+ }
588+
589+ export function partition < T > ( arr : T [ ] , predicate : ( t : T ) => boolean ) : [ T [ ] , T [ ] ] {
590+ const [ truthy , falsy ] = arr . reduce (
591+ ( acc , t ) => {
592+ acc [ predicate ( t ) ? 0 : 1 ] . push ( t )
593+ return acc
594+ } ,
595+ [ [ ] , [ ] ] as [ T [ ] , T [ ] ]
596+ )
597+ return [ truthy , falsy ]
598+ }
599+
600+ function isSameStream ( { metadataInfo } : { metadataInfo : MetadataInfo } ) : ( e : ListenerEntry ) => boolean {
601+ return ( e ) => e . stream === metadataInfo . stream
602+ }
0 commit comments