forked from cerebral/overmind
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBackendConnector.ts
More file actions
126 lines (117 loc) · 3.17 KB
/
BackendConnector.ts
File metadata and controls
126 lines (117 loc) · 3.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { AppMessage } from './overmind/types'
type Message = {
appName: string
message: AppMessage<any>
}
type MessageCallback = (message: Message) => void
class WebsocketConnector {
private nextEvaluationId = 0
private parser = Promise.resolve()
private callbacks: {
[event: string]: Function[]
} = {}
private socket: WebSocket
private messagesBeforeConnected: Array<[string, any, any]> = []
private isOpen = false
private schedulePing () {
setTimeout(() => {
this.send('ping')
}, 1000 * 10)
}
public connect(
port: string | number // TODO: return Promise so we can wait for it
) {
return new Promise((resolve, reject) => {
if (this.socket) {
reject(new Error('TODO: Socked already open, what now?'))
}
this.socket = new WebSocket(`ws://localhost:${port}?devtools=1`)
this.socket.onmessage = (message) => {
const parsedMessage = JSON.parse(message.data)
if (parsedMessage.type === 'pong') {
this.schedulePing()
} else {
this.emit(parsedMessage.type, parsedMessage.data)
}
}
this.socket.onopen = () => {
resolve()
this.isOpen = true
this.messagesBeforeConnected.forEach((message) => {
this.send(...message)
})
this.messagesBeforeConnected.length = 0
this.schedulePing()
}
this.socket.onclose = (reason) => {
console.log("Socket closed", reason)
setTimeout(() => this.connect(port), 1000)
}
})
}
emit(event: string, data?: any) {
if (this.callbacks[event]) {
this.callbacks[event].forEach((cb) => cb(data))
}
}
on(event: string, cb: Function) {
if (!this.callbacks[event]) {
this.callbacks[event] = []
}
this.callbacks[event].push(cb)
}
off(event, cb) {
if (this.callbacks[event]) {
this.callbacks[event].splice(this.callbacks[event].indexOf(cb), 1)
}
}
send(event: string, data?: any, onEvaluated?: Function) {
if (!this.isOpen) {
this.messagesBeforeConnected.push([event, data, onEvaluated])
return
}
const nextEvaluationId = this.nextEvaluationId++
this.socket.send(
JSON.stringify({
type: event,
data,
evaluate: onEvaluated ? nextEvaluationId : undefined,
})
)
if (onEvaluated) {
const cb = (data) => {
if (data.id === nextEvaluationId) {
this.off('evaluated', cb)
onEvaluated(data.error ? data.error : null, data.data)
}
}
this.on('evaluated', cb)
}
}
}
export class BackendConnector extends WebsocketConnector {
onMessage = (onMessage: MessageCallback) => {
this.on('message', (message) => {
onMessage(message)
})
}
onDisconnect = (onDisconnect: (name: string) => void) => {
this.on('disconnect', (name) => {
onDisconnect(name)
})
}
sendMessage(appName: string, eventName: string, payload: object = null) {
this.send('message', {
appName,
type: eventName,
data: payload,
})
}
relaunch() {
this.send('relaunch')
}
openChromeDevtools() {
this.send('openChromeDevtools')
}
}
export default BackendConnector