@nxtedition/deepstream.io-client-js 23.4.48 → 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.48",
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,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,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