@nxtedition/deepstream.io-client-js 31.2.17 → 31.2.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": "31.2.17",
3
+ "version": "31.2.19",
4
4
  "description": "the javascript client for deepstream.io",
5
5
  "homepage": "http://deepstream.io",
6
6
  "type": "module",
@@ -35,7 +35,7 @@ const GET2_DEFAULTS = {
35
35
 
36
36
  function onSync(subscription) {
37
37
  subscription.synced = true
38
- onUpdate(subscription.record, subscription)
38
+ onUpdate(null, subscription)
39
39
  }
40
40
 
41
41
  function onUpdate(record, subscription) {
@@ -128,7 +128,7 @@ class RecordHandler {
128
128
  }
129
129
 
130
130
  this._syncQueue = []
131
- this._syncMap = {}
131
+ this._syncMap = new Map()
132
132
 
133
133
  this.set = this.set.bind(this)
134
134
  this.get = this.get.bind(this)
@@ -228,13 +228,18 @@ class RecordHandler {
228
228
  * @returns {Record}
229
229
  */
230
230
  getRecord(name) {
231
- invariant(
232
- typeof name === 'string' &&
233
- name.length > 0 &&
234
- name !== '[object Object]' &&
235
- name.length <= 4096,
236
- `invalid name ${name}`,
237
- )
231
+ if (typeof name !== 'string' || name.length === 0) {
232
+ throw new Error('invalid argument: name')
233
+ }
234
+
235
+ if (name.startsWith('null') || name.startsWith('undefined') || name === '[object Object]') {
236
+ this._client._$onError(
237
+ C.TOPIC.RECORD,
238
+ C.EVENT.USER_ERROR,
239
+ 'name should not start with null or undefined',
240
+ name,
241
+ )
242
+ }
238
243
 
239
244
  let record = this._records.get(name)
240
245
 
@@ -561,78 +566,78 @@ class RecordHandler {
561
566
  * @returns {rxjs.Observable}
562
567
  */
563
568
  _observe(defaults, name, ...args) {
564
- return new rxjs.Observable((subscriber) => {
565
- let path
566
- let state = defaults?.state ?? C.RECORD_STATE.CLIENT
567
- let signal = null
568
- let timeout = defaults?.timeout ?? 0
569
- let dataOnly = defaults?.dataOnly ?? false
570
- let sync = defaults?.sync ?? false
571
-
572
- let idx = 0
573
-
574
- if (
575
- idx < args.length &&
576
- (args[idx] == null ||
577
- typeof args[idx] === 'string' ||
578
- Array.isArray(args[idx]) ||
579
- typeof args[idx] === 'function')
580
- ) {
581
- path = args[idx++]
582
- }
583
-
584
- if (idx < args.length && (args[idx] == null || typeof args[idx] === 'number')) {
585
- state = args[idx++]
586
- }
587
-
588
- if (idx < args.length && (args[idx] == null || typeof args[idx] === 'object')) {
589
- const options = args[idx++] || {}
590
-
591
- if (options.signal !== undefined) {
592
- signal = options.signal
593
- }
569
+ let path
570
+ let state = defaults?.state ?? C.RECORD_STATE.CLIENT
571
+ let signal = null
572
+ let timeout = defaults?.timeout ?? 0
573
+ let dataOnly = defaults?.dataOnly ?? false
574
+ let sync = defaults?.sync ?? false
594
575
 
595
- if (options.timeout !== undefined) {
596
- timeout = options.timeout
597
- }
576
+ let idx = 0
598
577
 
599
- if (options.path !== undefined) {
600
- path = options.path
601
- }
578
+ if (
579
+ idx < args.length &&
580
+ (args[idx] == null ||
581
+ typeof args[idx] === 'string' ||
582
+ Array.isArray(args[idx]) ||
583
+ typeof args[idx] === 'function')
584
+ ) {
585
+ path = args[idx++]
586
+ }
602
587
 
603
- if (options.state !== undefined) {
604
- state = options.state
605
- }
588
+ if (idx < args.length && (args[idx] == null || typeof args[idx] === 'number')) {
589
+ state = args[idx++]
590
+ }
606
591
 
607
- if (options.dataOnly !== undefined) {
608
- dataOnly = options.dataOnly
609
- }
592
+ if (idx < args.length && (args[idx] == null || typeof args[idx] === 'object')) {
593
+ const options = args[idx++] || {}
610
594
 
611
- if (options.sync !== undefined) {
612
- sync = options.sync
613
- }
595
+ if (options.signal !== undefined) {
596
+ signal = options.signal
614
597
  }
615
598
 
616
- if (typeof state === 'string') {
617
- state = C.RECORD_STATE[state.toUpperCase()]
599
+ if (options.timeout !== undefined) {
600
+ timeout = options.timeout
618
601
  }
619
602
 
620
- if (!Number.isInteger(state) || state < 0) {
621
- throw new Error('invalid argument: state')
603
+ if (options.path !== undefined) {
604
+ path = options.path
622
605
  }
623
606
 
624
- if (!Number.isInteger(timeout) || timeout < 0) {
625
- throw new Error('invalid argument: timeout')
607
+ if (options.state !== undefined) {
608
+ state = options.state
626
609
  }
627
610
 
628
- if (typeof dataOnly !== 'boolean') {
629
- throw new Error('invalid argument: dataOnly')
611
+ if (options.dataOnly !== undefined) {
612
+ dataOnly = options.dataOnly
630
613
  }
631
614
 
632
- if (typeof sync !== 'boolean') {
633
- throw new Error('invalid argument: sync')
615
+ if (options.sync !== undefined) {
616
+ sync = options.sync
634
617
  }
618
+ }
619
+
620
+ if (typeof state === 'string') {
621
+ state = C.RECORD_STATE[state.toUpperCase()]
622
+ }
635
623
 
624
+ if (!Number.isInteger(state) || state < 0) {
625
+ throw new Error('invalid argument: state')
626
+ }
627
+
628
+ if (!Number.isInteger(timeout) || timeout < 0) {
629
+ throw new Error('invalid argument: timeout')
630
+ }
631
+
632
+ if (typeof dataOnly !== 'boolean') {
633
+ throw new Error('invalid argument: dataOnly')
634
+ }
635
+
636
+ if (typeof sync !== 'boolean') {
637
+ throw new Error('invalid argument: sync')
638
+ }
639
+
640
+ return new rxjs.Observable((subscriber) => {
636
641
  // TODO (perf): Make a class
637
642
  const subscription = {
638
643
  /** @readonly @type {unknown} */
@@ -658,6 +663,9 @@ class RecordHandler {
658
663
  data: kEmpty,
659
664
  /** @type {boolean} */
660
665
  synced: false,
666
+
667
+ index: -1,
668
+ onUpdate,
661
669
  }
662
670
 
663
671
  subscriber.add(() => {
@@ -673,7 +681,7 @@ class RecordHandler {
673
681
  }
674
682
 
675
683
  if (subscription.record) {
676
- subscription.record.unsubscribe(onUpdate, subscription)
684
+ subscription.record._unobserve(subscription)
677
685
  subscription.record.unref()
678
686
  subscription.record = null
679
687
  }
@@ -684,10 +692,11 @@ class RecordHandler {
684
692
  utils.addAbortListener(subscription.signal, subscription.abort)
685
693
  }
686
694
 
687
- subscription.record = this.getRecord(name).subscribe(onUpdate, subscription)
695
+ subscription.record = this.getRecord(name)
696
+ subscription.record._observe(subscription)
688
697
 
689
698
  if (sync) {
690
- this._sync(onSync, sync === true ? 'WEAK' : sync, subscription)
699
+ this._sync(onSync, sync, subscription)
691
700
  } else {
692
701
  onSync(subscription)
693
702
  }
@@ -708,8 +717,8 @@ class RecordHandler {
708
717
  return true
709
718
  }
710
719
 
711
- const sync = this._syncMap[token]
712
- delete this._syncMap[token]
720
+ const sync = this._syncMap.get(token)
721
+ this._syncMap.delete(token)
713
722
 
714
723
  if (!sync) {
715
724
  return true
@@ -752,10 +761,10 @@ class RecordHandler {
752
761
  this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.PUT, update)
753
762
  }
754
763
 
755
- const syncMap = {}
756
- for (const sync of Object.values(this._syncMap)) {
764
+ const syncMap = new Map()
765
+ for (const sync of this._syncMap.values()) {
757
766
  const token = xuid()
758
- syncMap[token] = sync
767
+ syncMap.set(token, sync)
759
768
  this._connection.sendMsg(
760
769
  C.TOPIC.RECORD,
761
770
  C.ACTIONS.SYNC,
@@ -790,7 +799,7 @@ class RecordHandler {
790
799
  const token = xuid()
791
800
  const queue = this._syncQueue.splice(0)
792
801
 
793
- this._syncMap[token] = { queue, type }
802
+ this._syncMap.set(token, { queue, type })
794
803
  this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SYNC, type ? [token, type] : [token])
795
804
  }, 1)
796
805
  }
@@ -20,7 +20,13 @@ class Record {
20
20
  this._state = C.RECORD_STATE.VOID
21
21
  this._refs = 0
22
22
  this._subscriptions = []
23
- this._emitting = false
23
+
24
+ /** @type {Array|null} */
25
+ this._emittingArr = null
26
+ /** @type {number} */
27
+ this._emittingIndex = -1
28
+
29
+ this._observers = []
24
30
 
25
31
  /** @type Map? */ this._updating = null
26
32
  /** @type Array? */ this._patching = null
@@ -88,9 +94,8 @@ class Record {
88
94
  * @returns {Record}
89
95
  */
90
96
  subscribe(fn, opaque = null) {
91
- if (this._emitting) {
97
+ if (this._emittingArr == this._subscriptions) {
92
98
  this._subscriptions = this._subscriptions.slice()
93
- this._emitting = false
94
99
  }
95
100
 
96
101
  this._subscriptions.push(fn, opaque)
@@ -105,9 +110,8 @@ class Record {
105
110
  * @returns {Record}
106
111
  */
107
112
  unsubscribe(fn, opaque = null) {
108
- if (this._emitting) {
113
+ if (this._emittingArr == this._subscriptions) {
109
114
  this._subscriptions = this._subscriptions.slice()
110
- this._emitting = false
111
115
  }
112
116
 
113
117
  let idx = -1
@@ -128,6 +132,41 @@ class Record {
128
132
  return this
129
133
  }
130
134
 
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
+
131
170
  get(path) {
132
171
  if (!path) {
133
172
  return this._data
@@ -515,18 +554,41 @@ class Record {
515
554
  }
516
555
 
517
556
  _emitUpdate() {
518
- this._emitting = true
557
+ if (this._emittingArr != null) {
558
+ throw new Error('cannot reenter emitUpdate')
559
+ }
519
560
 
520
- const arr = this._subscriptions
521
- const len = arr.length
561
+ try {
562
+ const arr = this._subscriptions
563
+ const len = arr.length
522
564
 
523
- for (let n = 0; n < len; n += 2) {
524
- arr[n + 0](this, arr[n + 1])
565
+ this._emittingArr = arr
566
+ for (let n = 0; n < len; n += 2) {
567
+ this._emittingIndex = n
568
+ // TODO (fix): What if this throws?
569
+ arr[n + 0](this, arr[n + 1])
570
+ }
571
+ } finally {
572
+ this._emittingArr = null
573
+ this._emittingIndex = -1
525
574
  }
526
575
 
527
- this._handler._client.emit('recordUpdated', this)
576
+ try {
577
+ const arr = this._observers
578
+ const len = arr.length
528
579
 
529
- this._emitting = false
580
+ this._emittingArr = arr
581
+ for (let n = 0; n < len; n++) {
582
+ this._emittingIndex = n
583
+ // TODO (fix): What if this throws?
584
+ arr[n].onUpdate(this, arr[n])
585
+ }
586
+ } finally {
587
+ this._emittingArr = null
588
+ this._emittingIndex = -1
589
+ }
590
+
591
+ this._handler._client.emit('recordUpdated', this)
530
592
  }
531
593
  }
532
594