@nxtedition/deepstream.io-client-js 32.0.17 → 32.0.19

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": "32.0.17",
3
+ "version": "32.0.19",
4
4
  "description": "the javascript client for deepstream.io",
5
5
  "homepage": "http://deepstream.io",
6
6
  "type": "module",
@@ -1,7 +1,7 @@
1
1
  import * as C from '../constants/constants.js'
2
2
  import * as messageBuilder from '../message/message-builder.js'
3
3
  import * as messageParser from '../message/message-parser.js'
4
- import LegacyListener from '../utils/legacy-listener.js'
4
+ import MulticastListener from '../utils/multicast-listener.js'
5
5
  import UnicastListener from '../utils/unicast-listener.js'
6
6
  import EventEmitter from 'component-emitter2'
7
7
  import * as rxjs from 'rxjs'
@@ -126,7 +126,7 @@ EventHandler.prototype.provide = function (pattern, callback, options) {
126
126
  const listener =
127
127
  options.mode?.toLowerCase() === 'unicast'
128
128
  ? new UnicastListener(C.TOPIC.EVENT, pattern, callback, this, options)
129
- : new LegacyListener(C.TOPIC.EVENT, pattern, callback, this, options)
129
+ : new MulticastListener(C.TOPIC.EVENT, pattern, callback, this, options)
130
130
 
131
131
  this._listeners.set(pattern, listener)
132
132
  return () => {
@@ -1,5 +1,5 @@
1
1
  import Record from './record.js'
2
- import LegacyListener from '../utils/legacy-listener.js'
2
+ import MulticastListener from '../utils/multicast-listener.js'
3
3
  import UnicastListener from '../utils/unicast-listener.js'
4
4
  import * as C from '../constants/constants.js'
5
5
  import * as rxjs from 'rxjs'
@@ -35,7 +35,7 @@ const GET2_DEFAULTS = {
35
35
 
36
36
  function onSync(subscription) {
37
37
  subscription.synced = true
38
- onUpdate(null, subscription)
38
+ onUpdate(subscription.record, subscription)
39
39
  }
40
40
 
41
41
  function onUpdate(record, subscription) {
@@ -84,18 +84,9 @@ function onTimeout(subscription) {
84
84
 
85
85
  subscription.subscriber.error(
86
86
  Object.assign(
87
- new Error(
88
- !subscription.synced
89
- ? `timeout sync: ${subscription.record.name}`
90
- : `timeout state: ${subscription.record.name} [${current}<${expected}]`,
91
- ),
87
+ new Error(`timeout state: ${subscription.record.name} [${current}<${expected}]`),
92
88
  {
93
89
  code: 'ETIMEDOUT',
94
- timeout: subscription.timeoutValue,
95
- expected,
96
- current,
97
- synced: subscription.synced,
98
- name: subscription.record.name,
99
90
  },
100
91
  ),
101
92
  )
@@ -119,12 +110,16 @@ class RecordHandler {
119
110
 
120
111
  this._connected = 0
121
112
  this._stats = {
113
+ updating: 0,
122
114
  created: 0,
123
115
  destroyed: 0,
116
+ records: 0,
117
+ pruning: 0,
118
+ patching: 0,
124
119
  }
125
120
 
126
121
  this._syncQueue = []
127
- this._syncMap = new Map()
122
+ this._syncMap = {}
128
123
 
129
124
  this.set = this.set.bind(this)
130
125
  this.get = this.get.bind(this)
@@ -146,6 +141,8 @@ class RecordHandler {
146
141
  this._records.delete(rec.name)
147
142
  }
148
143
 
144
+ this._stats.pruning -= pruning.size
145
+ this._stats.records -= pruning.size
149
146
  this._stats.destroyed += pruning.size
150
147
 
151
148
  this._pruningTimeout.refresh()
@@ -155,6 +152,12 @@ class RecordHandler {
155
152
  }
156
153
 
157
154
  _onPruning(rec, value) {
155
+ if (value) {
156
+ this._stats.pruning += 1
157
+ } else {
158
+ this._stats.pruning -= 1
159
+ }
160
+
158
161
  if (value) {
159
162
  this._pruning.add(rec)
160
163
  } else {
@@ -167,9 +170,12 @@ class RecordHandler {
167
170
 
168
171
  if (value) {
169
172
  invariant(!callbacks, 'updating callbacks must not exist')
173
+ this._stats.updating += 1
170
174
  this._updating.set(rec, [])
171
175
  } else {
172
176
  invariant(callbacks, 'updating callbacks must exist')
177
+
178
+ this._stats.updating -= 1
173
179
  this._updating.delete(rec)
174
180
  for (const callback of callbacks) {
175
181
  callback()
@@ -179,8 +185,11 @@ class RecordHandler {
179
185
 
180
186
  _onPatching(rec, value) {
181
187
  if (value) {
188
+ this._stats.patching += 1
182
189
  this._patching.set(rec, [])
183
190
  } else {
191
+ this._stats.patching -= 1
192
+
184
193
  const callbacks = this._patching.get(rec)
185
194
  this._patching.delete(rec)
186
195
  for (const callback of callbacks) {
@@ -202,10 +211,6 @@ class RecordHandler {
202
211
  return {
203
212
  ...this._stats,
204
213
  subscriptions,
205
- updating: this._updating.size,
206
- patching: this._patching.size,
207
- putting: this._putting.size,
208
- pruning: this._pruning.size,
209
214
  }
210
215
  }
211
216
 
@@ -214,23 +219,19 @@ class RecordHandler {
214
219
  * @returns {Record}
215
220
  */
216
221
  getRecord(name) {
217
- if (typeof name !== 'string' || name.length === 0) {
218
- throw new Error('invalid argument: name')
219
- }
220
-
221
- if (name.startsWith('null') || name.startsWith('undefined') || name === '[object Object]') {
222
- this._client._$onError(
223
- C.TOPIC.RECORD,
224
- C.EVENT.USER_ERROR,
225
- 'name should not start with null or undefined',
226
- name,
227
- )
228
- }
222
+ invariant(
223
+ typeof name === 'string' &&
224
+ name.length > 0 &&
225
+ name !== '[object Object]' &&
226
+ name.length <= 4096,
227
+ `invalid name ${name}`,
228
+ )
229
229
 
230
230
  let record = this._records.get(name)
231
231
 
232
232
  if (!record) {
233
233
  record = new Record(name, this)
234
+ this._stats.records += 1
234
235
  this._stats.created += 1
235
236
  this._records.set(name, record)
236
237
  }
@@ -260,7 +261,7 @@ class RecordHandler {
260
261
  const listener =
261
262
  options.mode?.toLowerCase() === 'unicast'
262
263
  ? new UnicastListener(C.TOPIC.RECORD, pattern, callback, this, options)
263
- : new LegacyListener(C.TOPIC.RECORD, pattern, callback, this, options)
264
+ : new MulticastListener(C.TOPIC.RECORD, pattern, callback, this, options)
264
265
 
265
266
  this._stats.listeners += 1
266
267
  this._listeners.set(pattern, listener)
@@ -281,7 +282,7 @@ class RecordHandler {
281
282
  // TODO (perf): Slow implementation...
282
283
 
283
284
  const signal = opts?.signal
284
- const timeout = opts?.timeout ?? 10 * 60e3
285
+ const timeout = opts?.timeout
285
286
 
286
287
  let disposers
287
288
  try {
@@ -297,17 +298,18 @@ class RecordHandler {
297
298
  signalPromise?.catch(noop)
298
299
 
299
300
  if (this._patching.size) {
300
- const promises = []
301
+ let promises
301
302
 
302
303
  {
303
304
  const patchingPromises = []
304
305
  for (const callbacks of this._patching.values()) {
305
306
  patchingPromises.push(new Promise((resolve) => callbacks.push(resolve)))
306
307
  }
308
+ promises ??= []
307
309
  promises.push(Promise.all(patchingPromises))
308
310
  }
309
311
 
310
- if (timeout && promises.length) {
312
+ if (timeout) {
311
313
  promises.push(
312
314
  new Promise((resolve) => {
313
315
  const patchingTimeout = timers.setTimeout(() => {
@@ -324,28 +326,30 @@ class RecordHandler {
324
326
  )
325
327
  }
326
328
 
327
- if (signalPromise && promises.length) {
329
+ if (signalPromise) {
330
+ promises ??= []
328
331
  promises.push(signalPromise)
329
332
  }
330
333
 
331
- if (promises.length) {
334
+ if (promises) {
332
335
  await Promise.race(promises)
333
- signal?.throwIfAborted()
334
336
  }
335
337
  }
336
338
 
337
339
  if (this._updating.size) {
338
- const promises = []
340
+ let promises
339
341
 
340
342
  {
341
343
  const updatingPromises = []
342
344
  for (const callbacks of this._updating.values()) {
343
345
  updatingPromises.push(new Promise((resolve) => callbacks.push(resolve)))
344
346
  }
347
+ promises ??= []
345
348
  promises.push(Promise.all(updatingPromises))
346
349
  }
347
350
 
348
- if (timeout && promises.length) {
351
+ if (timeout) {
352
+ promises ??= []
349
353
  promises.push(
350
354
  new Promise((resolve) => {
351
355
  const updatingTimeout = timers.setTimeout(() => {
@@ -362,22 +366,18 @@ class RecordHandler {
362
366
  )
363
367
  }
364
368
 
365
- if (signalPromise && promises.length) {
366
- promises.push(signalPromise)
367
- }
368
-
369
- if (promises.length) {
369
+ if (promises) {
370
370
  await Promise.race(promises)
371
- signal?.throwIfAborted()
372
371
  }
373
372
  }
374
373
 
375
374
  {
376
- const promises = []
375
+ const syncPromise = new Promise((resolve) => this._sync(resolve))
377
376
 
378
- promises.push(new Promise((resolve) => this._sync(resolve)))
377
+ let promises
379
378
 
380
379
  if (timeout) {
380
+ promises ??= []
381
381
  promises.push(
382
382
  new Promise((resolve, reject) => {
383
383
  const serverTimeout = timers.setTimeout(() => {
@@ -390,12 +390,15 @@ class RecordHandler {
390
390
  }
391
391
 
392
392
  if (signalPromise) {
393
+ promises ??= []
393
394
  promises.push(signalPromise)
394
395
  }
395
396
 
396
- if (promises.length) {
397
+ if (promises) {
398
+ promises.push(syncPromise)
397
399
  await Promise.race(promises)
398
- signal?.throwIfAborted()
400
+ } else {
401
+ await syncPromise
399
402
  }
400
403
  }
401
404
  } finally {
@@ -551,81 +554,76 @@ class RecordHandler {
551
554
  * @returns {rxjs.Observable}
552
555
  */
553
556
  _observe(defaults, name, ...args) {
554
- let path
555
- let state = defaults?.state ?? C.RECORD_STATE.CLIENT
556
- let signal = null
557
- let timeout = defaults?.timeout ?? 0
558
- let dataOnly = defaults?.dataOnly ?? false
559
- let sync = defaults?.sync ?? false
557
+ return new rxjs.Observable((subscriber) => {
558
+ let path
559
+ let state = defaults?.state ?? C.RECORD_STATE.CLIENT
560
+ let signal = null
561
+ let timeout = defaults?.timeout ?? 0
562
+ let dataOnly = defaults?.dataOnly ?? false
563
+ let sync = defaults?.sync ?? false
564
+
565
+ let idx = 0
566
+
567
+ if (
568
+ idx < args.length &&
569
+ (args[idx] == null ||
570
+ typeof args[idx] === 'string' ||
571
+ Array.isArray(args[idx]) ||
572
+ typeof args[idx] === 'function')
573
+ ) {
574
+ path = args[idx++]
575
+ }
560
576
 
561
- let idx = 0
577
+ if (idx < args.length && (args[idx] == null || typeof args[idx] === 'number')) {
578
+ state = args[idx++]
579
+ }
562
580
 
563
- if (
564
- idx < args.length &&
565
- (args[idx] == null ||
566
- typeof args[idx] === 'string' ||
567
- Array.isArray(args[idx]) ||
568
- typeof args[idx] === 'function')
569
- ) {
570
- path = args[idx++]
571
- }
581
+ if (idx < args.length && (args[idx] == null || typeof args[idx] === 'object')) {
582
+ const options = args[idx++] || {}
572
583
 
573
- if (idx < args.length && (args[idx] == null || typeof args[idx] === 'number')) {
574
- state = args[idx++]
575
- }
584
+ if (options.signal !== undefined) {
585
+ signal = options.signal
586
+ }
576
587
 
577
- if (idx < args.length && (args[idx] == null || typeof args[idx] === 'object')) {
578
- const options = args[idx] ?? {}
588
+ if (options.timeout !== undefined) {
589
+ timeout = options.timeout
590
+ }
579
591
 
580
- if (options.signal !== undefined) {
581
- signal = options.signal
582
- }
592
+ if (options.path !== undefined) {
593
+ path = options.path
594
+ }
583
595
 
584
- if (options.timeout !== undefined) {
585
- timeout = options.timeout
586
- }
596
+ if (options.state !== undefined) {
597
+ state = options.state
598
+ }
587
599
 
588
- if (options.path !== undefined) {
589
- path = options.path
590
- }
600
+ if (options.dataOnly !== undefined) {
601
+ dataOnly = options.dataOnly
602
+ }
591
603
 
592
- if (options.state !== undefined) {
593
- state = options.state
604
+ if (options.sync !== undefined) {
605
+ sync = options.sync
606
+ }
594
607
  }
595
608
 
596
- if (options.dataOnly !== undefined) {
597
- dataOnly = options.dataOnly
609
+ if (typeof state === 'string') {
610
+ state = C.RECORD_STATE[state.toUpperCase()]
598
611
  }
599
612
 
600
- if (options.sync !== undefined) {
601
- sync = options.sync
613
+ if (!Number.isInteger(state) || state < 0) {
614
+ throw new Error('invalid argument: state')
602
615
  }
603
- }
604
-
605
- if (typeof state === 'string') {
606
- state = C.RECORD_STATE[state.toUpperCase()]
607
- }
608
-
609
- if (!Number.isInteger(state) || state < 0) {
610
- throw new Error('invalid argument: state')
611
- }
612
616
 
613
- if (!Number.isInteger(timeout) || timeout < 0) {
614
- throw new Error('invalid argument: timeout')
615
- }
616
-
617
- if (typeof dataOnly !== 'boolean') {
618
- throw new Error('invalid argument: dataOnly')
619
- }
617
+ if (!Number.isInteger(timeout) || timeout < 0) {
618
+ throw new Error('invalid argument: timeout')
619
+ }
620
620
 
621
- if (typeof sync !== 'boolean') {
622
- throw new Error('invalid argument: sync')
623
- }
621
+ if (typeof dataOnly !== 'boolean') {
622
+ throw new Error('invalid argument: dataOnly')
623
+ }
624
624
 
625
- return new rxjs.Observable((subscriber) => {
626
- if (signal?.aborted) {
627
- subscriber.error(new utils.AbortError())
628
- return
625
+ if (typeof sync !== 'boolean') {
626
+ throw new Error('invalid argument: sync')
629
627
  }
630
628
 
631
629
  // TODO (perf): Make a class
@@ -653,9 +651,6 @@ class RecordHandler {
653
651
  data: kEmpty,
654
652
  /** @type {boolean} */
655
653
  synced: false,
656
-
657
- index: -1,
658
- onUpdate,
659
654
  }
660
655
 
661
656
  subscriber.add(() => {
@@ -671,7 +666,7 @@ class RecordHandler {
671
666
  }
672
667
 
673
668
  if (subscription.record) {
674
- subscription.record._unobserve(subscription)
669
+ subscription.record.unsubscribe(onUpdate, subscription)
675
670
  subscription.record.unref()
676
671
  subscription.record = null
677
672
  }
@@ -682,11 +677,11 @@ class RecordHandler {
682
677
  utils.addAbortListener(subscription.signal, subscription.abort)
683
678
  }
684
679
 
685
- subscription.record = this.getRecord(name)
686
- subscription.record._observe(subscription)
680
+ subscription.record = this.getRecord(name).subscribe(onUpdate, subscription)
687
681
 
688
682
  if (sync) {
689
- this._sync(onSync, sync, subscription)
683
+ // TODO (fix): What about sync timeout?
684
+ this._sync(onSync, sync === true ? 'WEAK' : sync, subscription)
690
685
  } else {
691
686
  onSync(subscription)
692
687
  }
@@ -707,8 +702,8 @@ class RecordHandler {
707
702
  return true
708
703
  }
709
704
 
710
- const sync = this._syncMap.get(token)
711
- this._syncMap.delete(token)
705
+ const sync = this._syncMap[token]
706
+ delete this._syncMap[token]
712
707
 
713
708
  if (!sync) {
714
709
  return true
@@ -751,10 +746,10 @@ class RecordHandler {
751
746
  this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.PUT, update)
752
747
  }
753
748
 
754
- const syncMap = new Map()
755
- for (const sync of this._syncMap.values()) {
749
+ const syncMap = {}
750
+ for (const sync of Object.values(this._syncMap)) {
756
751
  const token = xuid()
757
- syncMap.set(token, sync)
752
+ syncMap[token] = sync
758
753
  this._connection.sendMsg(
759
754
  C.TOPIC.RECORD,
760
755
  C.ACTIONS.SYNC,
@@ -789,7 +784,7 @@ class RecordHandler {
789
784
  const token = xuid()
790
785
  const queue = this._syncQueue.splice(0)
791
786
 
792
- this._syncMap.set(token, { queue, type })
787
+ this._syncMap[token] = { queue, type }
793
788
  this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SYNC, type ? [token, type] : [token])
794
789
  }, 1)
795
790
  }
@@ -16,17 +16,11 @@ class Record {
16
16
  this._handler = handler
17
17
  this._name = name
18
18
  this._version = ''
19
- this._data = jsonPath.EMPTY_OBJ
19
+ this._data = jsonPath.EMPTY
20
20
  this._state = C.RECORD_STATE.VOID
21
21
  this._refs = 0
22
22
  this._subscriptions = []
23
-
24
- /** @type {Array|null} */
25
- this._emittingArr = null
26
- /** @type {number} */
27
- this._emittingIndex = -1
28
-
29
- this._observers = []
23
+ this._emitting = false
30
24
 
31
25
  /** @type Map? */ this._updating = null
32
26
  /** @type Array? */ this._patching = null
@@ -94,8 +88,9 @@ class Record {
94
88
  * @returns {Record}
95
89
  */
96
90
  subscribe(fn, opaque = null) {
97
- if (this._emittingArr == this._subscriptions) {
91
+ if (this._emitting) {
98
92
  this._subscriptions = this._subscriptions.slice()
93
+ this._emitting = false
99
94
  }
100
95
 
101
96
  this._subscriptions.push(fn, opaque)
@@ -110,8 +105,9 @@ class Record {
110
105
  * @returns {Record}
111
106
  */
112
107
  unsubscribe(fn, opaque = null) {
113
- if (this._emittingArr == this._subscriptions) {
108
+ if (this._emitting) {
114
109
  this._subscriptions = this._subscriptions.slice()
110
+ this._emitting = false
115
111
  }
116
112
 
117
113
  let idx = -1
@@ -132,41 +128,6 @@ class Record {
132
128
  return this
133
129
  }
134
130
 
135
- /**
136
- * @note Subscribers are unordered.
137
- * @param {{ index: number, onUpdate: (Record) => void}} subscription
138
- */
139
- _observe(subscription) {
140
- if (subscription.index != null && subscription.index !== -1) {
141
- throw new Error('already observing')
142
- }
143
-
144
- subscription.index = this._observers.push(subscription) - 1
145
- }
146
-
147
- /**
148
- * @param {{ index: number, onUpdate: (Record) => void}} subscription
149
- */
150
- _unobserve(subscription) {
151
- if (subscription.index == null || subscription.index === -1) {
152
- throw new Error('not observing')
153
- }
154
-
155
- if (this._emittingArr === this._observers) {
156
- // TODO (perf): Shift from start if emitting?
157
- this._observers = this._observers.slice()
158
- }
159
-
160
- const idx = subscription.index
161
- const tmp = this._observers.pop()
162
- subscription.index = -1
163
-
164
- if (tmp !== subscription) {
165
- this._observers[idx] = tmp
166
- this._observers[idx].index = idx
167
- }
168
- }
169
-
170
131
  get(path) {
171
132
  if (!path) {
172
133
  return this._data
@@ -229,7 +190,7 @@ class Record {
229
190
  when(stateOrNil, optionsOrNil) {
230
191
  invariant(this._refs > 0, 'missing refs')
231
192
 
232
- if (stateOrNil != null && typeof stateOrNil === 'object') {
193
+ if (stateOrNil != null && stateOrNil === 'object') {
233
194
  optionsOrNil = stateOrNil
234
195
  stateOrNil = optionsOrNil?.state
235
196
  }
@@ -253,14 +214,8 @@ class Record {
253
214
  }
254
215
 
255
216
  let timeoutHandle
256
- let done = false
257
217
 
258
218
  const onDone = (err) => {
259
- if (done) {
260
- return
261
- }
262
- done = true
263
-
264
219
  if (err) {
265
220
  reject(err)
266
221
  } else {
@@ -560,41 +515,18 @@ class Record {
560
515
  }
561
516
 
562
517
  _emitUpdate() {
563
- if (this._emittingArr != null) {
564
- throw new Error('cannot reenter emitUpdate')
565
- }
518
+ this._emitting = true
566
519
 
567
- try {
568
- const arr = this._subscriptions
569
- const len = arr.length
570
-
571
- this._emittingArr = arr
572
- for (let n = 0; n < len; n += 2) {
573
- this._emittingIndex = n
574
- // TODO (fix): What if this throws?
575
- arr[n + 0](this, arr[n + 1])
576
- }
577
- } finally {
578
- this._emittingArr = null
579
- this._emittingIndex = -1
580
- }
581
-
582
- try {
583
- const arr = this._observers
584
- const len = arr.length
520
+ const arr = this._subscriptions
521
+ const len = arr.length
585
522
 
586
- this._emittingArr = arr
587
- for (let n = 0; n < len; n++) {
588
- this._emittingIndex = n
589
- // TODO (fix): What if this throws?
590
- arr[n].onUpdate(this, arr[n])
591
- }
592
- } finally {
593
- this._emittingArr = null
594
- this._emittingIndex = -1
523
+ for (let n = 0; n < len; n += 2) {
524
+ arr[n + 0](this, arr[n + 1])
595
525
  }
596
526
 
597
527
  this._handler._client.emit('recordUpdated', this)
528
+
529
+ this._emitting = false
598
530
  }
599
531
  }
600
532
 
@@ -1,6 +1,6 @@
1
1
  import * as rxjs from 'rxjs'
2
2
  import * as C from '../constants/constants.js'
3
- import { h64ToString, findBigIntPaths } from './utils.js'
3
+ import { h64ToString, findBigIntPaths } from '../utils/utils.js'
4
4
 
5
5
  export default class Listener {
6
6
  constructor(topic, pattern, callback, handler, { recursive = false, stringify = null } = {}) {
@@ -3,8 +3,6 @@ import xxhash from 'xxhash-wasm'
3
3
  const NODE_ENV = typeof process !== 'undefined' && process.env && process.env.NODE_ENV
4
4
  const HASHER = await xxhash()
5
5
 
6
- // This is a hack to avoid top-level await
7
- // const HASHER = await xxhash()
8
6
  export const isNode = typeof process !== 'undefined' && process.toString() === '[object process]'
9
7
  export const isProduction = NODE_ENV === 'production'
10
8
 
@@ -83,14 +81,6 @@ export function setTimeout(callback, timeoutDuration) {
83
81
  }
84
82
  }
85
83
 
86
- export function setInterval(callback, intervalDuration) {
87
- if (intervalDuration !== null) {
88
- return globalThis.setInterval(callback, intervalDuration)
89
- } else {
90
- return -1
91
- }
92
- }
93
-
94
84
  export function compareRev(a, b) {
95
85
  if (!a) {
96
86
  return b ? -1 : 0