vrpc 3.6.0 → 3.7.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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/vrpc/VrpcClient.js +38 -53
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vrpc",
3
- "version": "3.6.0",
3
+ "version": "3.7.0",
4
4
  "description": "Variadic Remote Procedure Calls",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -45,7 +45,7 @@
45
45
  "bugs": {
46
46
  "url": "https://github.com/heisenware/vrpc/issues"
47
47
  },
48
- "homepage": "https://vrpc.io",
48
+ "homepage": "https://docs.heisenware.com/developers/vrpc",
49
49
  "devDependencies": {
50
50
  "@heisenware/storage": "^1.2.2",
51
51
  "chai": "^4.3.10",
@@ -235,11 +235,8 @@ class VrpcClient extends EventEmitter {
235
235
  `${this._domain}/${agent}/+/__classInfoConcise__`
236
236
  )
237
237
  }
238
- // RPC responses
239
- await this._mqttSubscribe(this._vrpcClientId)
240
-
241
- // Single wildcard subscription for ALL continuous remote events
242
- await this._mqttSubscribe(`${this._vrpcClientId}/__e__/#`)
238
+ // Single wildcard catches BOTH RPC responses (length 3) AND events (length 4)
239
+ await this._mqttSubscribe(`${this._vrpcClientId}/#`)
243
240
 
244
241
  // Re-subscribe to all cached event topics
245
242
  this._log.debug('Restoring event subscriptions after reconnect...')
@@ -767,7 +764,7 @@ class VrpcClient extends EventEmitter {
767
764
 
768
765
  // Private functions
769
766
 
770
- static _createHash (str, length = 20) {
767
+ static _createHash (str, length = 16) {
771
768
  // Extended DJB2 hash with two accumulators
772
769
  let hash1 = 5381
773
770
  let hash2 = 52711 // different seed
@@ -1007,27 +1004,19 @@ class VrpcClient extends EventEmitter {
1007
1004
 
1008
1005
  const obsoleteTopics = Object.keys(this._cachedSubscriptions).filter(
1009
1006
  topic => {
1010
- const tokens = topic.split('/')
1011
- if (tokens[3] === '__e__') {
1012
- const topicInstance = tokens[6]
1013
- const topicEvent = tokens[7]
1014
- return topicInstance === instance && topicEvent === eventName
1015
- }
1016
- // Legacy fallback
1017
- const expectedOld = `${this._domain}/${agent}/${className}/${instance}:${eventName}`
1018
- return topic.startsWith(expectedOld)
1007
+ const sub = this._cachedSubscriptions[topic]
1008
+ return sub.instance === instance && sub.event === eventName
1019
1009
  }
1020
1010
  )
1021
1011
 
1022
1012
  obsoleteTopics.forEach(topic => {
1023
1013
  const id = `__e__${topic}`
1024
- const subscriptions = this._cachedSubscriptions[topic]
1025
- subscriptions.forEach(({ handler }) => {
1014
+ const sub = this._cachedSubscriptions[topic]
1015
+
1016
+ sub.listeners.forEach(({ handler }) => {
1026
1017
  this._eventEmitter.removeListener(id, handler)
1027
1018
  })
1028
- if (!topic.includes('__e__')) {
1029
- this._mqttUnsubscribe(topic).catch(() => {})
1030
- }
1019
+
1031
1020
  delete this._cachedSubscriptions[topic]
1032
1021
  })
1033
1022
  try {
@@ -1284,10 +1273,14 @@ class VrpcClient extends EventEmitter {
1284
1273
  async _onRemoteEvent ({ agent, className, instance, event, callback }) {
1285
1274
  const callbackId = this._getCallbackId(callback)
1286
1275
 
1287
- // Route directly to the client's single event inbox wildcard.
1288
- const topic = `${this._vrpcClientId}/__e__/${agent}/${className}/${
1276
+ // Generate a short, sanitized hash
1277
+ const hashInput = `${agent}-${className}-${
1289
1278
  instance || '__static__'
1290
- }/${event}/${callbackId}`
1279
+ }-${event}-${callbackId}`
1280
+ const topicId = VrpcClient._createHash(hashInput, 12)
1281
+
1282
+ // Route directly to the client's single event inbox wildcard
1283
+ const topic = `${this._vrpcClientId}/${topicId}`
1291
1284
  const id = `__e__${topic}`
1292
1285
 
1293
1286
  const handler = ({ a }) => callback.apply(null, a)
@@ -1298,10 +1291,16 @@ class VrpcClient extends EventEmitter {
1298
1291
  }
1299
1292
 
1300
1293
  if (!this._cachedSubscriptions[topic]) {
1301
- this._cachedSubscriptions[topic] = [{ callback, handler }]
1294
+ // Store metadata explicitly
1295
+ this._cachedSubscriptions[topic] = {
1296
+ agent,
1297
+ className,
1298
+ instance,
1299
+ event,
1300
+ listeners: [{ callback, handler }]
1301
+ }
1302
1302
  } else {
1303
- // We MUST push duplicates to act as a reference counter!
1304
- this._cachedSubscriptions[topic].push({ callback, handler })
1303
+ this._cachedSubscriptions[topic].listeners.push({ callback, handler })
1305
1304
  }
1306
1305
 
1307
1306
  return id
@@ -1312,29 +1311,22 @@ class VrpcClient extends EventEmitter {
1312
1311
  if (!callbackId) return null
1313
1312
 
1314
1313
  let foundId = null
1315
- const topics = Object.keys(this._cachedSubscriptions).filter(
1316
- t => t.endsWith(`/${callbackId}`) || t.endsWith(`-${callbackId}`)
1317
- )
1314
+ const topics = Object.keys(this._cachedSubscriptions)
1318
1315
 
1319
1316
  topics.forEach(topic => {
1320
1317
  const id = `__e__${topic}`
1321
- const index = this._cachedSubscriptions[topic].findIndex(
1322
- x => x.callback === callback
1323
- )
1318
+ const sub = this._cachedSubscriptions[topic]
1319
+
1320
+ const index = sub.listeners.findIndex(x => x.callback === callback)
1324
1321
 
1325
1322
  if (index !== -1) {
1326
1323
  // Decrease the "reference count" by removing one instance
1327
- this._cachedSubscriptions[topic].splice(index, 1)
1324
+ sub.listeners.splice(index, 1)
1328
1325
 
1329
1326
  // Only remove from the local event emitter if NO references remain
1330
- if (this._cachedSubscriptions[topic].length === 0) {
1327
+ if (sub.listeners.length === 0) {
1331
1328
  delete this._cachedSubscriptions[topic]
1332
1329
 
1333
- // Legacy cleanup just in case
1334
- if (!topic.includes('__e__')) {
1335
- this._mqttUnsubscribe(topic).catch(() => {})
1336
- }
1337
-
1338
1330
  // Cleanly wipe all listeners for this specific id
1339
1331
  this._eventEmitter.removeAllListeners(id)
1340
1332
  }
@@ -1347,27 +1339,20 @@ class VrpcClient extends EventEmitter {
1347
1339
  _clearCachedSubscriptions ({ lostAgent, lostInstance }) {
1348
1340
  const obsoleteTopics = Object.keys(this._cachedSubscriptions).filter(
1349
1341
  topic => {
1350
- const tokens = topic.split('/')
1351
- if (tokens[3] === '__e__') {
1352
- const topicAgent = tokens[4]
1353
- const topicInstance = tokens[6]
1354
- return topicAgent === lostAgent || topicInstance === lostInstance
1355
- }
1356
- // Legacy fallback
1357
- const [, agent, , instanceEvent] = topic.split('/')
1358
- const [instance] = (instanceEvent || '').split(':')
1359
- return agent === lostAgent || instance === lostInstance
1342
+ const sub = this._cachedSubscriptions[topic]
1343
+ return sub.agent === lostAgent || sub.instance === lostInstance
1360
1344
  }
1361
1345
  )
1346
+
1362
1347
  obsoleteTopics.forEach(topic => {
1363
1348
  const id = `__e__${topic}`
1364
1349
  this._log.debug(`Clearing subscriptions for obsolete topic: ${topic}`)
1365
- const subscriptions = this._cachedSubscriptions[topic]
1366
- subscriptions.forEach(({ handler }) => {
1350
+ const sub = this._cachedSubscriptions[topic]
1351
+
1352
+ sub.listeners.forEach(({ handler }) => {
1367
1353
  this._eventEmitter.removeListener(id, handler)
1368
1354
  })
1369
- // this async function will never reject
1370
- this._mqttUnsubscribe(topic)
1355
+
1371
1356
  delete this._cachedSubscriptions[topic]
1372
1357
  })
1373
1358
  }