@nxtedition/deepstream.io-client-js 26.0.2 → 26.0.4
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/package.json +1 -1
- package/src/client.js +4 -4
- package/src/default-options.js +1 -1
- package/src/event/event-handler.js +8 -8
- package/src/message/connection.js +39 -76
- package/src/message/message-builder.js +6 -5
- package/src/message/message-parser.js +50 -1
- package/src/record/record-handler.js +59 -59
- package/src/record/record.js +47 -60
- package/src/rpc/rpc-handler.js +10 -10
- package/src/rpc/rpc-response.js +2 -2
- package/src/utils/fixed-queue.js +9 -9
- package/src/utils/multicast-listener.js +16 -20
- package/src/utils/timers.js +7 -7
- package/src/utils/unicast-listener.js +11 -15
- package/src/utils/utils.js +13 -13
package/package.json
CHANGED
package/src/client.js
CHANGED
|
@@ -36,13 +36,13 @@ const Client = function (url, options) {
|
|
|
36
36
|
Emitter(Client.prototype)
|
|
37
37
|
|
|
38
38
|
Object.defineProperty(Client.prototype, 'stats', {
|
|
39
|
-
get: function stats() {
|
|
39
|
+
get: function stats () {
|
|
40
40
|
return {
|
|
41
41
|
record: this.record.stats,
|
|
42
42
|
rpc: this.rpc.stats,
|
|
43
|
-
event: this.event.stats
|
|
43
|
+
event: this.event.stats
|
|
44
44
|
}
|
|
45
|
-
}
|
|
45
|
+
}
|
|
46
46
|
})
|
|
47
47
|
|
|
48
48
|
Client.prototype.login = function (authParamsOrCallback, callback) {
|
|
@@ -127,7 +127,7 @@ Client.prototype._getOptions = function (options) {
|
|
|
127
127
|
return mergedOptions
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
function createDeepstream(url, options) {
|
|
130
|
+
function createDeepstream (url, options) {
|
|
131
131
|
return new Client(url, options)
|
|
132
132
|
}
|
|
133
133
|
|
package/src/default-options.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as C from '../constants/constants.js'
|
|
2
2
|
import * as messageBuilder from '../message/message-builder.js'
|
|
3
|
-
import
|
|
3
|
+
import messageParser from '../message/message-parser.js'
|
|
4
4
|
import MulticastListener from '../utils/multicast-listener.js'
|
|
5
5
|
import UnicastListener from '../utils/unicast-listener.js'
|
|
6
6
|
import EventEmitter from 'component-emitter2'
|
|
@@ -13,7 +13,7 @@ const EventHandler = function (options, connection, client) {
|
|
|
13
13
|
this._emitter = new EventEmitter()
|
|
14
14
|
this._listeners = new Map()
|
|
15
15
|
this._stats = {
|
|
16
|
-
emitted: 0
|
|
16
|
+
emitted: 0
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
this.subscribe = this.subscribe.bind(this)
|
|
@@ -26,19 +26,19 @@ const EventHandler = function (options, connection, client) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
Object.defineProperty(EventHandler.prototype, 'connected', {
|
|
29
|
-
get: function connected() {
|
|
29
|
+
get: function connected () {
|
|
30
30
|
return this._client.getConnectionState() === C.CONNECTION_STATE.OPEN
|
|
31
|
-
}
|
|
31
|
+
}
|
|
32
32
|
})
|
|
33
33
|
|
|
34
34
|
Object.defineProperty(EventHandler.prototype, 'stats', {
|
|
35
|
-
get: function stats() {
|
|
35
|
+
get: function stats () {
|
|
36
36
|
return {
|
|
37
37
|
...this._stats,
|
|
38
38
|
listeners: this._listeners.size,
|
|
39
|
-
events: this._emitter.eventNames().length
|
|
39
|
+
events: this._emitter.eventNames().length
|
|
40
40
|
}
|
|
41
|
-
}
|
|
41
|
+
}
|
|
42
42
|
})
|
|
43
43
|
|
|
44
44
|
EventHandler.prototype.subscribe = function (name, callback) {
|
|
@@ -143,7 +143,7 @@ EventHandler.prototype._$handle = function (message) {
|
|
|
143
143
|
|
|
144
144
|
if (message.action === C.ACTIONS.EVENT) {
|
|
145
145
|
if (message.data && message.data.length === 2) {
|
|
146
|
-
this._emitter.emit(name, convertTyped(data, this._client))
|
|
146
|
+
this._emitter.emit(name, messageParser.convertTyped(data, this._client))
|
|
147
147
|
} else {
|
|
148
148
|
this._emitter.emit(name)
|
|
149
149
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import * as utils from '../utils/utils.js'
|
|
2
|
-
import
|
|
2
|
+
import messageParser from './message-parser.js'
|
|
3
3
|
import * as messageBuilder from './message-builder.js'
|
|
4
4
|
import * as C from '../constants/constants.js'
|
|
5
5
|
import xxhash from 'xxhash-wasm'
|
|
6
6
|
import FixedQueue from '../utils/fixed-queue.js'
|
|
7
7
|
import Emitter from 'component-emitter2'
|
|
8
|
-
import varint from 'varint'
|
|
9
8
|
|
|
10
9
|
const BrowserWebSocket = globalThis.WebSocket || globalThis.MozWebSocket
|
|
11
10
|
const NodeWebSocket = utils.isNode ? await import('ws').then((x) => x.default) : null
|
|
@@ -24,8 +23,12 @@ const Connection = function (client, url, options) {
|
|
|
24
23
|
this._tooManyAuthAttempts = false
|
|
25
24
|
this._connectionAuthenticationTimeout = false
|
|
26
25
|
this._challengeDenied = false
|
|
27
|
-
this.
|
|
28
|
-
|
|
26
|
+
this._message = {
|
|
27
|
+
raw: null,
|
|
28
|
+
topic: null,
|
|
29
|
+
action: null,
|
|
30
|
+
data: null
|
|
31
|
+
}
|
|
29
32
|
this._recvQueue = new FixedQueue()
|
|
30
33
|
this._reconnectTimeout = null
|
|
31
34
|
this._reconnectionAttempt = 0
|
|
@@ -46,9 +49,9 @@ Emitter(Connection.prototype)
|
|
|
46
49
|
|
|
47
50
|
// TODO (fix): Remove
|
|
48
51
|
Object.defineProperty(Connection.prototype, 'connected', {
|
|
49
|
-
get: function connected() {
|
|
52
|
+
get: function connected () {
|
|
50
53
|
return this._state === C.CONNECTION_STATE.OPEN
|
|
51
|
-
}
|
|
54
|
+
}
|
|
52
55
|
})
|
|
53
56
|
|
|
54
57
|
Connection.prototype.getState = function () {
|
|
@@ -91,7 +94,7 @@ Connection.prototype.close = function () {
|
|
|
91
94
|
Connection.prototype._createEndpoint = function () {
|
|
92
95
|
if (utils.isNode) {
|
|
93
96
|
this._endpoint = new NodeWebSocket(this._url, {
|
|
94
|
-
generateMask() {}
|
|
97
|
+
generateMask () {}
|
|
95
98
|
})
|
|
96
99
|
} else {
|
|
97
100
|
this._endpoint = new BrowserWebSocket(this._url)
|
|
@@ -103,8 +106,9 @@ Connection.prototype._createEndpoint = function () {
|
|
|
103
106
|
this._endpoint.onerror = this._onError.bind(this)
|
|
104
107
|
this._endpoint.onclose = this._onClose.bind(this)
|
|
105
108
|
|
|
109
|
+
const decoder = new TextDecoder()
|
|
106
110
|
this._endpoint.onmessage = ({ data }) => {
|
|
107
|
-
this._onMessage(data)
|
|
111
|
+
this._onMessage(typeof data === 'string' ? data : decoder.decode(data))
|
|
108
112
|
}
|
|
109
113
|
}
|
|
110
114
|
|
|
@@ -113,7 +117,12 @@ Connection.prototype.send = function (message) {
|
|
|
113
117
|
|
|
114
118
|
if (message.length > maxPacketSize) {
|
|
115
119
|
const err = new Error(`Packet to big: ${message.length} > ${maxPacketSize}`)
|
|
116
|
-
this._client._$onError(
|
|
120
|
+
this._client._$onError(
|
|
121
|
+
C.TOPIC.CONNECTION,
|
|
122
|
+
C.EVENT.CONNECTION_ERROR,
|
|
123
|
+
err,
|
|
124
|
+
message.split(C.MESSAGE_PART_SEPERATOR).map((x) => x.slice(0, 256))
|
|
125
|
+
)
|
|
117
126
|
return false
|
|
118
127
|
}
|
|
119
128
|
|
|
@@ -161,10 +170,10 @@ Connection.prototype._sendAuthParams = function () {
|
|
|
161
170
|
this._setState(C.CONNECTION_STATE.AUTHENTICATING)
|
|
162
171
|
const authMessage = messageBuilder.getMsg(C.TOPIC.AUTH, C.ACTIONS.REQUEST, [
|
|
163
172
|
this._authParams,
|
|
164
|
-
'
|
|
173
|
+
'24.3.1', // TODO (fix): How to read from package.json?
|
|
165
174
|
utils.isNode
|
|
166
175
|
? `Node/${process.version}`
|
|
167
|
-
: globalThis.navigator && globalThis.navigator.userAgent
|
|
176
|
+
: globalThis.navigator && globalThis.navigator.userAgent
|
|
168
177
|
])
|
|
169
178
|
this._submit(authMessage)
|
|
170
179
|
}
|
|
@@ -206,60 +215,13 @@ Connection.prototype._onClose = function () {
|
|
|
206
215
|
}
|
|
207
216
|
}
|
|
208
217
|
|
|
209
|
-
Connection.prototype._onMessage = function (
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
raw = new Uint8Array(raw)
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const len = raw.byteLength
|
|
217
|
-
|
|
218
|
-
const start = 0
|
|
219
|
-
|
|
220
|
-
let pos = start
|
|
221
|
-
|
|
222
|
-
let headerSize = 0
|
|
223
|
-
if (raw[pos] >= 128) {
|
|
224
|
-
headerSize = raw[pos] - 128
|
|
225
|
-
pos += headerSize
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// TODO (perf): Use numbers instead of string..
|
|
229
|
-
const topic = String.fromCharCode(raw[pos++])
|
|
230
|
-
pos++
|
|
231
|
-
|
|
232
|
-
let action = ''
|
|
233
|
-
while (pos < len && raw[pos] !== 31) {
|
|
234
|
-
// TODO (perf): Use numbers instead of string..
|
|
235
|
-
action += String.fromCharCode(raw[pos++])
|
|
236
|
-
}
|
|
237
|
-
pos++
|
|
238
|
-
|
|
239
|
-
// TODO (fix): Validate topic and action
|
|
240
|
-
|
|
241
|
-
let data
|
|
242
|
-
|
|
243
|
-
// TODO (fix): Don't stringify binary data...
|
|
244
|
-
|
|
245
|
-
if (headerSize > 0) {
|
|
246
|
-
data = []
|
|
247
|
-
let headerPos = start + 1
|
|
248
|
-
while (headerPos < headerSize) {
|
|
249
|
-
const len = varint.decode(raw, headerPos)
|
|
250
|
-
headerPos += varint.decode.bytes
|
|
251
|
-
if (len === 0) {
|
|
252
|
-
break
|
|
253
|
-
}
|
|
254
|
-
data.push(this._decoder.decode(raw.subarray(pos, pos + len - 1)))
|
|
255
|
-
pos += len
|
|
256
|
-
}
|
|
257
|
-
} else {
|
|
258
|
-
data =
|
|
259
|
-
pos < len ? this._decoder.decode(raw.subarray(pos, len)).split(C.MESSAGE_PART_SEPERATOR) : []
|
|
218
|
+
Connection.prototype._onMessage = function (data) {
|
|
219
|
+
// Remove MESSAGE_SEPERATOR if exists.
|
|
220
|
+
if (data.charCodeAt(data.length - 1) === 30) {
|
|
221
|
+
data = data.slice(0, -1)
|
|
260
222
|
}
|
|
261
223
|
|
|
262
|
-
this._recvQueue.push(
|
|
224
|
+
this._recvQueue.push(data)
|
|
263
225
|
if (!this._processingRecv) {
|
|
264
226
|
this._processingRecv = true
|
|
265
227
|
this._schedule(this._recvMessages)
|
|
@@ -283,19 +245,20 @@ Connection.prototype._recvMessages = function (deadline) {
|
|
|
283
245
|
continue
|
|
284
246
|
}
|
|
285
247
|
|
|
286
|
-
if (
|
|
287
|
-
this.
|
|
288
|
-
continue
|
|
248
|
+
if (this._logger) {
|
|
249
|
+
this._logger.trace(message, 'receive')
|
|
289
250
|
}
|
|
290
251
|
|
|
291
|
-
|
|
252
|
+
messageParser.parseMessage(message, this._client, this._message)
|
|
253
|
+
|
|
254
|
+
this.emit('recv', this._message)
|
|
292
255
|
|
|
293
|
-
if (
|
|
294
|
-
this._handleConnectionResponse(
|
|
295
|
-
} else if (
|
|
296
|
-
this._handleAuthResponse(
|
|
256
|
+
if (this._message.topic === C.TOPIC.CONNECTION) {
|
|
257
|
+
this._handleConnectionResponse(this._message)
|
|
258
|
+
} else if (this._message.topic === C.TOPIC.AUTH) {
|
|
259
|
+
this._handleAuthResponse(this._message)
|
|
297
260
|
} else {
|
|
298
|
-
this._client._$onMessage(
|
|
261
|
+
this._client._$onMessage(this._message)
|
|
299
262
|
}
|
|
300
263
|
}
|
|
301
264
|
|
|
@@ -313,7 +276,7 @@ Connection.prototype._handleConnectionResponse = function (message) {
|
|
|
313
276
|
} else if (message.action === C.ACTIONS.CHALLENGE) {
|
|
314
277
|
this._setState(C.CONNECTION_STATE.CHALLENGING)
|
|
315
278
|
this._submit(
|
|
316
|
-
messageBuilder.getMsg(C.TOPIC.CONNECTION, C.ACTIONS.CHALLENGE_RESPONSE, [this._url])
|
|
279
|
+
messageBuilder.getMsg(C.TOPIC.CONNECTION, C.ACTIONS.CHALLENGE_RESPONSE, [this._url])
|
|
317
280
|
)
|
|
318
281
|
} else if (message.action === C.ACTIONS.REJECTION) {
|
|
319
282
|
this._challengeDenied = true
|
|
@@ -352,7 +315,7 @@ Connection.prototype._getAuthData = function (data) {
|
|
|
352
315
|
if (data === undefined) {
|
|
353
316
|
return null
|
|
354
317
|
} else {
|
|
355
|
-
return convertTyped(data, this._client)
|
|
318
|
+
return messageParser.convertTyped(data, this._client)
|
|
356
319
|
}
|
|
357
320
|
}
|
|
358
321
|
|
|
@@ -388,8 +351,8 @@ Connection.prototype._tryReconnect = function () {
|
|
|
388
351
|
},
|
|
389
352
|
Math.min(
|
|
390
353
|
this._options.maxReconnectInterval,
|
|
391
|
-
this._options.reconnectIntervalIncrement * this._reconnectionAttempt
|
|
392
|
-
)
|
|
354
|
+
this._options.reconnectIntervalIncrement * this._reconnectionAttempt
|
|
355
|
+
)
|
|
393
356
|
)
|
|
394
357
|
this._reconnectionAttempt++
|
|
395
358
|
} else {
|
|
@@ -9,7 +9,7 @@ let poolBuffer
|
|
|
9
9
|
let poolView
|
|
10
10
|
let poolOffset
|
|
11
11
|
|
|
12
|
-
function allocPool(size) {
|
|
12
|
+
function allocPool (size) {
|
|
13
13
|
poolSize = size || poolSize || 1024 * 1024
|
|
14
14
|
poolBuffer = utils.isNode
|
|
15
15
|
? globalThis.Buffer.allocUnsafe(poolSize)
|
|
@@ -18,7 +18,7 @@ function allocPool(size) {
|
|
|
18
18
|
poolOffset = 0
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function alignPool() {
|
|
21
|
+
function alignPool () {
|
|
22
22
|
// Ensure aligned slices
|
|
23
23
|
if (poolOffset & 0x7) {
|
|
24
24
|
poolOffset |= 0x7
|
|
@@ -26,7 +26,7 @@ function alignPool() {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function writeString(dst, str, offset) {
|
|
29
|
+
function writeString (dst, str, offset) {
|
|
30
30
|
if (utils.isNode) {
|
|
31
31
|
return dst.write(str, offset)
|
|
32
32
|
} else {
|
|
@@ -35,7 +35,7 @@ function writeString(dst, str, offset) {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export function getMsg(topic, action, data) {
|
|
38
|
+
export function getMsg (topic, action, data) {
|
|
39
39
|
if (data && !(data instanceof Array)) {
|
|
40
40
|
throw new Error('data must be an array')
|
|
41
41
|
}
|
|
@@ -79,6 +79,7 @@ export function getMsg(topic, action, data) {
|
|
|
79
79
|
} else {
|
|
80
80
|
throw new Error('invalid data')
|
|
81
81
|
}
|
|
82
|
+
|
|
82
83
|
poolOffset += len
|
|
83
84
|
|
|
84
85
|
varint.encode(len + 1, poolBuffer, headerPos)
|
|
@@ -97,7 +98,7 @@ export function getMsg(topic, action, data) {
|
|
|
97
98
|
return new Uint8Array(poolBuffer.buffer, start, poolOffset - start)
|
|
98
99
|
}
|
|
99
100
|
|
|
100
|
-
export function typed(value) {
|
|
101
|
+
export function typed (value) {
|
|
101
102
|
const type = typeof value
|
|
102
103
|
|
|
103
104
|
if (type === 'string') {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import * as C from '../constants/constants.js'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const MessageParser = function () {
|
|
4
|
+
this._actions = this._getActions()
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
MessageParser.prototype.convertTyped = function (value, client) {
|
|
4
8
|
const type = value.charAt(0)
|
|
5
9
|
|
|
6
10
|
if (type === C.TYPES.STRING) {
|
|
@@ -40,3 +44,48 @@ export function convertTyped(value, client) {
|
|
|
40
44
|
|
|
41
45
|
return undefined
|
|
42
46
|
}
|
|
47
|
+
|
|
48
|
+
MessageParser.prototype._getActions = function () {
|
|
49
|
+
const actions = {}
|
|
50
|
+
|
|
51
|
+
for (const key in C.ACTIONS) {
|
|
52
|
+
actions[C.ACTIONS[key]] = key
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return actions
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
MessageParser.prototype.parseMessage = function (message, client, result) {
|
|
59
|
+
const parts = message.split(C.MESSAGE_PART_SEPERATOR)
|
|
60
|
+
|
|
61
|
+
if (parts.length < 2) {
|
|
62
|
+
client._$onError(
|
|
63
|
+
C.TOPIC.ERROR,
|
|
64
|
+
C.EVENT.MESSAGE_PARSE_ERROR,
|
|
65
|
+
new Error('Insufficiant message parts')
|
|
66
|
+
)
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (parts[0] === C.TOPIC.ERROR) {
|
|
71
|
+
client._$onError(C.TOPIC.ERROR, parts[1], new Error('Message error'), message)
|
|
72
|
+
return null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (this._actions[parts[1]] === undefined) {
|
|
76
|
+
client._$onError(
|
|
77
|
+
C.TOPIC.ERROR,
|
|
78
|
+
C.EVENT.MESSAGE_PARSE_ERROR,
|
|
79
|
+
new Error('Unknown action'),
|
|
80
|
+
message
|
|
81
|
+
)
|
|
82
|
+
return null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
result.raw = message
|
|
86
|
+
result.topic = parts[0]
|
|
87
|
+
result.action = parts[1]
|
|
88
|
+
result.data = parts.splice(2)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export default new MessageParser()
|