@@ -46,12 +46,16 @@ export type ConnectionState = {
4646
4747type Listener = ( ) => void ;
4848
49+ export const CONNECTION_MANAGER_RECONNECT_DELAY_MS = 1000 ;
50+
4951type ManagedConnection = {
5052 connection ?: DbConnectionImpl < any > ;
53+ builder ?: DbConnectionBuilder < any > ;
5154 refCount : number ;
5255 state : ConnectionState ;
5356 listeners : Set < Listener > ;
5457 pendingRelease : ReturnType < typeof setTimeout > | null ;
58+ reconnectTimer : ReturnType < typeof setTimeout > | null ;
5559 onConnect ?: ( conn : DbConnectionImpl < any > ) => void ;
5660 onDisconnect ?: ( ctx : ErrorContextInterface < any > , error ?: Error ) => void ;
5761 onConnectError ?: ( ctx : ErrorContextInterface < any > , error : Error ) => void ;
@@ -91,10 +95,12 @@ class ConnectionManagerImpl {
9195 }
9296 const managed : ManagedConnection = {
9397 connection : undefined ,
98+ builder : undefined ,
9499 refCount : 0 ,
95100 state : defaultState ( ) ,
96101 listeners : new Set ( ) ,
97102 pendingRelease : null ,
103+ reconnectTimer : null ,
98104 } ;
99105 this . #connections. set ( key , managed ) ;
100106 return managed ;
@@ -106,47 +112,24 @@ class ConnectionManagerImpl {
106112 }
107113 }
108114
109- /**
110- * Retains a connection, incrementing its reference count.
111- * Creates the connection on first call; returns existing connection on subsequent calls.
112- * Cancels any pending release if the connection was about to be cleaned up.
113- *
114- * @param key - Unique identifier for the connection (use getKey to generate)
115- * @param builder - Connection builder to create the connection if needed
116- * @returns The managed connection instance
117- */
118- retain < T extends DbConnectionImpl < any > > (
119- key : string ,
120- builder : DbConnectionBuilder < T >
121- ) : T {
122- const managed = this . #ensureEntry( key ) ;
123- if ( managed . pendingRelease ) {
124- clearTimeout ( managed . pendingRelease ) ;
125- managed . pendingRelease = null ;
126- }
127- managed . refCount += 1 ;
128- if ( managed . connection ) {
129- return managed . connection as T ;
130- }
131-
132- const connection = builder . build ( ) ;
133- managed . connection = connection ;
134-
135- const updateState = ( updates : Partial < ConnectionState > ) => {
136- managed . state = { ...managed . state , ...updates } ;
137- this . #notify( managed ) ;
138- } ;
115+ #updateState(
116+ managed : ManagedConnection ,
117+ updates : Partial < ConnectionState >
118+ ) : void {
119+ managed . state = { ...managed . state , ...updates } ;
120+ this . #notify( managed ) ;
121+ }
139122
140- updateState ( {
141- isActive : connection . isActive ,
142- identity : connection . identity ,
143- token : connection . token ,
144- connectionId : connection . connectionId ,
145- connectionError : undefined ,
146- } ) ;
123+ #ensureCallbacks( managed : ManagedConnection ) : void {
124+ if ( managed . onConnect ) {
125+ return ;
126+ }
147127
148128 managed . onConnect = conn => {
149- updateState ( {
129+ if ( conn !== managed . connection ) {
130+ return ;
131+ }
132+ this . #updateState( managed , {
150133 isActive : conn . isActive ,
151134 identity : conn . identity ,
152135 token : conn . token ,
@@ -156,26 +139,136 @@ class ConnectionManagerImpl {
156139 } ;
157140
158141 managed . onDisconnect = ( ctx , error ) => {
159- updateState ( {
160- isActive : ctx . isActive ,
142+ if ( ctx !== managed . connection ) {
143+ return ;
144+ }
145+ this . #updateState( managed , {
146+ isActive : false ,
161147 connectionError : error ?? undefined ,
162148 } ) ;
149+ this . #scheduleReconnect( managed ) ;
163150 } ;
164151
165152 managed . onConnectError = ( ctx , error ) => {
166- updateState ( {
167- isActive : ctx . isActive ,
153+ if ( ctx !== managed . connection ) {
154+ return ;
155+ }
156+ this . #updateState( managed , {
157+ isActive : false ,
168158 connectionError : error ,
169159 } ) ;
160+ this . #scheduleReconnect( managed ) ;
170161 } ;
162+ }
163+
164+ #attachCallbacks< T extends DbConnectionImpl < any > > (
165+ managed : ManagedConnection ,
166+ builder : DbConnectionBuilder < T >
167+ ) : void {
168+ this . #ensureCallbacks( managed ) ;
169+ builder . onConnect ( managed . onConnect ! ) ;
170+ builder . onDisconnect ( managed . onDisconnect ! ) ;
171+ builder . onConnectError ( managed . onConnectError ! ) ;
172+ }
173+
174+ #detachCallbacks(
175+ managed : ManagedConnection ,
176+ connection : DbConnectionImpl < any >
177+ ) : void {
178+ if ( managed . onConnect ) {
179+ connection . removeOnConnect ( managed . onConnect as any ) ;
180+ }
181+ if ( managed . onDisconnect ) {
182+ connection . removeOnDisconnect ( managed . onDisconnect as any ) ;
183+ }
184+ if ( managed . onConnectError ) {
185+ connection . removeOnConnectError ( managed . onConnectError as any ) ;
186+ }
187+ }
171188
172- builder . onConnect ( managed . onConnect ) ;
173- builder . onDisconnect ( managed . onDisconnect ) ;
174- builder . onConnectError ( managed . onConnectError ) ;
189+ #buildManagedConnection< T extends DbConnectionImpl < any > > (
190+ managed : ManagedConnection ,
191+ builder : DbConnectionBuilder < T >
192+ ) : T {
193+ managed . builder = builder ;
194+ const connection = builder . build ( ) ;
195+ managed . connection = connection ;
196+ this . #attachCallbacks( managed , builder ) ;
197+
198+ this . #updateState( managed , {
199+ isActive : connection . isActive ,
200+ identity : connection . identity ,
201+ token : connection . token ,
202+ connectionId : connection . connectionId ,
203+ connectionError : undefined ,
204+ } ) ;
175205
176206 return connection as T ;
177207 }
178208
209+ #scheduleReconnect( managed : ManagedConnection ) : void {
210+ if (
211+ managed . refCount <= 0 ||
212+ managed . pendingRelease ||
213+ managed . reconnectTimer ||
214+ ! managed . builder
215+ ) {
216+ return ;
217+ }
218+
219+ const connection = managed . connection ;
220+ if ( connection ) {
221+ this . #detachCallbacks( managed , connection ) ;
222+ }
223+ managed . connection = undefined ;
224+ managed . reconnectTimer = setTimeout ( ( ) => {
225+ managed . reconnectTimer = null ;
226+ if (
227+ managed . refCount <= 0 ||
228+ managed . pendingRelease ||
229+ managed . connection ||
230+ ! managed . builder
231+ ) {
232+ return ;
233+ }
234+
235+ this . #buildManagedConnection( managed , managed . builder ) ;
236+ } , CONNECTION_MANAGER_RECONNECT_DELAY_MS ) ;
237+ }
238+
239+ /**
240+ * Retains a connection, incrementing its reference count.
241+ * Creates the connection on first call; returns existing connection on subsequent calls.
242+ * Cancels any pending release if the connection was about to be cleaned up.
243+ *
244+ * @param key - Unique identifier for the connection (use getKey to generate)
245+ * @param builder - Connection builder to create the connection if needed
246+ * @returns The managed connection instance
247+ */
248+ retain < T extends DbConnectionImpl < any > > (
249+ key : string ,
250+ builder : DbConnectionBuilder < T >
251+ ) : T {
252+ const managed = this . #ensureEntry( key ) ;
253+ if ( managed . pendingRelease ) {
254+ clearTimeout ( managed . pendingRelease ) ;
255+ managed . pendingRelease = null ;
256+ }
257+ if ( managed . reconnectTimer ) {
258+ clearTimeout ( managed . reconnectTimer ) ;
259+ managed . reconnectTimer = null ;
260+ }
261+
262+ managed . refCount += 1 ;
263+ managed . builder = builder ;
264+
265+ if ( managed . connection ) {
266+ return managed . connection as T ;
267+ }
268+
269+ return this . #buildManagedConnection( managed , builder ) ;
270+ }
271+
179272 release ( key : string ) : void {
180273 const managed = this . #connections. get ( key ) ;
181274 if ( ! managed ) {
@@ -187,24 +280,21 @@ class ConnectionManagerImpl {
187280 return ;
188281 }
189282
283+ if ( managed . reconnectTimer ) {
284+ clearTimeout ( managed . reconnectTimer ) ;
285+ managed . reconnectTimer = null ;
286+ }
287+
190288 managed . pendingRelease = setTimeout ( ( ) => {
191289 managed . pendingRelease = null ;
192290 if ( managed . refCount > 0 ) {
193291 return ;
194292 }
195- if ( managed . connection ) {
196- if ( managed . onConnect ) {
197- managed . connection . removeOnConnect ( managed . onConnect as any ) ;
198- }
199- if ( managed . onDisconnect ) {
200- managed . connection . removeOnDisconnect ( managed . onDisconnect as any ) ;
201- }
202- if ( managed . onConnectError ) {
203- managed . connection . removeOnConnectError (
204- managed . onConnectError as any
205- ) ;
206- }
207- managed . connection . disconnect ( ) ;
293+ const connection = managed . connection ;
294+ managed . connection = undefined ;
295+ if ( connection ) {
296+ this . #detachCallbacks( managed , connection ) ;
297+ connection . disconnect ( ) ;
208298 }
209299 this . #connections. delete ( key ) ;
210300 } , 0 ) ;
0 commit comments