@nxtedition/deepstream.io-client-js 23.4.47 → 23.4.49

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