@nxtedition/deepstream.io-client-js 26.0.3 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/deepstream.io-client-js",
3
- "version": "26.0.3",
3
+ "version": "26.0.4",
4
4
  "description": "the javascript client for deepstream.io",
5
5
  "homepage": "http://deepstream.io",
6
6
  "type": "module",
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
 
@@ -5,5 +5,5 @@ export default {
5
5
  maxPacketSize: 1024 * 1024,
6
6
  batchSize: 4096,
7
7
  schedule: null,
8
- logger: null,
8
+ logger: null
9
9
  }
@@ -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 { convertTyped } from '../message/message-parser.js'
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 { convertTyped } from './message-parser.js'
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._decoder = new TextDecoder()
28
- this._encoder = new TextEncoder()
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(C.TOPIC.CONNECTION, C.EVENT.CONNECTION_ERROR, err, message)
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
- '26.0.0', // TODO (fix): How to read from package.json?
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 (raw) {
210
- if (typeof raw === 'string') {
211
- raw = this._encoder.encode(raw)
212
- } else if (!utils.isNode) {
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({ topic, action, data })
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 (message === C.TOPIC.ERROR) {
287
- this._client._$onError(C.TOPIC.ERROR, message.action, new Error('Message error'), message)
288
- continue
248
+ if (this._logger) {
249
+ this._logger.trace(message, 'receive')
289
250
  }
290
251
 
291
- this.emit('recv', message)
252
+ messageParser.parseMessage(message, this._client, this._message)
253
+
254
+ this.emit('recv', this._message)
292
255
 
293
- if (message.topic === C.TOPIC.CONNECTION) {
294
- this._handleConnectionResponse(message)
295
- } else if (message.topic === C.TOPIC.AUTH) {
296
- this._handleAuthResponse(message)
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(message)
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
- export function convertTyped(value, client) {
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()