@nxtedition/deepstream.io-client-js 27.0.1 → 28.0.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/benchmark/message-builder.mjs +47 -0
- package/eslint.config.mjs +80 -0
- package/package.json +9 -33
- package/src/event/event-handler.js +2 -2
- package/src/message/connection.js +22 -12
- package/src/message/message-builder.js +7 -4
- package/src/record/record-handler.js +4 -18
- package/src/record/record.js +8 -17
- package/src/utils/multicast-listener.js +9 -14
- package/src/utils/timers.js +5 -4
- package/src/utils/unicast-listener.js +13 -17
- package/src/utils/utils.js +5 -10
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto'
|
|
2
|
+
import { Bench } from 'tinybench'
|
|
3
|
+
import { getMsg } from '../src/message/message-builder.js'
|
|
4
|
+
|
|
5
|
+
const bench = new Bench()
|
|
6
|
+
|
|
7
|
+
const name = randomBytes(16).toString('hex')
|
|
8
|
+
const version = randomBytes(16).toString('hex')
|
|
9
|
+
const body = randomBytes(256).toString('hex')
|
|
10
|
+
|
|
11
|
+
function getMsgString(topic, action, data) {
|
|
12
|
+
let msg = `R${topic}U${action}`
|
|
13
|
+
if (data) {
|
|
14
|
+
for (let i = 0; i < data.length; i++) {
|
|
15
|
+
const type = typeof data[i]
|
|
16
|
+
if (data[i] == null) {
|
|
17
|
+
msg += '\u0001'
|
|
18
|
+
msg += ''
|
|
19
|
+
} else if (type === 'object') {
|
|
20
|
+
msg += '\u0001'
|
|
21
|
+
msg += JSON.stringify(data[i])
|
|
22
|
+
} else if (type === 'bigint') {
|
|
23
|
+
msg += '\u0001'
|
|
24
|
+
msg += data[i].toString()
|
|
25
|
+
} else if (type === 'string') {
|
|
26
|
+
msg += '\u0001'
|
|
27
|
+
msg += data[i]
|
|
28
|
+
} else {
|
|
29
|
+
throw new Error('invalid data')
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return msg
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
bench
|
|
37
|
+
.add('build message string', () => {
|
|
38
|
+
getMsgString('R', 'U', [ name, version, body ])
|
|
39
|
+
})
|
|
40
|
+
.add('build message buffer', () => {
|
|
41
|
+
getMsg('R', 'U', [ name, version, body ])
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
await bench.warmup()
|
|
45
|
+
await bench.run()
|
|
46
|
+
|
|
47
|
+
console.table(bench.table())
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import eslint from '@eslint/js'
|
|
2
|
+
import tseslint from 'typescript-eslint'
|
|
3
|
+
import eslintConfigPrettier from 'eslint-config-prettier'
|
|
4
|
+
import eslintPluginImport from 'eslint-plugin-import'
|
|
5
|
+
import eslintPluginN from 'eslint-plugin-n'
|
|
6
|
+
import eslintPluginPromise from 'eslint-plugin-promise'
|
|
7
|
+
import typescriptEslintParser from '@typescript-eslint/parser'
|
|
8
|
+
import globals from 'globals'
|
|
9
|
+
|
|
10
|
+
const reactFiles = [
|
|
11
|
+
'app/src/renderer/**/*{js,ts,tsx}',
|
|
12
|
+
'hub/src/client/**/*.{js,ts,tsx}',
|
|
13
|
+
'hub/.storybook/**/*.{js,ts,tsx}',
|
|
14
|
+
'plugins/adobe/**/*{js,ts,tsx}',
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
export default tseslint.config(
|
|
18
|
+
eslint.configs.recommended,
|
|
19
|
+
...tseslint.configs.recommended,
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
ignores: [
|
|
23
|
+
'tmp*',
|
|
24
|
+
'benchmarks/**/*',
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
// Base config:
|
|
29
|
+
{
|
|
30
|
+
plugins: {
|
|
31
|
+
import: eslintPluginImport,
|
|
32
|
+
promise: eslintPluginPromise,
|
|
33
|
+
},
|
|
34
|
+
languageOptions: {
|
|
35
|
+
parser: typescriptEslintParser,
|
|
36
|
+
parserOptions: {
|
|
37
|
+
tsconfigRootDir: import.meta.dirname,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
rules: {
|
|
41
|
+
'prefer-const': [
|
|
42
|
+
'error',
|
|
43
|
+
{
|
|
44
|
+
destructuring: 'all',
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
'no-empty': ['error', { allowEmptyCatch: true }],
|
|
48
|
+
'no-constant-condition': ['error', { checkLoops: false }],
|
|
49
|
+
'@typescript-eslint/no-unused-vars': [
|
|
50
|
+
'error',
|
|
51
|
+
{
|
|
52
|
+
args: 'none',
|
|
53
|
+
ignoreRestSiblings: true,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
'getter-return': 'off',
|
|
57
|
+
'object-shorthand': ['warn', 'properties'],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// Node specific
|
|
62
|
+
{
|
|
63
|
+
ignores: [...reactFiles],
|
|
64
|
+
languageOptions: {
|
|
65
|
+
globals: {
|
|
66
|
+
...globals.node,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
plugins: {
|
|
70
|
+
n: eslintPluginN,
|
|
71
|
+
},
|
|
72
|
+
rules: {
|
|
73
|
+
'no-redeclare': 'warn',
|
|
74
|
+
'require-yield': 'off',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// Prettier:
|
|
79
|
+
eslintConfigPrettier
|
|
80
|
+
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/deepstream.io-client-js",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "28.0.0",
|
|
4
4
|
"description": "the javascript client for deepstream.io",
|
|
5
5
|
"homepage": "http://deepstream.io",
|
|
6
6
|
"type": "module",
|
|
@@ -33,31 +33,6 @@
|
|
|
33
33
|
"semi": false,
|
|
34
34
|
"singleQuote": true
|
|
35
35
|
},
|
|
36
|
-
"eslintConfig": {
|
|
37
|
-
"parserOptions": {
|
|
38
|
-
"ecmaFeatures": {
|
|
39
|
-
"ecmaVersion": 2020
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
"extends": [
|
|
43
|
-
"standard",
|
|
44
|
-
"prettier",
|
|
45
|
-
"prettier/prettier"
|
|
46
|
-
],
|
|
47
|
-
"rules": {
|
|
48
|
-
"quotes": [
|
|
49
|
-
"error",
|
|
50
|
-
"single",
|
|
51
|
-
{
|
|
52
|
-
"avoidEscape": true,
|
|
53
|
-
"allowTemplateLiterals": true
|
|
54
|
-
}
|
|
55
|
-
]
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
"eslintIgnore": [
|
|
59
|
-
"/__tests__"
|
|
60
|
-
],
|
|
61
36
|
"dependencies": {
|
|
62
37
|
"@nxtedition/json-path": "^1.0.8",
|
|
63
38
|
"bufferutil": "^4.0.8",
|
|
@@ -71,20 +46,21 @@
|
|
|
71
46
|
"xxhash-wasm": "^1.0.2"
|
|
72
47
|
},
|
|
73
48
|
"devDependencies": {
|
|
74
|
-
"
|
|
75
|
-
"eslint": "^8.0.0",
|
|
49
|
+
"eslint": "^9.9.0",
|
|
76
50
|
"eslint-config-prettier": "^9.1.0",
|
|
77
51
|
"eslint-config-standard": "^17.1.0",
|
|
78
52
|
"eslint-plugin-import": "^2.29.1",
|
|
79
|
-
"eslint-plugin-n": "^17.10.
|
|
53
|
+
"eslint-plugin-n": "^17.10.2",
|
|
80
54
|
"eslint-plugin-node": "^11.1.0",
|
|
81
|
-
"eslint-plugin-promise": "^7.
|
|
82
|
-
"husky": "^9.1.
|
|
83
|
-
"lint-staged": "^15.2.
|
|
55
|
+
"eslint-plugin-promise": "^7.1.0",
|
|
56
|
+
"husky": "^9.1.4",
|
|
57
|
+
"lint-staged": "^15.2.8",
|
|
84
58
|
"mitata": "^0.1.11",
|
|
85
59
|
"pinst": "^3.0.0",
|
|
86
60
|
"prettier": "^3.3.3",
|
|
87
|
-
"rxjs": "^7.8.1"
|
|
61
|
+
"rxjs": "^7.8.1",
|
|
62
|
+
"typescript": "^5.5.4",
|
|
63
|
+
"typescript-eslint": "^8.0.1"
|
|
88
64
|
},
|
|
89
65
|
"peerDependencies": {
|
|
90
66
|
"rxjs": ">=6.x"
|
|
@@ -4,7 +4,7 @@ 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'
|
|
7
|
-
import rxjs from 'rxjs'
|
|
7
|
+
import * as rxjs from 'rxjs'
|
|
8
8
|
|
|
9
9
|
const EventHandler = function (options, connection, client) {
|
|
10
10
|
this._options = options
|
|
@@ -79,7 +79,7 @@ EventHandler.on = function (name, callback) {
|
|
|
79
79
|
EventHandler.once = function (name, callback) {
|
|
80
80
|
const fn = (...args) => {
|
|
81
81
|
this.unsubscribe(fn)
|
|
82
|
-
callback(...args)
|
|
82
|
+
callback(...args)
|
|
83
83
|
}
|
|
84
84
|
this.subscribe(name, fn)
|
|
85
85
|
return this
|
|
@@ -4,8 +4,9 @@ import * as messageBuilder from './message-builder.js'
|
|
|
4
4
|
import * as C from '../constants/constants.js'
|
|
5
5
|
import FixedQueue from '../utils/fixed-queue.js'
|
|
6
6
|
import Emitter from 'component-emitter2'
|
|
7
|
+
import pkg from '../../package.json' with { type: 'json' }
|
|
8
|
+
import NodeWebSocket from 'ws'
|
|
7
9
|
|
|
8
|
-
const NodeWebSocket = utils.isNode ? await import('ws').then((x) => x.default) : null
|
|
9
10
|
const BrowserWebSocket = globalThis.WebSocket || globalThis.MozWebSocket
|
|
10
11
|
|
|
11
12
|
export default function Connection(client, url, options) {
|
|
@@ -26,6 +27,7 @@ export default function Connection(client, url, options) {
|
|
|
26
27
|
action: null,
|
|
27
28
|
data: null,
|
|
28
29
|
}
|
|
30
|
+
this._decoder = new globalThis.TextDecoder()
|
|
29
31
|
this._recvQueue = new FixedQueue()
|
|
30
32
|
this._reconnectTimeout = null
|
|
31
33
|
this._reconnectionAttempt = 0
|
|
@@ -88,19 +90,23 @@ Connection.prototype.close = function () {
|
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
Connection.prototype._createEndpoint = function () {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
if (utils.isNode) {
|
|
94
|
+
this._endpoint = new NodeWebSocket(this._url, {
|
|
95
|
+
generateMask() {},
|
|
96
|
+
})
|
|
97
|
+
this._endpoint.binaryType = 'nodebuffer'
|
|
98
|
+
} else {
|
|
99
|
+
this._endpoint = new BrowserWebSocket(this._url)
|
|
100
|
+
this._endpoint.binaryType = 'arraybuffer'
|
|
101
|
+
}
|
|
102
|
+
|
|
96
103
|
this._corked = false
|
|
97
104
|
|
|
98
105
|
this._endpoint.onopen = this._onOpen.bind(this)
|
|
99
106
|
this._endpoint.onerror = this._onError.bind(this)
|
|
100
107
|
this._endpoint.onclose = this._onClose.bind(this)
|
|
101
|
-
this._endpoint.onmessage =
|
|
102
|
-
|
|
103
|
-
: ({ data }) => this._onMessage(typeof data === 'string' ? data : data.toString())
|
|
108
|
+
this._endpoint.onmessage = ({ data }) =>
|
|
109
|
+
this._onMessage(typeof data === 'string' ? data : this._decoder.decode(data))
|
|
104
110
|
}
|
|
105
111
|
|
|
106
112
|
Connection.prototype.send = function (message) {
|
|
@@ -117,6 +123,10 @@ Connection.prototype.send = function (message) {
|
|
|
117
123
|
return false
|
|
118
124
|
}
|
|
119
125
|
|
|
126
|
+
if (!this._endpoint) {
|
|
127
|
+
return false
|
|
128
|
+
}
|
|
129
|
+
|
|
120
130
|
if (
|
|
121
131
|
this._state !== C.CONNECTION_STATE.OPEN ||
|
|
122
132
|
this._endpoint.readyState !== this._endpoint.OPEN
|
|
@@ -134,6 +144,7 @@ Connection.prototype.send = function (message) {
|
|
|
134
144
|
}
|
|
135
145
|
|
|
136
146
|
this.emit('send', message)
|
|
147
|
+
|
|
137
148
|
this._endpoint.send(message)
|
|
138
149
|
|
|
139
150
|
return true
|
|
@@ -146,7 +157,7 @@ Connection.prototype._submit = function (message) {
|
|
|
146
157
|
const err = new Error(`Packet to big: ${message.length} > ${maxPacketSize}`)
|
|
147
158
|
this._client._$onError(C.TOPIC.CONNECTION, C.EVENT.CONNECTION_ERROR, err)
|
|
148
159
|
return false
|
|
149
|
-
} else if (this._endpoint.readyState === this._endpoint.OPEN) {
|
|
160
|
+
} else if (this._endpoint != null && this._endpoint.readyState === this._endpoint.OPEN) {
|
|
150
161
|
this.emit('send', message)
|
|
151
162
|
this._endpoint.send(message)
|
|
152
163
|
return true
|
|
@@ -161,7 +172,7 @@ Connection.prototype._sendAuthParams = function () {
|
|
|
161
172
|
this._setState(C.CONNECTION_STATE.AUTHENTICATING)
|
|
162
173
|
const authMessage = messageBuilder.getMsg(C.TOPIC.AUTH, C.ACTIONS.REQUEST, [
|
|
163
174
|
this._authParams,
|
|
164
|
-
|
|
175
|
+
pkg.version,
|
|
165
176
|
utils.isNode
|
|
166
177
|
? `Node/${process.version}`
|
|
167
178
|
: globalThis.navigator && globalThis.navigator.userAgent,
|
|
@@ -222,7 +233,6 @@ Connection.prototype._onMessage = function (data) {
|
|
|
222
233
|
Connection.prototype._recvMessages = function (deadline) {
|
|
223
234
|
for (
|
|
224
235
|
let n = 0;
|
|
225
|
-
// eslint-disable-next-line no-unmodified-loop-condition
|
|
226
236
|
deadline ? deadline.didTimeout || deadline.timeRemaining() : n < this._batchSize;
|
|
227
237
|
++n
|
|
228
238
|
) {
|
|
@@ -6,7 +6,7 @@ const poolEncoder = new globalThis.TextEncoder()
|
|
|
6
6
|
|
|
7
7
|
// TODO (fix): Don't assume maxMesageSize is 1MB
|
|
8
8
|
const maxMessageSize = 1024 * 1024
|
|
9
|
-
const poolSize = maxMessageSize *
|
|
9
|
+
const poolSize = maxMessageSize * 8
|
|
10
10
|
|
|
11
11
|
let poolBuffer
|
|
12
12
|
let poolView
|
|
@@ -51,9 +51,12 @@ export function getMsg(topic, action, data) {
|
|
|
51
51
|
const start = poolOffset
|
|
52
52
|
|
|
53
53
|
const headerSize = 8
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
for (let n = 0; n < headerSize; n++) {
|
|
55
|
+
poolBuffer[poolOffset++] = 0
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let headerPos = start
|
|
59
|
+
poolBuffer[headerPos++] = 128 + headerSize
|
|
57
60
|
|
|
58
61
|
poolBuffer[poolOffset++] = topic.charCodeAt(0)
|
|
59
62
|
poolBuffer[poolOffset++] = 31
|
|
@@ -2,12 +2,11 @@ import Record from './record.js'
|
|
|
2
2
|
import MulticastListener from '../utils/multicast-listener.js'
|
|
3
3
|
import UnicastListener from '../utils/unicast-listener.js'
|
|
4
4
|
import * as C from '../constants/constants.js'
|
|
5
|
-
import rxjs from 'rxjs'
|
|
5
|
+
import * as rxjs from 'rxjs'
|
|
6
6
|
import invariant from 'invariant'
|
|
7
7
|
import EventEmitter from 'component-emitter2'
|
|
8
8
|
import jsonPath from '@nxtedition/json-path'
|
|
9
9
|
import * as utils from '../utils/utils.js'
|
|
10
|
-
import rx from 'rxjs/operators'
|
|
11
10
|
import xuid from 'xuid'
|
|
12
11
|
import * as timers from '../utils/timers.js'
|
|
13
12
|
|
|
@@ -97,19 +96,11 @@ class RecordHandler {
|
|
|
97
96
|
this._connection = connection
|
|
98
97
|
this._client = client
|
|
99
98
|
this._records = new Map()
|
|
100
|
-
this._cache = new Map()
|
|
101
99
|
this._listeners = new Map()
|
|
102
100
|
this._pruning = new Set()
|
|
103
101
|
this._patching = new Map()
|
|
104
102
|
this._updating = new Map()
|
|
105
103
|
|
|
106
|
-
this._registry = new FinalizationRegistry((name) => {
|
|
107
|
-
const entry = this._cache.get(name)
|
|
108
|
-
if (entry && entry.deref && entry.deref() === undefined) {
|
|
109
|
-
this._cache.delete(name)
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
|
|
113
104
|
this._connected = 0
|
|
114
105
|
this._stats = {
|
|
115
106
|
updating: 0,
|
|
@@ -142,11 +133,6 @@ class RecordHandler {
|
|
|
142
133
|
for (const rec of pruning) {
|
|
143
134
|
rec._$dispose()
|
|
144
135
|
this._records.delete(rec.name)
|
|
145
|
-
|
|
146
|
-
if (!this._cache.has(rec.name)) {
|
|
147
|
-
this._cache.set(rec.name, new WeakRef(rec))
|
|
148
|
-
this._registry.register(rec, rec.name)
|
|
149
|
-
}
|
|
150
136
|
}
|
|
151
137
|
|
|
152
138
|
this._stats.pruning -= pruning.size
|
|
@@ -232,7 +218,7 @@ class RecordHandler {
|
|
|
232
218
|
let record = this._records.get(name)
|
|
233
219
|
|
|
234
220
|
if (!record) {
|
|
235
|
-
record =
|
|
221
|
+
record = new Record(name, this)
|
|
236
222
|
this._stats.records += 1
|
|
237
223
|
this._stats.created += 1
|
|
238
224
|
this._records.set(name, record)
|
|
@@ -485,7 +471,7 @@ class RecordHandler {
|
|
|
485
471
|
// TODO (fix): Missing sync..
|
|
486
472
|
return new Promise((resolve, reject) => {
|
|
487
473
|
this.observe(...args)
|
|
488
|
-
.pipe(
|
|
474
|
+
.pipe(rxjs.first())
|
|
489
475
|
.subscribe({
|
|
490
476
|
next: resolve,
|
|
491
477
|
error: reject,
|
|
@@ -501,7 +487,7 @@ class RecordHandler {
|
|
|
501
487
|
get2(...args) {
|
|
502
488
|
return new Promise((resolve, reject) => {
|
|
503
489
|
this.observe2(...args)
|
|
504
|
-
.pipe(
|
|
490
|
+
.pipe(rxjs.first())
|
|
505
491
|
.subscribe({
|
|
506
492
|
next: resolve,
|
|
507
493
|
error: reject,
|
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
|
-
|
|
10
|
+
class Record {
|
|
11
11
|
static STATE = C.RECORD_STATE
|
|
12
12
|
|
|
13
13
|
constructor(name, handler) {
|
|
@@ -15,7 +15,6 @@ export default class Record {
|
|
|
15
15
|
|
|
16
16
|
this._handler = handler
|
|
17
17
|
this._name = name
|
|
18
|
-
this._key = utils.h64(name)
|
|
19
18
|
this._version = ''
|
|
20
19
|
this._data = jsonPath.EMPTY
|
|
21
20
|
this._state = C.RECORD_STATE.VOID
|
|
@@ -25,15 +24,7 @@ export default class Record {
|
|
|
25
24
|
|
|
26
25
|
/** @type Map? */ this._updating = null
|
|
27
26
|
/** @type Array? */ this._patching = null
|
|
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
|
|
27
|
+
this._subscribed = connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [this._name])
|
|
37
28
|
}
|
|
38
29
|
|
|
39
30
|
/** @type {string} */
|
|
@@ -71,8 +62,7 @@ export default class Record {
|
|
|
71
62
|
if (this._refs === 1) {
|
|
72
63
|
this._handler._onPruning(this, false)
|
|
73
64
|
this._subscribed =
|
|
74
|
-
this._subscribed ||
|
|
75
|
-
connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [this._key, this._name])
|
|
65
|
+
this._subscribed || connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [this._name])
|
|
76
66
|
}
|
|
77
67
|
return this
|
|
78
68
|
}
|
|
@@ -334,8 +324,7 @@ export default class Record {
|
|
|
334
324
|
|
|
335
325
|
if (connected) {
|
|
336
326
|
this._subscribed =
|
|
337
|
-
this._refs > 0 &&
|
|
338
|
-
connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [this._key, this._name])
|
|
327
|
+
this._refs > 0 && connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, [this._name])
|
|
339
328
|
|
|
340
329
|
if (this._updating) {
|
|
341
330
|
for (const update of this._updating.values()) {
|
|
@@ -360,7 +349,7 @@ export default class Record {
|
|
|
360
349
|
invariant(!this._updating, 'must not have updates')
|
|
361
350
|
|
|
362
351
|
if (this._subscribed) {
|
|
363
|
-
connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UNSUBSCRIBE, [this.
|
|
352
|
+
connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UNSUBSCRIBE, [this._name])
|
|
364
353
|
this._subscribed = false
|
|
365
354
|
}
|
|
366
355
|
|
|
@@ -382,7 +371,7 @@ export default class Record {
|
|
|
382
371
|
const prevVersion = this._version
|
|
383
372
|
const nextVersion = this._makeVersion(parseInt(prevVersion) + 1)
|
|
384
373
|
|
|
385
|
-
const update = [this.
|
|
374
|
+
const update = [this._name, nextVersion, jsonPath.stringify(nextData), prevVersion]
|
|
386
375
|
|
|
387
376
|
if (!this._updating) {
|
|
388
377
|
this._onUpdating(true)
|
|
@@ -584,3 +573,5 @@ Object.defineProperty(Record.prototype, 'hasProvider', {
|
|
|
584
573
|
return this.state >= C.RECORD_STATE.PROVIDER
|
|
585
574
|
},
|
|
586
575
|
})
|
|
576
|
+
|
|
577
|
+
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 {
|
|
3
|
+
import { 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,12 +48,8 @@ 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
|
-
|
|
55
51
|
if (message.action === C.ACTIONS.SUBSCRIPTION_FOR_PATTERN_FOUND) {
|
|
56
|
-
if (this._subscriptions.has(
|
|
52
|
+
if (this._subscriptions.has(name)) {
|
|
57
53
|
this._error(name, 'invalid add: listener exists')
|
|
58
54
|
return
|
|
59
55
|
}
|
|
@@ -61,7 +57,6 @@ export default class Listener {
|
|
|
61
57
|
// TODO (refactor): Move to class
|
|
62
58
|
const provider = {
|
|
63
59
|
name,
|
|
64
|
-
key,
|
|
65
60
|
value$: null,
|
|
66
61
|
sending: false,
|
|
67
62
|
accepted: false,
|
|
@@ -74,7 +69,7 @@ export default class Listener {
|
|
|
74
69
|
if (this.connected && provider.accepted) {
|
|
75
70
|
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [
|
|
76
71
|
this._pattern,
|
|
77
|
-
provider.
|
|
72
|
+
provider.name,
|
|
78
73
|
])
|
|
79
74
|
}
|
|
80
75
|
|
|
@@ -107,7 +102,7 @@ export default class Listener {
|
|
|
107
102
|
this._connection.sendMsg(
|
|
108
103
|
this._topic,
|
|
109
104
|
accepted ? C.ACTIONS.LISTEN_ACCEPT : C.ACTIONS.LISTEN_REJECT,
|
|
110
|
-
[this._pattern, provider.
|
|
105
|
+
[this._pattern, provider.name],
|
|
111
106
|
)
|
|
112
107
|
|
|
113
108
|
provider.version = null
|
|
@@ -163,7 +158,7 @@ export default class Listener {
|
|
|
163
158
|
if (provider.version !== version) {
|
|
164
159
|
provider.version = version
|
|
165
160
|
this._connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UPDATE, [
|
|
166
|
-
provider.
|
|
161
|
+
provider.name,
|
|
167
162
|
version,
|
|
168
163
|
body,
|
|
169
164
|
])
|
|
@@ -187,9 +182,9 @@ export default class Listener {
|
|
|
187
182
|
|
|
188
183
|
provider.start()
|
|
189
184
|
|
|
190
|
-
this._subscriptions.set(provider.
|
|
185
|
+
this._subscriptions.set(provider.name, provider)
|
|
191
186
|
} else if (message.action === C.ACTIONS.LISTEN_ACCEPT) {
|
|
192
|
-
const provider = this._subscriptions.get(
|
|
187
|
+
const provider = this._subscriptions.get(name)
|
|
193
188
|
if (!provider?.value$) {
|
|
194
189
|
return
|
|
195
190
|
}
|
|
@@ -201,13 +196,13 @@ export default class Listener {
|
|
|
201
196
|
provider.valueSubscription = provider.value$.subscribe(provider.observer)
|
|
202
197
|
}
|
|
203
198
|
} else if (message.action === C.ACTIONS.SUBSCRIPTION_FOR_PATTERN_REMOVED) {
|
|
204
|
-
const provider = this._subscriptions.get(
|
|
199
|
+
const provider = this._subscriptions.get(name)
|
|
205
200
|
|
|
206
201
|
if (!provider) {
|
|
207
202
|
this._error(name, 'invalid remove: listener missing')
|
|
208
203
|
} else {
|
|
209
204
|
provider.stop()
|
|
210
|
-
this._subscriptions.delete(provider.
|
|
205
|
+
this._subscriptions.delete(provider.name)
|
|
211
206
|
}
|
|
212
207
|
} else {
|
|
213
208
|
return false
|
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,8 +1,8 @@
|
|
|
1
1
|
import * as rxjs from 'rxjs'
|
|
2
2
|
import * as C from '../constants/constants.js'
|
|
3
|
-
import {
|
|
3
|
+
import { h64ToString } from '../utils/utils.js'
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const PIPE = rxjs.pipe(
|
|
6
6
|
rxjs.map((value) => {
|
|
7
7
|
let data
|
|
8
8
|
if (value && typeof value === 'string') {
|
|
@@ -55,12 +55,8 @@ 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
|
-
|
|
62
58
|
if (message.action === C.ACTIONS.LISTEN_ACCEPT) {
|
|
63
|
-
if (this._subscriptions.has(
|
|
59
|
+
if (this._subscriptions.has(name)) {
|
|
64
60
|
this._error(name, 'invalid accept: listener exists')
|
|
65
61
|
return
|
|
66
62
|
}
|
|
@@ -73,32 +69,32 @@ export default class Listener {
|
|
|
73
69
|
}
|
|
74
70
|
|
|
75
71
|
if (value$) {
|
|
76
|
-
const subscription = value$.pipe(
|
|
72
|
+
const subscription = value$.pipe(PIPE).subscribe({
|
|
77
73
|
next: (data) => {
|
|
78
74
|
if (data == null) {
|
|
79
|
-
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [this._pattern,
|
|
80
|
-
this._subscriptions.delete(
|
|
75
|
+
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [this._pattern, name])
|
|
76
|
+
this._subscriptions.delete(name)
|
|
81
77
|
subscription.unsubscribe()
|
|
82
78
|
} else {
|
|
83
79
|
const version = `INF-${h64ToString(data)}`
|
|
84
|
-
this._connection.sendMsg(this._topic, C.ACTIONS.UPDATE, [
|
|
80
|
+
this._connection.sendMsg(this._topic, C.ACTIONS.UPDATE, [name, version, data])
|
|
85
81
|
}
|
|
86
82
|
},
|
|
87
83
|
error: (err) => {
|
|
88
84
|
this._error(name, err)
|
|
89
|
-
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [this._pattern,
|
|
90
|
-
this._subscriptions.delete(
|
|
85
|
+
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [this._pattern, name])
|
|
86
|
+
this._subscriptions.delete(name)
|
|
91
87
|
},
|
|
92
88
|
})
|
|
93
|
-
this._subscriptions.set(
|
|
89
|
+
this._subscriptions.set(name, subscription)
|
|
94
90
|
} else {
|
|
95
|
-
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [this._pattern,
|
|
91
|
+
this._connection.sendMsg(this._topic, C.ACTIONS.LISTEN_REJECT, [this._pattern, name])
|
|
96
92
|
}
|
|
97
93
|
} else if (message.action === C.ACTIONS.LISTEN_REJECT) {
|
|
98
|
-
const subscription = this._subscriptions.get(
|
|
94
|
+
const subscription = this._subscriptions.get(name)
|
|
99
95
|
|
|
100
96
|
if (subscription) {
|
|
101
|
-
this._subscriptions.delete(
|
|
97
|
+
this._subscriptions.delete(name)
|
|
102
98
|
subscription.unsubscribe()
|
|
103
99
|
} else {
|
|
104
100
|
this._error(name, 'invalid remove: listener missing')
|
package/src/utils/utils.js
CHANGED
|
@@ -129,7 +129,7 @@ function defaultSchedule(fn) {
|
|
|
129
129
|
setTimeout(fn, 0)
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
export const schedule = isNode ? defaultSchedule :
|
|
132
|
+
export const schedule = isNode ? defaultSchedule : globalThis.requestIdleCallback
|
|
133
133
|
|
|
134
134
|
const abortSignals = new WeakMap()
|
|
135
135
|
const onAbort = function () {
|
|
@@ -177,16 +177,11 @@ export function removeAbortListener(signal, handler) {
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
180
|
+
// This is a hack to avoid top-level await
|
|
181
|
+
// const HASHER = await xxhash()
|
|
182
|
+
let HASHER
|
|
183
|
+
xxhash().then((hasher) => (HASHER = hasher))
|
|
185
184
|
|
|
186
185
|
export function h64ToString(str) {
|
|
187
186
|
return HASHER.h64ToString(str)
|
|
188
187
|
}
|
|
189
|
-
|
|
190
|
-
export function h64Raw(str) {
|
|
191
|
-
return HASHER.h64Raw(str)
|
|
192
|
-
}
|