wspromisify 2.4.3 → 2.5.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.
Files changed (44) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1 -2
  3. package/TODO +9 -0
  4. package/dist/bundle.cjs +1 -1
  5. package/dist/bundle.d.ts +7 -2
  6. package/dist/bundle.mjs +1 -1
  7. package/package.json +91 -94
  8. package/rollup.config.js +1 -1
  9. package/src/WSC.ts +271 -0
  10. package/src/config.ts +7 -6
  11. package/src/utils.ts +5 -23
  12. package/test/index.ts +21 -0
  13. package/test/mock/WS.ts +43 -0
  14. package/test/mock/server.ts +17 -0
  15. package/test/specs/close.ts +18 -0
  16. package/test/specs/drops.ts +30 -0
  17. package/test/specs/echo.ts +24 -0
  18. package/test/specs/encode-decode.ts +1 -0
  19. package/test/specs/existing_socket.ts +55 -0
  20. package/test/specs/lazy.ts +22 -0
  21. package/test/specs/lazySendBeforeOpen.ts +19 -0
  22. package/test/specs/no-native-throws.ts +18 -0
  23. package/test/specs/ready.ts +12 -0
  24. package/test/specs/reconnect.ts +23 -0
  25. package/test/specs/socket.ts +13 -0
  26. package/test/suite.ts +3 -0
  27. package/test/utils.ts +27 -0
  28. package/tsconfig.json +3 -2
  29. package/src/WS.ts +0 -166
  30. package/src/connectLib.ts +0 -120
  31. package/test/mock/WS.js +0 -51
  32. package/test/mock/index.js +0 -52
  33. package/test/specs/close.js +0 -25
  34. package/test/specs/drops.js +0 -29
  35. package/test/specs/echo.js +0 -26
  36. package/test/specs/encode-decode.js +0 -3
  37. package/test/specs/existing_socket.js +0 -55
  38. package/test/specs/lazy.js +0 -26
  39. package/test/specs/ready.js +0 -16
  40. package/test/specs/reconnect.js +0 -28
  41. package/test/specs/sendBeforeOpen.js +0 -23
  42. package/test/specs/socket.js +0 -22
  43. package/test/specs/utils_once.js +0 -16
  44. package/test/utils.js +0 -27
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": "src",
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
package/src/connectLib.ts DELETED
@@ -1,120 +0,0 @@
1
- import './types'
2
- import { once, add_event } from './utils'
3
-
4
-
5
- const init = function(ws: wsc.Socket) {
6
- const config = this.config
7
- this.open = true
8
- this.onReadyQueue.forEach((fn: Function) => fn())
9
- this.onReadyQueue.splice(0)
10
- const {id_key, data_key} = config.server
11
- // Send all pending messages.
12
- this.handlers.open.forEach((h) => h())
13
- this.messages.forEach((message: any) => message.send())
14
- // It's reconnecting.
15
- if(this.reconnect_timeout !== null) {
16
- clearInterval(this.reconnect_timeout)
17
- this.reconnect_timeout = null
18
- }
19
- if(config.ping) {
20
- const ping_interval = setInterval(() => {
21
- if(this.open) this.send(config.ping.content)
22
- if(this.forcibly_closed) clearInterval(ping_interval)
23
- }, config.ping.interval*1e3)
24
- }
25
-
26
- add_event(ws, 'close', async () => {
27
- this.log('close')
28
- this.open = false
29
- this.onCloseQueue.forEach((fn: Function) => fn())
30
- this.onCloseQueue = []
31
- // Auto reconnect.
32
- const reconnect = config.reconnect
33
- if(
34
- typeof reconnect === 'number' &&
35
- !isNaN(reconnect) &&
36
- !this.forcibly_closed
37
- ) {
38
- const reconnectFunc = async () => {
39
- this.log('reconnect')
40
- if(this.ws !== null) {
41
- this.ws.close()
42
- this.ws = null
43
- }
44
- // If some error occured, try again.
45
- const status = await this.connect()
46
- if(status !== null) {
47
- this.reconnect_timeout = setTimeout(reconnectFunc, reconnect * 1000)
48
- }
49
- }
50
- // No need for await.
51
- reconnectFunc()
52
- } else {
53
- this.ws = null
54
- this.open = null
55
- }
56
- // reset the flag to reuse.
57
- this.forcibly_closed = false
58
- })
59
-
60
- add_event(ws, 'message', (e) => {
61
- try {
62
- const data = config.decode(e.data)
63
- this.handlers.message.forEach((h: any) => h({...e, data}))
64
- if(data[id_key]) {
65
- const q = this.queue[data[id_key]]
66
- if(q) {
67
- // Debug, Log.
68
- const time = q.sent_time ? (Date.now() - q.sent_time) : null
69
- this.log('message', data[data_key], time)
70
- // Play.
71
- q.ff(data[data_key])
72
- clearTimeout(q.timeout)
73
- delete this.queue[data[id_key]]
74
- }
75
- }
76
- } catch (err) {
77
- console.error(err, `WSP: Decode error. Got: ${e.data}`)
78
- }
79
- })
80
- }
81
-
82
- // ---------------------------------------------------------------------------
83
-
84
-
85
- const connectLib = function(ff: Function) {
86
- if(this.open === true) {
87
- return ff(null)
88
- }
89
- const config = this.config
90
- const ws = config.socket || config.adapter(config.url, config.protocols)
91
- this.ws = ws
92
-
93
- if(!ws || ws.readyState > 1) {
94
- this.ws = null
95
- this.log('error', 'ready() on closing or closed state! status 2.')
96
- return ff(2)
97
- }
98
-
99
- add_event(ws, 'error', once((e) => {
100
- this.log('error', 'status 3.')
101
- this.handlers.error.forEach((h) => h(e))
102
- this.ws = null
103
- // Some network error: Connection refused or so.
104
- return ff(3)
105
- }))
106
- // Because 'open' won't be envoked on opened socket.
107
- if(ws.readyState) {
108
- init.call(this, ws)
109
- ff(null)
110
- } else {
111
- add_event(ws, 'open', once(() => {
112
- this.log('open')
113
- init.call(this, ws)
114
- return ff(null)
115
- }))
116
- }
117
- }
118
-
119
-
120
- export default connectLib
package/test/mock/WS.js DELETED
@@ -1,51 +0,0 @@
1
-
2
- import { WebSocketServer } from 'ws'
3
-
4
- let mockServer = {}
5
-
6
- const createServer = (port = 40510) => {
7
- return new Promise((ff) => {
8
- if(mockServer[port] === undefined) {
9
- mockServer[port] = new WebSocketServer({ port }, () => {
10
- mockServer[port].on('connection', (socket) => {
11
- socket.on('message', (rawMessage) => {
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, true)
18
- return null
19
- } else if(data.echo) {
20
- response = data
21
- }
22
- socket.send(JSON.stringify({
23
- id,
24
- data: response
25
- }))
26
- return null
27
- })
28
- return true
29
- })
30
- return ff(true)
31
- })
32
- } else {
33
- return ff(false)
34
- }
35
- })
36
- }
37
-
38
- const killServer = async (port = 40510) => {
39
- return new Promise((ff) => {
40
- if(mockServer[port]) {
41
- mockServer[port].close(() => {
42
- delete mockServer[port]
43
- ff()
44
- })
45
- } else {
46
- ff()
47
- }
48
- })
49
- }
50
-
51
- export { createServer, killServer }
@@ -1,52 +0,0 @@
1
-
2
- import express from 'express'
3
- import { createServer, killServer } from './WS.js'
4
- import net from 'net';
5
-
6
- var portInUse = (port) => new Promise((ff) => {
7
- var server = net.createServer(function(socket) {
8
- socket.write('Echo server\r\n');
9
- socket.pipe(socket);
10
- });
11
-
12
- server.on('error', function (e) {
13
- ff(true);
14
- });
15
- server.on('listening', function (e) {
16
- server.close();
17
- ff(false);
18
- });
19
-
20
- server.listen(port, '127.0.0.1');
21
- });
22
-
23
- const ports = {}
24
-
25
- export default async () => {
26
- const app = express()
27
- const getPort = (req) => +req.originalUrl.split('/')[2] || 8095
28
-
29
- app.get(/\/on\/.*/, async (req, res) => {
30
- const port = getPort(req)
31
- if(!ports[port]) {
32
- await createServer(getPort(req))
33
- ports[port] = true
34
- } else {
35
- }
36
- res.send('on')
37
- })
38
-
39
- app.get(/\/off\/.*/, async (req, res) => {
40
- const port = getPort(req)
41
- if(ports[port]) {
42
- await killServer(port)
43
- delete ports[port]
44
- } else {
45
- }
46
- res.send('off')
47
- })
48
-
49
- const port = 8000 + Math.ceil(Math.random()*500)
50
- app.listen(port, () => {});
51
- return port
52
- }
@@ -1,25 +0,0 @@
1
- import test from 'ava'
2
- import { createNew, shutDown } from '../utils.js'
3
- import mockServer from '../mock/index.js'
4
-
5
- /** Closes the connenction. */
6
- test.serial('close', (t) => {
7
- return new Promise(async (ff) => {
8
- await mockServer()
9
- const port = 40513
10
- const ws = await createNew({}, port)
11
-
12
- setTimeout(async () => {
13
- await ws.close()
14
-
15
- if(ws.socket === null) {
16
- await shutDown(port)
17
- t.pass()
18
- } else {
19
- await shutDown(port)
20
- t.fail()
21
- }
22
- return ff()
23
- }, 500)
24
- })
25
- })
@@ -1,29 +0,0 @@
1
- import test from 'ava'
2
- import { createNew, shutDown } from '../utils.js'
3
- import mockServer from '../mock/index.js'
4
-
5
- /** Rejects messages by timout */
6
- test.serial('drops', (t) => {
7
- return new Promise(async (ff) => {
8
- await mockServer()
9
- const ws = await createNew({
10
- timeout: 500
11
- }, 8110)
12
-
13
- await shutDown(8110)
14
-
15
- setTimeout(async () => {
16
- const msg = {echo: true, msg: 'hello!'}
17
- try {
18
- setTimeout(() => {
19
- return ff(t.fail())
20
- }, 600)
21
- await ws.send(msg)
22
- return ff(t.fail())
23
- } catch(e) {
24
- t.pass()
25
- return ff()
26
- }
27
- }, 200)
28
- })
29
- })
@@ -1,26 +0,0 @@
1
- import test from 'ava'
2
- import { createNew, shutDown } from '../utils.js'
3
- import mockServer from '../mock/index.js'
4
-
5
- /** Proof of work */
6
- test.serial('echo', (t) => {
7
- t.timeout(5000)
8
- return new Promise(async (ff, rj) => {
9
- await mockServer()
10
- let to = setTimeout(() => rj(t.fail('cannot create')), 2e2)
11
- const ws = await createNew()
12
- clearTimeout(to)
13
-
14
- to = setTimeout(() => rj(t.fail('cannot ready')), 2e2)
15
- await ws.ready()
16
- clearTimeout(to)
17
-
18
- const msg = {echo: true, msg: 'hello!'}
19
- to = setTimeout(() => rj(t.fail('cannot send')), 2e2)
20
- const response = await ws.send(msg)
21
- clearTimeout(to)
22
-
23
- await shutDown()
24
- ff(t.deepEqual(response, msg))
25
- })
26
- })
@@ -1,3 +0,0 @@
1
- import test from 'ava'
2
-
3
- test.todo('TODO with mock server with specific port.')
@@ -1,55 +0,0 @@
1
- import test from 'ava'
2
- import { createNew, shutDown, turnOn } from '../utils.js'
3
- import mockServer from '../mock/index.js'
4
-
5
- import * as WS from 'ws'
6
-
7
- /** If an existing socket connection is provided via config. */
8
- test.serial('existing_socket', (t) => {
9
- const existing_port = 8095
10
- const existing_addr = 'ws://localhost:' + existing_port
11
- return new Promise(async (ff) => {
12
- await mockServer()
13
- const to = setTimeout(() => ff(t.fail()), 4e4)
14
-
15
- await turnOn(existing_port)
16
-
17
- // This one CANNOT connect as fast as we send to it,
18
- // So readyState is 0.
19
- const ws1 = await createNew({
20
- socket: new WS(existing_addr)
21
- })
22
-
23
- t.is(ws1.socket.readyState, 0)
24
-
25
- const msg1 = {echo: true, msg: 'existing_socket!'}
26
- const response1 = await ws1.send(msg1)
27
-
28
- t.is(ws1.socket.readyState, 1)
29
- t.deepEqual(response1, msg1)
30
- await ws1.close()
31
-
32
- // This one DO CAN connect as fast as we send to it,
33
- // So readyState should be 1.
34
- const ws2_0 = new WS(existing_addr)
35
-
36
- ws2_0.addEventListener('open', async () => {
37
- const ws2 = await createNew({
38
- socket: ws2_0
39
- })
40
-
41
- t.is(ws2.socket.readyState, 1)
42
-
43
- const msg2 = {echo: true, msg: 'existing_socket!'}
44
- const response2 = await ws2.send(msg2)
45
-
46
- t.is(ws2.socket.readyState, 1)
47
- t.deepEqual(response2, msg2)
48
- await ws2.close()
49
-
50
- clearTimeout(to)
51
- shutDown()
52
- ff()
53
- })
54
- })
55
- })
@@ -1,26 +0,0 @@
1
- import test from 'ava'
2
- import { createNew, shutDown } from '../utils.js'
3
- import mockServer from '../mock/index.js'
4
-
5
- /** Lazy connect */
6
- test.serial('lazy', (t) => {
7
- t.timeout(2000)
8
- return new Promise(async (ff) => {
9
- await mockServer()
10
- const ws = await createNew({
11
- lazy: true
12
- }, 8103)
13
-
14
- setTimeout(async () => {
15
- if(ws.socket !== null) {
16
- shutDown()
17
- ff(t.fail())
18
- } else {
19
- const msg = {echo: true, msg: 'hello!'}
20
- const response = await ws.send(msg)
21
- shutDown()
22
- ff(t.deepEqual(response, msg))
23
- }
24
- }, 500)
25
- })
26
- })
@@ -1,16 +0,0 @@
1
- import test from 'ava'
2
- import { createNew, shutDown } from '../utils.js'
3
- import mockServer from '../mock/index.js'
4
-
5
- /** Ready method. */
6
- test.serial('ready', async (t) => {
7
- await mockServer()
8
- t.timeout(4e3)
9
-
10
- const ws = await createNew()
11
- await ws.ready()
12
- // t.fail('fuck!')
13
-
14
- await shutDown()
15
- return ws.socket ? t.pass() : t.fail()
16
- })
@@ -1,28 +0,0 @@
1
- import test from 'ava'
2
- import { createNew, shutDown, turnOn } from '../utils.js'
3
- import mockServer from '../mock/index.js'
4
-
5
- /** Reconnects if connection is broken. */
6
- test.serial('reconnect', (t) => {
7
- const port = 8116
8
- return new Promise(async (ff) => {
9
- await mockServer()
10
- const to = setTimeout(() => ff(t.fail()), 4e4)
11
- const ws = await createNew({
12
- reconnect: 1
13
- }, port)
14
-
15
- setTimeout(async () => {
16
- await shutDown(port)
17
- setTimeout(async () => {
18
- await turnOn(port)
19
- setTimeout(async () => {
20
- const msg = {echo: true, msg: 'hello!'}
21
- const response = await ws.send(msg)
22
- clearTimeout(to)
23
- ff(t.deepEqual(response, msg))
24
- }, 1500)
25
- }, 1100)
26
- }, 500)
27
- })
28
- })
@@ -1,23 +0,0 @@
1
- import test from 'ava'
2
- import { createNew, shutDown } from '../utils.js'
3
- import mockServer from '../mock/index.js'
4
-
5
- /** Sends massages if they were .send() before connection is estabilished. */
6
- test.serial('sendBeforeOpen', (t) => {
7
- return new Promise(async (ff, rj) => {
8
- await mockServer()
9
- let to = setTimeout(() => rj(t.fail('cannot create')), 2e2)
10
- const ws = await createNew({
11
- lazy: true
12
- }, 8101)
13
- clearTimeout(to)
14
-
15
- const msg = {echo: true, msg: 'hello!'}
16
- to = setTimeout(() => rj(t.fail('cannot send')), 2e2)
17
- const response = await ws.send(msg)
18
- clearTimeout(to)
19
-
20
- await shutDown()
21
- ff(t.deepEqual(response, msg))
22
- })
23
- })
@@ -1,22 +0,0 @@
1
- import test from 'ava'
2
- import { createNew, shutDown } from '../utils.js'
3
- import mockServer from '../mock/index.js'
4
-
5
- /** Socket property check. */
6
- test.serial('sockets', (t) => {
7
- return new Promise(async ff => {
8
- await mockServer()
9
- const to = setTimeout(() => ff(t.fail()), 4e4)
10
- const ws = await createNew()
11
-
12
- await ws.ready()
13
-
14
- clearTimeout(to)
15
- shutDown()
16
- if(ws.socket && !isNaN(ws.socket.readyState)) {
17
- ff(t.pass())
18
- } else {
19
- ff(t.fail())
20
- }
21
- })
22
- })