@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 +11 -11
- package/src/utils/multicast-listener.js +252 -0
- package/src/utils/utils.js +8 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/deepstream.io-client-js",
|
|
3
|
-
"version": "32.0.
|
|
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": "
|
|
40
|
-
"bufferutil": "
|
|
41
|
-
"component-emitter2": "
|
|
42
|
-
"invariant": "
|
|
43
|
-
"lodash.clonedeep": "
|
|
44
|
-
"utf-8-validate": "
|
|
45
|
-
"varint": "
|
|
46
|
-
"ws": "
|
|
47
|
-
"xuid": "
|
|
48
|
-
"xxhash-wasm": "^1.0
|
|
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
|
+
}
|
package/src/utils/utils.js
CHANGED
|
@@ -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
|