sen-ether-client 0.1.7 → 0.2.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/lib/sen.js CHANGED
@@ -384,42 +384,57 @@ export class Sen extends EventEmitter {
384
384
 
385
385
  async #connectSingle(config) {
386
386
  const target = config.target ?? await this.#discoverTarget(config);
387
- if (!target) {
387
+ const activeNode = Boolean(config.session && (config.tcpHub || config.multicastDiscovery !== false));
388
+ if (!target && !activeNode) {
388
389
  throw new Error('no SEN ether process matches the requested filters');
389
390
  }
390
391
 
391
- const sessionName = target.session?.name ?? target.info?.sessionName ?? config.session;
392
+ const sessionName = target?.session?.name ?? target?.info?.sessionName ?? config.session;
392
393
  if (!sessionName) {
393
394
  throw new Error('cannot connect without a SEN session name');
394
395
  }
395
- if (!this.targets.includes(target)) {
396
+ if (target && !this.targets.includes(target)) {
396
397
  this.targets.push(target);
397
398
  }
398
- if (!this.targetsBySession.has(sessionName)) {
399
+ if (target && !this.targetsBySession.has(sessionName)) {
399
400
  this.targetsBySession.set(sessionName, target);
400
401
  }
401
402
 
402
403
  const client = new EtherClient({
403
404
  sessionName,
404
405
  appName: config.appName,
406
+ tcpHub: config.tcpHub,
407
+ multicastDiscovery: config.multicastDiscovery,
408
+ listen: config.listen,
409
+ listenHost: config.listenHost,
410
+ listenPort: config.listenPort,
411
+ advertisedHost: config.advertisedHost,
412
+ beamPeriodMs: config.beamPeriodMs,
405
413
  socketKeepAlive: config.socketKeepAlive,
406
414
  socketKeepAliveInitialDelayMs: config.socketKeepAliveInitialDelayMs,
407
415
  socketIdleTimeoutMs: config.socketIdleTimeoutMs,
408
416
  interfaceAddress: config.interfaceAddress,
417
+ group: config.group,
418
+ bindAddress: config.bindAddress,
409
419
  discoveryPort: config.port,
410
420
  busMulticast: config.busMulticast,
411
421
  busMulticastPort: config.busMulticastPort,
412
422
  busMulticastRange: config.busMulticastRange
413
423
  });
414
424
  this.client = client;
415
- this.target = target;
425
+ this.target = target ?? { session: { name: sessionName }, process: client.processInfo, info: client.processInfo, local: true };
416
426
  this.#wireClient(client);
417
427
 
418
428
  try {
419
- await client.connect(target);
420
- await waitForEvent(client, 'ready', config.timeout ?? 3000);
421
- this.#startPresenceWatchdog(target, config);
422
- this.emit('connect', { target, sessionName });
429
+ if (config.tcpHub || config.listen !== false) {
430
+ await client.start(config);
431
+ }
432
+ if (target) {
433
+ await client.connect(target);
434
+ await waitForEvent(client, 'ready', config.timeout ?? 3000);
435
+ this.#startPresenceWatchdog(target, config);
436
+ }
437
+ this.emit('connect', { target: this.target, sessionName });
423
438
  return this;
424
439
  } catch (error) {
425
440
  await client.close().catch(closeError => this.emit('warning', closeError));
@@ -534,6 +549,60 @@ export class Sen extends EventEmitter {
534
549
  return await this.subscribe(name, options);
535
550
  }
536
551
 
552
+ /**
553
+ * Publish local JavaScript objects on a SEN bus.
554
+ *
555
+ * @param {string} busName Session-qualified or ether-local bus name.
556
+ * @param {object|object[]} objects
557
+ * @param {object} [options]
558
+ */
559
+ async publishObjects(busName, objects, options = {}) {
560
+ if (!this.client) {
561
+ const sessionName = this.#sessionNameForBus(busName, options);
562
+ const session = await this.session(sessionName);
563
+ return await session.publishObjects(busName, objects, options);
564
+ }
565
+
566
+ if (!this.client || !this.target) {
567
+ throw new Error('Sen is not connected');
568
+ }
569
+
570
+ const sessionName = this.target.session?.name ?? this.client.processInfo.sessionName;
571
+ this.#assertBusBelongsToSession(busName, sessionName);
572
+ const bus = etherBusName(sessionName, busName);
573
+
574
+ if (!this.buses.has(bus)) {
575
+ const joined = await this.client.joinBus(bus, options);
576
+ this.buses.set(bus, new SenBus(this, bus, joined.busId));
577
+ }
578
+
579
+ return this.client.publishObjects(bus, objects, options);
580
+ }
581
+
582
+ /**
583
+ * Remove previously published local JavaScript objects from a SEN bus.
584
+ *
585
+ * @param {string} busName Session-qualified or ether-local bus name.
586
+ * @param {Array<string|number>|string|number} objects Object ids or names.
587
+ * @param {object} [options]
588
+ */
589
+ async removePublishedObjects(busName, objects, options = {}) {
590
+ if (!this.client) {
591
+ const sessionName = this.#sessionNameForBus(busName, options);
592
+ const session = await this.session(sessionName);
593
+ return await session.removePublishedObjects(busName, objects, options);
594
+ }
595
+
596
+ if (!this.client || !this.target) {
597
+ throw new Error('Sen is not connected');
598
+ }
599
+
600
+ const sessionName = this.target.session?.name ?? this.client.processInfo.sessionName;
601
+ this.#assertBusBelongsToSession(busName, sessionName);
602
+ const bus = etherBusName(sessionName, busName);
603
+ return this.client.removePublishedObjects(bus, objects);
604
+ }
605
+
537
606
  async session(name) {
538
607
  const sessionName = String(name || '').trim();
539
608
  if (!sessionName) {
@@ -970,17 +1039,36 @@ export class Sen extends EventEmitter {
970
1039
  client = new EtherClient({
971
1040
  sessionName,
972
1041
  appName: config.appName,
1042
+ tcpHub: config.tcpHub,
1043
+ multicastDiscovery: config.multicastDiscovery,
1044
+ listen: config.listen,
1045
+ listenHost: config.listenHost,
1046
+ listenPort: config.listenPort,
1047
+ advertisedHost: config.advertisedHost,
1048
+ beamPeriodMs: config.beamPeriodMs,
973
1049
  socketKeepAlive: config.socketKeepAlive,
974
1050
  socketKeepAliveInitialDelayMs: config.socketKeepAliveInitialDelayMs,
975
- socketIdleTimeoutMs: config.socketIdleTimeoutMs
1051
+ socketIdleTimeoutMs: config.socketIdleTimeoutMs,
1052
+ interfaceAddress: config.interfaceAddress,
1053
+ group: config.group,
1054
+ bindAddress: config.bindAddress,
1055
+ discoveryPort: config.port,
1056
+ busMulticast: config.busMulticast,
1057
+ busMulticastPort: config.busMulticastPort,
1058
+ busMulticastRange: config.busMulticastRange
976
1059
  });
977
1060
  this.client = client;
978
1061
  this.target = target;
979
1062
  this.#wireClient(client);
980
1063
 
981
- await client.connect(target);
982
- await waitForEvent(client, 'ready', config.timeout ?? 3000);
983
- this.#startPresenceWatchdog(target, config);
1064
+ if (config.tcpHub || config.listen !== false) {
1065
+ await client.start(config);
1066
+ }
1067
+ if (target) {
1068
+ await client.connect(target);
1069
+ await waitForEvent(client, 'ready', config.timeout ?? 3000);
1070
+ this.#startPresenceWatchdog(target, config);
1071
+ }
984
1072
 
985
1073
  for (const bus of this.buses.values()) {
986
1074
  await bus.rejoin(config.timeout ?? 3000);
package/lib/values.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { SenBinaryReader, SenBinaryWriter } from './codec.js';
2
2
  import { decodePropertyUpdateBuffer } from './bus.js';
3
+ import { propertyHash } from './hash32.js';
3
4
 
4
5
  function numberOrBigInt(value) {
5
6
  return value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : value;
@@ -323,6 +324,19 @@ export function encodeValue(value, typeName, typeRegistry) {
323
324
  return writer.toBuffer();
324
325
  }
325
326
 
327
+ export function encodePropertyUpdateBuffer(updates = [], typeRegistry) {
328
+ const writer = new SenBinaryWriter();
329
+ for (const update of updates) {
330
+ writer.writeUInt32(update.id ?? propertyHash(update.name));
331
+ const value = update.valueBuffer
332
+ ? Buffer.from(update.valueBuffer)
333
+ : encodeValue(update.value, update.type, typeRegistry);
334
+ writer.writeUInt32(value.length);
335
+ writer.chunks.push(value);
336
+ }
337
+ return writer.toBuffer();
338
+ }
339
+
326
340
  export function encodeArguments(values, argSpecs = [], typeRegistry) {
327
341
  if ((values?.length ?? 0) !== argSpecs.length) {
328
342
  throw new TypeError(`SEN method expects ${argSpecs.length} argument(s), got ${values?.length ?? 0}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sen-ether-client",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "Pure JavaScript SEN client for existing kernels over ether",
5
5
  "senCompatibility": {
6
6
  "kernelProtocolVersion": 9,
@@ -25,7 +25,7 @@
25
25
  "exports": "./index.js",
26
26
  "scripts": {
27
27
  "generate:protocol": "node ./scripts/generate-protocol.mjs",
28
- "test": "node --test ./test/protocol.test.js ./test/codec.test.js ./test/discovery.test.js ./test/sen.test.js",
28
+ "test": "node --test --test-concurrency=1 --test-reporter spec ./test/protocol.test.js ./test/codec.test.js ./test/discovery.test.js ./test/client.test.js ./test/sen.test.js",
29
29
  "test:integration": "node --test ./test/integration-*.test.js"
30
30
  }
31
31
  }