Skip to content

Commit 26b1faf

Browse files
committed
chore: use rettime for GraphQLServerSubscription event management
1 parent 94693eb commit 26b1faf

3 files changed

Lines changed: 31 additions & 21 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@
155155
"outvariant": "^1.4.3",
156156
"path-to-regexp": "^6.3.0",
157157
"picocolors": "^1.1.1",
158+
"rettime": "^0.4.1",
158159
"strict-event-emitter": "^0.5.1",
159160
"type-fest": "^4.26.1",
160161
"yargs": "^17.7.2"

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/handlers/GraphQLSubscriptionHandler.ts

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { invariant } from 'outvariant'
2+
import { Emitter, type InferListenerType } from 'rettime'
23
import { OperationTypeNode } from 'graphql'
34
import type {
45
WebSocketConnectionData,
@@ -372,9 +373,9 @@ export function createGraphQLSubscriptionHandler(
372373
}
373374
}
374375

375-
interface GraphQLServerSubscriptionEventMap {
376-
connection_ack: Event
377-
next: MessageEvent<GraphQLWebSocketNextMessage>
376+
type GraphQLServerSubscriptionEventMap = {
377+
connection_ack: never
378+
next: [GraphQLWebSocketNextMessage]
378379
}
379380

380381
/**
@@ -384,7 +385,7 @@ interface GraphQLServerSubscriptionEventMap {
384385
class GraphQLServerSubscription {
385386
protected readonly server: WebSocketServerConnection
386387

387-
private eventTarget: EventTarget
388+
private emitter: Emitter<GraphQLServerSubscriptionEventMap>
388389
private abortController: AbortController
389390

390391
constructor(
@@ -393,7 +394,10 @@ class GraphQLServerSubscription {
393394
message: GraphQLWebSocketSubscribeMessage
394395
},
395396
) {
396-
this.eventTarget = new EventTarget()
397+
this.emitter = new Emitter()
398+
399+
// An abort controller responsible for removing the server event listeners
400+
// once the subscription is unsubscribed.
397401
this.abortController = new AbortController()
398402
this.server = args.server
399403

@@ -425,7 +429,7 @@ class GraphQLServerSubscription {
425429

426430
switch (message.type) {
427431
case 'connection_ack': {
428-
this.eventTarget.dispatchEvent(new Event('connection_ack'))
432+
this.emitter.emit('connection_ack')
429433
break
430434
}
431435

@@ -434,11 +438,7 @@ class GraphQLServerSubscription {
434438
* @fixme This isn't the same `event` from the server
435439
* so calling `event.preventDefault()` won't prevent anything.
436440
*/
437-
this.eventTarget.dispatchEvent(
438-
new MessageEvent('next', {
439-
data: message,
440-
}),
441-
)
441+
this.emitter.emit('next', message)
442442
break
443443
}
444444
}
@@ -450,20 +450,21 @@ class GraphQLServerSubscription {
450450
}
451451

452452
public addEventListener<
453-
EventType extends keyof GraphQLServerSubscriptionEventMap,
454-
>(
455-
event: EventType,
456-
listener: (event: GraphQLServerSubscriptionEventMap[EventType]) => void,
457-
) {
458-
this.eventTarget.addEventListener(
459-
event,
460-
{ handleEvent: listener },
461-
{ signal: this.abortController.signal },
462-
)
453+
Type extends keyof GraphQLServerSubscriptionEventMap & string,
454+
>(event: Type, listener: InferListenerType<typeof this.emitter, Type>) {
455+
this.emitter.on(event, listener, {
456+
signal: this.abortController.signal,
457+
})
463458
}
464459

465460
public unsubscribe(): void {
461+
// Remove internal listeners on the server object.
466462
this.abortController.abort()
463+
464+
// Remove user-defined listeners.
465+
this.emitter.removeAllListeners()
466+
467+
// Serve the WebSocket connection.
467468
this.server.close()
468469
}
469470
}

0 commit comments

Comments
 (0)