@nxtedition/deepstream.io-client-js 26.0.24 → 27.0.0-beta.3
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": "
|
|
3
|
+
"version": "27.0.0-beta.3",
|
|
4
4
|
"description": "the javascript client for deepstream.io",
|
|
5
5
|
"homepage": "http://deepstream.io",
|
|
6
6
|
"type": "module",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"xxhash-wasm": "^1.0.2"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
+
"@types/node": "^22.0.0",
|
|
74
75
|
"eslint": "^8.0.0",
|
|
75
76
|
"eslint-config-prettier": "^9.1.0",
|
|
76
77
|
"eslint-config-standard": "^17.1.0",
|
|
@@ -37,6 +37,8 @@ export default function Connection(client, url, options) {
|
|
|
37
37
|
this._url = new URL(url)
|
|
38
38
|
|
|
39
39
|
this._state = C.CONNECTION_STATE.CLOSED
|
|
40
|
+
|
|
41
|
+
this._createEndpoint()
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
Emitter(Connection.prototype)
|
|
@@ -159,7 +161,7 @@ Connection.prototype._sendAuthParams = function () {
|
|
|
159
161
|
this._setState(C.CONNECTION_STATE.AUTHENTICATING)
|
|
160
162
|
const authMessage = messageBuilder.getMsg(C.TOPIC.AUTH, C.ACTIONS.REQUEST, [
|
|
161
163
|
this._authParams,
|
|
162
|
-
'
|
|
164
|
+
'27.0.0',
|
|
163
165
|
utils.isNode
|
|
164
166
|
? `Node/${process.version}`
|
|
165
167
|
: globalThis.navigator && globalThis.navigator.userAgent,
|
|
@@ -1,25 +1,101 @@
|
|
|
1
1
|
import * as C from '../constants/constants.js'
|
|
2
|
+
import * as utils from '../utils/utils.js'
|
|
3
|
+
import varint from 'varint'
|
|
2
4
|
|
|
3
|
-
const
|
|
5
|
+
const poolEncoder = new globalThis.TextEncoder()
|
|
6
|
+
|
|
7
|
+
// TODO (fix): Don't assume maxMesageSize is 1MB
|
|
8
|
+
const maxMessageSize = 1024 * 1024
|
|
9
|
+
const poolSize = maxMessageSize * 4
|
|
10
|
+
|
|
11
|
+
let poolBuffer
|
|
12
|
+
let poolView
|
|
13
|
+
let poolOffset
|
|
14
|
+
|
|
15
|
+
function reallocPool() {
|
|
16
|
+
poolBuffer = utils.isNode
|
|
17
|
+
? globalThis.Buffer.allocUnsafe(poolSize)
|
|
18
|
+
: new Uint8Array(new ArrayBuffer(poolSize))
|
|
19
|
+
poolView = new DataView(poolBuffer.buffer)
|
|
20
|
+
poolOffset = 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function alignPool() {
|
|
24
|
+
// Ensure aligned slices
|
|
25
|
+
if (poolOffset & 0x7) {
|
|
26
|
+
poolOffset |= 0x7
|
|
27
|
+
poolOffset++
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function writeString(dst, str, offset) {
|
|
32
|
+
if (utils.isNode) {
|
|
33
|
+
return dst.write(str, offset)
|
|
34
|
+
} else {
|
|
35
|
+
const res = poolEncoder.encodeInto(str, new Uint8Array(dst.buffer, offset))
|
|
36
|
+
return res.written
|
|
37
|
+
}
|
|
38
|
+
}
|
|
4
39
|
|
|
5
40
|
export function getMsg(topic, action, data) {
|
|
6
41
|
if (data && !(data instanceof Array)) {
|
|
7
42
|
throw new Error('data must be an array')
|
|
8
43
|
}
|
|
9
44
|
|
|
10
|
-
|
|
45
|
+
if (!poolBuffer || poolOffset + maxMessageSize > poolSize) {
|
|
46
|
+
reallocPool()
|
|
47
|
+
} else {
|
|
48
|
+
alignPool()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const start = poolOffset
|
|
52
|
+
|
|
53
|
+
const headerSize = 8
|
|
54
|
+
poolBuffer[poolOffset++] = 128 + headerSize
|
|
55
|
+
let headerPos = poolOffset
|
|
56
|
+
poolOffset += headerSize - 1
|
|
57
|
+
|
|
58
|
+
poolBuffer[poolOffset++] = topic.charCodeAt(0)
|
|
59
|
+
poolBuffer[poolOffset++] = 31
|
|
60
|
+
for (let n = 0; n < action.length; n++) {
|
|
61
|
+
poolBuffer[poolOffset++] = action.charCodeAt(n)
|
|
62
|
+
}
|
|
11
63
|
|
|
12
64
|
if (data) {
|
|
13
65
|
for (let i = 0; i < data.length; i++) {
|
|
14
|
-
|
|
15
|
-
|
|
66
|
+
const type = typeof data[i]
|
|
67
|
+
let len
|
|
68
|
+
if (data[i] == null) {
|
|
69
|
+
poolBuffer[poolOffset++] = 31
|
|
70
|
+
len = 0
|
|
71
|
+
} else if (type === 'object') {
|
|
72
|
+
poolBuffer[poolOffset++] = 31
|
|
73
|
+
len = writeString(poolBuffer, JSON.stringify(data[i]), poolOffset)
|
|
74
|
+
} else if (type === 'bigint') {
|
|
75
|
+
poolBuffer[poolOffset++] = 31
|
|
76
|
+
poolView.setBigUint64(poolOffset, data[i], false)
|
|
77
|
+
len = 8
|
|
78
|
+
} else if (type === 'string') {
|
|
79
|
+
poolBuffer[poolOffset++] = 31
|
|
80
|
+
len = writeString(poolBuffer, data[i], poolOffset)
|
|
16
81
|
} else {
|
|
17
|
-
|
|
82
|
+
throw new Error('invalid data')
|
|
83
|
+
}
|
|
84
|
+
poolOffset += len
|
|
85
|
+
|
|
86
|
+
varint.encode(len + 1, poolBuffer, headerPos)
|
|
87
|
+
headerPos += varint.encode.bytes
|
|
88
|
+
if (headerPos - start >= headerSize) {
|
|
89
|
+
throw new Error('header too large')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (poolOffset >= poolSize) {
|
|
93
|
+
throw new Error('message too large')
|
|
18
94
|
}
|
|
19
95
|
}
|
|
20
96
|
}
|
|
21
97
|
|
|
22
|
-
return
|
|
98
|
+
return new Uint8Array(poolBuffer.buffer, start, poolOffset - start)
|
|
23
99
|
}
|
|
24
100
|
|
|
25
101
|
export function typed(value) {
|
package/src/record/record.js
CHANGED
|
@@ -7,7 +7,7 @@ import invariant from 'invariant'
|
|
|
7
7
|
import cloneDeep from 'lodash.clonedeep'
|
|
8
8
|
import * as timers from '../utils/timers.js'
|
|
9
9
|
|
|
10
|
-
class Record {
|
|
10
|
+
export default class Record {
|
|
11
11
|
static STATE = C.RECORD_STATE
|
|
12
12
|
|
|
13
13
|
constructor(name, handler) {
|
|
@@ -15,6 +15,7 @@ class Record {
|
|
|
15
15
|
|
|
16
16
|
this._handler = handler
|
|
17
17
|
this._name = name
|
|
18
|
+
this._key = utils.h64(name)
|
|
18
19
|
this._version = ''
|
|
19
20
|
this._data = jsonPath.EMPTY
|
|
20
21
|
this._state = C.RECORD_STATE.VOID
|
|
@@ -24,7 +25,15 @@ class Record {
|
|
|
24
25
|
|
|
25
26
|
/** @type Map? */ this._updating = null
|
|
26
27
|
/** @type Array? */ this._patching = null
|
|
27
|
-
this._subscribed = connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [
|
|
28
|
+
this._subscribed = connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [
|
|
29
|
+
this._key,
|
|
30
|
+
this._name,
|
|
31
|
+
])
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** @type {bigint} */
|
|
35
|
+
get key() {
|
|
36
|
+
return this._key
|
|
28
37
|
}
|
|
29
38
|
|
|
30
39
|
/** @type {string} */
|
|
@@ -62,7 +71,8 @@ class Record {
|
|
|
62
71
|
if (this._refs === 1) {
|
|
63
72
|
this._handler._onPruning(this, false)
|
|
64
73
|
this._subscribed =
|
|
65
|
-
this._subscribed ||
|
|
74
|
+
this._subscribed ||
|
|
75
|
+
connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [this._key, this._name])
|
|
66
76
|
}
|
|
67
77
|
return this
|
|
68
78
|
}
|
|
@@ -324,7 +334,8 @@ class Record {
|
|
|
324
334
|
|
|
325
335
|
if (connected) {
|
|
326
336
|
this._subscribed =
|
|
327
|
-
this._refs > 0 &&
|
|
337
|
+
this._refs > 0 &&
|
|
338
|
+
connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [this._key, this._name])
|
|
328
339
|
|
|
329
340
|
if (this._updating) {
|
|
330
341
|
for (const update of this._updating.values()) {
|
|
@@ -349,7 +360,7 @@ class Record {
|
|
|
349
360
|
invariant(!this._updating, 'must not have updates')
|
|
350
361
|
|
|
351
362
|
if (this._subscribed) {
|
|
352
|
-
connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UNSUBSCRIBE, [this.
|
|
363
|
+
connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UNSUBSCRIBE, [this._key])
|
|
353
364
|
this._subscribed = false
|
|
354
365
|
}
|
|
355
366
|
|
|
@@ -371,7 +382,7 @@ class Record {
|
|
|
371
382
|
const prevVersion = this._version
|
|
372
383
|
const nextVersion = this._makeVersion(parseInt(prevVersion) + 1)
|
|
373
384
|
|
|
374
|
-
const update = [this.
|
|
385
|
+
const update = [this._key, nextVersion, jsonPath.stringify(nextData), prevVersion]
|
|
375
386
|
|
|
376
387
|
if (!this._updating) {
|
|
377
388
|
this._onUpdating(true)
|
|
@@ -389,7 +400,7 @@ class Record {
|
|
|
389
400
|
this._version = nextVersion
|
|
390
401
|
}
|
|
391
402
|
|
|
392
|
-
_onUpdate([, version, data]) {
|
|
403
|
+
_onUpdate([, version, data, hasProvider]) {
|
|
393
404
|
const prevData = this._data
|
|
394
405
|
const prevVersion = this._version
|
|
395
406
|
const prevState = this._state
|
|
@@ -422,7 +433,9 @@ class Record {
|
|
|
422
433
|
this._onPatching(false)
|
|
423
434
|
}
|
|
424
435
|
|
|
425
|
-
if (
|
|
436
|
+
if (hasProvider === 'T') {
|
|
437
|
+
this._state = C.RECORD_STATE.PROVIDER
|
|
438
|
+
} else if (this._state < C.RECORD_STATE.SERVER) {
|
|
426
439
|
this._state = this._version.charAt(0) === 'I' ? C.RECORD_STATE.STALE : C.RECORD_STATE.SERVER
|
|
427
440
|
}
|
|
428
441
|
|
|
@@ -571,5 +584,3 @@ Object.defineProperty(Record.prototype, 'hasProvider', {
|
|
|
571
584
|
return this.state >= C.RECORD_STATE.PROVIDER
|
|
572
585
|
},
|
|
573
586
|
})
|
|
574
|
-
|
|
575
|
-
export default Record
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as rxjs from 'rxjs'
|
|
2
2
|
import * as C from '../constants/constants.js'
|
|
3
|
-
import { h64ToString } from '../utils/utils.js'
|
|
3
|
+
import { h64, h64ToString } from '../utils/utils.js'
|
|
4
4
|
|
|
5
5
|
export default class Listener {
|
|
6
6
|
constructor(topic, pattern, callback, handler, { recursive = false, stringify = null } = {}) {
|
|
@@ -48,8 +48,12 @@ export default class Listener {
|
|
|
48
48
|
|
|
49
49
|
const name = message.data[1]
|
|
50
50
|
|
|
51
|
+
// TOOD (fix): Validate name
|
|
52
|
+
|
|
53
|
+
const key = h64(name)
|
|
54
|
+
|
|
51
55
|
if (message.action === C.ACTIONS.SUBSCRIPTION_FOR_PATTERN_FOUND) {
|
|
52
|
-
if (this._subscriptions.has(
|
|
56
|
+
if (this._subscriptions.has(key)) {
|
|
53
57
|
this._error(name, 'invalid add: listener exists')
|
|
54
58
|
return
|
|
55
59
|
}
|
|
@@ -57,6 +61,7 @@ export default class Listener {
|
|
|
57
61
|
// TODO (refactor): Move to class
|
|
58
62
|
const provider = {
|
|
59
63
|
name,
|
|
64
|
+
key,
|
|
60
65
|
value$: null,
|
|
61
66
|
sending: false,
|
|
62
67
|
accepted: false,
|
|
@@ -69,7 +74,7 @@ export default class Listener {
|
|
|
69
74
|
if (this.connected && provider.accepted) {
|
|
70
75
|
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [
|
|
71
76
|
this._pattern,
|
|
72
|
-
provider.
|
|
77
|
+
provider.key,
|
|
73
78
|
])
|
|
74
79
|
}
|
|
75
80
|
|
|
@@ -102,7 +107,7 @@ export default class Listener {
|
|
|
102
107
|
this._connection.sendMsg(
|
|
103
108
|
this._topic,
|
|
104
109
|
accepted ? C.ACTIONS.LISTEN_ACCEPT : C.ACTIONS.LISTEN_REJECT,
|
|
105
|
-
[this._pattern, provider.
|
|
110
|
+
[this._pattern, provider.key],
|
|
106
111
|
)
|
|
107
112
|
|
|
108
113
|
provider.version = null
|
|
@@ -158,7 +163,7 @@ export default class Listener {
|
|
|
158
163
|
if (provider.version !== version) {
|
|
159
164
|
provider.version = version
|
|
160
165
|
this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UPDATE, [
|
|
161
|
-
provider.
|
|
166
|
+
provider.key,
|
|
162
167
|
version,
|
|
163
168
|
body,
|
|
164
169
|
])
|
|
@@ -182,9 +187,9 @@ export default class Listener {
|
|
|
182
187
|
|
|
183
188
|
provider.start()
|
|
184
189
|
|
|
185
|
-
this._subscriptions.set(provider.
|
|
190
|
+
this._subscriptions.set(provider.key, provider)
|
|
186
191
|
} else if (message.action === C.ACTIONS.LISTEN_ACCEPT) {
|
|
187
|
-
const provider = this._subscriptions.get(
|
|
192
|
+
const provider = this._subscriptions.get(key)
|
|
188
193
|
if (!provider?.value$) {
|
|
189
194
|
return
|
|
190
195
|
}
|
|
@@ -196,13 +201,13 @@ export default class Listener {
|
|
|
196
201
|
provider.valueSubscription = provider.value$.subscribe(provider.observer)
|
|
197
202
|
}
|
|
198
203
|
} else if (message.action === C.ACTIONS.SUBSCRIPTION_FOR_PATTERN_REMOVED) {
|
|
199
|
-
const provider = this._subscriptions.get(
|
|
204
|
+
const provider = this._subscriptions.get(key)
|
|
200
205
|
|
|
201
206
|
if (!provider) {
|
|
202
207
|
this._error(name, 'invalid remove: listener missing')
|
|
203
208
|
} else {
|
|
204
209
|
provider.stop()
|
|
205
|
-
this._subscriptions.delete(provider.
|
|
210
|
+
this._subscriptions.delete(provider.key)
|
|
206
211
|
}
|
|
207
212
|
} else {
|
|
208
213
|
return false
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as rxjs from 'rxjs'
|
|
2
2
|
import * as C from '../constants/constants.js'
|
|
3
|
-
import { h64ToString } from '../utils/utils.js'
|
|
3
|
+
import { h64, h64ToString } from '../utils/utils.js'
|
|
4
4
|
|
|
5
5
|
const valuePipe = rxjs.pipe(
|
|
6
6
|
rxjs.map((value) => {
|
|
@@ -55,8 +55,12 @@ export default class Listener {
|
|
|
55
55
|
_$onMessage(message) {
|
|
56
56
|
const name = message.data[1]
|
|
57
57
|
|
|
58
|
+
// TODO (fix): Validate name
|
|
59
|
+
|
|
60
|
+
const key = h64(name)
|
|
61
|
+
|
|
58
62
|
if (message.action === C.ACTIONS.LISTEN_ACCEPT) {
|
|
59
|
-
if (this._subscriptions.has(
|
|
63
|
+
if (this._subscriptions.has(key)) {
|
|
60
64
|
this._error(name, 'invalid accept: listener exists')
|
|
61
65
|
return
|
|
62
66
|
}
|
|
@@ -72,29 +76,29 @@ export default class Listener {
|
|
|
72
76
|
const subscription = value$.pipe(valuePipe).subscribe({
|
|
73
77
|
next: (data) => {
|
|
74
78
|
if (data == null) {
|
|
75
|
-
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [this._pattern,
|
|
76
|
-
this._subscriptions.delete(
|
|
79
|
+
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [this._pattern, key])
|
|
80
|
+
this._subscriptions.delete(key)
|
|
77
81
|
subscription.unsubscribe()
|
|
78
82
|
} else {
|
|
79
83
|
const version = `INF-${h64ToString(data)}`
|
|
80
|
-
this._connection.sendMsg(this._topic, C.ACTIONS.UPDATE, [
|
|
84
|
+
this._connection.sendMsg(this._topic, C.ACTIONS.UPDATE, [key, version, data])
|
|
81
85
|
}
|
|
82
86
|
},
|
|
83
87
|
error: (err) => {
|
|
84
88
|
this._error(name, err)
|
|
85
|
-
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [this._pattern,
|
|
86
|
-
this._subscriptions.delete(
|
|
89
|
+
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [this._pattern, key])
|
|
90
|
+
this._subscriptions.delete(key)
|
|
87
91
|
},
|
|
88
92
|
})
|
|
89
|
-
this._subscriptions.set(
|
|
93
|
+
this._subscriptions.set(key, subscription)
|
|
90
94
|
} else {
|
|
91
|
-
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [this._pattern,
|
|
95
|
+
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [this._pattern, key])
|
|
92
96
|
}
|
|
93
97
|
} else if (message.action === C.ACTIONS.LISTEN_REJECT) {
|
|
94
|
-
const subscription = this._subscriptions.get(
|
|
98
|
+
const subscription = this._subscriptions.get(key)
|
|
95
99
|
|
|
96
100
|
if (subscription) {
|
|
97
|
-
this._subscriptions.delete(
|
|
101
|
+
this._subscriptions.delete(key)
|
|
98
102
|
subscription.unsubscribe()
|
|
99
103
|
} else {
|
|
100
104
|
this._error(name, 'invalid remove: listener missing')
|