@nxtedition/deepstream.io-client-js 32.0.18 → 32.0.20

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.18",
3
+ "version": "32.0.20",
4
4
  "description": "the javascript client for deepstream.io",
5
5
  "homepage": "http://deepstream.io",
6
6
  "type": "module",
@@ -36,16 +36,16 @@
36
36
  "singleQuote": true
37
37
  },
38
38
  "dependencies": {
39
- "@nxtedition/json-path": "^1.0.8",
40
- "bufferutil": "^4.0.8",
41
- "component-emitter2": "^1.3.5",
42
- "invariant": "^2.2.4",
43
- "lodash.clonedeep": "^4.5.0",
44
- "utf-8-validate": "^6.0.5",
45
- "varint": "^6.0.0",
46
- "ws": "^8.18.0",
47
- "xuid": "^4.1.3",
48
- "xxhash-wasm": "^1.0.2"
39
+ "@nxtedition/json-path": "1.0.9",
40
+ "bufferutil": "4.0.8",
41
+ "component-emitter2": "1.3.5",
42
+ "invariant": "2.2.4",
43
+ "lodash.clonedeep": "4.5.0",
44
+ "utf-8-validate": "6.0.6",
45
+ "varint": "6.0.0",
46
+ "ws": "8.20.0",
47
+ "xuid": "4.1.5",
48
+ "xxhash-wasm": "^1.1.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^22.10.3",
@@ -0,0 +1,252 @@
1
+ import * as rxjs from 'rxjs'
2
+ import * as C from '../constants/constants.js'
3
+ import { h64ToString, findBigIntPaths } from '../utils/utils.js'
4
+
5
+ export default class Listener {
6
+ constructor(topic, pattern, callback, handler, { recursive = false, stringify = null } = {}) {
7
+ this._topic = topic
8
+ this._pattern = pattern
9
+ this._callback = callback
10
+ this._handler = handler
11
+ this._client = this._handler._client
12
+ this._connection = this._handler._connection
13
+ this._subscriptions = new Map()
14
+ this._recursive = recursive
15
+ this._stringify = stringify || JSON.stringify
16
+
17
+ this._$onConnectionStateChange()
18
+ }
19
+
20
+ get connected() {
21
+ return this._connection.connected
22
+ }
23
+
24
+ get stats() {
25
+ return {
26
+ subscriptions: this._subscriptions.size,
27
+ }
28
+ }
29
+
30
+ _$destroy() {
31
+ this._reset()
32
+
33
+ if (this.connected) {
34
+ this._connection.sendMsg(this._topic, C.ACTIONS.UNLISTEN, [this._pattern])
35
+ }
36
+ }
37
+
38
+ _$onMessage(message) {
39
+ if (!this.connected) {
40
+ this._client._$onError(
41
+ C.TOPIC.RECORD,
42
+ C.EVENT.NOT_CONNECTED,
43
+ new Error('received message while not connected'),
44
+ message,
45
+ )
46
+ return
47
+ }
48
+
49
+ const name = message.data[1]
50
+
51
+ if (message.action === C.ACTIONS.SUBSCRIPTION_FOR_PATTERN_FOUND) {
52
+ if (this._subscriptions.has(name)) {
53
+ this._error(name, 'invalid add: listener exists')
54
+ return
55
+ }
56
+
57
+ // TODO (refactor): Move to class
58
+ const provider = {
59
+ name,
60
+ value$: null,
61
+ sending: false,
62
+ accepted: false,
63
+ version: null,
64
+ timeout: null,
65
+ patternSubscription: null,
66
+ valueSubscription: null,
67
+ }
68
+ provider.stop = () => {
69
+ if (this.connected && provider.accepted) {
70
+ this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [
71
+ this._pattern,
72
+ provider.name,
73
+ ])
74
+ }
75
+
76
+ provider.value$ = null
77
+ provider.version = null
78
+ provider.accepted = false
79
+ provider.sending = false
80
+
81
+ clearTimeout(provider.timeout)
82
+ provider.timeout = null
83
+
84
+ provider.patternSubscription?.unsubscribe()
85
+ provider.patternSubscription = null
86
+
87
+ provider.valueSubscription?.unsubscribe()
88
+ provider.valueSubscription = null
89
+ }
90
+ provider.send = () => {
91
+ provider.sending = false
92
+
93
+ if (!provider.patternSubscription) {
94
+ return
95
+ }
96
+
97
+ const accepted = Boolean(provider.value$)
98
+ if (provider.accepted === accepted) {
99
+ return
100
+ }
101
+
102
+ this._connection.sendMsg(
103
+ this._topic,
104
+ accepted ? C.ACTIONS.LISTEN_ACCEPT : C.ACTIONS.LISTEN_REJECT,
105
+ [this._pattern, provider.name],
106
+ )
107
+
108
+ provider.version = null
109
+ provider.accepted = accepted
110
+ }
111
+ provider.next = (value$) => {
112
+ if (!value$) {
113
+ value$ = null
114
+ } else if (typeof value$.subscribe !== 'function') {
115
+ value$ = rxjs.of(value$) // Compat for recursive with value
116
+ }
117
+
118
+ if (Boolean(provider.value$) !== Boolean(value$) && !provider.sending) {
119
+ provider.sending = true
120
+ queueMicrotask(provider.send)
121
+ }
122
+
123
+ provider.value$ = value$
124
+
125
+ if (provider.valueSubscription) {
126
+ provider.valueSubscription.unsubscribe()
127
+ provider.valueSubscription = provider.value$?.subscribe(provider.observer)
128
+ }
129
+ }
130
+ provider.error = (err) => {
131
+ provider.stop()
132
+ // TODO (feat): backoff retryCount * delay?
133
+ // TODO (feat): backoff option?
134
+ provider.timeout = setTimeout(() => {
135
+ provider.start()
136
+ }, 10e3)
137
+ this._error(provider.name, err)
138
+ }
139
+ provider.observer = {
140
+ next: (value) => {
141
+ if (value == null) {
142
+ provider.next(null) // TODO (fix): This is weird...
143
+ return
144
+ }
145
+
146
+ if (this._topic === C.TOPIC.EVENT) {
147
+ this._handler.emit(provider.name, value)
148
+ } else if (this._topic === C.TOPIC.RECORD) {
149
+ if (typeof value !== 'object' && typeof value !== 'string') {
150
+ this._error(provider.name, 'invalid value')
151
+ return
152
+ }
153
+
154
+ if (typeof value !== 'string') {
155
+ try {
156
+ value = this._stringify(value)
157
+ } catch (err) {
158
+ const bigIntPaths = /BigInt/.test(err.message) ? findBigIntPaths(value) : undefined
159
+ this._error(
160
+ Object.assign(new Error(`invalid value: ${value}`), {
161
+ cause: err,
162
+ data: { name: provider.name, bigIntPaths },
163
+ }),
164
+ )
165
+ return
166
+ }
167
+ }
168
+
169
+ const body = value
170
+ const hash = h64ToString(body)
171
+ const version = `INF-${hash}`
172
+
173
+ if (provider.version !== version) {
174
+ provider.version = version
175
+ this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UPDATE, [
176
+ provider.name,
177
+ version,
178
+ body,
179
+ ])
180
+ }
181
+ }
182
+ },
183
+ error: provider.error,
184
+ }
185
+ provider.start = () => {
186
+ try {
187
+ const ret$ = this._callback(name)
188
+ if (this._recursive && typeof ret$?.subscribe === 'function') {
189
+ provider.patternSubscription = ret$.subscribe(provider)
190
+ } else {
191
+ provider.patternSubscription = rxjs.of(ret$).subscribe(provider)
192
+ }
193
+ } catch (err) {
194
+ this._error(provider.name, err)
195
+ }
196
+ }
197
+
198
+ provider.start()
199
+
200
+ this._subscriptions.set(provider.name, provider)
201
+ } else if (message.action === C.ACTIONS.LISTEN_ACCEPT) {
202
+ const provider = this._subscriptions.get(name)
203
+ if (!provider?.value$) {
204
+ return
205
+ }
206
+
207
+ if (provider.valueSubscription) {
208
+ this._error(
209
+ name,
210
+ 'invalid accept: listener started (pattern:' + this._pattern + ' name:' + name + ')',
211
+ )
212
+ } else {
213
+ // TODO (fix): provider.version = message.data[2]
214
+ provider.valueSubscription = provider.value$.subscribe(provider.observer)
215
+ }
216
+ } else if (message.action === C.ACTIONS.SUBSCRIPTION_FOR_PATTERN_REMOVED) {
217
+ const provider = this._subscriptions.get(name)
218
+
219
+ if (!provider) {
220
+ this._error(
221
+ name,
222
+ 'invalid remove: listener missing (pattern:' + this._pattern + ' name:' + name + ')',
223
+ )
224
+ } else {
225
+ provider.stop()
226
+ this._subscriptions.delete(provider.name)
227
+ }
228
+ } else {
229
+ return false
230
+ }
231
+ return true
232
+ }
233
+
234
+ _$onConnectionStateChange() {
235
+ if (this.connected) {
236
+ this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN, [this._pattern])
237
+ } else {
238
+ this._reset()
239
+ }
240
+ }
241
+
242
+ _error(name, err) {
243
+ this._client._$onError(this._topic, C.EVENT.LISTENER_ERROR, err, [this._pattern, name])
244
+ }
245
+
246
+ _reset() {
247
+ for (const provider of this._subscriptions.values()) {
248
+ provider.stop()
249
+ }
250
+ this._subscriptions.clear()
251
+ }
252
+ }
@@ -83,6 +83,14 @@ export function setTimeout(callback, timeoutDuration) {
83
83
  }
84
84
  }
85
85
 
86
+ export function setInterval(callback, intervalDuration) {
87
+ if (intervalDuration !== null) {
88
+ return setInterval(callback, intervalDuration)
89
+ } else {
90
+ return -1
91
+ }
92
+ }
93
+
86
94
  export function compareRev(a, b) {
87
95
  if (!a) {
88
96
  return b ? -1 : 0