@nxtedition/deepstream.io-client-js 32.0.12 → 32.0.14

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.12",
3
+ "version": "32.0.14",
4
4
  "description": "the javascript client for deepstream.io",
5
5
  "homepage": "http://deepstream.io",
6
6
  "type": "module",
@@ -41,31 +41,32 @@
41
41
  "component-emitter2": "^1.3.5",
42
42
  "invariant": "^2.2.4",
43
43
  "lodash.clonedeep": "^4.5.0",
44
- "type-fest": "^5.4.1",
44
+ "type-fest": "^5.5.0",
45
45
  "utf-8-validate": "^6.0.6",
46
46
  "varint": "^6.0.0",
47
- "ws": "^8.19.0",
47
+ "ws": "^8.20.0",
48
48
  "xuid": "^4.1.3",
49
49
  "xxhash-wasm": "^1.0.2"
50
50
  },
51
51
  "devDependencies": {
52
- "@types/node": "^25.0.10",
53
- "eslint": "^9.39.2",
52
+ "@eslint/js": "^10.0.1",
53
+ "@types/node": "^25.5.0",
54
+ "eslint": "^10.1.0",
54
55
  "eslint-config-prettier": "^10.1.8",
55
56
  "eslint-config-standard": "^17.1.0",
56
57
  "eslint-plugin-import": "^2.31.0",
57
- "eslint-plugin-n": "^17.23.2",
58
+ "eslint-plugin-n": "^17.24.0",
58
59
  "eslint-plugin-node": "^11.1.0",
59
60
  "eslint-plugin-promise": "^7.1.0",
60
61
  "husky": "^9.1.6",
61
- "lint-staged": "^16.2.7",
62
+ "lint-staged": "^16.4.0",
62
63
  "mitata": "^1.0.10",
63
64
  "pinst": "^3.0.0",
64
65
  "prettier": "^3.8.1",
65
66
  "rxjs": "^7.8.1",
66
67
  "tsd": "^0.33.0",
67
- "typescript": "^5.6.3",
68
- "typescript-eslint": "^8.53.1"
68
+ "typescript": "^6.0.2",
69
+ "typescript-eslint": "^8.57.2"
69
70
  },
70
71
  "peerDependencies": {
71
72
  "rxjs": ">=6.x"
package/src/client.d.ts CHANGED
@@ -33,6 +33,8 @@ export type {
33
33
  SyncOptions,
34
34
  Paths,
35
35
  Get,
36
+ ConnectionStateName,
37
+ DeepstreamErrorEventName,
36
38
  }
37
39
 
38
40
  type RecordStateConstants = Readonly<{
@@ -99,6 +101,26 @@ export interface DeepstreamError extends Error {
99
101
  data?: unknown
100
102
  }
101
103
 
104
+ export interface DeepstreamMessage {
105
+ raw: string | null
106
+ topic: string | null
107
+ action: string | null
108
+ data: string[]
109
+ }
110
+
111
+ export interface DeepstreamClientEventMap {
112
+ connectionStateChanged: (state: ConnectionStateName) => void
113
+ connected: (connected: boolean) => void
114
+ MAX_RECONNECTION_ATTEMPTS_REACHED: (attempt: number) => void
115
+ error: (error: DeepstreamError) => void
116
+ recv: (message: DeepstreamMessage) => void
117
+ send: (message: DeepstreamMessage) => void
118
+ }
119
+
120
+ type DeepstreamErrorEventMap = {
121
+ [K in DeepstreamErrorEventName]: (error: DeepstreamError) => void
122
+ }
123
+
102
124
  export interface DeepstreamClient<
103
125
  Records extends Record<string, unknown> = Record<string, unknown>,
104
126
  Methods extends Record<string, RpcMethodDef> = Record<string, RpcMethodDef>,
@@ -108,18 +130,21 @@ export interface DeepstreamClient<
108
130
  rpc: RpcHandler<Methods>
109
131
  record: RecordHandler<Records>
110
132
  user: string | null
111
- on(evt: 'connectionStateChanged', callback: (state: ConnectionStateName) => void): this
112
- on(evt: 'connected', callback: (connected: boolean) => void): this
113
- on(evt: 'MAX_RECONNECTION_ATTEMPTS_REACHED', callback: (attempt: number) => void): this
114
- on(evt: 'error' | DeepstreamErrorEventName, callback: (error: DeepstreamError) => void): this
115
- off(evt: 'connectionStateChanged', callback: (state: ConnectionStateName) => void): this
116
- off(evt: 'connected', callback: (connected: boolean) => void): this
117
- off(evt: 'MAX_RECONNECTION_ATTEMPTS_REACHED', callback: (attempt: number) => void): this
118
- off(evt: 'error' | DeepstreamErrorEventName, callback: (error: DeepstreamError) => void): this
133
+ on<K extends keyof (DeepstreamClientEventMap & DeepstreamErrorEventMap)>(
134
+ evt: K,
135
+ callback: (DeepstreamClientEventMap & DeepstreamErrorEventMap)[K],
136
+ ): this
137
+ off<K extends keyof (DeepstreamClientEventMap & DeepstreamErrorEventMap)>(
138
+ evt: K,
139
+ callback: (DeepstreamClientEventMap & DeepstreamErrorEventMap)[K],
140
+ ): this
119
141
  getConnectionState: () => ConnectionStateName
120
142
  close: () => void
121
143
  login(callback: (success: boolean, authData: unknown) => void): this
122
- login(authParams: object, callback: (success: boolean, authData: unknown) => void): this
144
+ login(
145
+ authParams: Record<string, unknown>,
146
+ callback: (success: boolean, authData: unknown) => void,
147
+ ): this
123
148
  stats: {
124
149
  record: RecordStats
125
150
  rpc: RpcStats
@@ -78,7 +78,7 @@ EventHandler.on = function (name, callback) {
78
78
 
79
79
  EventHandler.once = function (name, callback) {
80
80
  const fn = (...args) => {
81
- this.unsubscribe(fn)
81
+ this.unsubscribe(name, fn)
82
82
  callback(...args)
83
83
  }
84
84
  this.subscribe(name, fn)
@@ -44,10 +44,13 @@ function onUpdate(record, subscription) {
44
44
  }
45
45
 
46
46
  if (!subscription.synced || subscription.record.state < subscription.state) {
47
+ if (subscription.timeoutValue > 0 && !subscription.timeout) {
48
+ subscription.timeout = timers.setTimeout(onTimeout, subscription.timeoutValue, subscription)
49
+ }
47
50
  return
48
51
  }
49
52
 
50
- if (subscription.timeout != null) {
53
+ if (subscription.timeout) {
51
54
  timers.clearTimeout(subscription.timeout)
52
55
  subscription.timeout = null
53
56
  }
@@ -94,36 +97,6 @@ function onTimeout(subscription) {
94
97
  )
95
98
  }
96
99
 
97
- class Subscription {
98
- /** @type {unknown} */
99
- key = null
100
- /** @type {unknown} */
101
- subscriber = null
102
- /** @type {unknown} */
103
- path = null
104
- /** @type {number} */
105
- state = 0
106
- /** @type {AbortSignal|null} */
107
- signal = null
108
- /** @type {boolean} */
109
- dataOnly = false
110
-
111
- /** @type {Record|null} */
112
- record = null
113
- /** @type {Timeout|null} */
114
- timeout = null
115
- /** @type {Function?} */
116
- abort = null
117
- /** @type {object|Array} */
118
- data = kEmpty
119
- /** @type {boolean} */
120
- synced = false
121
-
122
- index = -1
123
-
124
- onUpdate = onUpdate
125
- }
126
-
127
100
  class RecordHandler {
128
101
  constructor(options, connection, client) {
129
102
  this.JSON = jsonPath
@@ -142,12 +115,8 @@ class RecordHandler {
142
115
 
143
116
  this._connected = 0
144
117
  this._stats = {
145
- updating: 0,
146
118
  created: 0,
147
119
  destroyed: 0,
148
- records: 0,
149
- pruning: 0,
150
- patching: 0,
151
120
  }
152
121
 
153
122
  this._syncQueue = []
@@ -169,23 +138,11 @@ class RecordHandler {
169
138
  this._pruning = new Set()
170
139
 
171
140
  for (const rec of pruning) {
172
- try {
173
- rec._$dispose()
174
- if (!this._records.delete(rec.name)) {
175
- this._client._$onError(
176
- C.TOPIC.RECORD,
177
- C.EVENT.INTERNAL_ERROR,
178
- `failed to delete pruned record: ${rec.name}`,
179
- )
180
- }
181
- } catch (err) {
182
- this._client._$onError(C.TOPIC.RECORD, C.EVENT.INTERNAL_ERROR, err)
183
- }
141
+ rec._$dispose()
142
+ this._records.delete(rec.name)
184
143
  }
185
144
 
186
145
  this._stats.destroyed += pruning.size
187
- this._stats.pruning = this._pruning.size
188
- this._stats.records = this._records.size
189
146
 
190
147
  this._pruningTimeout.refresh()
191
148
  }
@@ -199,7 +156,6 @@ class RecordHandler {
199
156
  } else {
200
157
  this._pruning.delete(rec)
201
158
  }
202
- this._stats.pruning = this._pruning.size
203
159
  }
204
160
 
205
161
  _onUpdating(rec, value) {
@@ -207,12 +163,9 @@ class RecordHandler {
207
163
 
208
164
  if (value) {
209
165
  invariant(!callbacks, 'updating callbacks must not exist')
210
- this._stats.updating += 1
211
166
  this._updating.set(rec, [])
212
167
  } else {
213
168
  invariant(callbacks, 'updating callbacks must exist')
214
-
215
- this._stats.updating -= 1
216
169
  this._updating.delete(rec)
217
170
  for (const callback of callbacks) {
218
171
  callback()
@@ -230,7 +183,6 @@ class RecordHandler {
230
183
  callback()
231
184
  }
232
185
  }
233
- this._stats.patching = this._patching.size
234
186
  }
235
187
 
236
188
  get connected() {
@@ -246,6 +198,10 @@ class RecordHandler {
246
198
  return {
247
199
  ...this._stats,
248
200
  subscriptions,
201
+ updating: this._updating.size,
202
+ patching: this._patching.size,
203
+ putting: this._putting.size,
204
+ pruning: this._pruning.size,
249
205
  }
250
206
  }
251
207
 
@@ -268,16 +224,14 @@ class RecordHandler {
268
224
  }
269
225
 
270
226
  let record = this._records.get(name)
227
+
271
228
  if (!record) {
272
229
  record = new Record(name, this)
273
- this._records.set(name, record)
274
230
  this._stats.created += 1
275
- this._stats.records = this._records.size
276
- } else {
277
- record.ref()
231
+ this._records.set(name, record)
278
232
  }
279
233
 
280
- return record
234
+ return record.ref()
281
235
  }
282
236
 
283
237
  provide(pattern, callback, options) {
@@ -599,7 +553,6 @@ class RecordHandler {
599
553
  let timeout = defaults?.timeout ?? 0
600
554
  let dataOnly = defaults?.dataOnly ?? false
601
555
  let sync = defaults?.sync ?? false
602
- let key
603
556
 
604
557
  let idx = 0
605
558
 
@@ -618,7 +571,7 @@ class RecordHandler {
618
571
  }
619
572
 
620
573
  if (idx < args.length && (args[idx] == null || typeof args[idx] === 'object')) {
621
- const options = args[idx++] || {}
574
+ const options = args[idx] ?? {}
622
575
 
623
576
  if (options.signal !== undefined) {
624
577
  signal = options.signal
@@ -643,10 +596,6 @@ class RecordHandler {
643
596
  if (options.sync !== undefined) {
644
597
  sync = options.sync
645
598
  }
646
-
647
- if (options.key !== undefined) {
648
- key = options.key
649
- }
650
599
  }
651
600
 
652
601
  if (typeof state === 'string') {
@@ -675,14 +624,35 @@ class RecordHandler {
675
624
  return
676
625
  }
677
626
 
678
- const subscription = new Subscription()
679
-
680
- subscription.key = key
681
- subscription.subscriber = subscriber
682
- subscription.path = path
683
- subscription.state = state
684
- subscription.signal = signal
685
- subscription.dataOnly = dataOnly
627
+ // TODO (perf): Make a class
628
+ const subscription = {
629
+ /** @readonly @type {unknown} */
630
+ subscriber,
631
+ /** @readonly @type {unknown} */
632
+ path,
633
+ /** @readonly @type {number} */
634
+ state,
635
+ /** @type {AbortSignal|null} */
636
+ signal,
637
+ /** @readonly @type {boolean} */
638
+ dataOnly,
639
+ /** @readonly @type {number} */
640
+ timeoutValue: timeout,
641
+
642
+ /** @type {Record|null} */
643
+ record: null,
644
+ /** @type {Timeout|null} */
645
+ timeout: null,
646
+ /** @type {Function?} */
647
+ abort: null,
648
+ /** @type {object|Array} */
649
+ data: kEmpty,
650
+ /** @type {boolean} */
651
+ synced: false,
652
+
653
+ index: -1,
654
+ onUpdate,
655
+ }
686
656
 
687
657
  subscriber.add(() => {
688
658
  if (subscription.timeout) {
@@ -716,10 +686,6 @@ class RecordHandler {
716
686
  } else {
717
687
  onSync(subscription)
718
688
  }
719
-
720
- if (timeout > 0 && (!subscription.synced || subscription.record.state < subscription.state)) {
721
- subscription.timeout = timers.setTimeout(onTimeout, timeout, subscription)
722
- }
723
689
  })
724
690
  }
725
691
 
@@ -18,18 +18,15 @@ class Record {
18
18
  this._version = ''
19
19
  this._data = jsonPath.EMPTY_OBJ
20
20
  this._state = C.RECORD_STATE.VOID
21
- this._refs = 1
22
-
23
- /** @type {Array|null} */
24
- this._subscriptions = null
21
+ this._refs = 0
22
+ this._subscriptions = []
25
23
 
26
24
  /** @type {Array|null} */
27
25
  this._emittingArr = null
28
26
  /** @type {number} */
29
27
  this._emittingIndex = -1
30
28
 
31
- /** @type {Array|null} */
32
- this._observers = null
29
+ this._observers = []
33
30
 
34
31
  /** @type Map? */ this._updating = null
35
32
  /** @type Array? */ this._patching = null
@@ -97,8 +94,6 @@ class Record {
97
94
  * @returns {Record}
98
95
  */
99
96
  subscribe(fn, opaque = null) {
100
- this._subscriptions ??= []
101
-
102
97
  if (this._emittingArr == this._subscriptions) {
103
98
  this._subscriptions = this._subscriptions.slice()
104
99
  }
@@ -115,10 +110,6 @@ class Record {
115
110
  * @returns {Record}
116
111
  */
117
112
  unsubscribe(fn, opaque = null) {
118
- if (!this._subscriptions) {
119
- return this
120
- }
121
-
122
113
  if (this._emittingArr == this._subscriptions) {
123
114
  this._subscriptions = this._subscriptions.slice()
124
115
  }
@@ -146,17 +137,10 @@ class Record {
146
137
  * @param {{ index: number, onUpdate: (Record) => void}} subscription
147
138
  */
148
139
  _observe(subscription) {
149
- this._observers ??= []
150
-
151
140
  if (subscription.index != null && subscription.index !== -1) {
152
141
  throw new Error('already observing')
153
142
  }
154
143
 
155
- if (this._emittingArr === this._observers) {
156
- // TODO (perf): Shift from start if emitting?
157
- this._observers = this._observers.slice()
158
- }
159
-
160
144
  subscription.index = this._observers.push(subscription) - 1
161
145
  }
162
146
 
@@ -164,10 +148,6 @@ class Record {
164
148
  * @param {{ index: number, onUpdate: (Record) => void}} subscription
165
149
  */
166
150
  _unobserve(subscription) {
167
- if (!this._observers) {
168
- return this
169
- }
170
-
171
151
  if (subscription.index == null || subscription.index === -1) {
172
152
  throw new Error('not observing')
173
153
  }
@@ -249,7 +229,7 @@ class Record {
249
229
  when(stateOrNil, optionsOrNil) {
250
230
  invariant(this._refs > 0, 'missing refs')
251
231
 
252
- if (stateOrNil != null && stateOrNil === 'object') {
232
+ if (stateOrNil != null && typeof stateOrNil === 'object') {
253
233
  optionsOrNil = stateOrNil
254
234
  stateOrNil = optionsOrNil?.state
255
235
  }
@@ -257,7 +237,6 @@ class Record {
257
237
  const signal = optionsOrNil?.signal
258
238
  const state = stateOrNil ?? C.RECORD_STATE.SERVER
259
239
  const timeout = optionsOrNil?.timeout ?? 2 * 60e3
260
- const key = optionsOrNil?.key ?? 'when'
261
240
 
262
241
  if (signal?.aborted) {
263
242
  return Promise.reject(signal.reason || new utils.AbortError())
@@ -273,33 +252,14 @@ class Record {
273
252
  return
274
253
  }
275
254
 
276
- const subscription = {
277
- key,
278
- /** @type {timers.Timeout|NodeJS.Timeout|null} */
279
- timeout: null,
280
- /** @type {AbortSignal|null} */
281
- signal: null,
282
- done: false,
283
-
284
- state,
285
- index: -1,
286
- onUpdate(record, subscription) {
287
- if (record._state >= subscription.state) {
288
- onDone(null)
289
- }
290
- },
291
- }
292
-
293
- const onAbort = (e) => {
294
- onDone(signal.reason ?? new utils.AbortError())
295
- }
255
+ let timeoutHandle
256
+ let done = false
296
257
 
297
258
  const onDone = (err) => {
298
- if (subscription.done) {
259
+ if (done) {
299
260
  return
300
261
  }
301
-
302
- subscription.done = true
262
+ done = true
303
263
 
304
264
  if (err) {
305
265
  reject(err)
@@ -308,21 +268,30 @@ class Record {
308
268
  }
309
269
 
310
270
  this.unref()
311
- this._unobserve(subscription)
271
+ this.unsubscribe(onUpdate)
312
272
 
313
- if (subscription.timeout != null) {
314
- timers.clearTimeout(subscription.timeout)
315
- subscription.timeout = null
273
+ if (timeoutHandle) {
274
+ timers.clearTimeout(timeoutHandle)
275
+ timeoutHandle = null
316
276
  }
317
277
 
318
- if (subscription.signal != null) {
319
- subscription.signal.removeEventListener('abort', onAbort)
320
- subscription.signal = null
278
+ signal?.removeEventListener('abort', onAbort)
279
+ }
280
+
281
+ const onUpdate = () => {
282
+ if (this._state >= state) {
283
+ onDone(null)
321
284
  }
322
285
  }
323
286
 
287
+ const onAbort = signal
288
+ ? () => {
289
+ onDone(signal.reason ?? new utils.AbortError())
290
+ }
291
+ : null
292
+
324
293
  if (timeout > 0) {
325
- subscription.timeout = timers.setTimeout(() => {
294
+ timeoutHandle = timers.setTimeout(() => {
326
295
  const expected = C.RECORD_STATE_NAME[state]
327
296
  const current = C.RECORD_STATE_NAME[this._state]
328
297
 
@@ -334,13 +303,10 @@ class Record {
334
303
  }, timeout)
335
304
  }
336
305
 
337
- if (signal) {
338
- subscription.signal = signal
339
- signal?.addEventListener('abort', onAbort)
340
- }
306
+ signal?.addEventListener('abort', onAbort)
341
307
 
342
308
  this.ref()
343
- this._observe(subscription)
309
+ this.subscribe(onUpdate)
344
310
  })
345
311
  }
346
312
 
@@ -598,38 +564,34 @@ class Record {
598
564
  throw new Error('cannot reenter emitUpdate')
599
565
  }
600
566
 
601
- if (this._subscriptions != null) {
602
- try {
603
- const arr = this._subscriptions
604
- const len = arr.length
605
-
606
- this._emittingArr = arr
607
- for (let n = 0; n < len; n += 2) {
608
- this._emittingIndex = n
609
- // TODO (fix): What if this throws?
610
- arr[n + 0](this, arr[n + 1])
611
- }
612
- } finally {
613
- this._emittingArr = null
614
- this._emittingIndex = -1
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])
615
576
  }
577
+ } finally {
578
+ this._emittingArr = null
579
+ this._emittingIndex = -1
616
580
  }
617
581
 
618
- if (this._observers != null) {
619
- try {
620
- const arr = this._observers
621
- const len = arr.length
622
-
623
- this._emittingArr = arr
624
- for (let n = 0; n < len; n++) {
625
- this._emittingIndex = n
626
- // TODO (fix): What if this throws?
627
- arr[n].onUpdate(this, arr[n])
628
- }
629
- } finally {
630
- this._emittingArr = null
631
- this._emittingIndex = -1
582
+ try {
583
+ const arr = this._observers
584
+ const len = arr.length
585
+
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])
632
591
  }
592
+ } finally {
593
+ this._emittingArr = null
594
+ this._emittingIndex = -1
633
595
  }
634
596
 
635
597
  this._handler._client.emit('recordUpdated', this)
@@ -6,7 +6,6 @@ export default class Listener {
6
6
  constructor(topic, pattern, callback, handler, { recursive = false, stringify = null } = {}) {
7
7
  this._topic = topic
8
8
  this._pattern = pattern
9
- this._expr = new RegExp(pattern)
10
9
  this._callback = callback
11
10
  this._handler = handler
12
11
  this._client = this._handler._client
@@ -55,11 +54,6 @@ export default class Listener {
55
54
  return
56
55
  }
57
56
 
58
- if (!this._expr.test(name)) {
59
- this._error(name, 'invalid add: name does not match pattern')
60
- return
61
- }
62
-
63
57
  // TODO (refactor): Move to class
64
58
  const provider = {
65
59
  name,
@@ -37,7 +37,6 @@ export default class Listener {
37
37
 
38
38
  this._topic = topic
39
39
  this._pattern = pattern
40
- this._expr = new RegExp(pattern)
41
40
  this._callback = callback
42
41
  this._handler = handler
43
42
  this._client = this._handler._client
@@ -70,11 +69,6 @@ export default class Listener {
70
69
  return
71
70
  }
72
71
 
73
- if (!this._expr.test(name)) {
74
- this._error(name, 'invalid accept: name does not match pattern')
75
- return
76
- }
77
-
78
72
  let value$
79
73
  try {
80
74
  value$ = this._callback(name)
@@ -85,7 +85,7 @@ export function setTimeout(callback, timeoutDuration) {
85
85
 
86
86
  export function setInterval(callback, intervalDuration) {
87
87
  if (intervalDuration !== null) {
88
- return setInterval(callback, intervalDuration)
88
+ return globalThis.setInterval(callback, intervalDuration)
89
89
  } else {
90
90
  return -1
91
91
  }
@@ -1,9 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(npm run test:types:*)"
5
- ],
6
- "deny": [],
7
- "ask": []
8
- }
9
- }