@nxtedition/deepstream.io-client-js 23.4.48 → 23.4.50

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": "23.4.48",
3
+ "version": "23.4.50",
4
4
  "description": "the javascript client for deepstream.io",
5
5
  "homepage": "http://deepstream.io",
6
6
  "bugs": {
@@ -10,13 +10,11 @@ module.exports.CONNECTION_STATE.ERROR = 'ERROR'
10
10
  module.exports.CONNECTION_STATE.RECONNECTING = 'RECONNECTING'
11
11
 
12
12
  module.exports.RECORD_STATE = {}
13
- module.exports.RECORD_STATE.INIT = -1
14
13
  module.exports.RECORD_STATE.VOID = 0
15
14
  module.exports.RECORD_STATE.CLIENT = 1
16
- module.exports.RECORD_STATE.PENDING = 2
17
- module.exports.RECORD_STATE.SERVER = 3
18
- module.exports.RECORD_STATE.STALE = 4
19
- module.exports.RECORD_STATE.PROVIDER = 5
15
+ module.exports.RECORD_STATE.SERVER = 2
16
+ module.exports.RECORD_STATE.STALE = 3
17
+ module.exports.RECORD_STATE.PROVIDER = 4
20
18
 
21
19
  module.exports.RECORD_STATE_NAME = []
22
20
  for (const [key, val] of Object.entries(module.exports.RECORD_STATE)) {
@@ -35,6 +35,7 @@ class RecordHandler {
35
35
  }
36
36
 
37
37
  this._syncEmitter = new EventEmitter()
38
+ this._pendingEmitter = new EventEmitter()
38
39
 
39
40
  this.set = this.set.bind(this)
40
41
  this.get = this.get.bind(this)
@@ -49,52 +50,41 @@ class RecordHandler {
49
50
 
50
51
  this._pruningTimeout = null
51
52
 
52
- // TODO (perf): schedule & yield to avoid blocking event loop?
53
53
  const _prune = () => {
54
54
  const pruning = this._pruning
55
-
56
55
  this._pruning = new Set()
57
- for (const rec of pruning) {
58
- invariant(rec.refs === 0, 'cannot prune referenced record')
59
- invariant(!this._pending.has(rec), 'cannot prune pending record')
60
56
 
57
+ for (const rec of pruning) {
61
58
  rec._$dispose()
62
59
  this._records.delete(rec.name)
63
60
  this._stats.destroyed++
64
61
  }
65
62
 
66
- if (this._pruningTimeout && this._pruningTimeout.refresh) {
63
+ if (this._pruningTimeout) {
67
64
  this._pruningTimeout.refresh()
68
65
  } else {
69
- this._pruningTimeout = setTimeout(_prune, 1e3)
70
- if (this._pruningTimeout.unref) {
71
- this._pruningTimeout.unref()
72
- }
66
+ this._pruningTimeout = timers.setTimeout(_prune, 1e3)
73
67
  }
74
68
  }
75
69
 
76
70
  _prune()
77
71
  }
78
72
 
79
- _onRef(rec) {
80
- if (rec.refs === 0) {
73
+ _onPruning(rec, isPruning) {
74
+ if (isPruning) {
81
75
  this._pruning.add(rec)
82
- } else if (rec.refs === 1) {
76
+ } else {
83
77
  this._pruning.delete(rec)
84
78
  }
85
79
  }
86
80
 
87
- _onState(rec, prevState) {
88
- if (
89
- rec.state < Record.STATE.SERVER &&
90
- (prevState === Record.STATE.INIT || prevState >= Record.STATE.SERVER)
91
- ) {
92
- rec.ref()
81
+ _onPending(rec, isPending) {
82
+ if (isPending) {
93
83
  this._pending.add(rec)
94
- } else if (prevState >= Record.STATE.SERVER) {
95
- rec.unref()
84
+ } else {
96
85
  this._pending.delete(rec)
97
86
  }
87
+ this._pendingEmitter.emit(rec.name, isPending)
98
88
  }
99
89
 
100
90
  get connected() {
@@ -120,7 +110,7 @@ class RecordHandler {
120
110
  let record = this._records.get(name)
121
111
 
122
112
  if (!record) {
123
- record = new Record(name, this)
113
+ record = new Record(name, this).ref()
124
114
  this._records.set(name, record)
125
115
  this._stats.created++
126
116
  } else {
@@ -163,7 +153,7 @@ class RecordHandler {
163
153
 
164
154
  sync() {
165
155
  return new Promise((resolve) => {
166
- let counter = 0
156
+ let counter = this._pending.size
167
157
 
168
158
  const maybeSync = () => {
169
159
  if (counter > 0) {
@@ -178,24 +168,12 @@ class RecordHandler {
178
168
  }
179
169
  }
180
170
 
181
- const onUpdate = (rec) => {
182
- if (rec.state < C.RECORD_STATE.SERVER) {
183
- return
184
- }
185
-
186
- rec.unsubscribe(onUpdate)
187
- rec.unref()
188
- counter -= 1
189
-
190
- maybeSync()
191
- }
192
-
193
171
  for (const rec of this._pending) {
194
- if (rec.state < C.RECORD_STATE.SERVER) {
195
- rec.subscribe(onUpdate)
196
- rec.ref()
197
- counter += 1
198
- }
172
+ this._pendingEmitter.once(rec.name, (isPending) => {
173
+ invariant(!isPending, 'unexpected pending state')
174
+ counter -= 1
175
+ maybeSync()
176
+ })
199
177
  }
200
178
 
201
179
  maybeSync()
@@ -347,7 +325,7 @@ class RecordHandler {
347
325
  const record = this.getRecord(name).subscribe(onUpdate)
348
326
 
349
327
  if (timeoutValue && state && record.state < state) {
350
- timeoutHandle = timers.setTimeout(() => {
328
+ timeoutHandle = timers.timers.setTimeout(() => {
351
329
  const expected = C.RECORD_STATE_NAME[state]
352
330
  const current = C.RECORD_STATE_NAME[record.state]
353
331
  o.error(
@@ -16,16 +16,12 @@ class Record {
16
16
  this._version = ''
17
17
  this._data = jsonPath.EMPTY
18
18
  this._state = Record.STATE.VOID
19
- this._refs = 1
20
- this._subscribed = false
19
+ this._refs = 0
21
20
  this._subscriptions = []
22
21
  this._subscriptionsEmitting = false
23
22
  this._updating = null
24
23
  this._patches = null
25
-
26
- this._handler._onState(this, Record.STATE.INIT)
27
-
28
- this._subscribe()
24
+ this._subscribed = false
29
25
  }
30
26
 
31
27
  get name() {
@@ -49,26 +45,27 @@ class Record {
49
45
  }
50
46
 
51
47
  ref() {
52
- const prevRefs = this._refs
53
-
54
48
  this._refs += 1
55
- if (this._refs === 1 && !this._subscribed) {
56
- this._subscribe()
49
+ if (this._refs === 1) {
50
+ this._handler._onPruning(this, false)
57
51
  }
58
52
 
59
- this._handler._onRef(this, prevRefs)
53
+ if (this._refs === 1 && this._handler._connection.connected && !this._subscribed) {
54
+ this._subscribe()
55
+ }
60
56
 
61
57
  return this
62
58
  }
63
59
 
64
60
  unref() {
65
61
  invariant(this._refs > 0, 'missing refs')
66
-
67
- const prevRefs = this._refs
62
+ invariant(this._refs > 1 || !this._patches, 'must not have patches')
63
+ invariant(this._refs > 1 || this._state >= Record.STATE.SERVER, 'must be ready')
68
64
 
69
65
  this._refs -= 1
70
-
71
- this._handler._onRef(this, prevRefs)
66
+ if (this._refs === 0) {
67
+ this._handler._onPruning(this, true)
68
+ }
72
69
 
73
70
  return this
74
71
  }
@@ -103,7 +100,6 @@ class Record {
103
100
  }
104
101
 
105
102
  set(pathOrData, dataOrNil) {
106
- const prevState = this._state
107
103
  const prevData = this._data
108
104
  const prevVersion = this._version
109
105
 
@@ -131,15 +127,20 @@ class Record {
131
127
  throw new Error('invalid argument: path')
132
128
  }
133
129
 
134
- this._update(jsonPath.set(this._data, path, data, false))
135
-
136
130
  if (this._state < Record.STATE.SERVER) {
137
- this._patches = path && this._patches ? this._patches : []
131
+ if (!this._patches) {
132
+ this.ref()
133
+ this._patches = []
134
+ } else if (path) {
135
+ this._patches.splice(0)
136
+ }
137
+
138
138
  this._patches.push(path, cloneDeep(data))
139
- this._state = Record.STATE.PENDING
140
- this._handler._onState(this, prevState)
141
- this._emitUpdate()
142
- } else if (this._data !== prevData || this._version !== prevVersion) {
139
+ } else {
140
+ this._update(jsonPath.set(this._data, path, data, false))
141
+ }
142
+
143
+ if (this._data !== prevData || this._version !== prevVersion) {
143
144
  this._emitUpdate()
144
145
  }
145
146
  }
@@ -228,93 +229,97 @@ class Record {
228
229
  }
229
230
 
230
231
  _$onConnectionStateChange() {
231
- const connection = this._handler._connection
232
- if (connection.connected) {
233
- invariant(!this._subscribed, 'must not be subscribed')
234
-
232
+ if (this._handler._connection.connected) {
235
233
  if (this._refs > 0) {
236
234
  this._subscribe()
237
235
  }
238
236
 
239
237
  if (this._updating) {
240
238
  for (const update of this._updating.values()) {
241
- connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UPDATE, update)
239
+ this._handler._connection.connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UPDATE, update)
242
240
  }
243
241
  }
244
242
  } else {
245
- this._unsubscribe()
243
+ this._subscribed = false
246
244
  }
247
- }
248
245
 
249
- _$dispose() {
250
- this._unsubscribe()
246
+ if (this._state > Record.STATE.CLIENT) {
247
+ this._state = Record.STATE.CLIENT
248
+ this._emitUpdate()
249
+ }
251
250
  }
252
251
 
253
252
  _subscribe() {
254
- const connection = this._handler._connection
253
+ invariant(this._handler._connection.connected, 'must be connected')
255
254
 
256
- invariant(this._refs, 'missing refs')
257
- invariant(!this._subscribed, 'must not be subscribed')
255
+ this._handler._connection.sendMsg1(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, this._name)
256
+ this._subscribed = true
258
257
 
259
- if (!this._subscribed && connection.connected) {
260
- connection.sendMsg1(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, this._name)
261
- this._subscribed = true
262
- }
258
+ this.ref()
259
+ this._handler._onPending(this, true)
263
260
  }
264
261
 
265
- _unsubscribe() {
266
- const prevState = this._state
267
- const connection = this._handler._connection
268
-
269
- invariant(!connection.connected || !this._refs, 'must not have refs')
270
- invariant(!connection.connected || !this._patches, 'must not have patches')
271
- invariant(!connection.connected || this._subscribed, 'must be subscribed')
272
-
273
- if (this._subscribed && connection.connected) {
274
- connection.sendMsg1(C.TOPIC.RECORD, C.ACTIONS.UNSUBSCRIBE, this._name)
262
+ _$dispose() {
263
+ invariant(!this._refs, 'must not have refs')
264
+ invariant(!this._patches, 'must not have patches')
265
+ invariant(!this._updating, 'must not have updates')
266
+ invariant(this.state >= C.RECORD_STATE.SERVER, 'must not be pending')
267
+ invariant(
268
+ !this._subscribed || this._handler._connection.connected,
269
+ 'must be unsubscribed or connected'
270
+ )
271
+
272
+ if (this._subscribed) {
273
+ this._handler._connection.sendMsg1(C.TOPIC.RECORD, C.ACTIONS.UNSUBSCRIBE, this._name)
275
274
  this._subscribed = false
276
275
  }
277
276
 
278
- if (this._state > Record.STATE.CLIENT) {
279
- this._state = Record.STATE.CLIENT
280
- this._handler._onState(this, prevState)
277
+ if (this._state > C.RECORD_STATE.CLIENT) {
278
+ this._state = C.RECORD_STATE.CLIENT
281
279
  this._emitUpdate()
282
280
  }
283
281
  }
284
282
 
285
283
  _update(nextData) {
284
+ invariant(this._version, 'must have version')
285
+
286
286
  if (nextData === this._data) {
287
- return false
287
+ return
288
288
  }
289
289
 
290
- this._data = nextData
290
+ const prevVersion = this._version
291
+ const nextVersion = this._makeVersion(parseInt(prevVersion) + 1)
291
292
 
292
- if (this._version) {
293
- const prevVersion = this._version
294
- const nextVersion = this._makeVersion(parseInt(prevVersion) + 1)
295
- this._version = nextVersion
293
+ const update = [this._name, nextVersion, jsonPath.stringify(nextData), prevVersion]
296
294
 
297
- const update = [this._name, nextVersion, jsonPath.stringify(nextData), prevVersion]
298
- this._updating ??= new Map()
299
- this._updating.set(nextVersion, update)
300
- this._handler._stats.updating += 1
295
+ if (!this._updating) {
296
+ this._updating = new Map()
297
+ this.ref()
298
+ }
301
299
 
302
- const connection = this._handler._connection
303
- if (connection.connected) {
304
- connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UPDATE, update)
305
- }
300
+ this._updating.set(nextVersion, update)
301
+ this._handler._stats.updating += 1
302
+
303
+ const connection = this._handler._connection
304
+ if (connection.connected) {
305
+ connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UPDATE, update)
306
306
  }
307
307
 
308
- return true
308
+ this._data = nextData
309
+ this._version = nextVersion
309
310
  }
310
311
 
311
312
  _onUpdate([, version, data]) {
312
- const prevState = this._state
313
313
  const prevData = this._data
314
314
  const prevVersion = this._version
315
+ const prevState = this._state
315
316
 
316
317
  if (this._updating?.delete(version)) {
317
318
  this._handler._stats.updating -= 1
319
+ if (this._updating.size === 0) {
320
+ this._updating = null
321
+ this.unref()
322
+ }
318
323
  }
319
324
 
320
325
  if (
@@ -329,20 +334,26 @@ class Record {
329
334
  invariant(this._version, 'must have version')
330
335
  invariant(this._data, 'must have data')
331
336
 
332
- if (this._patches && this._version.charAt(0) !== 'I') {
333
- let patchData = this._data
334
- for (let n = 0; n < this._patches.length; n += 2) {
335
- patchData = jsonPath.set(patchData, this._patches[n + 0], this._patches[n + 1], false)
337
+ if (this._patches) {
338
+ if (this._version.charAt(0) !== 'I') {
339
+ let patchData = this._data
340
+ for (let n = 0; n < this._patches.length; n += 2) {
341
+ patchData = jsonPath.set(patchData, this._patches[n + 0], this._patches[n + 1], false)
342
+ }
343
+ this._update(patchData)
336
344
  }
337
- this._update(patchData)
345
+
346
+ this._patches = null
347
+ this.unref()
338
348
  }
339
- this._patches = null
340
349
 
341
- if (this._state < Record.STATE.SERVER) {
342
- this._state = this._version.charAt(0) === 'I' ? Record.STATE.STALE : Record.STATE.SERVER
343
- this._handler._onState(this, prevState)
344
- this._emitUpdate()
345
- } else if (this._data !== prevData || this._version !== prevVersion) {
350
+ if (this._state < C.RECORD_STATE.SERVER) {
351
+ this._state = this._version.charAt(0) === 'I' ? C.RECORD_STATE.STALE : C.RECORD_STATE.SERVER
352
+ this._handler._onPending(this, false)
353
+ this.unref()
354
+ }
355
+
356
+ if (this._state !== prevState || this._data !== prevData || this._version !== prevVersion) {
346
357
  this._emitUpdate()
347
358
  }
348
359
  }
@@ -353,14 +364,13 @@ class Record {
353
364
  this._state =
354
365
  hasProvider &&
355
366
  messageParser.convertTyped(hasProvider, this._handler._client) &&
356
- this._state >= Record.STATE.SERVER
357
- ? Record.STATE.PROVIDER
367
+ this._state >= C.RECORD_STATE.SERVER
368
+ ? C.RECORD_STATE.PROVIDER
358
369
  : this._version.charAt(0) === 'I'
359
- ? Record.STATE.STALE
360
- : Record.STATE.SERVER
370
+ ? C.RECORD_STATE.STALE
371
+ : C.RECORD_STATE.SERVER
361
372
 
362
373
  if (this._state !== prevState) {
363
- this._handler._onState(this, prevState)
364
374
  this._emitUpdate()
365
375
  }
366
376
  }
@@ -387,7 +397,14 @@ class Record {
387
397
  this._subscriptionsEmitting = true
388
398
  try {
389
399
  for (const fn of this._subscriptions) {
390
- fn(this)
400
+ try {
401
+ fn(this)
402
+ } catch (err) {
403
+ this._error(
404
+ C.EVENT.USER_ERROR,
405
+ Object.assign(new Error('user callback failed'), { cause: err })
406
+ )
407
+ }
391
408
  }
392
409
  } finally {
393
410
  this._subscriptionsEmitting = false