sen-ether-client 0.2.1 → 0.2.2
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/API.md +22 -18
- package/README.md +27 -27
- package/bin/node-sen-probe.js +1 -1
- package/index.js +9 -9
- package/lib/client.js +35 -11
- package/lib/sen.js +41 -6
- package/package.json +1 -1
package/API.md
CHANGED
|
@@ -95,15 +95,15 @@ Preferred multi-session usage:
|
|
|
95
95
|
```js
|
|
96
96
|
const sen = await Sen.connect();
|
|
97
97
|
|
|
98
|
-
const
|
|
99
|
-
const
|
|
98
|
+
const first = await sen.interest('SELECT * FROM session.bus');
|
|
99
|
+
const second = await sen.interest('SELECT * FROM otherSession.otherBus');
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
TCP discovery hub usage:
|
|
103
103
|
|
|
104
104
|
```js
|
|
105
105
|
const sen = await Sen.connect({
|
|
106
|
-
session: '
|
|
106
|
+
session: 'session',
|
|
107
107
|
tcpHub: '127.0.0.1:65222'
|
|
108
108
|
});
|
|
109
109
|
```
|
|
@@ -116,7 +116,7 @@ Multicast discovery usage:
|
|
|
116
116
|
|
|
117
117
|
```js
|
|
118
118
|
const sen = await Sen.connect({
|
|
119
|
-
session: '
|
|
119
|
+
session: 'session',
|
|
120
120
|
interfaceAddress: '127.0.0.1',
|
|
121
121
|
listenHost: '127.0.0.1',
|
|
122
122
|
advertisedHost: '127.0.0.1'
|
|
@@ -129,21 +129,21 @@ and received on the intended network device.
|
|
|
129
129
|
Explicit single-session usage is still supported:
|
|
130
130
|
|
|
131
131
|
```js
|
|
132
|
-
const
|
|
133
|
-
await
|
|
132
|
+
const session = await Sen.connect({ session: 'session' });
|
|
133
|
+
await session.interest('SELECT * FROM session.bus');
|
|
134
134
|
```
|
|
135
135
|
|
|
136
136
|
If a SEN bus name itself contains dots and is not a session-qualified bus, pass
|
|
137
137
|
it explicitly. This is useful for standalone scenarios that run in one Ether
|
|
138
|
-
session but publish on a bus such as `
|
|
138
|
+
session but publish on a bus such as `domain.bus`:
|
|
139
139
|
|
|
140
140
|
```js
|
|
141
|
-
const
|
|
142
|
-
session: '
|
|
141
|
+
const session = await Sen.connect({
|
|
142
|
+
session: 'session'
|
|
143
143
|
});
|
|
144
144
|
|
|
145
|
-
const objects = await
|
|
146
|
-
bus: '
|
|
145
|
+
const objects = await session.interest('SELECT * FROM domain.bus', {
|
|
146
|
+
bus: 'domain.bus',
|
|
147
147
|
forceBus: true
|
|
148
148
|
});
|
|
149
149
|
```
|
|
@@ -164,20 +164,24 @@ Main methods:
|
|
|
164
164
|
- `await sen.waitForObject(selector, options)`
|
|
165
165
|
- `await sen.close()`
|
|
166
166
|
|
|
167
|
+
By default, interest creation uses the SEN-native `CRC32(query)` value as the
|
|
168
|
+
interest id. Pass `options.id` only when a caller must force a specific native
|
|
169
|
+
interest id.
|
|
170
|
+
|
|
167
171
|
Session and bus navigation:
|
|
168
172
|
|
|
169
173
|
```js
|
|
170
174
|
const sen = await Sen.connect();
|
|
171
175
|
|
|
172
176
|
console.log(await sen.discoverBuses());
|
|
173
|
-
// [{ session: '
|
|
177
|
+
// [{ session: 'session', bus: 'bus', qualified: 'session.bus' }]
|
|
174
178
|
|
|
175
179
|
for (const sessionName of sen.listSessions()) {
|
|
176
180
|
const session = await sen.session(sessionName);
|
|
177
181
|
console.log(sessionName, session.listBuses());
|
|
178
182
|
}
|
|
179
183
|
|
|
180
|
-
const
|
|
184
|
+
const bus = await sen.session('session').then(session => session.bus('bus'));
|
|
181
185
|
```
|
|
182
186
|
|
|
183
187
|
`discoverBuses()` does not create interests and does not join any SEN bus. It
|
|
@@ -226,8 +230,8 @@ Main events:
|
|
|
226
230
|
Returned by `await sen.interest(query)`.
|
|
227
231
|
|
|
228
232
|
```js
|
|
229
|
-
const interest = await sen.interest('SELECT * FROM
|
|
230
|
-
const object = await interest.waitFor('
|
|
233
|
+
const interest = await sen.interest('SELECT * FROM session.bus');
|
|
234
|
+
const object = await interest.waitFor('object-1');
|
|
231
235
|
```
|
|
232
236
|
|
|
233
237
|
Main methods:
|
|
@@ -251,7 +255,7 @@ For browser gateways or high-frequency telemetry, request only the properties
|
|
|
251
255
|
you need and emit batches instead of one JS event per property update:
|
|
252
256
|
|
|
253
257
|
```js
|
|
254
|
-
const
|
|
258
|
+
const objects = await sen.interest('SELECT demo.Object FROM session.bus', {
|
|
255
259
|
properties: ['latitude', 'longitude', 'altitude', 'heading'],
|
|
256
260
|
changeMode: 'batch',
|
|
257
261
|
batchIntervalMs: 16,
|
|
@@ -261,7 +265,7 @@ const tracks = await sen.interest('SELECT bus.Track FROM hmi.loadtest', {
|
|
|
261
265
|
coalesce: true
|
|
262
266
|
});
|
|
263
267
|
|
|
264
|
-
|
|
268
|
+
objects.on('changes', ({ changes, dropped }) => {
|
|
265
269
|
// Send one compact WebSocket frame to the browser.
|
|
266
270
|
});
|
|
267
271
|
```
|
|
@@ -309,7 +313,7 @@ Main events:
|
|
|
309
313
|
64-bit timestamp precision. Convert it explicitly at JSON boundaries:
|
|
310
314
|
|
|
311
315
|
```js
|
|
312
|
-
|
|
316
|
+
objects.on('change', ({ object, name, value, timestampNs }) => {
|
|
313
317
|
websocket.send(JSON.stringify({
|
|
314
318
|
object: object.name,
|
|
315
319
|
name,
|
package/README.md
CHANGED
|
@@ -11,16 +11,16 @@ import { Sen } from 'sen-ether-client';
|
|
|
11
11
|
|
|
12
12
|
const sen = await Sen.connect();
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
const
|
|
14
|
+
const objects = await sen.interest('SELECT * FROM session.bus');
|
|
15
|
+
const object = await objects.waitFor('object-1');
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
object.on('change:label', ({ value }) => {
|
|
18
18
|
console.log('label changed:', value);
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
console.log(await
|
|
22
|
-
await
|
|
23
|
-
console.log(await
|
|
21
|
+
console.log(await object.get('label'));
|
|
22
|
+
await object.set('label', 'from-js');
|
|
23
|
+
console.log(await object.call('ping', ['hello']));
|
|
24
24
|
|
|
25
25
|
await sen.close();
|
|
26
26
|
```
|
|
@@ -85,7 +85,7 @@ explicitly:
|
|
|
85
85
|
|
|
86
86
|
```js
|
|
87
87
|
const sen = await Sen.connect({
|
|
88
|
-
session: '
|
|
88
|
+
session: 'session',
|
|
89
89
|
tcpHub: '127.0.0.1:65222'
|
|
90
90
|
});
|
|
91
91
|
```
|
|
@@ -100,7 +100,7 @@ For local multicast tests, select loopback explicitly:
|
|
|
100
100
|
|
|
101
101
|
```js
|
|
102
102
|
const sen = await Sen.connect({
|
|
103
|
-
session: '
|
|
103
|
+
session: 'session',
|
|
104
104
|
interfaceAddress: '127.0.0.1',
|
|
105
105
|
listenHost: '127.0.0.1',
|
|
106
106
|
advertisedHost: '127.0.0.1'
|
|
@@ -116,8 +116,8 @@ configured interests.
|
|
|
116
116
|
is inferred from the query:
|
|
117
117
|
|
|
118
118
|
```js
|
|
119
|
-
const
|
|
120
|
-
const
|
|
119
|
+
const first = await sen.interest('SELECT * FROM session.bus');
|
|
120
|
+
const second = await sen.interest('SELECT * FROM otherSession.otherBus');
|
|
121
121
|
```
|
|
122
122
|
|
|
123
123
|
You can also navigate explicitly through sessions and buses:
|
|
@@ -127,13 +127,13 @@ const sen = await Sen.connect();
|
|
|
127
127
|
|
|
128
128
|
console.log(sen.listSessions());
|
|
129
129
|
console.log(await sen.discoverBuses());
|
|
130
|
-
// [{ session: '
|
|
130
|
+
// [{ session: 'session', bus: 'bus', qualified: 'session.bus' }]
|
|
131
131
|
|
|
132
|
-
const
|
|
133
|
-
console.log(
|
|
132
|
+
const session = await sen.session('session');
|
|
133
|
+
console.log(session.listBuses());
|
|
134
134
|
|
|
135
|
-
const
|
|
136
|
-
const
|
|
135
|
+
const bus = await session.bus('bus');
|
|
136
|
+
const object = await bus.waitFor('object-1');
|
|
137
137
|
```
|
|
138
138
|
|
|
139
139
|
`discoverBuses()` does not create interests and does not join any SEN bus. It
|
|
@@ -144,11 +144,11 @@ process connection, it waits up to `busDiscoverySettleMs` milliseconds.
|
|
|
144
144
|
You can also connect to one explicit session:
|
|
145
145
|
|
|
146
146
|
```js
|
|
147
|
-
const
|
|
148
|
-
session: '
|
|
147
|
+
const session = await Sen.connect({
|
|
148
|
+
session: 'session'
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
-
const
|
|
151
|
+
const objects = await session.interest('SELECT * FROM session.bus');
|
|
152
152
|
```
|
|
153
153
|
|
|
154
154
|
## Interests
|
|
@@ -156,17 +156,17 @@ const diagnostics = await hmi.interest('SELECT * FROM hmi.diagnostics');
|
|
|
156
156
|
Create an interest with a normal SEN query:
|
|
157
157
|
|
|
158
158
|
```js
|
|
159
|
-
const
|
|
159
|
+
const objects = await sen.interest('SELECT * FROM session.bus');
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
Listen for objects and changes:
|
|
163
163
|
|
|
164
164
|
```js
|
|
165
|
-
|
|
165
|
+
objects.on('object', object => {
|
|
166
166
|
console.log(object.name, object.className);
|
|
167
167
|
});
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
objects.on('change', ({ object, name, value }) => {
|
|
170
170
|
console.log(object.name, name, value);
|
|
171
171
|
});
|
|
172
172
|
```
|
|
@@ -175,13 +175,13 @@ For browser gateways or high-frequency telemetry, batch changes and decode only
|
|
|
175
175
|
the properties needed by the UI:
|
|
176
176
|
|
|
177
177
|
```js
|
|
178
|
-
const
|
|
178
|
+
const objects = await sen.interest('SELECT demo.Object FROM session.bus', {
|
|
179
179
|
properties: ['latitude', 'longitude', 'altitude', 'heading'],
|
|
180
180
|
changeMode: 'batch',
|
|
181
181
|
coalesce: true
|
|
182
182
|
});
|
|
183
183
|
|
|
184
|
-
|
|
184
|
+
objects.on('changes', ({ changes }) => {
|
|
185
185
|
websocket.send(JSON.stringify(changes.map(({ object, name, value, timestampNs }) => ({
|
|
186
186
|
object: object.name,
|
|
187
187
|
name,
|
|
@@ -194,10 +194,10 @@ tracks.on('changes', ({ changes }) => {
|
|
|
194
194
|
Get an object by name, id, class name, or predicate:
|
|
195
195
|
|
|
196
196
|
```js
|
|
197
|
-
const
|
|
197
|
+
const object = await objects.waitFor('object-1');
|
|
198
198
|
|
|
199
|
-
const
|
|
200
|
-
object => object.className === '
|
|
199
|
+
const firstDemoObject = await objects.waitFor(
|
|
200
|
+
object => object.className === 'demo.Object'
|
|
201
201
|
);
|
|
202
202
|
```
|
|
203
203
|
|
|
@@ -277,7 +277,7 @@ Probe a bus:
|
|
|
277
277
|
```bash
|
|
278
278
|
npx sen-ether-probe \
|
|
279
279
|
--tcp-hub 127.0.0.1:65222 \
|
|
280
|
-
--bus
|
|
280
|
+
--bus session.bus
|
|
281
281
|
```
|
|
282
282
|
|
|
283
283
|
## API
|
package/bin/node-sen-probe.js
CHANGED
|
@@ -66,7 +66,7 @@ Options:
|
|
|
66
66
|
Examples:
|
|
67
67
|
sen-ether-probe --bus scenario.control
|
|
68
68
|
sen-ether-probe --tcp-hub 127.0.0.1:64222 --bus scenario.control
|
|
69
|
-
sen-ether-probe --bus
|
|
69
|
+
sen-ether-probe --bus session.bus --query "SELECT * FROM session.bus"
|
|
70
70
|
|
|
71
71
|
Environment:
|
|
72
72
|
SEN_ETHER_DISCOVERY_PORT Default multicast discovery port
|
package/index.js
CHANGED
|
@@ -11,17 +11,16 @@
|
|
|
11
11
|
* const sen = await Sen.connect();
|
|
12
12
|
*
|
|
13
13
|
* console.log(sen.listSessions());
|
|
14
|
-
* const
|
|
15
|
-
* console.log(
|
|
14
|
+
* const session = await sen.session('session');
|
|
15
|
+
* console.log(session.listBuses());
|
|
16
16
|
*
|
|
17
|
-
* const
|
|
18
|
-
* const
|
|
19
|
-
* const probe = await diagnostics.waitFor('EtherProbe');
|
|
17
|
+
* const objects = await sen.interest('SELECT * FROM session.bus');
|
|
18
|
+
* const object = await objects.waitFor('object-1');
|
|
20
19
|
*
|
|
21
|
-
*
|
|
22
|
-
* console.log(await
|
|
23
|
-
* await
|
|
24
|
-
* console.log(await
|
|
20
|
+
* object.on('change:label', ({ value }) => console.log(value));
|
|
21
|
+
* console.log(await object.get('label'));
|
|
22
|
+
* await object.set('label', 'from-js');
|
|
23
|
+
* console.log(await object.call('ping', ['hello']));
|
|
25
24
|
*
|
|
26
25
|
* await sen.close();
|
|
27
26
|
*/
|
|
@@ -61,6 +60,7 @@
|
|
|
61
60
|
* @property {string} [bus] Explicit bus name when it cannot be inferred from the query.
|
|
62
61
|
* @property {boolean} [forceBus=false] Join without waiting for the remote process to announce the bus.
|
|
63
62
|
* @property {number} [timeout] Operation timeout in ms.
|
|
63
|
+
* @property {number} [id] Optional native interest id. Defaults to CRC32(query).
|
|
64
64
|
* @property {string[]|string} [properties] Optional property names to decode and emit.
|
|
65
65
|
* @property {'individual'|'batch'|'both'} [changeMode='individual'] Change emission mode.
|
|
66
66
|
* @property {number} [batchIntervalMs=16] Batched change flush interval in ms.
|
package/lib/client.js
CHANGED
|
@@ -788,6 +788,12 @@ export class EtherClient extends EventEmitter {
|
|
|
788
788
|
this.socket = socket;
|
|
789
789
|
}
|
|
790
790
|
socket.on('data', chunk => this.#onTcpData(connection, chunk));
|
|
791
|
+
socket.on('error', error => {
|
|
792
|
+
this.#removeConnection(connection);
|
|
793
|
+
if (!['EPIPE', 'ECONNRESET'].includes(error?.code)) {
|
|
794
|
+
this.emit('error', error);
|
|
795
|
+
}
|
|
796
|
+
});
|
|
791
797
|
socket.on('close', hadError => {
|
|
792
798
|
this.#removeConnection(connection);
|
|
793
799
|
this.emit('connectionClose', { connection, hadError });
|
|
@@ -804,10 +810,18 @@ export class EtherClient extends EventEmitter {
|
|
|
804
810
|
if (connection.processKey) {
|
|
805
811
|
this.connectionsByProcessKey.delete(connection.processKey);
|
|
806
812
|
}
|
|
813
|
+
const leftParticipants = [];
|
|
807
814
|
for (const [busId, participants] of this.remoteParticipantsByBusId) {
|
|
808
815
|
for (const [participantId, participant] of participants) {
|
|
809
816
|
if (participant.connection === connection) {
|
|
810
817
|
participants.delete(participantId);
|
|
818
|
+
leftParticipants.push({
|
|
819
|
+
participantId,
|
|
820
|
+
busId,
|
|
821
|
+
busName: participant.busName,
|
|
822
|
+
connection,
|
|
823
|
+
reason: 'connectionClose'
|
|
824
|
+
});
|
|
811
825
|
}
|
|
812
826
|
}
|
|
813
827
|
if (!participants.size) {
|
|
@@ -824,6 +838,9 @@ export class EtherClient extends EventEmitter {
|
|
|
824
838
|
if (this.socket === connection.socket) {
|
|
825
839
|
this.socket = [...this.connections.values()][0]?.socket;
|
|
826
840
|
}
|
|
841
|
+
for (const participant of leftParticipants) {
|
|
842
|
+
this.emit('busLeft', participant);
|
|
843
|
+
}
|
|
827
844
|
}
|
|
828
845
|
|
|
829
846
|
#configureTcpSocket(socket) {
|
|
@@ -998,7 +1015,7 @@ export class EtherClient extends EventEmitter {
|
|
|
998
1015
|
*/
|
|
999
1016
|
startInterest(bus, query, options = {}) {
|
|
1000
1017
|
const busState = this.#getBus(bus);
|
|
1001
|
-
const id = options.id ?? this.#nextInterestId(busState);
|
|
1018
|
+
const id = options.id ?? this.#nextInterestId(busState, crc32(query));
|
|
1002
1019
|
this.#sendBusControlToRemoteParticipants(busState, {
|
|
1003
1020
|
type: 'InterestStarted',
|
|
1004
1021
|
value: { query, id }
|
|
@@ -1008,7 +1025,13 @@ export class EtherClient extends EventEmitter {
|
|
|
1008
1025
|
return { busName: busState.busName, busId: busState.busId, id, query };
|
|
1009
1026
|
}
|
|
1010
1027
|
|
|
1011
|
-
#nextInterestId(busState) {
|
|
1028
|
+
#nextInterestId(busState, preferredId) {
|
|
1029
|
+
const preferred = preferredId >>> 0;
|
|
1030
|
+
if (preferred && !busState.interests.has(preferred)) {
|
|
1031
|
+
this.nextInterestId = preferred;
|
|
1032
|
+
return preferred;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1012
1035
|
for (let attempts = 0; attempts < 0xffff_ffff; attempts += 1) {
|
|
1013
1036
|
this.nextInterestId = (this.nextInterestId + 1) >>> 0;
|
|
1014
1037
|
const id = this.nextInterestId || 1;
|
|
@@ -1746,25 +1769,26 @@ export class EtherClient extends EventEmitter {
|
|
|
1746
1769
|
return;
|
|
1747
1770
|
}
|
|
1748
1771
|
const socket = this.#writableConnectionSocket(connection);
|
|
1772
|
+
if (!socket) {
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1749
1775
|
const processBusPayload = encodeConfirmedBusFrame({
|
|
1750
1776
|
to: busState.participantId,
|
|
1751
1777
|
busId: busState.busId,
|
|
1752
1778
|
message: busPayload
|
|
1753
1779
|
});
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
}
|
|
1780
|
+
try {
|
|
1781
|
+
socket.write(encodeProcessTcpFrame(PROCESS_MESSAGE_CATEGORY.busMessage, processBusPayload));
|
|
1782
|
+
} catch {
|
|
1783
|
+
this.#removeConnection(connection);
|
|
1784
|
+
}
|
|
1759
1785
|
}
|
|
1760
1786
|
|
|
1761
1787
|
#writableConnectionSocket(connection) {
|
|
1762
1788
|
const socket = connection?.socket;
|
|
1763
1789
|
if (!socket || socket.destroyed || !socket.writable) {
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
this.emit('error', error);
|
|
1767
|
-
throw error;
|
|
1790
|
+
this.#removeConnection(connection);
|
|
1791
|
+
return undefined;
|
|
1768
1792
|
}
|
|
1769
1793
|
return socket;
|
|
1770
1794
|
}
|
package/lib/sen.js
CHANGED
|
@@ -4,6 +4,7 @@ import { decodePropertyValues, decodeValue, encodeArguments, decodeArguments } f
|
|
|
4
4
|
import { EtherClient } from './client.js';
|
|
5
5
|
import { EtherDiscoveryScanner, TcpDiscoveryHubScanner, scan, scanTcpDiscoveryHub } from './discovery.js';
|
|
6
6
|
import { methodHash } from './hash32.js';
|
|
7
|
+
import { crc32 } from './crc32.js';
|
|
7
8
|
|
|
8
9
|
function wait(ms) {
|
|
9
10
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
@@ -467,6 +468,7 @@ export class Sen extends EventEmitter {
|
|
|
467
468
|
* @param {string} [options.query]
|
|
468
469
|
* @param {boolean} [options.forceBus]
|
|
469
470
|
* @param {number} [options.timeout]
|
|
471
|
+
* @param {number} [options.id] Optional native interest id. Defaults to CRC32(query).
|
|
470
472
|
*/
|
|
471
473
|
async subscribe(busName, options = {}) {
|
|
472
474
|
if (!this.client) {
|
|
@@ -507,7 +509,7 @@ export class Sen extends EventEmitter {
|
|
|
507
509
|
/**
|
|
508
510
|
* Start a native SEN interest and return a live object collection.
|
|
509
511
|
*
|
|
510
|
-
* @param {string} query Native SEN interest query, for example `SELECT * FROM
|
|
512
|
+
* @param {string} query Native SEN interest query, for example `SELECT * FROM session.bus`.
|
|
511
513
|
* @param {object} [options]
|
|
512
514
|
* @param {string} [options.bus] Explicit bus when it cannot be inferred from the query.
|
|
513
515
|
* @param {boolean} [options.forceBus]
|
|
@@ -944,7 +946,7 @@ export class Sen extends EventEmitter {
|
|
|
944
946
|
return this.targetsBySession.keys().next().value;
|
|
945
947
|
}
|
|
946
948
|
|
|
947
|
-
throw new Error(`cannot infer SEN session from bus "${busName}"; use a session-qualified query such as SELECT * FROM
|
|
949
|
+
throw new Error(`cannot infer SEN session from bus "${busName}"; use a session-qualified query such as SELECT * FROM session.${busName}`);
|
|
948
950
|
}
|
|
949
951
|
|
|
950
952
|
#assertBusBelongsToSession(busName, sessionName) {
|
|
@@ -987,6 +989,7 @@ export class Sen extends EventEmitter {
|
|
|
987
989
|
});
|
|
988
990
|
client.on('busLeft', value => {
|
|
989
991
|
this.remoteBuses.delete(value.busName);
|
|
992
|
+
this.#busForEvent(value)?.handleParticipantLeft(value);
|
|
990
993
|
this.emit('busUnavailable', value);
|
|
991
994
|
});
|
|
992
995
|
client.on('objectsPublished', event => this.#busForEvent(event)?.handleObjectsPublished(event));
|
|
@@ -998,6 +1001,10 @@ export class Sen extends EventEmitter {
|
|
|
998
1001
|
client.on('runtimeEvents', event => this.#busForEvent(event)?.handleRuntimeEvents(event));
|
|
999
1002
|
client.on('runtimeMethodResponse', event => this.#busForEvent(event)?.handleRuntimeMethodResponse(event));
|
|
1000
1003
|
client.on('error', error => {
|
|
1004
|
+
if (['EPIPE', 'ECONNRESET', 'SEN_TCP_NOT_WRITABLE'].includes(error?.code)) {
|
|
1005
|
+
this.emit('warning', error);
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1001
1008
|
if (this.manualClose || this.reconnecting || this.options.reconnect !== false) {
|
|
1002
1009
|
this.emit('warning', error);
|
|
1003
1010
|
return;
|
|
@@ -1232,7 +1239,7 @@ export class SenBus extends EventEmitter {
|
|
|
1232
1239
|
}
|
|
1233
1240
|
|
|
1234
1241
|
startInterest(query, options = {}) {
|
|
1235
|
-
const started = this.sen.client.startInterest(this.name, query);
|
|
1242
|
+
const started = this.sen.client.startInterest(this.name, query, { id: options.id });
|
|
1236
1243
|
const interest = new SenInterest(this, started.id, query, options);
|
|
1237
1244
|
this.interests.set(interest.id, interest);
|
|
1238
1245
|
return interest;
|
|
@@ -1240,7 +1247,7 @@ export class SenBus extends EventEmitter {
|
|
|
1240
1247
|
|
|
1241
1248
|
stopInterest(id) {
|
|
1242
1249
|
const interestId = typeof id === 'object' ? id.id : id;
|
|
1243
|
-
this.sen.client?.stopInterest(this.name, interestId);
|
|
1250
|
+
this.sen.client?.stopInterest(this.id ?? this.name, interestId);
|
|
1244
1251
|
const interest = this.interests.get(interestId);
|
|
1245
1252
|
this.#detachInterestObjects(interestId, interest);
|
|
1246
1253
|
this.interests.delete(interestId);
|
|
@@ -1264,7 +1271,7 @@ export class SenBus extends EventEmitter {
|
|
|
1264
1271
|
}
|
|
1265
1272
|
}
|
|
1266
1273
|
try {
|
|
1267
|
-
this.sen.client?.leaveBus(this.name);
|
|
1274
|
+
this.sen.client?.leaveBus(this.id ?? this.name);
|
|
1268
1275
|
} catch (error) {
|
|
1269
1276
|
this.sen.emit('warning', error);
|
|
1270
1277
|
}
|
|
@@ -1307,7 +1314,7 @@ export class SenBus extends EventEmitter {
|
|
|
1307
1314
|
const interests = [...this.interests.values()];
|
|
1308
1315
|
this.interests.clear();
|
|
1309
1316
|
for (const interest of interests) {
|
|
1310
|
-
const started = this.sen.client.startInterest(this.name, interest.query);
|
|
1317
|
+
const started = this.sen.client.startInterest(this.name, interest.query, { id: interest.options.id ?? interest.id });
|
|
1311
1318
|
interest.id = started.id;
|
|
1312
1319
|
interest.resetLocal();
|
|
1313
1320
|
this.interests.set(interest.id, interest);
|
|
@@ -1353,6 +1360,7 @@ export class SenBus extends EventEmitter {
|
|
|
1353
1360
|
ownerId
|
|
1354
1361
|
});
|
|
1355
1362
|
}
|
|
1363
|
+
this.#attachKnownType(object);
|
|
1356
1364
|
interest?.objectsById.set(object.key, object);
|
|
1357
1365
|
if (info.state?.length) {
|
|
1358
1366
|
object.applyState(info.state, 'published', info.time, { interestId: discovery.interestId });
|
|
@@ -1387,6 +1395,19 @@ export class SenBus extends EventEmitter {
|
|
|
1387
1395
|
}
|
|
1388
1396
|
}
|
|
1389
1397
|
|
|
1398
|
+
handleParticipantLeft(event) {
|
|
1399
|
+
const ownerId = event?.participantId;
|
|
1400
|
+
if (ownerId === undefined || ownerId === null) {
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
const detail = { reason: event?.reason ?? 'busLeft', ownerId: ownerId >>> 0 };
|
|
1404
|
+
for (const object of [...this.objectsById.values()]) {
|
|
1405
|
+
if (object.ownerId === (ownerId >>> 0)) {
|
|
1406
|
+
this.#removeObjectFromAllInterests(object, detail);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1390
1411
|
#detachInterestObjects(interestId, interest) {
|
|
1391
1412
|
const normalizedInterestId = interestId >>> 0;
|
|
1392
1413
|
const keyPrefix = `${normalizedInterestId}:`;
|
|
@@ -1674,6 +1695,20 @@ export class SenBus extends EventEmitter {
|
|
|
1674
1695
|
}
|
|
1675
1696
|
}
|
|
1676
1697
|
|
|
1698
|
+
#attachKnownType(object) {
|
|
1699
|
+
if (object.spec) {
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
for (const spec of this.typeRegistry.values()) {
|
|
1703
|
+
const hash = crc32(spec?.qualifiedName ?? spec?.name ?? '');
|
|
1704
|
+
if (hash === object.typeHash) {
|
|
1705
|
+
object.spec = spec;
|
|
1706
|
+
object.emit('type', spec);
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1677
1712
|
#objectByOwnerAndId(ownerId, objectId) {
|
|
1678
1713
|
if (ownerId !== undefined && ownerId !== null) {
|
|
1679
1714
|
return this.objectsById.get(remoteObjectKey(ownerId, objectId));
|