wspromisify 2.4.4 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +1 -2
- package/TODO +9 -0
- package/dist/bundle.cjs +1 -1
- package/dist/bundle.d.ts +9 -3
- package/dist/bundle.mjs +1 -1
- package/package.json +79 -94
- package/rollup.config.js +1 -1
- package/src/WSC.ts +266 -0
- package/src/config.ts +7 -6
- package/src/types.ts +1 -1
- package/src/utils.ts +5 -23
- package/test/index.ts +21 -0
- package/test/mock/WS.ts +43 -0
- package/test/mock/server.ts +17 -0
- package/test/specs/close.ts +18 -0
- package/test/specs/drops.ts +30 -0
- package/test/specs/echo.ts +24 -0
- package/test/specs/encode-decode.ts +1 -0
- package/test/specs/existing_socket.ts +55 -0
- package/test/specs/lazy.ts +22 -0
- package/test/specs/lazySendBeforeOpen.ts +19 -0
- package/test/specs/no-native-throws.ts +18 -0
- package/test/specs/ready.ts +12 -0
- package/test/specs/reconnect.ts +23 -0
- package/test/specs/socket.ts +13 -0
- package/test/suite.ts +3 -0
- package/test/utils.ts +27 -0
- package/tsconfig.json +3 -2
- package/src/WS.ts +0 -166
- package/src/connectLib.ts +0 -120
- package/test/mock/WS.js +0 -51
- package/test/mock/index.js +0 -52
- package/test/specs/close.js +0 -25
- package/test/specs/drops.js +0 -29
- package/test/specs/echo.js +0 -26
- package/test/specs/encode-decode.js +0 -3
- package/test/specs/existing_socket.js +0 -55
- package/test/specs/lazy.js +0 -26
- package/test/specs/ready.js +0 -16
- package/test/specs/reconnect.js +0 -28
- package/test/specs/sendBeforeOpen.js +0 -23
- package/test/specs/socket.js +0 -22
- package/test/specs/utils_once.js +0 -16
- package/test/utils.js +0 -27
package/src/config.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import './types'
|
|
2
|
+
import { native_ws } from './utils'
|
|
2
3
|
|
|
3
4
|
const default_config = <wsc.Config>{
|
|
4
5
|
data_type: 'json', // ToDo some other stuff maybe.
|
|
@@ -29,13 +30,17 @@ const default_config = <wsc.Config>{
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
const
|
|
33
|
+
export const processConfig = (config: wsc.UserConfig) => {
|
|
34
|
+
if(native_ws===null && !('adapter' in config)) throw new Error(`
|
|
35
|
+
This platform has no native WebSocket implementation.
|
|
36
|
+
Please use 'ws' package as an adapter.
|
|
37
|
+
See https://github.com/houd1ni/WebsocketPromisify/issues/23
|
|
38
|
+
`)
|
|
33
39
|
const full_config: wsc.Config = Object.assign(
|
|
34
40
|
{},
|
|
35
41
|
default_config,
|
|
36
42
|
config
|
|
37
43
|
)
|
|
38
|
-
|
|
39
44
|
const url = full_config.url
|
|
40
45
|
if(url[0] == '/') {
|
|
41
46
|
try {
|
|
@@ -47,8 +52,4 @@ const enrichConfig = (config: wsc.UserConfig) => {
|
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
return full_config
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export {
|
|
53
|
-
enrichConfig
|
|
54
55
|
}
|
package/src/types.ts
CHANGED
package/src/utils.ts
CHANGED
|
@@ -1,30 +1,12 @@
|
|
|
1
1
|
import './types'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
return o.addEventListener(e, handler)
|
|
5
|
-
}
|
|
3
|
+
export const native_ws = (() => {try {return WebSocket||null}catch { return null }})()
|
|
6
4
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
let cached = null
|
|
10
|
-
return (...args: any) => {
|
|
11
|
-
if(has_been_cached) {
|
|
12
|
-
return cached
|
|
13
|
-
} else {
|
|
14
|
-
has_been_cached = true
|
|
15
|
-
return cached = fn(...args)
|
|
16
|
-
}
|
|
17
|
-
}
|
|
5
|
+
export const add_event = (o: wsc.Socket, e: string, handler: wsc.EventHandler) => {
|
|
6
|
+
return o.addEventListener(e, handler)
|
|
18
7
|
}
|
|
19
8
|
|
|
20
|
-
const sett = (
|
|
9
|
+
export const sett = (
|
|
21
10
|
a: number,
|
|
22
11
|
b: { (): void; (...args: any[]): void; }
|
|
23
|
-
) => setTimeout(b, a)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export {
|
|
27
|
-
add_event,
|
|
28
|
-
once,
|
|
29
|
-
sett
|
|
30
|
-
}
|
|
12
|
+
) => setTimeout(b, a)
|
package/test/index.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import './specs/close'
|
|
2
|
+
import './specs/drops'
|
|
3
|
+
import './specs/echo'
|
|
4
|
+
import './specs/existing_socket'
|
|
5
|
+
import './specs/lazy'
|
|
6
|
+
import './specs/ready'
|
|
7
|
+
import './specs/reconnect'
|
|
8
|
+
import './specs/lazySendBeforeOpen'
|
|
9
|
+
import './specs/socket'
|
|
10
|
+
import './specs/no-native-throws'
|
|
11
|
+
import mockServer from './mock/server'
|
|
12
|
+
import {test} from './suite'
|
|
13
|
+
|
|
14
|
+
const {shutDown} = await mockServer()
|
|
15
|
+
test.after(() => {
|
|
16
|
+
setTimeout(async () => {
|
|
17
|
+
await shutDown()
|
|
18
|
+
process.exit()
|
|
19
|
+
}, 100)
|
|
20
|
+
})
|
|
21
|
+
test.run()
|
package/test/mock/WS.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
|
|
2
|
+
import WebSocket, { WebSocketServer } from 'ws'
|
|
3
|
+
import {noop} from 'pepka'
|
|
4
|
+
|
|
5
|
+
let server: WebSocketServer|null = null
|
|
6
|
+
|
|
7
|
+
const createServer = (port = 40510) => new Promise<WebSocketServer>((ff, rj) => {
|
|
8
|
+
if(server) return rj('The server is already running!')
|
|
9
|
+
server = new WebSocketServer({ port }, () => {
|
|
10
|
+
server!.on('connection', (socket: WebSocket&{isAlive: boolean}) => {
|
|
11
|
+
socket.on('message', (rawMessage: string) => {
|
|
12
|
+
const {id, data} = JSON.parse(rawMessage)
|
|
13
|
+
let response = ''
|
|
14
|
+
if(data.shut) {
|
|
15
|
+
socket.terminate()
|
|
16
|
+
socket.isAlive = false
|
|
17
|
+
socket.ping('', false, noop)
|
|
18
|
+
return null
|
|
19
|
+
} else if(data.echo) {
|
|
20
|
+
response = data
|
|
21
|
+
}
|
|
22
|
+
socket.send(JSON.stringify({ id, data: response }))
|
|
23
|
+
return null
|
|
24
|
+
})
|
|
25
|
+
return true
|
|
26
|
+
})
|
|
27
|
+
return ff(server!)
|
|
28
|
+
})
|
|
29
|
+
server.on('', console.log)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const killServer = async () => new Promise<void>((ff, rj) => {
|
|
33
|
+
if(server) {
|
|
34
|
+
for(const socket of server.clients) socket.terminate()
|
|
35
|
+
server.close(() => {
|
|
36
|
+
server = null
|
|
37
|
+
ff()
|
|
38
|
+
})
|
|
39
|
+
} else
|
|
40
|
+
rj('The server is already down!')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
export { createServer, killServer }
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
import { WebSocketServer } from 'ws'
|
|
3
|
+
import { createServer, killServer } from './WS.js'
|
|
4
|
+
|
|
5
|
+
let port: number,
|
|
6
|
+
server: WebSocketServer|null
|
|
7
|
+
|
|
8
|
+
export default async (new_port?: number) => {
|
|
9
|
+
if(!server) {
|
|
10
|
+
port = new_port || 8000 + Math.ceil(Math.random()*500)
|
|
11
|
+
server = await createServer(port)
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
server, port,
|
|
15
|
+
shutDown: async () => { await killServer(); server=null }
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { test } from '../suite'
|
|
2
|
+
import { createNew } from '../utils.js'
|
|
3
|
+
import mockServer from '../mock/server'
|
|
4
|
+
|
|
5
|
+
/** Closes the connenction. */
|
|
6
|
+
test('close', () => new Promise<void>(async (ff, rj) => {
|
|
7
|
+
const {port} = await mockServer()
|
|
8
|
+
const ws = createNew({}, port)
|
|
9
|
+
|
|
10
|
+
setTimeout(async () => {
|
|
11
|
+
try {
|
|
12
|
+
await ws.close()
|
|
13
|
+
if(ws.socket === null) ff(); else rj()
|
|
14
|
+
} catch(e) {
|
|
15
|
+
rj()
|
|
16
|
+
}
|
|
17
|
+
}, 500)
|
|
18
|
+
}))
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createNew } from '../utils'
|
|
2
|
+
import mockServer from '../mock/server'
|
|
3
|
+
import { test } from '../suite'
|
|
4
|
+
|
|
5
|
+
/** Rejects messages by timout */
|
|
6
|
+
test('drops', () => new Promise(async (ff, rj) => {
|
|
7
|
+
const {port, shutDown} = await mockServer()
|
|
8
|
+
const ws = createNew({timeout: 500}, port)
|
|
9
|
+
|
|
10
|
+
await shutDown()
|
|
11
|
+
const lauchServ = async () => await mockServer(port)
|
|
12
|
+
|
|
13
|
+
setTimeout(async () => {
|
|
14
|
+
const msg = {echo: true, msg: 'hello!'}
|
|
15
|
+
let to: NodeJS.Timeout
|
|
16
|
+
try {
|
|
17
|
+
to = setTimeout(() => {
|
|
18
|
+
return rj()
|
|
19
|
+
}, 600)
|
|
20
|
+
await ws.send(msg)
|
|
21
|
+
if(to) clearTimeout(to)
|
|
22
|
+
await lauchServ()
|
|
23
|
+
return rj()
|
|
24
|
+
} catch(e) {
|
|
25
|
+
if(to!) clearTimeout(to)
|
|
26
|
+
await lauchServ()
|
|
27
|
+
return ff()
|
|
28
|
+
}
|
|
29
|
+
}, 200)
|
|
30
|
+
}))
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { equals } from 'pepka'
|
|
2
|
+
import { createNew, timeout } from '../utils'
|
|
3
|
+
import mockServer from '../mock/server'
|
|
4
|
+
import { test } from '../suite'
|
|
5
|
+
|
|
6
|
+
/** Proof of work. */
|
|
7
|
+
test('echo', timeout(5e3, () => new Promise<void>(async (ff, rj) => {
|
|
8
|
+
const {port} = await mockServer()
|
|
9
|
+
let to = setTimeout(() => rj('cannot create'), 2e2)
|
|
10
|
+
const ws = createNew({}, port)
|
|
11
|
+
clearTimeout(to)
|
|
12
|
+
|
|
13
|
+
to = setTimeout(() => rj('cannot ready'), 2e2)
|
|
14
|
+
await ws.ready()
|
|
15
|
+
clearTimeout(to)
|
|
16
|
+
|
|
17
|
+
const msg = {echo: true, msg: 'hello!'}
|
|
18
|
+
to = setTimeout(() => rj('cannot send'), 2e2)
|
|
19
|
+
const response = await ws.send(msg)
|
|
20
|
+
clearTimeout(to)
|
|
21
|
+
|
|
22
|
+
if(equals(response, msg)) ff(); else rj('echo msg is not equal.')
|
|
23
|
+
})
|
|
24
|
+
))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// TODO with mock server with specific port.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createNew } from '../utils'
|
|
2
|
+
import mockServer from '../mock/server'
|
|
3
|
+
|
|
4
|
+
import WS from 'ws'
|
|
5
|
+
import { test } from '../suite'
|
|
6
|
+
import { equals } from 'pepka'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
const addr = (port: number) => 'ws://localhost:' + port
|
|
10
|
+
|
|
11
|
+
/** If an existing socket connection is provided via config. */
|
|
12
|
+
test('existing_socket', () => {
|
|
13
|
+
return new Promise(async (ff, rj) => {
|
|
14
|
+
const {port} = await mockServer()
|
|
15
|
+
const to = setTimeout(() => rj(), 4e4)
|
|
16
|
+
const existing_addr = addr(port)
|
|
17
|
+
|
|
18
|
+
// This one CANNOT connect as fast as we send to it,
|
|
19
|
+
// So readyState is 0.
|
|
20
|
+
const ws1 = createNew({socket: new WS(existing_addr)}, port)
|
|
21
|
+
|
|
22
|
+
if(ws1.socket?.readyState !== 0) return rj('not ready.')
|
|
23
|
+
|
|
24
|
+
const msg1 = {echo: true, msg: 'existing_socket!'}
|
|
25
|
+
const response1 = await ws1.send(msg1)
|
|
26
|
+
|
|
27
|
+
if(
|
|
28
|
+
ws1.socket?.readyState as number !== 1
|
|
29
|
+
|| !equals(response1, msg1)
|
|
30
|
+
) return rj('not ready.')
|
|
31
|
+
await ws1.close()
|
|
32
|
+
|
|
33
|
+
// This one DO CAN connect as fast as we send to it,
|
|
34
|
+
// So readyState should be 1.
|
|
35
|
+
const ws2_0 = new WS(existing_addr)
|
|
36
|
+
|
|
37
|
+
ws2_0.addEventListener('open', async () => {
|
|
38
|
+
const ws2 = await createNew({socket: ws2_0}, port)
|
|
39
|
+
|
|
40
|
+
if(ws2.socket?.readyState !== 1) return rj('not ready.')
|
|
41
|
+
|
|
42
|
+
const msg2 = {echo: true, msg: 'existing_socket!'}
|
|
43
|
+
const response2 = await ws2.send(msg2)
|
|
44
|
+
|
|
45
|
+
if(
|
|
46
|
+
ws2.socket?.readyState as number !== 1
|
|
47
|
+
|| !equals(response2, msg2)
|
|
48
|
+
) return rj('not ready.')
|
|
49
|
+
await ws2.close()
|
|
50
|
+
|
|
51
|
+
clearTimeout(to)
|
|
52
|
+
ff()
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createNew, timeout } from '../utils'
|
|
2
|
+
import mockServer from '../mock/server'
|
|
3
|
+
import { equals } from 'pepka'
|
|
4
|
+
import { test } from '../suite'
|
|
5
|
+
|
|
6
|
+
/** Lazy connect */
|
|
7
|
+
test('lazy', timeout(2e3, () => {
|
|
8
|
+
return new Promise<void>(async (ff, rj) => {
|
|
9
|
+
const {port} = await mockServer()
|
|
10
|
+
const ws = createNew({ lazy: true }, port)
|
|
11
|
+
|
|
12
|
+
setTimeout(async () => {
|
|
13
|
+
if(ws.socket !== null) {
|
|
14
|
+
rj()
|
|
15
|
+
} else {
|
|
16
|
+
const msg = {echo: true, msg: 'hello!'}
|
|
17
|
+
const response = await ws.send(msg)
|
|
18
|
+
if(equals(response, msg)) ff(); else rj()
|
|
19
|
+
}
|
|
20
|
+
}, 500)
|
|
21
|
+
})
|
|
22
|
+
}))
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createNew } from '../utils.js'
|
|
2
|
+
import mockServer from '../mock/server'
|
|
3
|
+
import { equals } from 'pepka'
|
|
4
|
+
import { test } from '../suite'
|
|
5
|
+
|
|
6
|
+
/** Sends massages if they were .send() before connection is estabilished. */
|
|
7
|
+
test('lazy send before open queued.', () => new Promise(async (ff, rj) => {
|
|
8
|
+
const {port} = await mockServer()
|
|
9
|
+
let to = setTimeout(() => rj('cannot create'), 2e2)
|
|
10
|
+
const ws = createNew({lazy: true}, port)
|
|
11
|
+
clearTimeout(to)
|
|
12
|
+
|
|
13
|
+
const msg = {echo: true, msg: 'hello!'}
|
|
14
|
+
to = setTimeout(() => rj('cannot send'), 2e2)
|
|
15
|
+
const response = await ws.send(msg)
|
|
16
|
+
clearTimeout(to)
|
|
17
|
+
|
|
18
|
+
if(equals(response, msg)) ff(); else rj()
|
|
19
|
+
}))
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import mockServer from '../mock/server'
|
|
2
|
+
import { test } from '../suite'
|
|
3
|
+
import WebSocketClient from '../../src/WSC'
|
|
4
|
+
|
|
5
|
+
/** Ready method. */
|
|
6
|
+
test('No native throws without an adapter', async () => {
|
|
7
|
+
const {port} = await mockServer()
|
|
8
|
+
let pass = false
|
|
9
|
+
try {
|
|
10
|
+
new WebSocketClient({ url: 'ws://127.0.0.1:' + port })
|
|
11
|
+
try {
|
|
12
|
+
if(WebSocket) pass = true
|
|
13
|
+
} catch {}
|
|
14
|
+
} catch {
|
|
15
|
+
pass=true
|
|
16
|
+
}
|
|
17
|
+
if(!pass) throw new Error('Does not throw.')
|
|
18
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createNew, timeout } from '../utils'
|
|
2
|
+
import mockServer from '../mock/server'
|
|
3
|
+
import { test } from '../suite'
|
|
4
|
+
|
|
5
|
+
/** Ready method. */
|
|
6
|
+
test('ready', timeout(4e3, async () => {
|
|
7
|
+
const {port} = await mockServer()
|
|
8
|
+
const ws = createNew({}, port)
|
|
9
|
+
|
|
10
|
+
await ws.ready()
|
|
11
|
+
if(!ws.socket) throw new Error()
|
|
12
|
+
}))
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createNew, timeout } from '../utils'
|
|
2
|
+
import mockServer from '../mock/server'
|
|
3
|
+
import { equals } from 'pepka'
|
|
4
|
+
import { test } from '../suite'
|
|
5
|
+
|
|
6
|
+
/** Reconnects if connection is broken. */
|
|
7
|
+
test('reconnect', timeout(1e4, () => new Promise<void>(async (ff, rj) => {
|
|
8
|
+
const {port, shutDown} = await mockServer()
|
|
9
|
+
const ws = createNew({ reconnect: 1 }, port)
|
|
10
|
+
|
|
11
|
+
setTimeout(async () => {
|
|
12
|
+
await shutDown()
|
|
13
|
+
setTimeout(async () => {
|
|
14
|
+
await mockServer(port)
|
|
15
|
+
setTimeout(async () => {
|
|
16
|
+
const msg = {echo: true, msg: 'hello!'}
|
|
17
|
+
const response = await ws.send(msg)
|
|
18
|
+
if(equals(response, msg)) ff(); else rj('not equals.')
|
|
19
|
+
}, 1500)
|
|
20
|
+
}, 1100)
|
|
21
|
+
}, 500)
|
|
22
|
+
})
|
|
23
|
+
))
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createNew, timeout } from '../utils'
|
|
2
|
+
import mockServer from '../mock/server'
|
|
3
|
+
import { test } from '../suite'
|
|
4
|
+
|
|
5
|
+
/** Socket property check. */
|
|
6
|
+
test('sockets', timeout(1e4, () => new Promise<void>(async (ff, rj) => {
|
|
7
|
+
const {port} = await mockServer()
|
|
8
|
+
const ws = createNew({}, port)
|
|
9
|
+
|
|
10
|
+
await ws.ready()
|
|
11
|
+
|
|
12
|
+
if(ws.socket && !isNaN(ws.socket.readyState)) ff(); else rj()
|
|
13
|
+
})))
|
package/test/suite.ts
ADDED
package/test/utils.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
import WebSocketClient from '../src/WSC'
|
|
3
|
+
import {AnyFunc, AnyObject} from 'pepka'
|
|
4
|
+
import { native_ws } from '../src/utils'
|
|
5
|
+
import WS from 'ws'
|
|
6
|
+
|
|
7
|
+
export const createNew = (config = {}, port: number) => new WebSocketClient(Object.assign({
|
|
8
|
+
url: 'ws://127.0.0.1:' + port,
|
|
9
|
+
// log: (...a) => console.log(...a),
|
|
10
|
+
adapter: (host: string, protocols?: string|string[]) => new (native_ws || WS)(host, protocols)
|
|
11
|
+
}, config)
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
// Inspired by tinchoz49 https://github.com/lukeed/uvu/issues/33#issuecomment-879870292
|
|
15
|
+
export const timeout = (time: number, handler: AnyFunc) => async (context: AnyObject) => {
|
|
16
|
+
let timer: NodeJS.Timeout
|
|
17
|
+
try {
|
|
18
|
+
return await Promise.race([
|
|
19
|
+
handler(context),
|
|
20
|
+
new Promise((_resolve, reject) =>
|
|
21
|
+
timer = setTimeout(() => reject(new Error('timeout')), time)
|
|
22
|
+
)
|
|
23
|
+
])
|
|
24
|
+
} finally {
|
|
25
|
+
if(timer!) clearTimeout(timer)
|
|
26
|
+
}
|
|
27
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
"experimentalDecorators": true,
|
|
12
12
|
"noUnusedParameters": true,
|
|
13
13
|
"noUnusedLocals": false,
|
|
14
|
-
"rootDir": "
|
|
14
|
+
"rootDir": ".",
|
|
15
15
|
"baseUrl": ".",
|
|
16
|
+
"allowSyntheticDefaultImports": true,
|
|
16
17
|
"outDir": "dist/ts"
|
|
17
18
|
},
|
|
18
|
-
"include": [ "src/**/*" ]
|
|
19
|
+
"include": [ "src/**/*", "test/**/*" ]
|
|
19
20
|
}
|
package/src/WS.ts
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import { zipnum } from 'zipnum'
|
|
2
|
-
import connectLib from './connectLib'
|
|
3
|
-
import { add_event, sett } from './utils'
|
|
4
|
-
import { enrichConfig } from './config'
|
|
5
|
-
import './types'
|
|
6
|
-
import { AnyFunc, T } from 'pepka'
|
|
7
|
-
|
|
8
|
-
const MAX_32 = 2**31 - 1
|
|
9
|
-
|
|
10
|
-
/* .send(your_data) wraps request to server with {id: `hash`, data: `actually your data`},
|
|
11
|
-
returns a Promise, that will be rejected after a timeout or
|
|
12
|
-
resolved if server returns the same signature: {id: `same_hash`, data: `response data`}
|
|
13
|
-
*/
|
|
14
|
-
class WebSocketClient {
|
|
15
|
-
|
|
16
|
-
private open = false
|
|
17
|
-
private ws: wsc.Socket|null = null
|
|
18
|
-
// in use by side functions.
|
|
19
|
-
private forcibly_closed = false
|
|
20
|
-
private reconnect_timeout: NodeJS.Timer|null = null
|
|
21
|
-
private queue = {}
|
|
22
|
-
private messages: any[] = []
|
|
23
|
-
private onReadyQueue: AnyFunc[] = []
|
|
24
|
-
private onCloseQueue: AnyFunc[] = []
|
|
25
|
-
private handlers = <{[event: string]: ((e: any) => void)[]}>{
|
|
26
|
-
open: [], message: [], close: [], error: []
|
|
27
|
-
}
|
|
28
|
-
private config = <wsc.Config>{}
|
|
29
|
-
|
|
30
|
-
private init_flush(): void {
|
|
31
|
-
this.queue = {} // data queuse
|
|
32
|
-
this.messages = [] // send() queue
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
private log(event: string, message: any = null, time: number|null = null): void {
|
|
36
|
-
const config = this.config
|
|
37
|
-
if(time !== null) {
|
|
38
|
-
config.log(event, time, message)
|
|
39
|
-
} else {
|
|
40
|
-
if(config.timer) {
|
|
41
|
-
config.log(event, null, message)
|
|
42
|
-
} else {
|
|
43
|
-
config.log(event, message)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
private async connect() { // returns status if won't open or null if ok.
|
|
49
|
-
return new Promise((ff) => {
|
|
50
|
-
connectLib.call(this, ff)
|
|
51
|
-
})
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
public get socket() {
|
|
55
|
-
return this.ws
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
public async ready() {
|
|
59
|
-
return new Promise<void>((ff) => {
|
|
60
|
-
if(this.open) {
|
|
61
|
-
ff()
|
|
62
|
-
} else {
|
|
63
|
-
this.onReadyQueue.push(ff)
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
public on(
|
|
69
|
-
event_name: wsc.WSEvent,
|
|
70
|
-
handler: (data: any) => any,
|
|
71
|
-
predicate: (data: any) => boolean = T,
|
|
72
|
-
raw = false
|
|
73
|
-
) {
|
|
74
|
-
const _handler: wsc.EventHandler = (event) =>
|
|
75
|
-
predicate(event) && handler(event)
|
|
76
|
-
return raw
|
|
77
|
-
? add_event(this.ws as wsc.Socket, event_name, _handler)
|
|
78
|
-
: this.handlers[event_name].push(_handler)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
public async close(): wsc.AsyncErrCode {
|
|
82
|
-
return new Promise((ff, rj) => {
|
|
83
|
-
if(this.ws === null) {
|
|
84
|
-
rj('WSP: closing a non-inited socket!')
|
|
85
|
-
} else {
|
|
86
|
-
this.open = false
|
|
87
|
-
this.onCloseQueue.push(() => {
|
|
88
|
-
this.init_flush()
|
|
89
|
-
this.ws = null
|
|
90
|
-
this.forcibly_closed = true
|
|
91
|
-
ff(null)
|
|
92
|
-
})
|
|
93
|
-
this.ws.close()
|
|
94
|
-
}
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
public async send<RequestDataType = any, ResponseDataType = any>(
|
|
99
|
-
message_data: RequestDataType,
|
|
100
|
-
opts = <wsc.SendOptions>{}
|
|
101
|
-
): Promise<ResponseDataType> {
|
|
102
|
-
this.log('send', message_data)
|
|
103
|
-
const config = this.config
|
|
104
|
-
const message = {}
|
|
105
|
-
const data_key = config.server.data_key
|
|
106
|
-
const first_time_lazy = config.lazy && !this.open
|
|
107
|
-
|
|
108
|
-
const message_id = zipnum((Math.random()*(MAX_32-10))|0)
|
|
109
|
-
if(typeof opts.top === 'object') {
|
|
110
|
-
if(opts.top[data_key]) {
|
|
111
|
-
throw new Error('Attempting to set data key/token via send() options!')
|
|
112
|
-
}
|
|
113
|
-
Object.assign(message, opts.top)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
config.pipes.forEach(
|
|
117
|
-
(pipe) => message_data = pipe(message_data)
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
if(this.open === true) {
|
|
121
|
-
(this.ws as wsc.Socket).send(config.encode(message_id, message_data, config))
|
|
122
|
-
} else if(this.open === false || first_time_lazy) {
|
|
123
|
-
this.messages.push({
|
|
124
|
-
send: () => (this.ws as wsc.Socket).send(config.encode(message_id, message_data, config))
|
|
125
|
-
})
|
|
126
|
-
if(first_time_lazy) {
|
|
127
|
-
this.connect()
|
|
128
|
-
}
|
|
129
|
-
} else if(this.open === null) {
|
|
130
|
-
throw new Error('Attempting to send via closed WebSocket connection!')
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return new Promise((ff, rj) => {
|
|
134
|
-
this.queue[message_id] = {
|
|
135
|
-
ff,
|
|
136
|
-
data_type: config.data_type,
|
|
137
|
-
sent_time: config.timer ? Date.now() : null,
|
|
138
|
-
timeout: sett(config.timeout, () => {
|
|
139
|
-
if(this.queue[message_id]) {
|
|
140
|
-
rj({
|
|
141
|
-
'Websocket timeout expired: ': config.timeout,
|
|
142
|
-
'for the message ': message_data
|
|
143
|
-
})
|
|
144
|
-
delete this.queue[message_id]
|
|
145
|
-
}
|
|
146
|
-
})
|
|
147
|
-
}
|
|
148
|
-
})
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
constructor(user_config: wsc.UserConfig = {}) {
|
|
153
|
-
this.config = enrichConfig(user_config)
|
|
154
|
-
// Init.
|
|
155
|
-
this.init_flush()
|
|
156
|
-
// Flags.
|
|
157
|
-
this.open = false
|
|
158
|
-
this.reconnect_timeout = null
|
|
159
|
-
this.forcibly_closed = false
|
|
160
|
-
if(!this.config.lazy) {
|
|
161
|
-
this.connect()
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export default WebSocketClient
|