• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Cameri / nostr-ts-relay / 3762686547

pending completion
3762686547

push

github

GitHub
chore: add SECURITY.md

319 of 375 branches covered (85.07%)

Branch coverage included in aggregate %.

825 of 932 relevant lines covered (88.52%)

24.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.57
/src/adapters/web-socket-server-adapter.ts
1
import { IncomingMessage, Server } from 'http'
2
import WebSocket, { OPEN, WebSocketServer } from 'ws'
2✔
3

4
import { IWebSocketAdapter, IWebSocketServerAdapter } from '../@types/adapters'
5
import { WebSocketAdapterEvent, WebSocketServerAdapterEvent } from '../constants/adapter'
2✔
6
import { createLogger } from '../factories/logger-factory'
2✔
7
import { Event } from '../@types/event'
8
import { Factory } from '../@types/base'
9
import { ISettings } from '../@types/settings'
10
import { propEq } from 'ramda'
2✔
11
import { WebServerAdapter } from './web-server-adapter'
2✔
12

13
const debug = createLogger('web-socket-server-adapter')
2✔
14

15
const WSS_CLIENT_HEALTH_PROBE_INTERVAL = 30000
2✔
16

17
export class WebSocketServerAdapter extends WebServerAdapter implements IWebSocketServerAdapter {
2✔
18
  private webSocketsAdapters: WeakMap<WebSocket, IWebSocketAdapter>
19

20
  private heartbeatInterval: NodeJS.Timer
21

22
  public constructor(
23
    webServer: Server,
24
    private readonly webSocketServer: WebSocketServer,
2✔
25
    private readonly createWebSocketAdapter: Factory<
2✔
26
      IWebSocketAdapter,
27
      [WebSocket, IncomingMessage, IWebSocketServerAdapter]
28
    >,
29
    settings: () => ISettings,
30
  ) {
31
    super(webServer, settings)
2✔
32

33
    this.webSocketsAdapters = new WeakMap()
2✔
34

35
    this
2✔
36
      .on(WebSocketServerAdapterEvent.Broadcast, this.onBroadcast.bind(this))
37

38
    this.webSocketServer
2✔
39
      .on(WebSocketServerAdapterEvent.Close, this.onClose.bind(this))
40
      .on(WebSocketServerAdapterEvent.Connection, this.onConnection.bind(this))
41
      .on('error', (error) => {
42
        debug('error: %o', error)
×
43
        throw error
×
44
      })
45
    this.heartbeatInterval = setInterval(this.onHeartbeat.bind(this), WSS_CLIENT_HEALTH_PROBE_INTERVAL)
2✔
46
  }
47

48
  public close(callback: () => void): void {
49
    this.onClose()
2✔
50
    this.webSocketServer.close(() => {
2✔
51
      this.webServer.close(callback)
2✔
52
    })
53
  }
54

55
  private onBroadcast(event: Event) {
56
    this.webSocketServer.clients.forEach((webSocket: WebSocket) => {
19✔
57
      if (!propEq('readyState', OPEN)(webSocket)) {
32!
58
        return
×
59
      }
60
      const webSocketAdapter = this.webSocketsAdapters.get(webSocket)
32✔
61
      debug('broadcasting event to client %s: %o', webSocketAdapter.getClientId(), event)
32✔
62
      webSocketAdapter.emit(WebSocketAdapterEvent.Event, event)
32✔
63
    })
64
  }
65

66
  public getConnectedClients(): number {
67
    return Array.from(this.webSocketServer.clients).filter(propEq('readyState', OPEN)).length
×
68
  }
69

70
  private onConnection(client: WebSocket, req: IncomingMessage) {
71
    debug('client connected: %o', req.headers)
22✔
72
    this.webSocketsAdapters.set(client, this.createWebSocketAdapter([client, req, this]))
22✔
73
  }
74

75
  private onHeartbeat() {
76
    this.webSocketServer.clients.forEach((webSocket) =>
×
77
      this.webSocketsAdapters.get(webSocket).emit(WebSocketAdapterEvent.Heartbeat)
×
78
    )
79
  }
80

81
  protected onClose() {
82
    debug('closing')
2✔
83
    clearInterval(this.heartbeatInterval)
2✔
84
    this.webSocketServer.clients.forEach((webSocket: WebSocket) => {
2✔
85
      debug('terminating client')
×
86
      webSocket.terminate()
×
87
    })
88
    this.removeAllListeners()
2✔
89
    this.webSocketServer.removeAllListeners()
2✔
90
    super.onClose()
2✔
91
    debug('closed')
2✔
92
  }
93
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc