1
1
import { Request , Response } from "express" ;
2
2
import logger from "../logger" ;
3
3
import { Context } from "../../types" ;
4
- const SSEChannel = require ( "sse-pubsub" ) ;
4
+ import { SSEChannel , Options } from "./ssePubsub" ;
5
5
6
- // START hacky TS binding for sse-pubsub
7
- interface SSEChannel {
8
- /* eslint-disable @typescript-eslint/no-explicit-any */
9
- constructor : ( options : SSEChannelOptions ) => void ;
10
- /* eslint-disable @typescript-eslint/no-explicit-any */
11
- publish : ( data : any , eventName : string ) => number ;
12
- subscribe : (
13
- req : Request ,
14
- res : Response ,
15
- /* eslint-disable @typescript-eslint/no-explicit-any */
16
- events ?: any [ ]
17
- /* eslint-disable @typescript-eslint/no-explicit-any */
18
- ) => { req : Request ; res : Response ; events ?: any [ ] } ;
19
- /* eslint-disable @typescript-eslint/no-explicit-any */
20
- unsubscribe : ( c : { req : Request ; res : Response ; events ?: any [ ] } ) => void ;
21
- close : ( ) => void ;
22
- listClients : ( ) => { [ ip : string ] : number } ;
23
- getSubscriberCount : ( ) => number ;
24
- }
25
- interface SSEChannelOptions {
26
- pingInterval ?: number ; // default 3000
27
- maxStreamDuration ?: number ; // default 30000
28
- clientRetryInterval ?: number ; // default 1000
29
- startId ?: number ; // default 1
30
- historySize ?: number ; // default 100
31
- rewind ?: number ; // default 0
32
- }
33
- // END hacky TS binding for sse-pubsub
34
-
35
- const defaultOptions : SSEChannelOptions = {
6
+ const defaultOptions : Partial < Options > = {
36
7
historySize : 1 ,
37
8
} ;
38
9
@@ -41,9 +12,15 @@ interface ScopedChannel {
41
12
channel : SSEChannel ;
42
13
}
43
14
44
- export class EventStreamManager {
15
+ export class SSEManager {
45
16
private scopedChannels = new Map < string , ScopedChannel > ( ) ;
46
17
18
+ private appContext : Context ;
19
+
20
+ constructor ( appContext : Context ) {
21
+ this . appContext = appContext ;
22
+ }
23
+
47
24
public subscribe ( req : Request , res : Response ) {
48
25
const apiKey = res . locals . apiKey ;
49
26
if ( apiKey ) {
@@ -92,49 +69,55 @@ export class EventStreamManager {
92
69
/* eslint-disable @typescript-eslint/no-explicit-any */
93
70
payload : any ,
94
71
/* eslint-disable @typescript-eslint/no-explicit-any */
95
- oldPayload ?: any ,
96
- ctx ?: Context
72
+ oldPayload ?: any
97
73
) {
98
- ctx ?. verboseDebugging &&
74
+ this . appContext ?. verboseDebugging &&
99
75
logger . info (
100
76
{ apiKey, event, payload, oldPayload } ,
101
77
"EventStreamManager.publish"
102
78
) ;
103
79
const scopedChannel = this . getScopedChannel ( apiKey ) ;
104
80
if ( scopedChannel ) {
105
81
if ( oldPayload === undefined ) {
106
- ctx ?. verboseDebugging &&
82
+ this . appContext ?. verboseDebugging &&
107
83
logger . info ( { payload, event } , "publishing SSE" ) ;
108
84
scopedChannel . channel . publish ( payload , event ) ;
109
85
} else {
110
86
const hasChanges =
111
87
JSON . stringify ( payload ) !== JSON . stringify ( oldPayload ) ;
112
88
if ( hasChanges ) {
113
- ctx ?. verboseDebugging &&
89
+ this . appContext ?. verboseDebugging &&
114
90
logger . info ( { payload, event } , "publishing SSE" ) ;
115
91
scopedChannel . channel . publish ( payload , event ) ;
116
92
return ;
117
93
}
118
- ctx ?. verboseDebugging &&
94
+ this . appContext ?. verboseDebugging &&
119
95
logger . info ( { payload, event } , "skipping SSE publish, no changes" ) ;
120
96
}
121
97
return ;
122
98
}
123
- ctx ?. verboseDebugging && logger . info ( "No scoped channel found" ) ;
99
+ this . appContext ?. verboseDebugging && logger . info ( "No scoped channel found" ) ;
124
100
}
125
101
126
102
private getScopedChannel ( apiKey : string ) : ScopedChannel | undefined {
127
103
let scopedChannel = this . scopedChannels . get ( apiKey ) ;
128
104
if ( ! scopedChannel ) {
129
105
this . scopedChannels . set ( apiKey , {
130
106
apiKey,
131
- channel : new SSEChannel ( defaultOptions ) ,
107
+ channel : new SSEChannel ( defaultOptions , this . appContext ) ,
132
108
} ) ;
133
109
scopedChannel = this . scopedChannels . get ( apiKey ) ;
134
110
}
135
111
return scopedChannel ;
136
112
}
137
113
}
138
114
139
- export const eventStreamManager = new EventStreamManager ( ) ;
140
- Object . freeze ( eventStreamManager ) ;
115
+ export type EventStreamManager = SSEManager | null ;
116
+ export let eventStreamManager : SSEManager | null = null ;
117
+
118
+ export const initializeEventStreamManager = ( appContext : Context ) => {
119
+ if ( appContext . enableEventStream ) {
120
+ eventStreamManager = new SSEManager ( appContext ) ;
121
+ }
122
+ Object . freeze ( eventStreamManager ) ;
123
+ } ;
0 commit comments