@@ -19,6 +19,26 @@ import {
19
19
/** This file is the entry point for browsers, re-export common elements. */
20
20
export * from './common' ;
21
21
22
+ /** @category Client */
23
+ export interface EventListeners < SingleConnection extends boolean = false > {
24
+ /**
25
+ * Emitted when the client starts connecting to the server.
26
+ *
27
+ * @param reconnecting - Whether the client is reconnecting after the connection was broken.
28
+ */
29
+ connecting ?: ( reconnecting : boolean ) => void ;
30
+ /**
31
+ * Emitted when the client receives a message from the server.
32
+ */
33
+ message ?: ( message : StreamMessage < SingleConnection , StreamEvent > ) => void ;
34
+ /**
35
+ * Emitted when the client has successfully connected to the server.
36
+ *
37
+ * @param reconnecting - Whether the client has reconnected after the connection was broken.
38
+ */
39
+ connected ?: ( reconnected : boolean ) => void ;
40
+ }
41
+
22
42
/** @category Client */
23
43
export interface ClientOptions < SingleConnection extends boolean = false > {
24
44
/**
@@ -44,7 +64,7 @@ export interface ClientOptions<SingleConnection extends boolean = false> {
44
64
* - `true`: Establish a connection on first subscribe and close on last unsubscribe.
45
65
*
46
66
* Note that the `lazy` option has NO EFFECT when using the client
47
- * in "distinct connection mode" (`singleConnection = false`).
67
+ * in "distinct connections mode" (`singleConnection = false`).
48
68
*
49
69
* @default true
50
70
*/
@@ -56,7 +76,7 @@ export interface ClientOptions<SingleConnection extends boolean = false> {
56
76
* Meant to be used in combination with `lazy`.
57
77
*
58
78
* Note that the `lazy` option has NO EFFECT when using the client
59
- * in "distinct connection mode" (`singleConnection = false`).
79
+ * in "distinct connections mode" (`singleConnection = false`).
60
80
*
61
81
* @default 0
62
82
*/
@@ -190,28 +210,44 @@ export interface ClientOptions<SingleConnection extends boolean = false> {
190
210
* and because `graphql-sse` implements a custom SSE parser - received messages will **not** appear in browser's DevTools.
191
211
*
192
212
* Use this function if you want to inspect valid messages received through the active SSE connection.
213
+ *
214
+ * @deprecated Consider using {@link ClientOptions.on} instead.
193
215
*/
194
216
onMessage ?: ( message : StreamMessage < SingleConnection , StreamEvent > ) => void ;
217
+ /**
218
+ * Event listeners for events happening in teh SSE connection.
219
+ *
220
+ * Will emit events for both the "single connection mode" and the default "distinct connections mode".
221
+ *
222
+ * Beware that the `connecting` event will be called for **each** subscription when using with "distinct connections mode".
223
+ */
224
+ on ?: EventListeners < SingleConnection > ;
195
225
}
196
226
197
227
/** @category Client */
198
- export interface Client {
228
+ export interface Client < SingleConnection extends boolean = false > {
199
229
/**
200
230
* Subscribes to receive through a SSE connection.
201
231
*
202
232
* It uses the `sink` to emit received data or errors. Returns a _dispose_
203
233
* function used for dropping the subscription and cleaning up.
234
+ *
235
+ * @param on - The event listener for "distinct connections mode". Note that **no events will be emitted** in "single connection mode"; for that, consider using the event listener in {@link ClientOptions}.
204
236
*/
205
237
subscribe < Data = Record < string , unknown > , Extensions = unknown > (
206
238
request : RequestParams ,
207
239
sink : Sink < ExecutionResult < Data , Extensions > > ,
240
+ on ?: SingleConnection extends true ? never : EventListeners < false > ,
208
241
) : ( ) => void ;
209
242
/**
210
243
* Subscribes and iterates over emitted results from an SSE connection
211
244
* through the returned async iterator.
245
+ *
246
+ * @param on - The event listener for "distinct connections mode". Note that **no events will be emitted** in "single connection mode"; for that, consider using the event listener in {@link ClientOptions}.
212
247
*/
213
248
iterate < Data = Record < string , unknown > , Extensions = unknown > (
214
249
request : RequestParams ,
250
+ on ?: SingleConnection extends true ? never : EventListeners < false > ,
215
251
) : AsyncIterableIterator < ExecutionResult < Data , Extensions > > ;
216
252
/**
217
253
* Dispose of the client, destroy connections and clean up resources.
@@ -235,7 +271,7 @@ export interface Client {
235
271
*/
236
272
export function createClient < SingleConnection extends boolean = false > (
237
273
options : ClientOptions < SingleConnection > ,
238
- ) : Client {
274
+ ) : Client < SingleConnection > {
239
275
const {
240
276
singleConnection = false ,
241
277
lazy = true ,
@@ -274,6 +310,7 @@ export function createClient<SingleConnection extends boolean = false>(
274
310
referrer,
275
311
referrerPolicy,
276
312
onMessage,
313
+ on : clientOn ,
277
314
} = options ;
278
315
const fetchFn = ( options . fetchFn || fetch ) as typeof fetch ;
279
316
const AbortControllerImpl = ( options . abortControllerImpl ||
@@ -333,6 +370,8 @@ export function createClient<SingleConnection extends boolean = false>(
333
370
retries ++ ;
334
371
}
335
372
373
+ clientOn ?. connecting ?.( ! ! retryingErr ) ;
374
+
336
375
// we must create a new controller here because lazy mode aborts currently active ones
337
376
connCtrl = new AbortControllerImpl ( ) ;
338
377
const unlistenDispose = client . onDispose ( ( ) => connCtrl . abort ( ) ) ;
@@ -381,9 +420,14 @@ export function createClient<SingleConnection extends boolean = false>(
381
420
referrerPolicy,
382
421
url,
383
422
fetchFn,
384
- onMessage,
423
+ onMessage : ( msg ) => {
424
+ clientOn ?. message ?.( msg ) ;
425
+ onMessage ?.( msg ) ; // @deprecated
426
+ } ,
385
427
} ) ;
386
428
429
+ clientOn ?. connected ?.( ! ! retryingErr ) ;
430
+
387
431
connected . waitForThrow ( ) . catch ( ( ) => ( conn = undefined ) ) ;
388
432
389
433
return connected ;
@@ -423,7 +467,11 @@ export function createClient<SingleConnection extends boolean = false>(
423
467
} ) ( ) ;
424
468
}
425
469
426
- function subscribe ( request : RequestParams , sink : Sink ) {
470
+ function subscribe (
471
+ request : RequestParams ,
472
+ sink : Sink ,
473
+ on ?: EventListeners < false > ,
474
+ ) {
427
475
if ( ! singleConnection ) {
428
476
// distinct connections mode
429
477
@@ -449,6 +497,9 @@ export function createClient<SingleConnection extends boolean = false>(
449
497
retries ++ ;
450
498
}
451
499
500
+ clientOn ?. connecting ?.( ! ! retryingErr ) ;
501
+ on ?. connecting ?.( ! ! retryingErr ) ;
502
+
452
503
const url =
453
504
typeof options . url === 'function'
454
505
? await options . url ( )
@@ -475,9 +526,16 @@ export function createClient<SingleConnection extends boolean = false>(
475
526
url,
476
527
body : JSON . stringify ( request ) ,
477
528
fetchFn,
478
- onMessage,
529
+ onMessage : ( msg ) => {
530
+ clientOn ?. message ?.( msg ) ;
531
+ on ?. message ?.( msg ) ;
532
+ onMessage ?.( msg ) ; // @deprecated
533
+ } ,
479
534
} ) ;
480
535
536
+ clientOn ?. connected ?.( ! ! retryingErr ) ;
537
+ on ?. connected ?.( ! ! retryingErr ) ;
538
+
481
539
for await ( const result of getResults ( ) ) {
482
540
// only after receiving results are future connects not considered retries.
483
541
// this is because a client might successfully connect, but the server
@@ -645,7 +703,7 @@ export function createClient<SingleConnection extends boolean = false>(
645
703
646
704
return {
647
705
subscribe,
648
- iterate ( request ) {
706
+ iterate ( request , on ) {
649
707
const pending : ExecutionResult <
650
708
// TODO: how to not use `any` and not have a redundant function signature?
651
709
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -660,22 +718,26 @@ export function createClient<SingleConnection extends boolean = false>(
660
718
// noop
661
719
} ,
662
720
} ;
663
- const dispose = subscribe ( request , {
664
- next ( val ) {
665
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
666
- pending . push ( val as any ) ;
667
- deferred . resolve ( ) ;
668
- } ,
669
- error ( err ) {
670
- deferred . done = true ;
671
- deferred . error = err ;
672
- deferred . resolve ( ) ;
673
- } ,
674
- complete ( ) {
675
- deferred . done = true ;
676
- deferred . resolve ( ) ;
721
+ const dispose = subscribe (
722
+ request ,
723
+ {
724
+ next ( val ) {
725
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
726
+ pending . push ( val as any ) ;
727
+ deferred . resolve ( ) ;
728
+ } ,
729
+ error ( err ) {
730
+ deferred . done = true ;
731
+ deferred . error = err ;
732
+ deferred . resolve ( ) ;
733
+ } ,
734
+ complete ( ) {
735
+ deferred . done = true ;
736
+ deferred . resolve ( ) ;
737
+ } ,
677
738
} ,
678
- } ) ;
739
+ on ,
740
+ ) ;
679
741
680
742
const iterator = ( async function * iterator ( ) {
681
743
for ( ; ; ) {
0 commit comments