mqtt-plus 1.4.0 → 1.4.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.
Files changed (73) hide show
  1. package/AGENTS.md +55 -44
  2. package/CHANGELOG.md +14 -0
  3. package/README.md +4 -3
  4. package/doc/mqtt-plus-api.md +693 -680
  5. package/doc/mqtt-plus-architecture.d2 +139 -0
  6. package/doc/mqtt-plus-architecture.md +10 -0
  7. package/doc/mqtt-plus-architecture.svg +95 -0
  8. package/doc/mqtt-plus-broker-setup.md +9 -3
  9. package/doc/mqtt-plus-comm.md +73 -0
  10. package/doc/mqtt-plus-internals.md +3 -3
  11. package/dst-stage1/mqtt-plus-base.d.ts +3 -2
  12. package/dst-stage1/mqtt-plus-base.js +53 -22
  13. package/dst-stage1/mqtt-plus-event.d.ts +0 -2
  14. package/dst-stage1/mqtt-plus-event.js +6 -26
  15. package/dst-stage1/mqtt-plus-meta.d.ts +2 -2
  16. package/dst-stage1/mqtt-plus-meta.js +2 -2
  17. package/dst-stage1/mqtt-plus-msg.d.ts +2 -0
  18. package/dst-stage1/mqtt-plus-msg.js +17 -0
  19. package/dst-stage1/mqtt-plus-service.d.ts +0 -5
  20. package/dst-stage1/mqtt-plus-service.js +12 -48
  21. package/dst-stage1/mqtt-plus-sink.d.ts +0 -10
  22. package/dst-stage1/mqtt-plus-sink.js +25 -92
  23. package/dst-stage1/mqtt-plus-source.d.ts +0 -10
  24. package/dst-stage1/mqtt-plus-source.js +23 -88
  25. package/dst-stage1/mqtt-plus-subscription.d.ts +20 -0
  26. package/dst-stage1/mqtt-plus-subscription.js +126 -0
  27. package/dst-stage1/mqtt-plus-timer.d.ts +8 -0
  28. package/dst-stage1/mqtt-plus-timer.js +57 -0
  29. package/dst-stage1/mqtt-plus-topic.d.ts +20 -0
  30. package/dst-stage1/mqtt-plus-topic.js +112 -0
  31. package/dst-stage1/mqtt-plus-trace.js +2 -0
  32. package/dst-stage1/mqtt-plus-util.d.ts +0 -13
  33. package/dst-stage1/mqtt-plus-util.js +1 -77
  34. package/dst-stage1/mqtt-plus-version.d.ts +0 -1
  35. package/dst-stage1/mqtt-plus-version.js +0 -6
  36. package/dst-stage1/tsc.tsbuildinfo +1 -1
  37. package/dst-stage2/mqtt-plus.cjs.js +242 -292
  38. package/dst-stage2/mqtt-plus.esm.js +240 -290
  39. package/dst-stage2/mqtt-plus.umd.js +12 -12
  40. package/etc/knip.jsonc +1 -1
  41. package/etc/stx.conf +6 -4
  42. package/package.json +1 -1
  43. package/src/mqtt-plus-base.ts +56 -26
  44. package/src/mqtt-plus-event.ts +8 -24
  45. package/src/mqtt-plus-meta.ts +3 -3
  46. package/src/mqtt-plus-msg.ts +28 -0
  47. package/src/mqtt-plus-service.ts +12 -50
  48. package/src/mqtt-plus-sink.ts +32 -105
  49. package/src/mqtt-plus-source.ts +29 -99
  50. package/src/mqtt-plus-subscription.ts +141 -0
  51. package/src/mqtt-plus-timer.ts +61 -0
  52. package/src/mqtt-plus-trace.ts +4 -0
  53. package/src/mqtt-plus-util.ts +1 -81
  54. package/src/mqtt-plus-version.ts +0 -7
  55. package/tst/mqtt-plus-0-fixture.ts +2 -2
  56. package/tst/mqtt-plus-0-mosquitto.ts +5 -0
  57. package/tst/mqtt-plus-1-api.spec.ts +1 -1
  58. package/tst/mqtt-plus-2-event.spec.ts +0 -6
  59. package/tst/mqtt-plus-3-service.spec.ts +3 -7
  60. package/tst/mqtt-plus-4-sink.spec.ts +14 -9
  61. package/tst/mqtt-plus-5-source.spec.ts +11 -5
  62. package/tst/mqtt-plus-6-misc.spec.ts +23 -23
  63. package/tst/tsc.json +1 -1
  64. package/doc/mqtt-plus-communication.md +0 -68
  65. /package/doc/{mqtt-plus-1-event-emission.d2 → mqtt-plus-comm-event-emission.d2} +0 -0
  66. /package/doc/{mqtt-plus-1-event-emission.svg → mqtt-plus-comm-event-emission.svg} +0 -0
  67. /package/doc/{mqtt-plus-2-service-call.d2 → mqtt-plus-comm-service-call.d2} +0 -0
  68. /package/doc/{mqtt-plus-2-service-call.svg → mqtt-plus-comm-service-call.svg} +0 -0
  69. /package/doc/{mqtt-plus-3-sink-push.d2 → mqtt-plus-comm-sink-push.d2} +0 -0
  70. /package/doc/{mqtt-plus-3-sink-push.svg → mqtt-plus-comm-sink-push.svg} +0 -0
  71. /package/doc/{mqtt-plus-4-source-fetch.d2 → mqtt-plus-comm-source-fetch.d2} +0 -0
  72. /package/doc/{mqtt-plus-4-source-fetch.svg → mqtt-plus-comm-source-fetch.svg} +0 -0
  73. /package/{doc/theme.d2 → etc/d2.theme.d2} +0 -0
@@ -3,13 +3,13 @@ const node_stream = require("node:stream");
3
3
  const nanoid = require("nanoid");
4
4
  const node_buffer = require("node:buffer");
5
5
  const PLazy = require("p-lazy");
6
- const v = require("valibot");
7
- const CBOR = require("cbor2");
8
6
  const sign = require("jose/jwt/sign");
9
7
  const verify = require("jose/jwt/verify");
10
8
  const pbkdf2 = require("@stablelib/pbkdf2");
11
9
  const sha256 = require("@stablelib/sha256");
12
10
  const node_events = require("node:events");
11
+ const v = require("valibot");
12
+ const CBOR = require("cbor2");
13
13
  function _interopNamespaceDefault(e) {
14
14
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
15
15
  if (e) {
@@ -26,78 +26,10 @@ function _interopNamespaceDefault(e) {
26
26
  n.default = e;
27
27
  return Object.freeze(n);
28
28
  }
29
- const v__namespace = /* @__PURE__ */ _interopNamespaceDefault(v);
30
- const CBOR__namespace = /* @__PURE__ */ _interopNamespaceDefault(CBOR);
31
29
  const pbkdf2__namespace = /* @__PURE__ */ _interopNamespaceDefault(pbkdf2);
32
30
  const sha256__namespace = /* @__PURE__ */ _interopNamespaceDefault(sha256);
33
- class RefCountedSubscription {
34
- constructor(subscribeFn, unsubscribeFn, lingerMs = 30 * 1e3) {
35
- this.subscribeFn = subscribeFn;
36
- this.unsubscribeFn = unsubscribeFn;
37
- this.lingerMs = lingerMs;
38
- this.counts = /* @__PURE__ */ new Map();
39
- this.pending = /* @__PURE__ */ new Map();
40
- this.lingers = /* @__PURE__ */ new Map();
41
- }
42
- async subscribe(topic, options = { qos: 2 }) {
43
- const count = this.counts.get(topic) ?? 0;
44
- this.counts.set(topic, count + 1);
45
- const linger = this.lingers.get(topic);
46
- if (linger) {
47
- clearTimeout(linger);
48
- this.lingers.delete(topic);
49
- return;
50
- }
51
- if (count === 0) {
52
- const promise = this.subscribeFn(topic, options).finally(() => {
53
- this.pending.delete(topic);
54
- }).catch((err) => {
55
- const count2 = this.counts.get(topic);
56
- if (count2) {
57
- if (count2 <= 1)
58
- this.counts.delete(topic);
59
- else
60
- this.counts.set(topic, count2 - 1);
61
- }
62
- throw err;
63
- });
64
- this.pending.set(topic, promise);
65
- return promise;
66
- } else {
67
- const pending = this.pending.get(topic);
68
- if (pending)
69
- return pending;
70
- }
71
- }
72
- async unsubscribe(topic) {
73
- const count = this.counts.get(topic);
74
- if (count) {
75
- if (count <= 1) {
76
- this.counts.delete(topic);
77
- if (this.lingerMs > 0) {
78
- const timer = setTimeout(() => {
79
- this.lingers.delete(topic);
80
- this.unsubscribeFn(topic).catch(() => {
81
- });
82
- }, this.lingerMs);
83
- this.lingers.set(topic, timer);
84
- } else
85
- await this.unsubscribeFn(topic).catch(() => {
86
- });
87
- } else
88
- this.counts.set(topic, count - 1);
89
- }
90
- }
91
- async flush() {
92
- const topics = [...this.lingers.keys()];
93
- for (const topic of topics) {
94
- clearTimeout(this.lingers.get(topic));
95
- this.lingers.delete(topic);
96
- await this.unsubscribeFn(topic).catch(() => {
97
- });
98
- }
99
- }
100
- }
31
+ const v__namespace = /* @__PURE__ */ _interopNamespaceDefault(v);
32
+ const CBOR__namespace = /* @__PURE__ */ _interopNamespaceDefault(CBOR);
101
33
  class CreditGate {
102
34
  constructor(initialCredit) {
103
35
  this.waiters = [];
@@ -887,6 +819,14 @@ class Msg {
887
819
  } else
888
820
  throw new Error("invalid object: not of any known type");
889
821
  }
822
+ /* guard for request messages */
823
+ isRequest(msg) {
824
+ return msg instanceof EventEmission || msg instanceof ServiceCallRequest || msg instanceof SourceFetchRequest || msg instanceof SinkPushRequest;
825
+ }
826
+ /* guard for response messages */
827
+ isResponse(msg) {
828
+ return msg instanceof ServiceCallResponse || msg instanceof SinkPushResponse || msg instanceof SinkPushChunk || msg instanceof SinkPushCredit || msg instanceof SourceFetchResponse || msg instanceof SourceFetchChunk || msg instanceof SourceFetchCredit;
829
+ }
890
830
  }
891
831
  class MsgTrait extends EncodeTrait {
892
832
  constructor() {
@@ -901,6 +841,7 @@ class LogEvent {
901
841
  this.msg = msg;
902
842
  this.data = data;
903
843
  }
844
+ /* resolve all pending promises in the log event */
904
845
  async resolve() {
905
846
  if (this.msg instanceof Promise)
906
847
  this.msg = await this.msg.catch(() => "<resolve-failed>");
@@ -910,6 +851,7 @@ class LogEvent {
910
851
  this.data[field] = await this.data[field].catch(() => "<resolve-failed>");
911
852
  }
912
853
  }
854
+ /* render log event as string */
913
855
  toString() {
914
856
  const timestamp = new Date(this.timestamp);
915
857
  const year = timestamp.getFullYear();
@@ -967,6 +909,8 @@ class BaseTrait extends TraceTrait {
967
909
  /* construct API class */
968
910
  constructor(mqtt, options = {}) {
969
911
  super(options);
912
+ this.onRequest = /* @__PURE__ */ new Map();
913
+ this.onResponse = /* @__PURE__ */ new Map();
970
914
  if (mqtt === null) {
971
915
  this.log("info", "establishing proxy MQTT client");
972
916
  mqtt = new Proxy({}, {
@@ -998,7 +942,7 @@ class BaseTrait extends TraceTrait {
998
942
  this.mqtt.on("message", this._messageHandler);
999
943
  }
1000
944
  /* destroy API class */
1001
- destroy() {
945
+ async destroy() {
1002
946
  this.log("info", "un-hooking from MQTT client");
1003
947
  this.mqtt.off("message", this._messageHandler);
1004
948
  }
@@ -1057,31 +1001,174 @@ class BaseTrait extends TraceTrait {
1057
1001
  });
1058
1002
  }
1059
1003
  /* handle incoming MQTT message */
1060
- _onMessage(topic, message, packet) {
1061
- if (typeof message === "string")
1062
- this.log("info", `received from MQTT topic "${topic}" (type: string, length: ${message.length} chars)`);
1004
+ _onMessage(topic, data, packet) {
1005
+ const topicMatch = this.options.topicMatch(topic);
1006
+ if (topicMatch === null)
1007
+ return;
1008
+ if (typeof data === "string")
1009
+ this.log("info", `received from MQTT topic "${topic}" (type: string, length: ${data.length} chars)`);
1063
1010
  else
1064
- this.log("info", `received from MQTT topic "${topic}" (type: buffer, length: ${message.byteLength} bytes)`);
1065
- let parsed;
1011
+ this.log("info", `received from MQTT topic "${topic}" (type: buffer, length: ${data.byteLength} bytes)`);
1012
+ let payload;
1066
1013
  try {
1067
- const payload = this.codec.decode(message);
1068
- parsed = this.msg.parse(payload);
1069
- } catch (_err) {
1070
- const err = _err instanceof Error ? new Error(`failed to parse message: ${_err.message}`, { cause: _err }) : new Error("failed to parse message");
1071
- this.error(err);
1014
+ payload = this.codec.decode(data);
1015
+ } catch (err) {
1016
+ this.error(ensureError(err, "failed to parse message into object"));
1072
1017
  return;
1073
1018
  }
1074
- this.log("debug", `received from MQTT topic "${topic}"`, { message: parsed });
1075
- this._dispatchMessage(topic, parsed).catch((err) => {
1076
- this.error(err, `dispatching message from MQTT topic "${topic}" failed`);
1077
- });
1019
+ let message;
1020
+ try {
1021
+ message = this.msg.parse(payload);
1022
+ } catch (err) {
1023
+ this.error(ensureError(err, "failed to parse object into typed message object"));
1024
+ return;
1025
+ }
1026
+ this.log("debug", `received from MQTT topic "${topic}"`, { message });
1027
+ if (this.msg.isRequest(message)) {
1028
+ const handler = this.onRequest.get(`${topicMatch.operation}:${message.name}`);
1029
+ if (handler !== void 0) {
1030
+ try {
1031
+ handler(message, topicMatch.name);
1032
+ } catch (err) {
1033
+ this.error(ensureError(err, `dispatching request message from MQTT topic "${topic}" failed`));
1034
+ }
1035
+ }
1036
+ } else if (this.msg.isResponse(message)) {
1037
+ const handler = this.onResponse.get(`${topicMatch.operation}:${message.id}`);
1038
+ if (handler !== void 0) {
1039
+ try {
1040
+ handler(message, topicMatch.name);
1041
+ } catch (err) {
1042
+ this.error(ensureError(err, `dispatching response message from MQTT topic "${topic}" failed`));
1043
+ }
1044
+ }
1045
+ }
1078
1046
  }
1079
- /* dispatch parsed message to appropriate handler
1080
- (base implementation, to be overridden in sub-traits) */
1081
- async _dispatchMessage(_topic, _parsed) {
1047
+ }
1048
+ class RefCountedSubscription {
1049
+ constructor(subscribeFn, unsubscribeFn, lingerMs = 30 * 1e3) {
1050
+ this.subscribeFn = subscribeFn;
1051
+ this.unsubscribeFn = unsubscribeFn;
1052
+ this.lingerMs = lingerMs;
1053
+ this.counts = /* @__PURE__ */ new Map();
1054
+ this.pending = /* @__PURE__ */ new Map();
1055
+ this.lingers = /* @__PURE__ */ new Map();
1056
+ }
1057
+ /* subscribe to a topic (reference-counted) */
1058
+ async subscribe(topic, options = { qos: 2 }) {
1059
+ const count = this.counts.get(topic) ?? 0;
1060
+ this.counts.set(topic, count + 1);
1061
+ const linger = this.lingers.get(topic);
1062
+ if (linger) {
1063
+ clearTimeout(linger);
1064
+ this.lingers.delete(topic);
1065
+ return;
1066
+ }
1067
+ if (count === 0) {
1068
+ const promise = this.subscribeFn(topic, options).finally(() => {
1069
+ this.pending.delete(topic);
1070
+ }).catch((err) => {
1071
+ const count2 = this.counts.get(topic);
1072
+ if (count2) {
1073
+ if (count2 <= 1)
1074
+ this.counts.delete(topic);
1075
+ else
1076
+ this.counts.set(topic, count2 - 1);
1077
+ }
1078
+ throw err;
1079
+ });
1080
+ this.pending.set(topic, promise);
1081
+ return promise;
1082
+ } else {
1083
+ const pending = this.pending.get(topic);
1084
+ if (pending)
1085
+ return pending.catch((err) => {
1086
+ const count2 = this.counts.get(topic);
1087
+ if (count2) {
1088
+ if (count2 <= 1)
1089
+ this.counts.delete(topic);
1090
+ else
1091
+ this.counts.set(topic, count2 - 1);
1092
+ }
1093
+ throw err;
1094
+ });
1095
+ }
1096
+ }
1097
+ /* unsubscribe from a topic (reference-counted) */
1098
+ async unsubscribe(topic) {
1099
+ const count = this.counts.get(topic);
1100
+ if (count) {
1101
+ if (count <= 1) {
1102
+ this.counts.delete(topic);
1103
+ if (this.lingerMs > 0) {
1104
+ const timer = setTimeout(() => {
1105
+ this.lingers.delete(topic);
1106
+ this.unsubscribeFn(topic).catch(() => {
1107
+ });
1108
+ }, this.lingerMs);
1109
+ this.lingers.set(topic, timer);
1110
+ } else
1111
+ await this.unsubscribeFn(topic).catch(() => {
1112
+ });
1113
+ } else
1114
+ this.counts.set(topic, count - 1);
1115
+ }
1116
+ }
1117
+ /* flush all pending linger timers and unsubscribe */
1118
+ async flush() {
1119
+ const topics = [...this.lingers.keys()];
1120
+ for (const topic of topics) {
1121
+ clearTimeout(this.lingers.get(topic));
1122
+ this.lingers.delete(topic);
1123
+ }
1124
+ for (const topic of topics)
1125
+ await this.unsubscribeFn(topic).catch(() => {
1126
+ });
1127
+ }
1128
+ }
1129
+ class SubscriptionTrait extends BaseTrait {
1130
+ constructor() {
1131
+ super(...arguments);
1132
+ this.subscriptions = new RefCountedSubscription((topic, options) => this._subscribeTopic(topic, options), (topic) => this._unsubscribeTopic(topic));
1133
+ }
1134
+ /* destroy topic trait */
1135
+ async destroy() {
1136
+ await this.subscriptions.flush();
1137
+ await super.destroy();
1082
1138
  }
1083
1139
  }
1084
- class MetaTrait extends BaseTrait {
1140
+ class TimerTrait extends SubscriptionTrait {
1141
+ constructor() {
1142
+ super(...arguments);
1143
+ this.timers = /* @__PURE__ */ new Map();
1144
+ }
1145
+ /* destroy timer trait */
1146
+ async destroy() {
1147
+ for (const timer of this.timers.values())
1148
+ clearTimeout(timer);
1149
+ this.timers.clear();
1150
+ await super.destroy();
1151
+ }
1152
+ /* refresh (or start) a named timer */
1153
+ timerRefresh(id, onTimeout) {
1154
+ const timer = this.timers.get(id);
1155
+ if (timer !== void 0)
1156
+ clearTimeout(timer);
1157
+ this.timers.set(id, setTimeout(() => {
1158
+ this.timers.delete(id);
1159
+ onTimeout();
1160
+ }, this.options.timeout));
1161
+ }
1162
+ /* clear a named timer */
1163
+ timerClear(id) {
1164
+ const timer = this.timers.get(id);
1165
+ if (timer !== void 0) {
1166
+ clearTimeout(timer);
1167
+ this.timers.delete(id);
1168
+ }
1169
+ }
1170
+ }
1171
+ class MetaTrait extends TimerTrait {
1085
1172
  constructor() {
1086
1173
  super(...arguments);
1087
1174
  this._meta = /* @__PURE__ */ new Map();
@@ -1188,10 +1275,6 @@ class AuthTrait extends MetaTrait {
1188
1275
  }
1189
1276
  }
1190
1277
  class EventTrait extends AuthTrait {
1191
- constructor() {
1192
- super(...arguments);
1193
- this.events = /* @__PURE__ */ new Map();
1194
- }
1195
1278
  async event(nameOrConfig, ...args) {
1196
1279
  let name;
1197
1280
  let callback;
@@ -1209,12 +1292,12 @@ class EventTrait extends AuthTrait {
1209
1292
  callback = args[0];
1210
1293
  }
1211
1294
  const spool = new Spool();
1212
- if (this.events.has(name))
1295
+ if (this.onRequest.has(`event-emission:${name}`))
1213
1296
  throw new Error(`event: event "${name}" already registered`);
1214
1297
  const topicS = share ? `$share/${share}/${name}` : name;
1215
1298
  const topicB = this.options.topicMake(topicS, "event-emission");
1216
1299
  const topicD = this.options.topicMake(name, "event-emission", this.options.id);
1217
- this.events.set(name, (request, topicName) => {
1300
+ this.onRequest.set(`event-emission:${name}`, (request, topicName) => {
1218
1301
  const senderId = request.sender;
1219
1302
  const params = request.params ?? [];
1220
1303
  const info = { sender: senderId ?? "" };
@@ -1236,7 +1319,7 @@ class EventTrait extends AuthTrait {
1236
1319
  });
1237
1320
  });
1238
1321
  spool.roll(() => {
1239
- this.events.delete(name);
1322
+ this.onRequest.delete(`event-emission:${name}`);
1240
1323
  });
1241
1324
  await run(`subscribe to MQTT topic "${topicB}"`, spool, () => this._subscribeTopic(topicB, { qos: 2, ...options }));
1242
1325
  spool.roll(() => this._unsubscribeTopic(topicB).catch(() => {
@@ -1246,7 +1329,7 @@ class EventTrait extends AuthTrait {
1246
1329
  }));
1247
1330
  return {
1248
1331
  destroy: async () => {
1249
- if (!this.events.has(name))
1332
+ if (!this.onRequest.has(`event-emission:${name}`))
1250
1333
  throw new Error(`destroy: event "${name}" not registered`);
1251
1334
  await spool.unroll(false)?.catch((err) => {
1252
1335
  this.error(err, `destroy: failed to cleanup: ${err.message}`);
@@ -1259,14 +1342,14 @@ class EventTrait extends AuthTrait {
1259
1342
  let params;
1260
1343
  let receiver;
1261
1344
  let options = {};
1262
- let meta = {};
1345
+ let meta;
1263
1346
  let dry;
1264
1347
  if (typeof eventOrConfig === "object" && eventOrConfig !== null) {
1265
1348
  event = eventOrConfig.event;
1266
1349
  params = eventOrConfig.params;
1267
1350
  receiver = eventOrConfig.receiver;
1268
1351
  options = eventOrConfig.options ?? {};
1269
- meta = eventOrConfig.meta ?? {};
1352
+ meta = eventOrConfig.meta;
1270
1353
  dry = eventOrConfig.dry;
1271
1354
  } else {
1272
1355
  event = eventOrConfig;
@@ -1285,29 +1368,8 @@ class EventTrait extends AuthTrait {
1285
1368
  this.error(err, `emitting event "${event}" failed`);
1286
1369
  });
1287
1370
  }
1288
- /* dispatch message (Event pattern handling) */
1289
- async _dispatchMessage(topic, message) {
1290
- await super._dispatchMessage(topic, message);
1291
- const topicMatch = this.options.topicMatch(topic);
1292
- if (topicMatch !== null && topicMatch.operation === "event-emission" && message instanceof EventEmission) {
1293
- const handler = this.events.get(message.name);
1294
- if (handler !== void 0)
1295
- handler(message, topicMatch.name);
1296
- }
1297
- }
1298
1371
  }
1299
1372
  class ServiceTrait extends EventTrait {
1300
- constructor() {
1301
- super(...arguments);
1302
- this.services = /* @__PURE__ */ new Map();
1303
- this.callCallbacks = /* @__PURE__ */ new Map();
1304
- this.callSubscriptions = new RefCountedSubscription((topic, options) => this._subscribeTopic(topic, options), (topic) => this._unsubscribeTopic(topic));
1305
- }
1306
- /* destroy service trait */
1307
- destroy() {
1308
- super.destroy();
1309
- this.callSubscriptions.flush();
1310
- }
1311
1373
  async service(nameOrConfig, ...args) {
1312
1374
  let name;
1313
1375
  let callback;
@@ -1325,12 +1387,12 @@ class ServiceTrait extends EventTrait {
1325
1387
  callback = args[0];
1326
1388
  }
1327
1389
  const spool = new Spool();
1328
- if (this.services.has(name))
1329
- throw new Error(`register: service "${name}" already registered`);
1390
+ if (this.onRequest.has(`service-call-request:${name}`))
1391
+ throw new Error(`service: service "${name}" already registered`);
1330
1392
  const topicS = `$share/${share}/${name}`;
1331
1393
  const topicB = this.options.topicMake(topicS, "service-call-request");
1332
1394
  const topicD = this.options.topicMake(name, "service-call-request", this.options.id);
1333
- this.services.set(name, (request, topicName) => {
1395
+ this.onRequest.set(`service-call-request:${name}`, (request, topicName) => {
1334
1396
  const requestId = request.id;
1335
1397
  const senderId = request.sender;
1336
1398
  if (senderId === void 0 || senderId === "")
@@ -1364,7 +1426,7 @@ class ServiceTrait extends EventTrait {
1364
1426
  });
1365
1427
  });
1366
1428
  spool.roll(() => {
1367
- this.services.delete(name);
1429
+ this.onRequest.delete(`service-call-request:${name}`);
1368
1430
  });
1369
1431
  await run(`subscribe to MQTT topic "${topicB}"`, spool, () => this._subscribeTopic(topicB, { qos: 2, ...options }));
1370
1432
  spool.roll(() => this._unsubscribeTopic(topicB).catch(() => {
@@ -1374,8 +1436,8 @@ class ServiceTrait extends EventTrait {
1374
1436
  }));
1375
1437
  return {
1376
1438
  destroy: async () => {
1377
- if (!this.services.has(name))
1378
- throw new Error(`destroy: service "${name}" no longer registered`);
1439
+ if (!this.onRequest.has(`service-call-request:${name}`))
1440
+ throw new Error(`destroy: service "${name}" not registered`);
1379
1441
  await spool.unroll(false)?.catch((err) => {
1380
1442
  this.error(err, `destroy: failed to cleanup: ${err.message}`);
1381
1443
  });
@@ -1387,13 +1449,13 @@ class ServiceTrait extends EventTrait {
1387
1449
  let params;
1388
1450
  let receiver;
1389
1451
  let options = {};
1390
- let meta = {};
1452
+ let meta;
1391
1453
  if (typeof nameOrConfig === "object" && nameOrConfig !== null) {
1392
1454
  name = nameOrConfig.name;
1393
1455
  params = nameOrConfig.params;
1394
1456
  receiver = nameOrConfig.receiver;
1395
1457
  options = nameOrConfig.options ?? {};
1396
- meta = nameOrConfig.meta ?? {};
1458
+ meta = nameOrConfig.meta;
1397
1459
  } else {
1398
1460
  name = nameOrConfig;
1399
1461
  params = args;
@@ -1401,8 +1463,8 @@ class ServiceTrait extends EventTrait {
1401
1463
  const spool = new Spool();
1402
1464
  const requestId = nanoid.nanoid();
1403
1465
  const responseTopic = this.options.topicMake(name, "service-call-response", this.options.id);
1404
- await run(`subscribe to MQTT topic "${responseTopic}"`, spool, () => this.callSubscriptions.subscribe(responseTopic, { qos: options.qos ?? 2 }));
1405
- spool.roll(() => this.callSubscriptions.unsubscribe(responseTopic));
1466
+ await run(`subscribe to MQTT topic "${responseTopic}"`, spool, () => this.subscriptions.subscribe(responseTopic, { qos: options.qos ?? 2 }));
1467
+ spool.roll(() => this.subscriptions.unsubscribe(responseTopic));
1406
1468
  const promise = new Promise((resolve, reject) => {
1407
1469
  let timer = setTimeout(async () => {
1408
1470
  timer = null;
@@ -1415,7 +1477,7 @@ class ServiceTrait extends EventTrait {
1415
1477
  timer = null;
1416
1478
  }
1417
1479
  });
1418
- this.callCallbacks.set(requestId, async (response) => {
1480
+ this.onResponse.set(`service-call-response:${requestId}`, async (response) => {
1419
1481
  await spool.unroll();
1420
1482
  if (response.error !== void 0)
1421
1483
  reject(new Error(response.error));
@@ -1423,7 +1485,7 @@ class ServiceTrait extends EventTrait {
1423
1485
  resolve(response.result);
1424
1486
  });
1425
1487
  spool.roll(() => {
1426
- this.callCallbacks.delete(requestId);
1488
+ this.onResponse.delete(`service-call-response:${requestId}`);
1427
1489
  });
1428
1490
  });
1429
1491
  const auth = this.authenticate();
@@ -1434,56 +1496,11 @@ class ServiceTrait extends EventTrait {
1434
1496
  await run(`publish service request as MQTT message to topic "${topic}"`, spool, () => this._publishToTopic(topic, message, { qos: 2, ...options }));
1435
1497
  return promise;
1436
1498
  }
1437
- /* dispatch message (Service pattern handling) */
1438
- async _dispatchMessage(topic, message) {
1439
- await super._dispatchMessage(topic, message);
1440
- const topicMatch = this.options.topicMatch(topic);
1441
- if (topicMatch !== null && topicMatch.operation === "service-call-request" && message instanceof ServiceCallRequest) {
1442
- const handler = this.services.get(message.name);
1443
- if (handler !== void 0)
1444
- handler(message, topicMatch.name);
1445
- } else if (topicMatch !== null && topicMatch.operation === "service-call-response" && topicMatch.peerId === this.options.id && message instanceof ServiceCallResponse) {
1446
- const handler = this.callCallbacks.get(message.id);
1447
- if (handler !== void 0)
1448
- handler(message);
1449
- }
1450
- }
1451
1499
  }
1452
1500
  class SourceTrait extends ServiceTrait {
1453
1501
  constructor() {
1454
1502
  super(...arguments);
1455
- this.sources = /* @__PURE__ */ new Map();
1456
- this.fetchResponseCallbacks = /* @__PURE__ */ new Map();
1457
- this.fetchChunkCallbacks = /* @__PURE__ */ new Map();
1458
- this.sourceCreditCallbacks = /* @__PURE__ */ new Map();
1459
1503
  this.sourceCreditGates = /* @__PURE__ */ new Map();
1460
- this.sourceTimers = /* @__PURE__ */ new Map();
1461
- this.fetchSubscriptions = new RefCountedSubscription((topic, options) => this._subscribeTopic(topic, options), (topic) => this._unsubscribeTopic(topic));
1462
- }
1463
- /* refresh source timer for a specific request */
1464
- _refreshSourceTimer(requestId) {
1465
- const timer = this.sourceTimers.get(requestId);
1466
- if (timer !== void 0)
1467
- clearTimeout(timer);
1468
- this.sourceTimers.set(requestId, setTimeout(() => {
1469
- this.sourceTimers.delete(requestId);
1470
- const gate = this.sourceCreditGates.get(requestId);
1471
- if (gate !== void 0)
1472
- gate.abort();
1473
- }, this.options.timeout));
1474
- }
1475
- /* clear source timer for a specific request */
1476
- _clearSourceTimer(requestId) {
1477
- const timer = this.sourceTimers.get(requestId);
1478
- if (timer !== void 0) {
1479
- clearTimeout(timer);
1480
- this.sourceTimers.delete(requestId);
1481
- }
1482
- }
1483
- /* destroy source trait */
1484
- destroy() {
1485
- super.destroy();
1486
- this.fetchSubscriptions.flush();
1487
1504
  }
1488
1505
  async source(nameOrConfig, ...args) {
1489
1506
  let name;
@@ -1502,13 +1519,13 @@ class SourceTrait extends ServiceTrait {
1502
1519
  callback = args[0];
1503
1520
  }
1504
1521
  const spool = new Spool();
1505
- if (this.sources.has(name))
1522
+ if (this.onRequest.has(`source-fetch-request:${name}`))
1506
1523
  throw new Error(`source: source "${name}" already established`);
1507
1524
  const topicS = `$share/${share}/${name}`;
1508
1525
  const topicReqB = this.options.topicMake(topicS, "source-fetch-request");
1509
1526
  const topicReqD = this.options.topicMake(name, "source-fetch-request", this.options.id);
1510
1527
  const topicCreditD = this.options.topicMake(name, "source-fetch-credit", this.options.id);
1511
- this.sources.set(name, (request, topicName) => {
1528
+ this.onRequest.set(`source-fetch-request:${name}`, (request, topicName) => {
1512
1529
  const requestId = request.id;
1513
1530
  const params = request.params ?? [];
1514
1531
  const sender = request.sender;
@@ -1529,8 +1546,12 @@ class SourceTrait extends ServiceTrait {
1529
1546
  const message = this.codec.encode(response);
1530
1547
  await this._publishToTopic(responseTopic, message, { qos: 2 });
1531
1548
  };
1532
- const refreshSourceTimeout = () => this._refreshSourceTimer(requestId);
1533
- const clearSourceTimeout = () => this._clearSourceTimer(requestId);
1549
+ const refreshSourceTimeout = () => this.timerRefresh(requestId, () => {
1550
+ const gate = this.sourceCreditGates.get(requestId);
1551
+ if (gate !== void 0)
1552
+ gate.abort();
1553
+ });
1554
+ const clearSourceTimeout = () => this.timerClear(requestId);
1534
1555
  refreshSourceTimeout();
1535
1556
  const sendChunk = async (chunk, error, final) => {
1536
1557
  refreshSourceTimeout();
@@ -1542,9 +1563,9 @@ class SourceTrait extends ServiceTrait {
1542
1563
  const creditGate = initialCredit !== void 0 && initialCredit > 0 ? new CreditGate(initialCredit) : void 0;
1543
1564
  if (creditGate) {
1544
1565
  this.sourceCreditGates.set(requestId, creditGate);
1545
- this.sourceCreditCallbacks.set(requestId, (creditParsed) => {
1566
+ this.onResponse.set(`source-fetch-credit:${requestId}`, (creditParsed) => {
1546
1567
  creditGate.replenish(creditParsed.credit);
1547
- this._refreshSourceTimer(requestId);
1568
+ refreshSourceTimeout();
1548
1569
  });
1549
1570
  }
1550
1571
  let ackSent = false;
@@ -1582,11 +1603,11 @@ class SourceTrait extends ServiceTrait {
1582
1603
  creditGate.abort();
1583
1604
  this.sourceCreditGates.delete(requestId);
1584
1605
  }
1585
- this.sourceCreditCallbacks.delete(requestId);
1606
+ this.onResponse.delete(`source-fetch-credit:${requestId}`);
1586
1607
  });
1587
1608
  });
1588
1609
  spool.roll(() => {
1589
- this.sources.delete(name);
1610
+ this.onRequest.delete(`source-fetch-request:${name}`);
1590
1611
  });
1591
1612
  await run(`subscribe to MQTT topic "${topicReqB}"`, spool, () => this._subscribeTopic(topicReqB, { qos: 2, ...options }));
1592
1613
  spool.roll(() => this._unsubscribeTopic(topicReqB).catch(() => {
@@ -1599,7 +1620,7 @@ class SourceTrait extends ServiceTrait {
1599
1620
  }));
1600
1621
  return {
1601
1622
  destroy: async () => {
1602
- if (!this.sources.has(name))
1623
+ if (!this.onRequest.has(`source-fetch-request:${name}`))
1603
1624
  throw new Error(`destroy: source "${name}" not established`);
1604
1625
  await spool.unroll(false)?.catch((err) => {
1605
1626
  this.error(err, `destroy: failed to cleanup: ${err.message}`);
@@ -1627,10 +1648,10 @@ class SourceTrait extends ServiceTrait {
1627
1648
  const requestId = nanoid.nanoid();
1628
1649
  const responseTopic = this.options.topicMake(name, "source-fetch-response", this.options.id);
1629
1650
  const chunkTopic = this.options.topicMake(name, "source-fetch-chunk", this.options.id);
1630
- await run(`subscribe to MQTT topic "${responseTopic}"`, spool, () => this.fetchSubscriptions.subscribe(responseTopic, { qos: options.qos ?? 2 }));
1631
- spool.roll(() => this.fetchSubscriptions.unsubscribe(responseTopic));
1632
- await run(`subscribe to MQTT topic "${chunkTopic}"`, spool, () => this.fetchSubscriptions.subscribe(chunkTopic, { qos: options.qos ?? 2 }));
1633
- spool.roll(() => this.fetchSubscriptions.unsubscribe(chunkTopic));
1651
+ await run(`subscribe to MQTT topic "${responseTopic}"`, spool, () => this.subscriptions.subscribe(responseTopic, { qos: options.qos ?? 2 }));
1652
+ spool.roll(() => this.subscriptions.unsubscribe(responseTopic));
1653
+ await run(`subscribe to MQTT topic "${chunkTopic}"`, spool, () => this.subscriptions.subscribe(chunkTopic, { qos: options.qos ?? 2 }));
1654
+ spool.roll(() => this.subscriptions.unsubscribe(chunkTopic));
1634
1655
  const chunkCredit = this.options.chunkCredit;
1635
1656
  let chunksReceived = 0;
1636
1657
  let creditGranted = chunkCredit;
@@ -1638,7 +1659,7 @@ class SourceTrait extends ServiceTrait {
1638
1659
  const stream = new node_stream.Readable({
1639
1660
  highWaterMark: chunkCredit > 0 ? chunkCredit * this.options.chunkSize : 16 * 1024,
1640
1661
  read: (_size) => {
1641
- if (chunkCredit <= 0 || !this.fetchChunkCallbacks.has(requestId))
1662
+ if (chunkCredit <= 0 || !this.onResponse.has(`source-fetch-chunk:${requestId}`))
1642
1663
  return;
1643
1664
  const targetId = serverId ?? receiver;
1644
1665
  if (!targetId)
@@ -1681,7 +1702,7 @@ class SourceTrait extends ServiceTrait {
1681
1702
  refreshTimeout();
1682
1703
  stream.once("close", () => spool.unroll());
1683
1704
  stream.once("error", () => spool.unroll());
1684
- this.fetchResponseCallbacks.set(requestId, (response) => {
1705
+ this.onResponse.set(`source-fetch-response:${requestId}`, (response) => {
1685
1706
  if (response.sender)
1686
1707
  serverId = response.sender;
1687
1708
  metaResolve?.(response.meta);
@@ -1691,7 +1712,7 @@ class SourceTrait extends ServiceTrait {
1691
1712
  } else
1692
1713
  refreshTimeout();
1693
1714
  });
1694
- this.fetchChunkCallbacks.set(requestId, (response) => {
1715
+ this.onResponse.set(`source-fetch-chunk:${requestId}`, (response) => {
1695
1716
  if (response.sender)
1696
1717
  serverId = response.sender;
1697
1718
  if (response.error) {
@@ -1710,8 +1731,8 @@ class SourceTrait extends ServiceTrait {
1710
1731
  }
1711
1732
  });
1712
1733
  spool.roll(() => {
1713
- this.fetchResponseCallbacks.delete(requestId);
1714
- this.fetchChunkCallbacks.delete(requestId);
1734
+ this.onResponse.delete(`source-fetch-response:${requestId}`);
1735
+ this.onResponse.delete(`source-fetch-chunk:${requestId}`);
1715
1736
  });
1716
1737
  const auth = this.authenticate();
1717
1738
  const metaStore = this.metaStore(meta);
@@ -1727,67 +1748,12 @@ class SourceTrait extends ServiceTrait {
1727
1748
  makeMutuallyExclusiveFields(result, "stream", "buffer");
1728
1749
  return result;
1729
1750
  }
1730
- /* dispatch message (Source Fetch pattern handling) */
1731
- async _dispatchMessage(topic, message) {
1732
- await super._dispatchMessage(topic, message);
1733
- const topicMatch = this.options.topicMatch(topic);
1734
- if (topicMatch !== null && topicMatch.operation === "source-fetch-request" && message instanceof SourceFetchRequest) {
1735
- const handler = this.sources.get(message.name);
1736
- if (handler !== void 0)
1737
- handler(message, topicMatch.name);
1738
- } else if (topicMatch !== null && topicMatch.operation === "source-fetch-response" && message instanceof SourceFetchResponse) {
1739
- const handler = this.fetchResponseCallbacks.get(message.id);
1740
- if (handler !== void 0)
1741
- handler(message);
1742
- } else if (topicMatch !== null && topicMatch.operation === "source-fetch-chunk" && message instanceof SourceFetchChunk) {
1743
- const handler = this.fetchChunkCallbacks.get(message.id);
1744
- if (handler !== void 0)
1745
- handler(message);
1746
- } else if (topicMatch !== null && topicMatch.operation === "source-fetch-credit" && message instanceof SourceFetchCredit) {
1747
- const handler = this.sourceCreditCallbacks.get(message.id);
1748
- if (handler !== void 0)
1749
- handler(message);
1750
- }
1751
- }
1752
1751
  }
1753
1752
  class SinkTrait extends SourceTrait {
1754
1753
  constructor() {
1755
1754
  super(...arguments);
1756
- this.sinks = /* @__PURE__ */ new Map();
1757
1755
  this.pushStreams = /* @__PURE__ */ new Map();
1758
1756
  this.pushSpools = /* @__PURE__ */ new Map();
1759
- this.pushTimers = /* @__PURE__ */ new Map();
1760
- this.pushChunkCallbacks = /* @__PURE__ */ new Map();
1761
- this.pushResponseCallbacks = /* @__PURE__ */ new Map();
1762
- this.pushCreditCallbacks = /* @__PURE__ */ new Map();
1763
- this.pushSubscriptions = new RefCountedSubscription((topic, options) => this._subscribeTopic(topic, options), (topic) => this._unsubscribeTopic(topic));
1764
- }
1765
- /* destroy sink trait */
1766
- destroy() {
1767
- super.destroy();
1768
- this.pushSubscriptions.flush();
1769
- }
1770
- /* refresh push timer for a specific request */
1771
- _refreshPushTimer(requestId) {
1772
- const timer = this.pushTimers.get(requestId);
1773
- if (timer !== void 0)
1774
- clearTimeout(timer);
1775
- this.pushTimers.set(requestId, setTimeout(() => {
1776
- this.pushTimers.delete(requestId);
1777
- const stream = this.pushStreams.get(requestId);
1778
- if (stream !== void 0)
1779
- stream.destroy(new Error("push stream timeout"));
1780
- const spool = this.pushSpools.get(requestId);
1781
- spool?.unroll();
1782
- }, this.options.timeout));
1783
- }
1784
- /* clear push timer for a specific request */
1785
- _clearPushTimer(requestId) {
1786
- const timer = this.pushTimers.get(requestId);
1787
- if (timer !== void 0) {
1788
- clearTimeout(timer);
1789
- this.pushTimers.delete(requestId);
1790
- }
1791
1757
  }
1792
1758
  async sink(nameOrConfig, ...args) {
1793
1759
  let name;
@@ -1806,13 +1772,13 @@ class SinkTrait extends SourceTrait {
1806
1772
  callback = args[0];
1807
1773
  }
1808
1774
  const spool = new Spool();
1809
- if (this.sinks.has(name))
1775
+ if (this.onRequest.has(`sink-push-request:${name}`))
1810
1776
  throw new Error(`sink: sink "${name}" already established`);
1811
1777
  const topicS = `$share/${share}/${name}`;
1812
1778
  const topicReqB = this.options.topicMake(topicS, "sink-push-request");
1813
1779
  const topicReqD = this.options.topicMake(name, "sink-push-request", this.options.id);
1814
1780
  const topicChunkD = this.options.topicMake(name, "sink-push-chunk", this.options.id);
1815
- this.sinks.set(name, (request, topicName) => {
1781
+ this.onRequest.set(`sink-push-request:${name}`, (request, topicName) => {
1816
1782
  const requestId = request.id;
1817
1783
  const params = request.params ?? [];
1818
1784
  const sender = request.sender;
@@ -1850,8 +1816,14 @@ class SinkTrait extends SourceTrait {
1850
1816
  chunksReceived: 0,
1851
1817
  creditGranted: chunkCredit
1852
1818
  } : void 0;
1853
- const refreshPushTimeout = () => this._refreshPushTimer(requestId);
1854
- const clearPushTimeout = () => this._clearPushTimer(requestId);
1819
+ const refreshPushTimeout = () => this.timerRefresh(requestId, () => {
1820
+ const stream = this.pushStreams.get(requestId);
1821
+ if (stream !== void 0)
1822
+ stream.destroy(new Error("push stream timeout"));
1823
+ const spool2 = this.pushSpools.get(requestId);
1824
+ spool2?.unroll();
1825
+ });
1826
+ const clearPushTimeout = () => this.timerClear(requestId);
1855
1827
  const readable = new node_stream.Readable({
1856
1828
  highWaterMark: chunkCredit > 0 ? chunkCredit * this.options.chunkSize : 16 * 1024,
1857
1829
  read: (_size) => {
@@ -1876,7 +1848,7 @@ class SinkTrait extends SourceTrait {
1876
1848
  });
1877
1849
  readable.once("close", () => reqSpool.unroll());
1878
1850
  readable.once("error", () => reqSpool.unroll());
1879
- this.pushChunkCallbacks.set(requestId, (chunkParsed, chunkTopicName) => {
1851
+ this.onResponse.set(`sink-push-chunk:${requestId}`, (chunkParsed, chunkTopicName) => {
1880
1852
  if (chunkTopicName !== chunkParsed.name)
1881
1853
  throw new Error(`sink name mismatch between topic "${chunkTopicName}" and payload "${chunkParsed.name}"`);
1882
1854
  if (chunkParsed.error !== void 0) {
@@ -1896,7 +1868,7 @@ class SinkTrait extends SourceTrait {
1896
1868
  }
1897
1869
  });
1898
1870
  reqSpool.roll(() => {
1899
- this.pushChunkCallbacks.delete(requestId);
1871
+ this.onResponse.delete(`sink-push-chunk:${requestId}`);
1900
1872
  });
1901
1873
  refreshPushTimeout();
1902
1874
  reqSpool.roll(() => {
@@ -1919,7 +1891,7 @@ class SinkTrait extends SourceTrait {
1919
1891
  });
1920
1892
  });
1921
1893
  spool.roll(() => {
1922
- this.sinks.delete(name);
1894
+ this.onRequest.delete(`sink-push-request:${name}`);
1923
1895
  });
1924
1896
  await run(`subscribe to MQTT topic "${topicReqB}"`, spool, () => this._subscribeTopic(topicReqB, { qos: 2, ...options }));
1925
1897
  spool.roll(() => this._unsubscribeTopic(topicReqB).catch(() => {
@@ -1932,7 +1904,7 @@ class SinkTrait extends SourceTrait {
1932
1904
  }));
1933
1905
  return {
1934
1906
  destroy: async () => {
1935
- if (!this.sinks.has(name))
1907
+ if (!this.onRequest.has(`sink-push-request:${name}`))
1936
1908
  throw new Error(`destroy: sink "${name}" not established`);
1937
1909
  await spool.unroll(false)?.catch((err) => {
1938
1910
  this.error(err, `destroy: failed to cleanup: ${err.message}`);
@@ -1962,8 +1934,8 @@ class SinkTrait extends SourceTrait {
1962
1934
  const spool = new Spool();
1963
1935
  const requestId = nanoid.nanoid();
1964
1936
  const responseTopic = this.options.topicMake(name, "sink-push-response", this.options.id);
1965
- await run(`subscribe to MQTT topic "${responseTopic}"`, spool, () => this.pushSubscriptions.subscribe(responseTopic, { qos: options.qos ?? 2 }));
1966
- spool.roll(() => this.pushSubscriptions.unsubscribe(responseTopic));
1937
+ await run(`subscribe to MQTT topic "${responseTopic}"`, spool, () => this.subscriptions.subscribe(responseTopic, { qos: options.qos ?? 2 }));
1938
+ spool.roll(() => this.subscriptions.unsubscribe(responseTopic));
1967
1939
  const abortController = new AbortController();
1968
1940
  const abortSignal = abortController.signal;
1969
1941
  let timer = null;
@@ -1993,7 +1965,7 @@ class SinkTrait extends SourceTrait {
1993
1965
  spool.roll(() => {
1994
1966
  abortSignal.removeEventListener("abort", onAbort);
1995
1967
  });
1996
- this.pushResponseCallbacks.set(requestId, (response) => {
1968
+ this.onResponse.set(`sink-push-response:${requestId}`, (response) => {
1997
1969
  if (response.error)
1998
1970
  reject(new Error(response.error));
1999
1971
  else {
@@ -2004,13 +1976,13 @@ class SinkTrait extends SourceTrait {
2004
1976
  }
2005
1977
  });
2006
1978
  spool.roll(() => {
2007
- this.pushResponseCallbacks.delete(requestId);
1979
+ this.onResponse.delete(`sink-push-response:${requestId}`);
2008
1980
  });
2009
- this.pushCreditCallbacks.set(requestId, (_response) => {
1981
+ this.onResponse.set(`sink-push-credit:${requestId}`, (_response) => {
2010
1982
  refreshTimeout();
2011
1983
  });
2012
1984
  spool.roll(() => {
2013
- this.pushCreditCallbacks.delete(requestId);
1985
+ this.onResponse.delete(`sink-push-credit:${requestId}`);
2014
1986
  });
2015
1987
  const auth = this.authenticate();
2016
1988
  const metaStore = this.metaStore(meta);
@@ -2021,7 +1993,7 @@ class SinkTrait extends SourceTrait {
2021
1993
  reject(err);
2022
1994
  });
2023
1995
  });
2024
- this.pushResponseCallbacks.set(requestId, (response) => {
1996
+ this.onResponse.set(`sink-push-response:${requestId}`, (response) => {
2025
1997
  if (response.error)
2026
1998
  abortController.abort(new Error(response.error));
2027
1999
  });
@@ -2029,13 +2001,13 @@ class SinkTrait extends SourceTrait {
2029
2001
  creditGate = new CreditGate(initialCredit);
2030
2002
  if (creditGate) {
2031
2003
  const creditTopic = this.options.topicMake(name, "sink-push-credit", this.options.id);
2032
- await run(`subscribe to MQTT topic "${creditTopic}"`, spool, () => this.pushSubscriptions.subscribe(creditTopic, { qos: 2 }));
2033
- spool.roll(() => this.pushSubscriptions.unsubscribe(creditTopic));
2004
+ await run(`subscribe to MQTT topic "${creditTopic}"`, spool, () => this.subscriptions.subscribe(creditTopic, { qos: 2 }));
2005
+ spool.roll(() => this.subscriptions.unsubscribe(creditTopic));
2034
2006
  const gate = creditGate;
2035
2007
  spool.roll(() => {
2036
2008
  gate.abort();
2037
2009
  });
2038
- this.pushCreditCallbacks.set(requestId, (response) => {
2010
+ this.onResponse.set(`sink-push-credit:${requestId}`, (response) => {
2039
2011
  gate.replenish(response.credit);
2040
2012
  refreshTimeout();
2041
2013
  });
@@ -2063,28 +2035,6 @@ class SinkTrait extends SourceTrait {
2063
2035
  await spool.unroll();
2064
2036
  }
2065
2037
  }
2066
- /* dispatch incoming MQTT message */
2067
- async _dispatchMessage(topic, message) {
2068
- await super._dispatchMessage(topic, message);
2069
- const topicMatch = this.options.topicMatch(topic);
2070
- if (topicMatch !== null && topicMatch.operation === "sink-push-request" && message instanceof SinkPushRequest) {
2071
- const handler = this.sinks.get(message.name);
2072
- if (handler !== void 0)
2073
- handler(message, topicMatch.name);
2074
- } else if (topicMatch !== null && topicMatch.operation === "sink-push-response" && message instanceof SinkPushResponse) {
2075
- const handler = this.pushResponseCallbacks.get(message.id);
2076
- if (handler !== void 0)
2077
- handler(message);
2078
- } else if (topicMatch !== null && topicMatch.operation === "sink-push-chunk" && message instanceof SinkPushChunk) {
2079
- const handler = this.pushChunkCallbacks.get(message.id);
2080
- if (handler !== void 0)
2081
- handler(message, topicMatch.name);
2082
- } else if (topicMatch !== null && topicMatch.operation === "sink-push-credit" && message instanceof SinkPushCredit) {
2083
- const handler = this.pushCreditCallbacks.get(message.id);
2084
- if (handler !== void 0)
2085
- handler(message);
2086
- }
2087
- }
2088
2038
  }
2089
2039
  class MQTTp extends SinkTrait {
2090
2040
  }