@nxtedition/deepstream.io-client-js 24.3.3 → 24.4.0
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/.husky/pre-commit +1 -3
- package/package.json +17 -16
- package/src/client.js +2 -4
- package/src/event/event-handler.js +1 -1
- package/src/message/connection.js +27 -32
- package/src/message/message-builder.js +6 -75
- package/src/message/message-parser.js +9 -19
- package/src/record/record-handler.js +45 -39
- package/src/record/record.js +10 -19
- package/src/rpc/rpc-handler.js +2 -2
- package/src/utils/multicast-listener.js +6 -7
- package/src/utils/timers.js +5 -4
- package/src/utils/unicast-listener.js +6 -8
- package/src/utils/utils.js +8 -0
package/.husky/pre-commit
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/deepstream.io-client-js",
|
|
3
|
-
"version": "24.
|
|
3
|
+
"version": "24.4.0",
|
|
4
4
|
"description": "the javascript client for deepstream.io",
|
|
5
5
|
"homepage": "http://deepstream.io",
|
|
6
6
|
"type": "module",
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
"ws": false
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
|
-
"_postinstall": "husky install",
|
|
22
21
|
"prepublishOnly": "pinst --disable",
|
|
23
|
-
"postpublish": "pinst --enable"
|
|
22
|
+
"postpublish": "pinst --enable",
|
|
23
|
+
"prepare": "husky"
|
|
24
24
|
},
|
|
25
25
|
"lint-staged": {
|
|
26
26
|
"*.{js,jsx,md,ts}": [
|
|
@@ -59,30 +59,31 @@
|
|
|
59
59
|
"/__tests__"
|
|
60
60
|
],
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@nxtedition/json-path": "^1.0.
|
|
63
|
-
"bufferutil": "^4.0.
|
|
62
|
+
"@nxtedition/json-path": "^1.0.8",
|
|
63
|
+
"bufferutil": "^4.0.8",
|
|
64
64
|
"component-emitter2": "^1.3.5",
|
|
65
65
|
"invariant": "^2.2.4",
|
|
66
66
|
"lodash.clonedeep": "^4.5.0",
|
|
67
|
-
"utf-8-validate": "^6.0.
|
|
67
|
+
"utf-8-validate": "^6.0.4",
|
|
68
68
|
"varint": "^6.0.0",
|
|
69
|
-
"ws": "^8.
|
|
70
|
-
"xuid": "^4.1.
|
|
69
|
+
"ws": "^8.18.0",
|
|
70
|
+
"xuid": "^4.1.3",
|
|
71
71
|
"xxhash-wasm": "^1.0.2"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"eslint": "^8.
|
|
75
|
-
"eslint-config-prettier": "^
|
|
74
|
+
"eslint": "^8.0.0",
|
|
75
|
+
"eslint-config-prettier": "^9.1.0",
|
|
76
76
|
"eslint-config-standard": "^17.1.0",
|
|
77
|
-
"eslint-plugin-import": "^2.
|
|
78
|
-
"eslint-plugin-n": "^
|
|
77
|
+
"eslint-plugin-import": "^2.29.1",
|
|
78
|
+
"eslint-plugin-n": "^17.10.1",
|
|
79
79
|
"eslint-plugin-node": "^11.1.0",
|
|
80
|
-
"eslint-plugin-promise": "^
|
|
81
|
-
"husky": "^
|
|
82
|
-
"lint-staged": "^
|
|
80
|
+
"eslint-plugin-promise": "^7.0.0",
|
|
81
|
+
"husky": "^9.1.3",
|
|
82
|
+
"lint-staged": "^15.2.7",
|
|
83
83
|
"mitata": "^0.1.11",
|
|
84
84
|
"pinst": "^3.0.0",
|
|
85
|
-
"prettier": "^
|
|
85
|
+
"prettier": "^3.3.3",
|
|
86
|
+
"rxjs": "^7.8.1"
|
|
86
87
|
},
|
|
87
88
|
"peerDependencies": {
|
|
88
89
|
"rxjs": ">=6.x"
|
package/src/client.js
CHANGED
|
@@ -81,7 +81,7 @@ Client.prototype._$onMessage = function (message) {
|
|
|
81
81
|
this._$onError(
|
|
82
82
|
message.topic,
|
|
83
83
|
C.EVENT.MESSAGE_PARSE_ERROR,
|
|
84
|
-
`Received message for unknown topic ${message.topic}
|
|
84
|
+
`Received message for unknown topic ${message.topic}`,
|
|
85
85
|
)
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -127,12 +127,10 @@ Client.prototype._getOptions = function (options) {
|
|
|
127
127
|
return mergedOptions
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
function createDeepstream(url, options) {
|
|
130
|
+
export default function createDeepstream(url, options) {
|
|
131
131
|
return new Client(url, options)
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
Client.prototype.isSameOrNewer = utils.isSameOrNewer
|
|
135
135
|
Client.prototype.CONSTANTS = C
|
|
136
136
|
createDeepstream.CONSTANTS = C
|
|
137
|
-
|
|
138
|
-
export default createDeepstream
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as C from '../constants/constants.js'
|
|
2
2
|
import * as messageBuilder from '../message/message-builder.js'
|
|
3
|
-
import messageParser from '../message/message-parser.js'
|
|
3
|
+
import * as messageParser from '../message/message-parser.js'
|
|
4
4
|
import MulticastListener from '../utils/multicast-listener.js'
|
|
5
5
|
import UnicastListener from '../utils/unicast-listener.js'
|
|
6
6
|
import EventEmitter from 'component-emitter2'
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import * as utils from '../utils/utils.js'
|
|
2
|
-
import messageParser from './message-parser.js'
|
|
2
|
+
import * as messageParser from './message-parser.js'
|
|
3
3
|
import * as messageBuilder from './message-builder.js'
|
|
4
4
|
import * as C from '../constants/constants.js'
|
|
5
|
-
import xxhash from 'xxhash-wasm'
|
|
6
5
|
import FixedQueue from '../utils/fixed-queue.js'
|
|
7
6
|
import Emitter from 'component-emitter2'
|
|
8
7
|
|
|
9
|
-
const BrowserWebSocket = globalThis.WebSocket || globalThis.MozWebSocket
|
|
10
8
|
const NodeWebSocket = utils.isNode ? await import('ws').then((x) => x.default) : null
|
|
9
|
+
const BrowserWebSocket = globalThis.WebSocket || globalThis.MozWebSocket
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const Connection = function (client, url, options) {
|
|
11
|
+
export default function Connection(client, url, options) {
|
|
15
12
|
this._client = client
|
|
16
13
|
this._options = options
|
|
17
14
|
this._logger = options.logger
|
|
@@ -32,6 +29,7 @@ const Connection = function (client, url, options) {
|
|
|
32
29
|
this._recvQueue = new FixedQueue()
|
|
33
30
|
this._reconnectTimeout = null
|
|
34
31
|
this._reconnectionAttempt = 0
|
|
32
|
+
this._endpoint = null
|
|
35
33
|
|
|
36
34
|
this._processingRecv = false
|
|
37
35
|
this._recvMessages = this._recvMessages.bind(this)
|
|
@@ -40,8 +38,6 @@ const Connection = function (client, url, options) {
|
|
|
40
38
|
|
|
41
39
|
this._state = C.CONNECTION_STATE.CLOSED
|
|
42
40
|
|
|
43
|
-
this.hasher = HASHER
|
|
44
|
-
|
|
45
41
|
this._createEndpoint()
|
|
46
42
|
}
|
|
47
43
|
|
|
@@ -92,24 +88,19 @@ Connection.prototype.close = function () {
|
|
|
92
88
|
}
|
|
93
89
|
|
|
94
90
|
Connection.prototype._createEndpoint = function () {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
this._endpoint = new BrowserWebSocket(this._url)
|
|
101
|
-
this._endpoint.binaryType = 'arraybuffer'
|
|
102
|
-
}
|
|
91
|
+
this._endpoint = NodeWebSocket
|
|
92
|
+
? new NodeWebSocket(this._url, {
|
|
93
|
+
generateMask() {},
|
|
94
|
+
})
|
|
95
|
+
: new BrowserWebSocket(this._url)
|
|
103
96
|
this._corked = false
|
|
104
97
|
|
|
105
98
|
this._endpoint.onopen = this._onOpen.bind(this)
|
|
106
99
|
this._endpoint.onerror = this._onError.bind(this)
|
|
107
100
|
this._endpoint.onclose = this._onClose.bind(this)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
this._onMessage(typeof data === 'string' ? data : decoder.decode(data))
|
|
112
|
-
}
|
|
101
|
+
this._endpoint.onmessage = BrowserWebSocket
|
|
102
|
+
? ({ data }) => this._onMessage(typeof data === 'string' ? data : Buffer.from(data).toString())
|
|
103
|
+
: ({ data }) => this._onMessage(typeof data === 'string' ? data : data.toString())
|
|
113
104
|
}
|
|
114
105
|
|
|
115
106
|
Connection.prototype.send = function (message) {
|
|
@@ -121,7 +112,7 @@ Connection.prototype.send = function (message) {
|
|
|
121
112
|
C.TOPIC.CONNECTION,
|
|
122
113
|
C.EVENT.CONNECTION_ERROR,
|
|
123
114
|
err,
|
|
124
|
-
message.split(C.MESSAGE_PART_SEPERATOR).map((x) => x.slice(0, 256))
|
|
115
|
+
message.split(C.MESSAGE_PART_SEPERATOR).map((x) => x.slice(0, 256)),
|
|
125
116
|
)
|
|
126
117
|
return false
|
|
127
118
|
}
|
|
@@ -151,8 +142,8 @@ Connection.prototype.send = function (message) {
|
|
|
151
142
|
Connection.prototype._submit = function (message) {
|
|
152
143
|
const { maxPacketSize } = this._options
|
|
153
144
|
|
|
154
|
-
if (message.
|
|
155
|
-
const err = new Error(`Packet to big: ${message.
|
|
145
|
+
if (message.length > maxPacketSize) {
|
|
146
|
+
const err = new Error(`Packet to big: ${message.length} > ${maxPacketSize}`)
|
|
156
147
|
this._client._$onError(C.TOPIC.CONNECTION, C.EVENT.CONNECTION_ERROR, err)
|
|
157
148
|
return false
|
|
158
149
|
} else if (this._endpoint.readyState === this._endpoint.OPEN) {
|
|
@@ -170,7 +161,7 @@ Connection.prototype._sendAuthParams = function () {
|
|
|
170
161
|
this._setState(C.CONNECTION_STATE.AUTHENTICATING)
|
|
171
162
|
const authMessage = messageBuilder.getMsg(C.TOPIC.AUTH, C.ACTIONS.REQUEST, [
|
|
172
163
|
this._authParams,
|
|
173
|
-
'24.
|
|
164
|
+
'24.4.0',
|
|
174
165
|
utils.isNode
|
|
175
166
|
? `Node/${process.version}`
|
|
176
167
|
: globalThis.navigator && globalThis.navigator.userAgent,
|
|
@@ -276,7 +267,7 @@ Connection.prototype._handleConnectionResponse = function (message) {
|
|
|
276
267
|
} else if (message.action === C.ACTIONS.CHALLENGE) {
|
|
277
268
|
this._setState(C.CONNECTION_STATE.CHALLENGING)
|
|
278
269
|
this._submit(
|
|
279
|
-
messageBuilder.getMsg(C.TOPIC.CONNECTION, C.ACTIONS.CHALLENGE_RESPONSE, [this._url])
|
|
270
|
+
messageBuilder.getMsg(C.TOPIC.CONNECTION, C.ACTIONS.CHALLENGE_RESPONSE, [this._url]),
|
|
280
271
|
)
|
|
281
272
|
} else if (message.action === C.ACTIONS.REJECTION) {
|
|
282
273
|
this._challengeDenied = true
|
|
@@ -344,10 +335,16 @@ Connection.prototype._tryReconnect = function () {
|
|
|
344
335
|
|
|
345
336
|
if (this._reconnectionAttempt < this._options.maxReconnectAttempts) {
|
|
346
337
|
this._setState(C.CONNECTION_STATE.RECONNECTING)
|
|
347
|
-
this._reconnectTimeout = setTimeout(
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
338
|
+
this._reconnectTimeout = setTimeout(
|
|
339
|
+
() => {
|
|
340
|
+
this._reconnectTimeout = null
|
|
341
|
+
this._createEndpoint()
|
|
342
|
+
},
|
|
343
|
+
Math.min(
|
|
344
|
+
this._options.maxReconnectInterval,
|
|
345
|
+
this._options.reconnectIntervalIncrement * this._reconnectionAttempt,
|
|
346
|
+
),
|
|
347
|
+
)
|
|
351
348
|
this._reconnectionAttempt++
|
|
352
349
|
} else {
|
|
353
350
|
this._clearReconnect()
|
|
@@ -363,5 +360,3 @@ Connection.prototype._clearReconnect = function () {
|
|
|
363
360
|
}
|
|
364
361
|
this._reconnectionAttempt = 0
|
|
365
362
|
}
|
|
366
|
-
|
|
367
|
-
export default Connection
|
|
@@ -1,94 +1,25 @@
|
|
|
1
1
|
import * as C from '../constants/constants.js'
|
|
2
|
-
import varint from 'varint'
|
|
3
2
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
let poolSize
|
|
7
|
-
let poolBuffer
|
|
8
|
-
let poolView
|
|
9
|
-
let poolOffset
|
|
10
|
-
|
|
11
|
-
function reallocPool(size) {
|
|
12
|
-
poolSize = size || poolSize || 1024 * 1024
|
|
13
|
-
poolBuffer = new Uint8Array(new ArrayBuffer(poolSize))
|
|
14
|
-
poolView = new DataView(poolBuffer.buffer)
|
|
15
|
-
poolOffset = 0
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function alignPool() {
|
|
19
|
-
// Ensure aligned slices
|
|
20
|
-
if (poolOffset & 0x7) {
|
|
21
|
-
poolOffset |= 0x7
|
|
22
|
-
poolOffset++
|
|
23
|
-
}
|
|
24
|
-
}
|
|
3
|
+
const SEP = C.MESSAGE_PART_SEPERATOR
|
|
25
4
|
|
|
26
5
|
export function getMsg(topic, action, data) {
|
|
27
6
|
if (data && !(data instanceof Array)) {
|
|
28
7
|
throw new Error('data must be an array')
|
|
29
8
|
}
|
|
30
9
|
|
|
31
|
-
|
|
32
|
-
reallocPool()
|
|
33
|
-
} else {
|
|
34
|
-
alignPool()
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const start = poolOffset
|
|
38
|
-
|
|
39
|
-
const headerSize = 8
|
|
40
|
-
poolBuffer[poolOffset++] = 128 + headerSize
|
|
41
|
-
let headerPos = poolOffset
|
|
42
|
-
poolOffset += headerSize - 1
|
|
43
|
-
|
|
44
|
-
poolBuffer[poolOffset++] = topic.charCodeAt(0)
|
|
45
|
-
poolBuffer[poolOffset++] = 31
|
|
46
|
-
for (let n = 0; n < action.length; n++) {
|
|
47
|
-
poolBuffer[poolOffset++] = action.charCodeAt(n)
|
|
48
|
-
}
|
|
10
|
+
const sendData = [topic, action]
|
|
49
11
|
|
|
50
12
|
if (data) {
|
|
51
13
|
for (let i = 0; i < data.length; i++) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (data[i] == null) {
|
|
55
|
-
poolBuffer[poolOffset++] = 31
|
|
56
|
-
len = 0
|
|
57
|
-
} else if (type === 'object') {
|
|
58
|
-
poolBuffer[poolOffset++] = 31
|
|
59
|
-
const res = poolEncoder.encodeInto(
|
|
60
|
-
JSON.stringify(data[i]),
|
|
61
|
-
new Uint8Array(poolBuffer.buffer, poolOffset)
|
|
62
|
-
)
|
|
63
|
-
len = res.written
|
|
64
|
-
} else if (type === 'bigint') {
|
|
65
|
-
poolBuffer[poolOffset++] = 31
|
|
66
|
-
poolView.setBigUint64(poolOffset, data[i], false)
|
|
67
|
-
len = 8
|
|
68
|
-
} else if (type === 'string') {
|
|
69
|
-
poolBuffer[poolOffset++] = 31
|
|
70
|
-
const res = poolEncoder.encodeInto(data[i], new Uint8Array(poolBuffer.buffer, poolOffset))
|
|
71
|
-
len = res.written
|
|
14
|
+
if (typeof data[i] === 'object') {
|
|
15
|
+
sendData.push(JSON.stringify(data[i]))
|
|
72
16
|
} else {
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
poolOffset += len
|
|
77
|
-
|
|
78
|
-
varint.encode(len + 1, poolBuffer, headerPos)
|
|
79
|
-
headerPos += varint.encode.bytes
|
|
80
|
-
if (headerPos - start >= headerSize) {
|
|
81
|
-
throw new Error(`header too large: ${headerPos - start} ${headerSize}`)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (poolOffset >= poolBuffer.length) {
|
|
85
|
-
reallocPool(start === 0 ? poolSize * 2 : poolSize)
|
|
86
|
-
return getMsg(topic, action, data)
|
|
17
|
+
sendData.push(data[i])
|
|
87
18
|
}
|
|
88
19
|
}
|
|
89
20
|
}
|
|
90
21
|
|
|
91
|
-
return
|
|
22
|
+
return sendData.join(SEP)
|
|
92
23
|
}
|
|
93
24
|
|
|
94
25
|
export function typed(value) {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import * as C from '../constants/constants.js'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const actions = {}
|
|
4
|
+
|
|
5
|
+
for (const key in C.ACTIONS) {
|
|
6
|
+
actions[C.ACTIONS[key]] = key
|
|
5
7
|
}
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
export function convertTyped(value, client) {
|
|
8
10
|
const type = value.charAt(0)
|
|
9
11
|
|
|
10
12
|
if (type === C.TYPES.STRING) {
|
|
@@ -45,24 +47,14 @@ MessageParser.prototype.convertTyped = function (value, client) {
|
|
|
45
47
|
return undefined
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
const actions = {}
|
|
50
|
-
|
|
51
|
-
for (const key in C.ACTIONS) {
|
|
52
|
-
actions[C.ACTIONS[key]] = key
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return actions
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
MessageParser.prototype.parseMessage = function (message, client, result) {
|
|
50
|
+
export function parseMessage(message, client, result) {
|
|
59
51
|
const parts = message.split(C.MESSAGE_PART_SEPERATOR)
|
|
60
52
|
|
|
61
53
|
if (parts.length < 2) {
|
|
62
54
|
client._$onError(
|
|
63
55
|
C.TOPIC.ERROR,
|
|
64
56
|
C.EVENT.MESSAGE_PARSE_ERROR,
|
|
65
|
-
new Error('Insufficiant message parts')
|
|
57
|
+
new Error('Insufficiant message parts'),
|
|
66
58
|
)
|
|
67
59
|
return null
|
|
68
60
|
}
|
|
@@ -72,12 +64,12 @@ MessageParser.prototype.parseMessage = function (message, client, result) {
|
|
|
72
64
|
return null
|
|
73
65
|
}
|
|
74
66
|
|
|
75
|
-
if (
|
|
67
|
+
if (actions[parts[1]] === undefined) {
|
|
76
68
|
client._$onError(
|
|
77
69
|
C.TOPIC.ERROR,
|
|
78
70
|
C.EVENT.MESSAGE_PARSE_ERROR,
|
|
79
71
|
new Error('Unknown action'),
|
|
80
|
-
message
|
|
72
|
+
message,
|
|
81
73
|
)
|
|
82
74
|
return null
|
|
83
75
|
}
|
|
@@ -87,5 +79,3 @@ MessageParser.prototype.parseMessage = function (message, client, result) {
|
|
|
87
79
|
result.action = parts[1]
|
|
88
80
|
result.data = parts.splice(2)
|
|
89
81
|
}
|
|
90
|
-
|
|
91
|
-
export default new MessageParser()
|
|
@@ -49,8 +49,8 @@ function onTimeout(subscription) {
|
|
|
49
49
|
new Error(`timeout state: ${subscription.record.name} [${current}<${expected}]`),
|
|
50
50
|
{
|
|
51
51
|
code: 'ETIMEDOUT',
|
|
52
|
-
}
|
|
53
|
-
)
|
|
52
|
+
},
|
|
53
|
+
),
|
|
54
54
|
)
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -101,7 +101,6 @@ class RecordHandler {
|
|
|
101
101
|
this._pruning = new Set()
|
|
102
102
|
this._patching = new Map()
|
|
103
103
|
this._updating = new Map()
|
|
104
|
-
this._encoder = new TextEncoder()
|
|
105
104
|
|
|
106
105
|
this._connected = 0
|
|
107
106
|
this._stats = {
|
|
@@ -207,12 +206,6 @@ class RecordHandler {
|
|
|
207
206
|
}
|
|
208
207
|
}
|
|
209
208
|
|
|
210
|
-
_getKey(name) {
|
|
211
|
-
return name.length <= 8 && this._encoder.encode(name).byteLength === 8
|
|
212
|
-
? name
|
|
213
|
-
: this._connection.hasher.h64(name)
|
|
214
|
-
}
|
|
215
|
-
|
|
216
209
|
/**
|
|
217
210
|
* @param {string} name
|
|
218
211
|
* @returns {Record}
|
|
@@ -220,13 +213,13 @@ class RecordHandler {
|
|
|
220
213
|
getRecord(name) {
|
|
221
214
|
invariant(
|
|
222
215
|
typeof name === 'string' && name.length > 0 && name !== '[object Object]',
|
|
223
|
-
`invalid name ${name}
|
|
216
|
+
`invalid name ${name}`,
|
|
224
217
|
)
|
|
225
218
|
|
|
226
219
|
let record = this._records.get(name)
|
|
227
220
|
|
|
228
221
|
if (!record) {
|
|
229
|
-
record = new Record(
|
|
222
|
+
record = new Record(name, this)
|
|
230
223
|
this._stats.records += 1
|
|
231
224
|
this._stats.created += 1
|
|
232
225
|
this._records.set(name, record)
|
|
@@ -294,17 +287,22 @@ class RecordHandler {
|
|
|
294
287
|
const patching = [...this._patching.values()]
|
|
295
288
|
await Promise.race([
|
|
296
289
|
Promise.all(
|
|
297
|
-
patching.map((callbacks) => new Promise((resolve) => callbacks.push(resolve)))
|
|
290
|
+
patching.map((callbacks) => new Promise((resolve) => callbacks.push(resolve))),
|
|
298
291
|
),
|
|
299
292
|
new Promise((resolve) => {
|
|
300
|
-
patchingTimeout = timers.setTimeout(
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
293
|
+
patchingTimeout = timers.setTimeout(
|
|
294
|
+
() => {
|
|
295
|
+
this._client._$onError(
|
|
296
|
+
C.TOPIC.RECORD,
|
|
297
|
+
C.EVENT.TIMEOUT,
|
|
298
|
+
Object.assign(new Error('sync patching timeout'), {
|
|
299
|
+
data: { patching, timeout },
|
|
300
|
+
}),
|
|
301
|
+
)
|
|
302
|
+
resolve(null)
|
|
303
|
+
},
|
|
304
|
+
timeout ?? 2 * 60e3,
|
|
305
|
+
)
|
|
308
306
|
}),
|
|
309
307
|
signalPromise,
|
|
310
308
|
]).finally(() => {
|
|
@@ -317,17 +315,22 @@ class RecordHandler {
|
|
|
317
315
|
const updating = [...this._updating.values()]
|
|
318
316
|
await Promise.race([
|
|
319
317
|
Promise.all(
|
|
320
|
-
updating.map((callbacks) => new Promise((resolve) => callbacks.push(resolve)))
|
|
318
|
+
updating.map((callbacks) => new Promise((resolve) => callbacks.push(resolve))),
|
|
321
319
|
),
|
|
322
320
|
new Promise((resolve) => {
|
|
323
|
-
updatingTimeout = timers.setTimeout(
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
321
|
+
updatingTimeout = timers.setTimeout(
|
|
322
|
+
() => {
|
|
323
|
+
this._client._$onError(
|
|
324
|
+
C.TOPIC.RECORD,
|
|
325
|
+
C.EVENT.TIMEOUT,
|
|
326
|
+
Object.assign(new Error('sync updating timeout'), {
|
|
327
|
+
data: { updating, timeout },
|
|
328
|
+
}),
|
|
329
|
+
)
|
|
330
|
+
resolve(null)
|
|
331
|
+
},
|
|
332
|
+
timeout ?? 2 * 60e3,
|
|
333
|
+
)
|
|
331
334
|
}),
|
|
332
335
|
signalPromise,
|
|
333
336
|
]).finally(() => {
|
|
@@ -343,14 +346,17 @@ class RecordHandler {
|
|
|
343
346
|
this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SYNC, [token])
|
|
344
347
|
}),
|
|
345
348
|
new Promise((resolve) => {
|
|
346
|
-
serverTimeout = timers.setTimeout(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
349
|
+
serverTimeout = timers.setTimeout(
|
|
350
|
+
() => {
|
|
351
|
+
this._client._$onError(
|
|
352
|
+
C.TOPIC.RECORD,
|
|
353
|
+
C.EVENT.TIMEOUT,
|
|
354
|
+
Object.assign(new Error('sync server timeout'), { data: { token, timeout } }),
|
|
355
|
+
)
|
|
356
|
+
resolve(null)
|
|
357
|
+
},
|
|
358
|
+
timeout ?? 2 * 60e3,
|
|
359
|
+
)
|
|
354
360
|
}),
|
|
355
361
|
signalPromise,
|
|
356
362
|
]).finally(() => {
|
|
@@ -423,7 +429,7 @@ class RecordHandler {
|
|
|
423
429
|
timeout: 2 * 60e3,
|
|
424
430
|
dataOnly: true,
|
|
425
431
|
},
|
|
426
|
-
...args
|
|
432
|
+
...args,
|
|
427
433
|
)
|
|
428
434
|
}
|
|
429
435
|
|
|
@@ -499,7 +505,7 @@ class RecordHandler {
|
|
|
499
505
|
{
|
|
500
506
|
timeout: 2 * 60e3,
|
|
501
507
|
},
|
|
502
|
-
...args
|
|
508
|
+
...args,
|
|
503
509
|
)
|
|
504
510
|
}
|
|
505
511
|
|
package/src/record/record.js
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
import jsonPath from '@nxtedition/json-path'
|
|
2
2
|
import * as utils from '../utils/utils.js'
|
|
3
3
|
import * as C from '../constants/constants.js'
|
|
4
|
-
import messageParser from '../message/message-parser.js'
|
|
4
|
+
import * as messageParser from '../message/message-parser.js'
|
|
5
5
|
import xuid from 'xuid'
|
|
6
6
|
import invariant from 'invariant'
|
|
7
7
|
import cloneDeep from 'lodash.clonedeep'
|
|
8
8
|
import * as timers from '../utils/timers.js'
|
|
9
9
|
|
|
10
|
-
// const encoder = new TextEncoder()
|
|
11
|
-
|
|
12
10
|
class Record {
|
|
13
11
|
static STATE = C.RECORD_STATE
|
|
14
12
|
|
|
15
|
-
constructor(
|
|
13
|
+
constructor(name, handler) {
|
|
16
14
|
const connection = handler._connection
|
|
17
15
|
|
|
18
16
|
this._handler = handler
|
|
19
|
-
|
|
20
17
|
this._name = name
|
|
21
|
-
this._key = key
|
|
22
18
|
this._version = ''
|
|
23
19
|
this._data = jsonPath.EMPTY
|
|
24
20
|
this._state = C.RECORD_STATE.VOID
|
|
@@ -28,10 +24,7 @@ class Record {
|
|
|
28
24
|
|
|
29
25
|
/** @type Map? */ this._updating = null
|
|
30
26
|
/** @type Array? */ this._patching = null
|
|
31
|
-
this._subscribed = connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [
|
|
32
|
-
this._name,
|
|
33
|
-
this._key,
|
|
34
|
-
])
|
|
27
|
+
this._subscribed = connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [this._name])
|
|
35
28
|
}
|
|
36
29
|
|
|
37
30
|
/** @type {string} */
|
|
@@ -69,8 +62,7 @@ class Record {
|
|
|
69
62
|
if (this._refs === 1) {
|
|
70
63
|
this._handler._onPruning(this, false)
|
|
71
64
|
this._subscribed =
|
|
72
|
-
this._subscribed ||
|
|
73
|
-
connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [this._name, this._key])
|
|
65
|
+
this._subscribed || connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [this._name])
|
|
74
66
|
}
|
|
75
67
|
return this
|
|
76
68
|
}
|
|
@@ -257,7 +249,7 @@ class Record {
|
|
|
257
249
|
onDone(
|
|
258
250
|
Object.assign(new Error(`timeout ${this.name} [${current}<${expected}]`), {
|
|
259
251
|
code: 'ETIMEDOUT',
|
|
260
|
-
})
|
|
252
|
+
}),
|
|
261
253
|
)
|
|
262
254
|
}, timeout)
|
|
263
255
|
}
|
|
@@ -332,8 +324,7 @@ class Record {
|
|
|
332
324
|
|
|
333
325
|
if (connected) {
|
|
334
326
|
this._subscribed =
|
|
335
|
-
this._refs > 0 &&
|
|
336
|
-
connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [this._name, this._key])
|
|
327
|
+
this._refs > 0 && connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [this._name])
|
|
337
328
|
|
|
338
329
|
if (this._updating) {
|
|
339
330
|
for (const update of this._updating.values()) {
|
|
@@ -358,7 +349,7 @@ class Record {
|
|
|
358
349
|
invariant(!this._updating, 'must not have updates')
|
|
359
350
|
|
|
360
351
|
if (this._subscribed) {
|
|
361
|
-
connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UNSUBSCRIBE, [this.
|
|
352
|
+
connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UNSUBSCRIBE, [this._name])
|
|
362
353
|
this._subscribed = false
|
|
363
354
|
}
|
|
364
355
|
|
|
@@ -380,7 +371,7 @@ class Record {
|
|
|
380
371
|
const prevVersion = this._version
|
|
381
372
|
const nextVersion = this._makeVersion(parseInt(prevVersion) + 1)
|
|
382
373
|
|
|
383
|
-
const update = [this.
|
|
374
|
+
const update = [this._name, nextVersion, jsonPath.stringify(nextData), prevVersion]
|
|
384
375
|
|
|
385
376
|
if (!this._updating) {
|
|
386
377
|
this._onUpdating(true)
|
|
@@ -479,8 +470,8 @@ class Record {
|
|
|
479
470
|
hasProvider && messageParser.convertTyped(hasProvider, this._handler._client)
|
|
480
471
|
? C.RECORD_STATE.PROVIDER
|
|
481
472
|
: this._version.charAt(0) === 'I'
|
|
482
|
-
|
|
483
|
-
|
|
473
|
+
? C.RECORD_STATE.STALE
|
|
474
|
+
: C.RECORD_STATE.SERVER
|
|
484
475
|
|
|
485
476
|
if (this._state !== prevState) {
|
|
486
477
|
this._emitUpdate()
|
package/src/rpc/rpc-handler.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as C from '../constants/constants.js'
|
|
2
2
|
import RpcResponse from './rpc-response.js'
|
|
3
|
-
import messageParser from '../message/message-parser.js'
|
|
3
|
+
import * as messageParser from '../message/message-parser.js'
|
|
4
4
|
import * as messageBuilder from '../message/message-builder.js'
|
|
5
5
|
import xuid from 'xuid'
|
|
6
6
|
|
|
@@ -153,7 +153,7 @@ RpcHandler.prototype._$handle = function (message) {
|
|
|
153
153
|
rpcId: rpc.id,
|
|
154
154
|
rpcName: rpc.name,
|
|
155
155
|
rpcData: rpc.data,
|
|
156
|
-
})
|
|
156
|
+
}),
|
|
157
157
|
)
|
|
158
158
|
} else {
|
|
159
159
|
rpc.callback(null, messageParser.convertTyped(data, this._client))
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import * as rxjs from 'rxjs'
|
|
1
2
|
import * as C from '../constants/constants.js'
|
|
2
|
-
import
|
|
3
|
+
import { h64ToString } from '../utils/utils.js'
|
|
3
4
|
|
|
4
|
-
class Listener {
|
|
5
|
+
export default class Listener {
|
|
5
6
|
constructor(topic, pattern, callback, handler, { recursive = false, stringify = null } = {}) {
|
|
6
7
|
this._topic = topic
|
|
7
8
|
this._pattern = pattern
|
|
@@ -40,7 +41,7 @@ class Listener {
|
|
|
40
41
|
C.TOPIC.RECORD,
|
|
41
42
|
C.EVENT.NOT_CONNECTED,
|
|
42
43
|
new Error('received message while not connected'),
|
|
43
|
-
message
|
|
44
|
+
message,
|
|
44
45
|
)
|
|
45
46
|
return
|
|
46
47
|
}
|
|
@@ -101,7 +102,7 @@ class Listener {
|
|
|
101
102
|
this._connection.sendMsg(
|
|
102
103
|
this._topic,
|
|
103
104
|
accepted ? C.ACTIONS.LISTEN_ACCEPT : C.ACTIONS.LISTEN_REJECT,
|
|
104
|
-
[this._pattern, provider.name]
|
|
105
|
+
[this._pattern, provider.name],
|
|
105
106
|
)
|
|
106
107
|
|
|
107
108
|
provider.version = null
|
|
@@ -151,7 +152,7 @@ class Listener {
|
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
const body = typeof value !== 'string' ? this._stringify(value) : value
|
|
154
|
-
const hash =
|
|
155
|
+
const hash = h64ToString(body)
|
|
155
156
|
const version = `INF-${hash}`
|
|
156
157
|
|
|
157
158
|
if (provider.version !== version) {
|
|
@@ -228,5 +229,3 @@ class Listener {
|
|
|
228
229
|
this._subscriptions.clear()
|
|
229
230
|
}
|
|
230
231
|
}
|
|
231
|
-
|
|
232
|
-
export default Listener
|
package/src/utils/timers.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
const fastNowInterval = 1e3
|
|
2
|
+
let fastNow = 0
|
|
2
3
|
let fastNowTimeout
|
|
3
4
|
|
|
4
5
|
const fastTimers = []
|
|
5
6
|
|
|
6
7
|
function onTimeout() {
|
|
7
|
-
fastNow
|
|
8
|
+
fastNow += fastNowInterval
|
|
8
9
|
|
|
9
10
|
let len = fastTimers.length
|
|
10
11
|
let idx = 0
|
|
@@ -41,7 +42,7 @@ function refreshTimeout() {
|
|
|
41
42
|
fastNowTimeout.refresh()
|
|
42
43
|
} else {
|
|
43
44
|
globalThis.clearTimeout(fastNowTimeout)
|
|
44
|
-
fastNowTimeout = globalThis.setTimeout(onTimeout,
|
|
45
|
+
fastNowTimeout = globalThis.setTimeout(onTimeout, fastNowInterval)
|
|
45
46
|
if (fastNowTimeout.unref) {
|
|
46
47
|
fastNowTimeout.unref()
|
|
47
48
|
}
|
|
@@ -80,7 +81,7 @@ class Timeout {
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
export function setTimeout(callback, delay, opaque) {
|
|
83
|
-
return delay <
|
|
84
|
+
return delay < fastNowInterval
|
|
84
85
|
? globalThis.setTimeout(callback, delay, opaque)
|
|
85
86
|
: new Timeout(callback, delay, opaque)
|
|
86
87
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import * as rxjs from 'rxjs'
|
|
1
2
|
import * as C from '../constants/constants.js'
|
|
2
|
-
import
|
|
3
|
-
import rxjs from 'rxjs'
|
|
3
|
+
import { h64ToString } from '../utils/utils.js'
|
|
4
4
|
|
|
5
5
|
const PIPE = rxjs.pipe(
|
|
6
|
-
|
|
6
|
+
rxjs.map((value) => {
|
|
7
7
|
let data
|
|
8
8
|
if (value && typeof value === 'string') {
|
|
9
9
|
if (value.charAt(0) !== '{' && value.charAt(0) !== '[') {
|
|
@@ -18,10 +18,10 @@ const PIPE = rxjs.pipe(
|
|
|
18
18
|
|
|
19
19
|
return data
|
|
20
20
|
}),
|
|
21
|
-
|
|
21
|
+
rxjs.distinctUntilChanged(),
|
|
22
22
|
)
|
|
23
23
|
|
|
24
|
-
class Listener {
|
|
24
|
+
export default class Listener {
|
|
25
25
|
constructor(topic, pattern, callback, handler, opts) {
|
|
26
26
|
if (opts.recursive) {
|
|
27
27
|
throw new Error('invalid argument: recursive')
|
|
@@ -76,7 +76,7 @@ class Listener {
|
|
|
76
76
|
this._subscriptions.delete(name)
|
|
77
77
|
subscription.unsubscribe()
|
|
78
78
|
} else {
|
|
79
|
-
const version = `INF-${
|
|
79
|
+
const version = `INF-${h64ToString(data)}`
|
|
80
80
|
this._connection.sendMsg(this._topic, C.ACTIONS.UPDATE, [name, version, data])
|
|
81
81
|
}
|
|
82
82
|
},
|
|
@@ -126,5 +126,3 @@ class Listener {
|
|
|
126
126
|
this._connection.sendMsg(this._topic, C.ACTIONS.UNLISTEN, [this._pattern])
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
-
|
|
130
|
-
export default Listener
|
package/src/utils/utils.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import xxhash from 'xxhash-wasm'
|
|
2
|
+
|
|
1
3
|
const NODE_ENV = typeof process !== 'undefined' && process.env && process.env.NODE_ENV
|
|
2
4
|
export const isNode = typeof process !== 'undefined' && process.toString() === '[object process]'
|
|
3
5
|
export const isProduction = NODE_ENV === 'production'
|
|
@@ -174,3 +176,9 @@ export function removeAbortListener(signal, handler) {
|
|
|
174
176
|
}
|
|
175
177
|
}
|
|
176
178
|
}
|
|
179
|
+
|
|
180
|
+
const HASHER = await xxhash()
|
|
181
|
+
|
|
182
|
+
export function h64ToString(str) {
|
|
183
|
+
return HASHER.h64ToString(str)
|
|
184
|
+
}
|