sen-ether-client 0.1.5 → 0.1.7
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 +6 -1
- package/lib/discovery.js +42 -1
- package/lib/sen.js +77 -22
- package/package.json +1 -1
package/API.md
CHANGED
|
@@ -67,7 +67,12 @@ variable as its multicast default:
|
|
|
67
67
|
- `SEN_ETHER_DISCOVERY_PORT`
|
|
68
68
|
|
|
69
69
|
Multicast group, bind address and interface selection are explicit `sen-ether-client`
|
|
70
|
-
options, not SEN environment variables.
|
|
70
|
+
options, not SEN environment variables. When no `interfaceAddress` is provided,
|
|
71
|
+
multicast discovery joins every local IPv4 interface visible to Node.js. If a
|
|
72
|
+
SEN producer on the same host sends discovery through a physical interface that
|
|
73
|
+
does not loop multicast packets back locally, discovery can still return no
|
|
74
|
+
processes; in that case run the producer discovery on `lo`, pass the matching
|
|
75
|
+
`interfaceAddress`, or use SEN TCP discovery.
|
|
71
76
|
|
|
72
77
|
Preferred multi-session usage:
|
|
73
78
|
|
package/lib/discovery.js
CHANGED
|
@@ -50,6 +50,26 @@ function resolveInterfaceAddress(value) {
|
|
|
50
50
|
return ipv4.address;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
function multicastInterfaceCandidates(interfaceAddress) {
|
|
54
|
+
if (interfaceAddress) {
|
|
55
|
+
return [interfaceAddress];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const addresses = [];
|
|
60
|
+
for (const candidates of Object.values(os.networkInterfaces())) {
|
|
61
|
+
for (const item of candidates ?? []) {
|
|
62
|
+
if ((item.family === 'IPv4' || item.family === 4) && item.address) {
|
|
63
|
+
addresses.push(item.address);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return [...new Set(addresses)];
|
|
68
|
+
} catch {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
53
73
|
function normalizeTcpRemote(socket) {
|
|
54
74
|
return {
|
|
55
75
|
address: socket.remoteAddress,
|
|
@@ -149,8 +169,29 @@ export class EtherDiscoveryScanner extends EventEmitter {
|
|
|
149
169
|
const onListening = () => {
|
|
150
170
|
socket.off('error', onError);
|
|
151
171
|
try {
|
|
152
|
-
|
|
172
|
+
const interfaces = multicastInterfaceCandidates(this.interfaceAddress);
|
|
173
|
+
if (interfaces.length) {
|
|
174
|
+
let joined = 0;
|
|
175
|
+
/** @type {Error | undefined} */
|
|
176
|
+
let firstError;
|
|
177
|
+
for (const interfaceAddress of interfaces) {
|
|
178
|
+
try {
|
|
179
|
+
socket.addMembership(this.group, interfaceAddress);
|
|
180
|
+
joined += 1;
|
|
181
|
+
} catch (error) {
|
|
182
|
+
firstError ??= /** @type {Error} */ (error);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (!joined) {
|
|
186
|
+
throw firstError ?? new Error(`could not join multicast group ${this.group}`);
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
socket.addMembership(this.group);
|
|
190
|
+
}
|
|
153
191
|
socket.setMulticastLoopback(true);
|
|
192
|
+
if (this.interfaceAddress) {
|
|
193
|
+
socket.setMulticastInterface(this.interfaceAddress);
|
|
194
|
+
}
|
|
154
195
|
} catch (error) {
|
|
155
196
|
reject(error);
|
|
156
197
|
return;
|
package/lib/sen.js
CHANGED
|
@@ -132,6 +132,13 @@ function busSummary(sessionName, busName) {
|
|
|
132
132
|
};
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
function knownBusNames(sen) {
|
|
136
|
+
return [...new Set([
|
|
137
|
+
...sen.remoteBuses,
|
|
138
|
+
...sen.buses.keys()
|
|
139
|
+
])].sort();
|
|
140
|
+
}
|
|
141
|
+
|
|
135
142
|
function selectorDescription(selector) {
|
|
136
143
|
return typeof selector === 'function' ? '<predicate>' : String(selector);
|
|
137
144
|
}
|
|
@@ -364,12 +371,7 @@ export class Sen extends EventEmitter {
|
|
|
364
371
|
throw new Error('no SEN ether processes discovered');
|
|
365
372
|
}
|
|
366
373
|
this.targets = targets;
|
|
367
|
-
|
|
368
|
-
const sessionName = targetSessionName(target);
|
|
369
|
-
if (sessionName && !this.targetsBySession.has(sessionName)) {
|
|
370
|
-
this.targetsBySession.set(sessionName, target);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
374
|
+
this.#rememberTargets(targets, { replace: false });
|
|
373
375
|
this.emit('connect', {
|
|
374
376
|
sessions: [...this.targetsBySession.keys()],
|
|
375
377
|
targets
|
|
@@ -602,8 +604,7 @@ export class Sen extends EventEmitter {
|
|
|
602
604
|
}
|
|
603
605
|
|
|
604
606
|
const sessionName = this.target?.session?.name ?? this.client.processInfo.sessionName;
|
|
605
|
-
return
|
|
606
|
-
.sort()
|
|
607
|
+
return knownBusNames(this)
|
|
607
608
|
.map(busName => options.qualified ? queryBusName(sessionName, busName) : busName);
|
|
608
609
|
}
|
|
609
610
|
|
|
@@ -621,24 +622,68 @@ export class Sen extends EventEmitter {
|
|
|
621
622
|
|
|
622
623
|
if (this.client) {
|
|
623
624
|
const sessionName = this.target?.session?.name ?? this.client.processInfo.sessionName;
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
625
|
+
const summaries = new Map();
|
|
626
|
+
const addBus = busName => {
|
|
627
|
+
const summary = busSummary(sessionName, busName);
|
|
628
|
+
if (summary) {
|
|
629
|
+
summaries.set(summary.qualified, summary);
|
|
630
|
+
}
|
|
631
|
+
};
|
|
629
632
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
if (!targets.length) {
|
|
633
|
-
throw new Error('no SEN ether processes discovered');
|
|
633
|
+
for (const busName of await waitForSessionBuses(this, settleMs)) {
|
|
634
|
+
addBus(busName);
|
|
634
635
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
636
|
+
|
|
637
|
+
if (!summaries.size || config.refreshTargets === true) {
|
|
638
|
+
try {
|
|
639
|
+
const target = await this.#discoverTarget({ ...config, session: sessionName });
|
|
640
|
+
if (target) {
|
|
641
|
+
const session = new Sen({
|
|
642
|
+
...config,
|
|
643
|
+
session: sessionName,
|
|
644
|
+
reconnect: false
|
|
645
|
+
});
|
|
646
|
+
session.on('warning', error => this.emit('warning', error));
|
|
647
|
+
session.on('error', error => this.emit('warning', error));
|
|
648
|
+
try {
|
|
649
|
+
await session.connect({
|
|
650
|
+
...config,
|
|
651
|
+
session: sessionName,
|
|
652
|
+
target,
|
|
653
|
+
reconnect: false
|
|
654
|
+
});
|
|
655
|
+
for (const busName of await waitForSessionBuses(session, settleMs)) {
|
|
656
|
+
addBus(busName);
|
|
657
|
+
}
|
|
658
|
+
} finally {
|
|
659
|
+
await session.close().catch(error => this.emit('warning', error));
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
} catch (error) {
|
|
663
|
+
this.emit('warning', error);
|
|
640
664
|
}
|
|
641
665
|
}
|
|
666
|
+
|
|
667
|
+
return [...summaries.values()].sort((a, b) => a.qualified.localeCompare(b.qualified));
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
let discoveredTargets = [];
|
|
671
|
+
if (!this.targets.length || this.sessions.size || config.refreshTargets === true) {
|
|
672
|
+
try {
|
|
673
|
+
discoveredTargets = await this.#discoverTargets(config);
|
|
674
|
+
} catch (error) {
|
|
675
|
+
if (!this.targets.length && !this.sessions.size) {
|
|
676
|
+
throw error;
|
|
677
|
+
}
|
|
678
|
+
this.emit('warning', error);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
if (discoveredTargets.length) {
|
|
683
|
+
this.targets = discoveredTargets;
|
|
684
|
+
this.#rememberTargets(discoveredTargets, { replace: true });
|
|
685
|
+
} else if (!this.targets.length && !this.sessions.size) {
|
|
686
|
+
throw new Error('no SEN ether processes discovered');
|
|
642
687
|
}
|
|
643
688
|
|
|
644
689
|
const summaries = new Map();
|
|
@@ -782,6 +827,16 @@ export class Sen extends EventEmitter {
|
|
|
782
827
|
return target;
|
|
783
828
|
}
|
|
784
829
|
|
|
830
|
+
#rememberTargets(targets, options = {}) {
|
|
831
|
+
const replace = options.replace !== false;
|
|
832
|
+
for (const target of targets) {
|
|
833
|
+
const sessionName = targetSessionName(target);
|
|
834
|
+
if (sessionName && (replace || !this.targetsBySession.has(sessionName))) {
|
|
835
|
+
this.targetsBySession.set(sessionName, target);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
785
840
|
async #reconnectTarget(options) {
|
|
786
841
|
if (options.tcpHub || !options.target) {
|
|
787
842
|
return await this.#discoverTarget(options);
|