@ya-modbus/mqtt-bridge 0.6.1 → 0.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 (145) hide show
  1. package/CHANGELOG.md +4 -2
  2. package/dist/cjs/bin/ya-modbus-bridge.d.ts.map +1 -0
  3. package/dist/cjs/bin/ya-modbus-bridge.js +12 -0
  4. package/dist/cjs/bin/ya-modbus-bridge.js.map +1 -0
  5. package/dist/cjs/package.json +1 -0
  6. package/dist/cjs/src/cli.d.ts.map +1 -0
  7. package/dist/cjs/src/cli.js +113 -0
  8. package/dist/cjs/src/cli.js.map +1 -0
  9. package/dist/cjs/src/device-manager.d.ts.map +1 -0
  10. package/dist/cjs/src/device-manager.js +82 -0
  11. package/dist/cjs/src/device-manager.js.map +1 -0
  12. package/dist/cjs/src/driver-loader.d.ts.map +1 -0
  13. package/dist/cjs/src/driver-loader.js +122 -0
  14. package/dist/cjs/src/driver-loader.js.map +1 -0
  15. package/dist/cjs/src/index.d.ts.map +1 -0
  16. package/dist/cjs/src/index.js +291 -0
  17. package/dist/cjs/src/index.js.map +1 -0
  18. package/dist/cjs/src/polling-scheduler.d.ts.map +1 -0
  19. package/dist/cjs/src/polling-scheduler.js +130 -0
  20. package/dist/cjs/src/polling-scheduler.js.map +1 -0
  21. package/dist/cjs/src/types.d.ts.map +1 -0
  22. package/dist/cjs/src/types.js +3 -0
  23. package/dist/{src → cjs/src}/types.js.map +1 -1
  24. package/dist/cjs/src/utils/config-validator.d.ts.map +1 -0
  25. package/dist/cjs/src/utils/config-validator.js +35 -0
  26. package/dist/cjs/src/utils/config-validator.js.map +1 -0
  27. package/dist/cjs/src/utils/config.d.ts.map +1 -0
  28. package/dist/cjs/src/utils/config.js +55 -0
  29. package/dist/cjs/src/utils/config.js.map +1 -0
  30. package/dist/cjs/src/utils/device-validation.d.ts.map +1 -0
  31. package/dist/cjs/src/utils/device-validation.js +74 -0
  32. package/dist/cjs/src/utils/device-validation.js.map +1 -0
  33. package/dist/cjs/src/utils/package-info.d.ts.map +1 -0
  34. package/dist/cjs/src/utils/package-info.js +30 -0
  35. package/dist/cjs/src/utils/package-info.js.map +1 -0
  36. package/dist/cjs/src/utils/process.d.ts.map +1 -0
  37. package/dist/cjs/src/utils/process.js +16 -0
  38. package/dist/cjs/src/utils/process.js.map +1 -0
  39. package/dist/cjs/src/utils/test-utils.d.ts.map +1 -0
  40. package/dist/cjs/src/utils/test-utils.js +556 -0
  41. package/dist/cjs/src/utils/test-utils.js.map +1 -0
  42. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
  43. package/dist/esm/bin/ya-modbus-bridge.d.ts +9 -0
  44. package/dist/esm/bin/ya-modbus-bridge.d.ts.map +1 -0
  45. package/dist/esm/bin/ya-modbus-bridge.js.map +1 -0
  46. package/dist/esm/package.json +1 -0
  47. package/dist/esm/src/cli.d.ts +4 -0
  48. package/dist/esm/src/cli.d.ts.map +1 -0
  49. package/dist/esm/src/cli.js.map +1 -0
  50. package/dist/esm/src/device-manager.d.ts +17 -0
  51. package/dist/esm/src/device-manager.d.ts.map +1 -0
  52. package/dist/{src → esm/src}/device-manager.js +2 -3
  53. package/dist/esm/src/device-manager.js.map +1 -0
  54. package/dist/esm/src/driver-loader.d.ts +53 -0
  55. package/dist/esm/src/driver-loader.d.ts.map +1 -0
  56. package/dist/{src → esm/src}/driver-loader.js +2 -4
  57. package/dist/esm/src/driver-loader.js.map +1 -0
  58. package/dist/esm/src/index.d.ts +13 -0
  59. package/dist/esm/src/index.d.ts.map +1 -0
  60. package/dist/esm/src/index.js.map +1 -0
  61. package/dist/esm/src/polling-scheduler.d.ts +48 -0
  62. package/dist/esm/src/polling-scheduler.d.ts.map +1 -0
  63. package/dist/{src → esm/src}/polling-scheduler.js +2 -4
  64. package/dist/esm/src/polling-scheduler.js.map +1 -0
  65. package/dist/esm/src/types.d.ts +86 -0
  66. package/dist/esm/src/types.d.ts.map +1 -0
  67. package/dist/esm/src/types.js.map +1 -0
  68. package/dist/esm/src/utils/config-validator.d.ts +3 -0
  69. package/dist/esm/src/utils/config-validator.d.ts.map +1 -0
  70. package/dist/esm/src/utils/config-validator.js.map +1 -0
  71. package/dist/esm/src/utils/config.d.ts +3 -0
  72. package/dist/esm/src/utils/config.d.ts.map +1 -0
  73. package/dist/esm/src/utils/config.js.map +1 -0
  74. package/dist/esm/src/utils/device-validation.d.ts +31 -0
  75. package/dist/esm/src/utils/device-validation.d.ts.map +1 -0
  76. package/dist/esm/src/utils/device-validation.js.map +1 -0
  77. package/dist/esm/src/utils/package-info.d.ts +5 -0
  78. package/dist/esm/src/utils/package-info.d.ts.map +1 -0
  79. package/dist/esm/src/utils/package-info.js +27 -0
  80. package/dist/esm/src/utils/package-info.js.map +1 -0
  81. package/dist/esm/src/utils/process.d.ts +10 -0
  82. package/dist/esm/src/utils/process.d.ts.map +1 -0
  83. package/dist/esm/src/utils/process.js.map +1 -0
  84. package/dist/esm/src/utils/test-utils.d.ts +313 -0
  85. package/dist/esm/src/utils/test-utils.d.ts.map +1 -0
  86. package/dist/esm/src/utils/test-utils.js.map +1 -0
  87. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
  88. package/package.json +27 -10
  89. package/dist/bin/ya-modbus-bridge.d.ts.map +0 -1
  90. package/dist/bin/ya-modbus-bridge.js.map +0 -1
  91. package/dist/src/cli.d.ts.map +0 -1
  92. package/dist/src/cli.js.map +0 -1
  93. package/dist/src/device-manager.d.ts.map +0 -1
  94. package/dist/src/device-manager.js.map +0 -1
  95. package/dist/src/driver-loader.d.ts.map +0 -1
  96. package/dist/src/driver-loader.js.map +0 -1
  97. package/dist/src/index.d.ts.map +0 -1
  98. package/dist/src/index.js.map +0 -1
  99. package/dist/src/polling-scheduler.d.ts.map +0 -1
  100. package/dist/src/polling-scheduler.js.map +0 -1
  101. package/dist/src/types.d.ts.map +0 -1
  102. package/dist/src/utils/__mocks__/package-info.d.ts +0 -9
  103. package/dist/src/utils/__mocks__/package-info.d.ts.map +0 -1
  104. package/dist/src/utils/__mocks__/package-info.js +0 -11
  105. package/dist/src/utils/__mocks__/package-info.js.map +0 -1
  106. package/dist/src/utils/__mocks__/process.d.ts +0 -20
  107. package/dist/src/utils/__mocks__/process.d.ts.map +0 -1
  108. package/dist/src/utils/__mocks__/process.js +0 -37
  109. package/dist/src/utils/__mocks__/process.js.map +0 -1
  110. package/dist/src/utils/config-validator.d.ts.map +0 -1
  111. package/dist/src/utils/config-validator.js.map +0 -1
  112. package/dist/src/utils/config.d.ts.map +0 -1
  113. package/dist/src/utils/config.js.map +0 -1
  114. package/dist/src/utils/device-validation.d.ts.map +0 -1
  115. package/dist/src/utils/device-validation.js.map +0 -1
  116. package/dist/src/utils/package-info.d.ts.map +0 -1
  117. package/dist/src/utils/package-info.js +0 -11
  118. package/dist/src/utils/package-info.js.map +0 -1
  119. package/dist/src/utils/process.d.ts.map +0 -1
  120. package/dist/src/utils/process.js.map +0 -1
  121. package/dist/src/utils/test-utils.d.ts.map +0 -1
  122. package/dist/src/utils/test-utils.js.map +0 -1
  123. package/dist/tsconfig.tsbuildinfo +0 -1
  124. /package/dist/{bin → cjs/bin}/ya-modbus-bridge.d.ts +0 -0
  125. /package/dist/{src → cjs/src}/cli.d.ts +0 -0
  126. /package/dist/{src → cjs/src}/device-manager.d.ts +0 -0
  127. /package/dist/{src → cjs/src}/driver-loader.d.ts +0 -0
  128. /package/dist/{src → cjs/src}/index.d.ts +0 -0
  129. /package/dist/{src → cjs/src}/polling-scheduler.d.ts +0 -0
  130. /package/dist/{src → cjs/src}/types.d.ts +0 -0
  131. /package/dist/{src → cjs/src}/utils/config-validator.d.ts +0 -0
  132. /package/dist/{src → cjs/src}/utils/config.d.ts +0 -0
  133. /package/dist/{src → cjs/src}/utils/device-validation.d.ts +0 -0
  134. /package/dist/{src → cjs/src}/utils/package-info.d.ts +0 -0
  135. /package/dist/{src → cjs/src}/utils/process.d.ts +0 -0
  136. /package/dist/{src → cjs/src}/utils/test-utils.d.ts +0 -0
  137. /package/dist/{bin → esm/bin}/ya-modbus-bridge.js +0 -0
  138. /package/dist/{src → esm/src}/cli.js +0 -0
  139. /package/dist/{src → esm/src}/index.js +0 -0
  140. /package/dist/{src → esm/src}/types.js +0 -0
  141. /package/dist/{src → esm/src}/utils/config-validator.js +0 -0
  142. /package/dist/{src → esm/src}/utils/config.js +0 -0
  143. /package/dist/{src → esm/src}/utils/device-validation.js +0 -0
  144. /package/dist/{src → esm/src}/utils/process.js +0 -0
  145. /package/dist/{src → esm/src}/utils/test-utils.js +0 -0
@@ -0,0 +1,291 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadConfig = void 0;
4
+ exports.createBridge = createBridge;
5
+ const tslib_1 = require("tslib");
6
+ const mqtt_1 = tslib_1.__importDefault(require("mqtt"));
7
+ const device_manager_js_1 = require("./device-manager.js");
8
+ const driver_loader_js_1 = require("./driver-loader.js");
9
+ const polling_scheduler_js_1 = require("./polling-scheduler.js");
10
+ var config_js_1 = require("./utils/config.js");
11
+ Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_js_1.loadConfig; } });
12
+ function createBridge(config, dependencies) {
13
+ let status = {
14
+ state: 'stopped',
15
+ timestamp: Date.now(),
16
+ };
17
+ let client = null;
18
+ const topicPrefix = config.topicPrefix ?? 'modbus';
19
+ // Subscriptions map needed because mqtt client doesn't expose handler lookup
20
+ const subscriptions = new Map();
21
+ // Use injected dependencies or create new instances
22
+ const driverLoader = dependencies?.driverLoader ?? new driver_loader_js_1.DriverLoader();
23
+ const deviceManager = dependencies?.deviceManager ?? new device_manager_js_1.DeviceManager(driverLoader);
24
+ // Bridge reference to be set after bridge object is created
25
+ // This ensures publishData is always defined before PollingScheduler starts
26
+ let bridgeRef = null;
27
+ // Publish function that will be called from polling
28
+ const publishData = (deviceId, data) => {
29
+ if (!bridgeRef) {
30
+ // Bridge not fully initialized yet - skip publishing
31
+ return;
32
+ }
33
+ const payload = JSON.stringify({
34
+ deviceId,
35
+ timestamp: Date.now(),
36
+ data,
37
+ });
38
+ // Publish to device-specific topic
39
+ void bridgeRef.publish(`${deviceId}/data`, payload, { qos: 0 }).catch((error) => {
40
+ console.error(`Failed to publish data for device ${deviceId}:`, error);
41
+ });
42
+ };
43
+ // Handle data from polling
44
+ const handlePollingData = (deviceId, data) => {
45
+ publishData(deviceId, data);
46
+ // Update device status and reset failure count on success
47
+ deviceManager.updateDeviceState(deviceId, {
48
+ lastPoll: Date.now(),
49
+ lastUpdate: Date.now(),
50
+ consecutiveFailures: 0,
51
+ });
52
+ };
53
+ // Handle polling errors
54
+ const handlePollingError = (deviceId, error, failureCount) => {
55
+ console.error(`Polling error for device ${deviceId}:`, error);
56
+ deviceManager.updateDeviceState(deviceId, {
57
+ consecutiveFailures: failureCount,
58
+ errors: [error.message],
59
+ });
60
+ };
61
+ const pollingScheduler = dependencies?.pollingScheduler ?? new polling_scheduler_js_1.PollingScheduler(handlePollingData, handlePollingError);
62
+ const bridge = {
63
+ start() {
64
+ return new Promise((resolve, reject) => {
65
+ status = {
66
+ state: 'starting',
67
+ timestamp: Date.now(),
68
+ };
69
+ const mqttOptions = {
70
+ clean: true,
71
+ reconnectPeriod: config.mqtt.reconnectPeriod ?? 5000,
72
+ resubscribe: true, // Automatic resubscription on reconnect (default: true)
73
+ };
74
+ if (config.mqtt.clientId) {
75
+ mqttOptions.clientId = config.mqtt.clientId;
76
+ }
77
+ if (config.mqtt.username) {
78
+ mqttOptions.username = config.mqtt.username;
79
+ }
80
+ if (config.mqtt.password) {
81
+ mqttOptions.password = config.mqtt.password;
82
+ }
83
+ client = mqtt_1.default.connect(config.mqtt.url, mqttOptions);
84
+ let isInitialConnection = true;
85
+ client.on('connect', () => {
86
+ status = {
87
+ state: 'running',
88
+ timestamp: Date.now(),
89
+ };
90
+ // Automatic resubscription is handled by mqtt.js (resubscribe: true)
91
+ // Start polling scheduler
92
+ pollingScheduler.start();
93
+ if (isInitialConnection) {
94
+ isInitialConnection = false;
95
+ resolve();
96
+ }
97
+ });
98
+ client.on('error', (error) => {
99
+ status = {
100
+ state: 'error',
101
+ timestamp: Date.now(),
102
+ errors: [error.message],
103
+ };
104
+ reject(error);
105
+ });
106
+ // No need to track disconnect/offline - client.connected property handles this
107
+ client.on('reconnect', () => {
108
+ // Note: reconnect event fires when attempting to reconnect
109
+ // mqttConnected status is updated by 'connect' event
110
+ });
111
+ client.on('message', (topic, payload, packet) => {
112
+ const handler = subscriptions.get(topic);
113
+ if (handler) {
114
+ const message = {
115
+ topic,
116
+ payload,
117
+ qos: packet.qos,
118
+ retain: packet.retain,
119
+ };
120
+ try {
121
+ handler(message);
122
+ }
123
+ catch (error) {
124
+ // Prevent handler errors from crashing the bridge
125
+ console.error(`Error in message handler for topic ${topic}:`, error);
126
+ status = {
127
+ ...status,
128
+ errors: [
129
+ ...(status.errors ?? []),
130
+ /* istanbul ignore next - defensive: handlers always throw Error */
131
+ `Handler error for ${topic}: ${error instanceof Error ? error.message : String(error)}`,
132
+ ],
133
+ };
134
+ }
135
+ }
136
+ });
137
+ });
138
+ },
139
+ async stop() {
140
+ status = {
141
+ state: 'stopping',
142
+ timestamp: Date.now(),
143
+ };
144
+ // Stop polling first
145
+ pollingScheduler.stop();
146
+ // Cleanup device manager (unloads drivers)
147
+ await deviceManager.clear();
148
+ // Close all transports managed by the transport manager
149
+ await driverLoader.closeAllTransports();
150
+ if (client) {
151
+ // Cleanup event listeners and subscriptions to prevent memory leaks
152
+ client.removeAllListeners();
153
+ subscriptions.clear();
154
+ await new Promise((resolve) => {
155
+ if (!client) {
156
+ resolve();
157
+ return;
158
+ }
159
+ client.end(false, {}, () => {
160
+ status = {
161
+ state: 'stopped',
162
+ timestamp: Date.now(),
163
+ };
164
+ client = null;
165
+ resolve();
166
+ });
167
+ });
168
+ }
169
+ else {
170
+ status = {
171
+ state: 'stopped',
172
+ timestamp: Date.now(),
173
+ };
174
+ }
175
+ },
176
+ getStatus() {
177
+ return {
178
+ ...status,
179
+ deviceCount: deviceManager.getDeviceCount(),
180
+ // Use client.connected property directly
181
+ mqttConnected: client?.connected ?? false,
182
+ };
183
+ },
184
+ publish(topic, payload, options) {
185
+ return new Promise((resolve, reject) => {
186
+ if (!client) {
187
+ reject(new Error('MQTT client not initialized'));
188
+ return;
189
+ }
190
+ if (!client.connected) {
191
+ reject(new Error('MQTT client not connected'));
192
+ return;
193
+ }
194
+ const fullTopic = `${topicPrefix}/${topic}`;
195
+ const publishOptions = {};
196
+ if (options?.qos !== undefined) {
197
+ publishOptions.qos = options.qos;
198
+ }
199
+ if (options?.retain !== undefined) {
200
+ publishOptions.retain = options.retain;
201
+ }
202
+ client.publish(fullTopic, payload, publishOptions, (error) => {
203
+ if (error) {
204
+ reject(error);
205
+ }
206
+ else {
207
+ resolve();
208
+ }
209
+ });
210
+ });
211
+ },
212
+ subscribe(topic, handler, options) {
213
+ return new Promise((resolve, reject) => {
214
+ if (!client) {
215
+ reject(new Error('MQTT client not initialized'));
216
+ return;
217
+ }
218
+ if (!client.connected) {
219
+ reject(new Error('MQTT client not connected'));
220
+ return;
221
+ }
222
+ const fullTopic = `${topicPrefix}/${topic}`;
223
+ const subscribeOptions = {
224
+ qos: options?.qos ?? 0,
225
+ };
226
+ client.subscribe(fullTopic, subscribeOptions, (error) => {
227
+ if (error) {
228
+ reject(error);
229
+ }
230
+ else {
231
+ subscriptions.set(fullTopic, handler);
232
+ resolve();
233
+ }
234
+ });
235
+ });
236
+ },
237
+ unsubscribe(topic) {
238
+ return new Promise((resolve, reject) => {
239
+ if (!client) {
240
+ reject(new Error('MQTT client not initialized'));
241
+ return;
242
+ }
243
+ if (!client.connected) {
244
+ reject(new Error('MQTT client not connected'));
245
+ return;
246
+ }
247
+ const fullTopic = `${topicPrefix}/${topic}`;
248
+ client.unsubscribe(fullTopic, (error) => {
249
+ if (error) {
250
+ reject(error);
251
+ }
252
+ else {
253
+ subscriptions.delete(fullTopic);
254
+ resolve();
255
+ }
256
+ });
257
+ });
258
+ },
259
+ async addDevice(deviceConfig) {
260
+ await deviceManager.addDevice(deviceConfig);
261
+ // Schedule device for polling if enabled
262
+ if (deviceConfig.enabled !== false) {
263
+ const driver = driverLoader.getDriver(deviceConfig.deviceId);
264
+ if (!driver) {
265
+ throw new Error(`Driver for ${deviceConfig.deviceId} not found after loading. ` +
266
+ `This may indicate the device was removed during initialization.`);
267
+ }
268
+ pollingScheduler.scheduleDevice(deviceConfig.deviceId, deviceConfig, driver);
269
+ }
270
+ },
271
+ async removeDevice(deviceId) {
272
+ // Unschedule polling first
273
+ pollingScheduler.unscheduleDevice(deviceId);
274
+ // Remove device (unloads driver)
275
+ await deviceManager.removeDevice(deviceId);
276
+ },
277
+ getDevice(deviceId) {
278
+ return deviceManager.getDevice(deviceId);
279
+ },
280
+ listDevices() {
281
+ return deviceManager.listDevices();
282
+ },
283
+ getDeviceConfig(deviceId) {
284
+ return deviceManager.getDeviceConfig(deviceId);
285
+ },
286
+ };
287
+ // Set bridge reference now that bridge is defined
288
+ bridgeRef = bridge;
289
+ return bridge;
290
+ }
291
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":";;;AA4CA,oCA6UC;;AAzXD,wDAAuB;AAEvB,2DAAmD;AACnD,yDAAiD;AACjD,iEAAyD;AA+BzD,+CAA8C;AAArC,uGAAA,UAAU,OAAA;AASnB,SAAgB,YAAY,CAC1B,MAAwB,EACxB,YAAiC;IAEjC,IAAI,MAAM,GAAmB;QAC3B,KAAK,EAAE,SAAS;QAChB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAA;IAED,IAAI,MAAM,GAA2B,IAAI,CAAA;IACzC,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,QAAQ,CAAA;IAClD,6EAA6E;IAC7E,MAAM,aAAa,GAAG,IAAI,GAAG,EAA0B,CAAA;IAEvD,oDAAoD;IACpD,MAAM,YAAY,GAAG,YAAY,EAAE,YAAY,IAAI,IAAI,+BAAY,EAAE,CAAA;IACrE,MAAM,aAAa,GAAG,YAAY,EAAE,aAAa,IAAI,IAAI,iCAAa,CAAC,YAAY,CAAC,CAAA;IAEpF,4DAA4D;IAC5D,4EAA4E;IAC5E,IAAI,SAAS,GAAsB,IAAI,CAAA;IAEvC,oDAAoD;IACpD,MAAM,WAAW,GAAG,CAAC,QAAgB,EAAE,IAA6B,EAAQ,EAAE;QAC5E,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,qDAAqD;YACrD,OAAM;QACR,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC;YAC7B,QAAQ;YACR,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,IAAI;SACL,CAAC,CAAA;QAEF,mCAAmC;QACnC,KAAK,SAAS,CAAC,OAAO,CAAC,GAAG,QAAQ,OAAO,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAY,EAAE,EAAE;YACrF,OAAO,CAAC,KAAK,CAAC,qCAAqC,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAA;QACxE,CAAC,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,2BAA2B;IAC3B,MAAM,iBAAiB,GAAG,CAAC,QAAgB,EAAE,IAA6B,EAAQ,EAAE;QAClF,WAAW,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;QAE3B,0DAA0D;QAC1D,aAAa,CAAC,iBAAiB,CAAC,QAAQ,EAAE;YACxC,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE;YACpB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;YACtB,mBAAmB,EAAE,CAAC;SACvB,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,wBAAwB;IACxB,MAAM,kBAAkB,GAAG,CAAC,QAAgB,EAAE,KAAY,EAAE,YAAoB,EAAQ,EAAE;QACxF,OAAO,CAAC,KAAK,CAAC,4BAA4B,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAA;QAE7D,aAAa,CAAC,iBAAiB,CAAC,QAAQ,EAAE;YACxC,mBAAmB,EAAE,YAAY;YACjC,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;SACxB,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,MAAM,gBAAgB,GACpB,YAAY,EAAE,gBAAgB,IAAI,IAAI,uCAAgB,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,CAAA;IAE/F,MAAM,MAAM,GAAe;QACzB,KAAK;YACH,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC3C,MAAM,GAAG;oBACP,KAAK,EAAE,UAAU;oBACjB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;iBACtB,CAAA;gBAED,MAAM,WAAW,GAAwB;oBACvC,KAAK,EAAE,IAAI;oBACX,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,eAAe,IAAI,IAAI;oBACpD,WAAW,EAAE,IAAI,EAAE,wDAAwD;iBAC5E,CAAA;gBAED,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACzB,WAAW,CAAC,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAA;gBAC7C,CAAC;gBACD,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACzB,WAAW,CAAC,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAA;gBAC7C,CAAC;gBACD,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACzB,WAAW,CAAC,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAA;gBAC7C,CAAC;gBAED,MAAM,GAAG,cAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAA;gBAEnD,IAAI,mBAAmB,GAAG,IAAI,CAAA;gBAE9B,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;oBACxB,MAAM,GAAG;wBACP,KAAK,EAAE,SAAS;wBAChB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;qBACtB,CAAA;oBAED,qEAAqE;oBAErE,0BAA0B;oBAC1B,gBAAgB,CAAC,KAAK,EAAE,CAAA;oBAExB,IAAI,mBAAmB,EAAE,CAAC;wBACxB,mBAAmB,GAAG,KAAK,CAAA;wBAC3B,OAAO,EAAE,CAAA;oBACX,CAAC;gBACH,CAAC,CAAC,CAAA;gBAEF,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;oBAC3B,MAAM,GAAG;wBACP,KAAK,EAAE,OAAO;wBACd,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;wBACrB,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;qBACxB,CAAA;oBACD,MAAM,CAAC,KAAK,CAAC,CAAA;gBACf,CAAC,CAAC,CAAA;gBAEF,+EAA+E;gBAE/E,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE;oBAC1B,2DAA2D;oBAC3D,qDAAqD;gBACvD,CAAC,CAAC,CAAA;gBAEF,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;oBAC9C,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;oBACxC,IAAI,OAAO,EAAE,CAAC;wBACZ,MAAM,OAAO,GAAgB;4BAC3B,KAAK;4BACL,OAAO;4BACP,GAAG,EAAE,MAAM,CAAC,GAAgB;4BAC5B,MAAM,EAAE,MAAM,CAAC,MAAM;yBACtB,CAAA;wBACD,IAAI,CAAC;4BACH,OAAO,CAAC,OAAO,CAAC,CAAA;wBAClB,CAAC;wBAAC,OAAO,KAAK,EAAE,CAAC;4BACf,kDAAkD;4BAClD,OAAO,CAAC,KAAK,CAAC,sCAAsC,KAAK,GAAG,EAAE,KAAK,CAAC,CAAA;4BACpE,MAAM,GAAG;gCACP,GAAG,MAAM;gCACT,MAAM,EAAE;oCACN,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;oCACxB,mEAAmE;oCACnE,qBAAqB,KAAK,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;iCACxF;6BACF,CAAA;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;QACJ,CAAC;QAED,KAAK,CAAC,IAAI;YACR,MAAM,GAAG;gBACP,KAAK,EAAE,UAAU;gBACjB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAA;YAED,qBAAqB;YACrB,gBAAgB,CAAC,IAAI,EAAE,CAAA;YAEvB,2CAA2C;YAC3C,MAAM,aAAa,CAAC,KAAK,EAAE,CAAA;YAE3B,wDAAwD;YACxD,MAAM,YAAY,CAAC,kBAAkB,EAAE,CAAA;YAEvC,IAAI,MAAM,EAAE,CAAC;gBACX,oEAAoE;gBACpE,MAAM,CAAC,kBAAkB,EAAE,CAAA;gBAC3B,aAAa,CAAC,KAAK,EAAE,CAAA;gBAErB,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;oBAClC,IAAI,CAAC,MAAM,EAAE,CAAC;wBACZ,OAAO,EAAE,CAAA;wBACT,OAAM;oBACR,CAAC;oBACD,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE;wBACzB,MAAM,GAAG;4BACP,KAAK,EAAE,SAAS;4BAChB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;yBACtB,CAAA;wBACD,MAAM,GAAG,IAAI,CAAA;wBACb,OAAO,EAAE,CAAA;oBACX,CAAC,CAAC,CAAA;gBACJ,CAAC,CAAC,CAAA;YACJ,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG;oBACP,KAAK,EAAE,SAAS;oBAChB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;iBACtB,CAAA;YACH,CAAC;QACH,CAAC;QAED,SAAS;YACP,OAAO;gBACL,GAAG,MAAM;gBACT,WAAW,EAAE,aAAa,CAAC,cAAc,EAAE;gBAC3C,yCAAyC;gBACzC,aAAa,EAAE,MAAM,EAAE,SAAS,IAAI,KAAK;aAC1C,CAAA;QACH,CAAC;QAED,OAAO,CAAC,KAAa,EAAE,OAAwB,EAAE,OAAwB;YACvE,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,MAAM,CAAC,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAA;oBAChD,OAAM;gBACR,CAAC;gBAED,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;oBACtB,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAA;oBAC9C,OAAM;gBACR,CAAC;gBAED,MAAM,SAAS,GAAG,GAAG,WAAW,IAAI,KAAK,EAAE,CAAA;gBAE3C,MAAM,cAAc,GAA+B,EAAE,CAAA;gBACrD,IAAI,OAAO,EAAE,GAAG,KAAK,SAAS,EAAE,CAAC;oBAC/B,cAAc,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAA;gBAClC,CAAC;gBACD,IAAI,OAAO,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;oBAClC,cAAc,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;gBACxC,CAAC;gBAED,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE;oBAC3D,IAAI,KAAK,EAAE,CAAC;wBACV,MAAM,CAAC,KAAK,CAAC,CAAA;oBACf,CAAC;yBAAM,CAAC;wBACN,OAAO,EAAE,CAAA;oBACX,CAAC;gBACH,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;QACJ,CAAC;QAED,SAAS,CAAC,KAAa,EAAE,OAAuB,EAAE,OAA0B;YAC1E,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,MAAM,CAAC,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAA;oBAChD,OAAM;gBACR,CAAC;gBAED,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;oBACtB,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAA;oBAC9C,OAAM;gBACR,CAAC;gBAED,MAAM,SAAS,GAAG,GAAG,WAAW,IAAI,KAAK,EAAE,CAAA;gBAE3C,MAAM,gBAAgB,GAAiC;oBACrD,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;iBACvB,CAAA;gBAED,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE;oBACtD,IAAI,KAAK,EAAE,CAAC;wBACV,MAAM,CAAC,KAAK,CAAC,CAAA;oBACf,CAAC;yBAAM,CAAC;wBACN,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;wBACrC,OAAO,EAAE,CAAA;oBACX,CAAC;gBACH,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;QACJ,CAAC;QAED,WAAW,CAAC,KAAa;YACvB,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,MAAM,CAAC,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAA;oBAChD,OAAM;gBACR,CAAC;gBAED,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;oBACtB,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAA;oBAC9C,OAAM;gBACR,CAAC;gBAED,MAAM,SAAS,GAAG,GAAG,WAAW,IAAI,KAAK,EAAE,CAAA;gBAE3C,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;oBACtC,IAAI,KAAK,EAAE,CAAC;wBACV,MAAM,CAAC,KAAK,CAAC,CAAA;oBACf,CAAC;yBAAM,CAAC;wBACN,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;wBAC/B,OAAO,EAAE,CAAA;oBACX,CAAC;gBACH,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;QACJ,CAAC;QAED,KAAK,CAAC,SAAS,CAAC,YAA0B;YACxC,MAAM,aAAa,CAAC,SAAS,CAAC,YAAY,CAAC,CAAA;YAE3C,yCAAyC;YACzC,IAAI,YAAY,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;gBACnC,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAA;gBAC5D,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,MAAM,IAAI,KAAK,CACb,cAAc,YAAY,CAAC,QAAQ,4BAA4B;wBAC7D,iEAAiE,CACpE,CAAA;gBACH,CAAC;gBACD,gBAAgB,CAAC,cAAc,CAAC,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,CAAC,CAAA;YAC9E,CAAC;QACH,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,QAAgB;YACjC,2BAA2B;YAC3B,gBAAgB,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAA;YAE3C,iCAAiC;YACjC,MAAM,aAAa,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAA;QAC5C,CAAC;QAED,SAAS,CAAC,QAAgB;YACxB,OAAO,aAAa,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;QAC1C,CAAC;QAED,WAAW;YACT,OAAO,aAAa,CAAC,WAAW,EAAE,CAAA;QACpC,CAAC;QAED,eAAe,CAAC,QAAgB;YAC9B,OAAO,aAAa,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAA;QAChD,CAAC;KACF,CAAA;IAED,kDAAkD;IAClD,SAAS,GAAG,MAAM,CAAA;IAElB,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polling-scheduler.d.ts","sourceRoot":"","sources":["../../../src/polling-scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAE3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAU9C,KAAK,YAAY,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;AAC7E,KAAK,aAAa,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;AAInF;;GAEG;AACH,qBAAa,gBAAgB;IAKzB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,OAAO;IALjB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,OAAO,CAAQ;gBAGb,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,aAAa;IAGhC;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI;IAelF;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQxC;;OAEG;IACH,KAAK,IAAI,IAAI;IAQb;;OAEG;IACH,IAAI,IAAI,IAAI;IAWZ;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAItC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAkBxB;;OAEG;YACW,UAAU;CAyCzB"}
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PollingScheduler = void 0;
4
+ const DEFAULT_POLLING_INTERVAL = 5000; // 5 seconds
5
+ /**
6
+ * Coordinates polling across multiple devices
7
+ */
8
+ class PollingScheduler {
9
+ constructor(onData, onError) {
10
+ this.onData = onData;
11
+ this.onError = onError;
12
+ this.devices = new Map();
13
+ this.running = false;
14
+ }
15
+ /**
16
+ * Schedule a device for polling
17
+ */
18
+ scheduleDevice(deviceId, config, driver) {
19
+ const interval = config.polling?.interval ?? DEFAULT_POLLING_INTERVAL;
20
+ this.devices.set(deviceId, {
21
+ config,
22
+ driver,
23
+ interval,
24
+ lastFailureCount: 0,
25
+ });
26
+ if (this.running) {
27
+ this.startDevicePolling(deviceId);
28
+ }
29
+ }
30
+ /**
31
+ * Stop polling a device and remove it from the schedule
32
+ */
33
+ unscheduleDevice(deviceId) {
34
+ const device = this.devices.get(deviceId);
35
+ if (device?.timer) {
36
+ clearTimeout(device.timer);
37
+ }
38
+ this.devices.delete(deviceId);
39
+ }
40
+ /**
41
+ * Start polling all scheduled devices
42
+ */
43
+ start() {
44
+ this.running = true;
45
+ for (const deviceId of this.devices.keys()) {
46
+ this.startDevicePolling(deviceId);
47
+ }
48
+ }
49
+ /**
50
+ * Stop polling all devices
51
+ */
52
+ stop() {
53
+ this.running = false;
54
+ for (const device of this.devices.values()) {
55
+ if (device.timer) {
56
+ clearTimeout(device.timer);
57
+ delete device.timer;
58
+ }
59
+ }
60
+ }
61
+ /**
62
+ * Check if a device is scheduled for polling
63
+ */
64
+ isScheduled(deviceId) {
65
+ return this.devices.has(deviceId);
66
+ }
67
+ /**
68
+ * Start polling for a specific device
69
+ */
70
+ startDevicePolling(deviceId) {
71
+ const device = this.devices.get(deviceId);
72
+ if (!device)
73
+ return;
74
+ // Schedule first poll after the interval
75
+ this.scheduleNextPoll(deviceId);
76
+ }
77
+ /**
78
+ * Schedule the next poll for a device, with backoff if needed
79
+ */
80
+ scheduleNextPoll(deviceId) {
81
+ const device = this.devices.get(deviceId);
82
+ if (!device || !this.running)
83
+ return;
84
+ const maxRetries = device.config.polling?.maxRetries ?? 3;
85
+ const retryBackoff = device.config.polling?.retryBackoff ?? device.interval * 2;
86
+ // Use backoff if we've exceeded max retries
87
+ const delay = device.lastFailureCount >= maxRetries ? retryBackoff : device.interval;
88
+ device.timer = setTimeout(() => {
89
+ const currentDevice = this.devices.get(deviceId);
90
+ if (!currentDevice || !this.running)
91
+ return;
92
+ void this.pollDevice(deviceId);
93
+ }, delay);
94
+ }
95
+ /**
96
+ * Poll a device once
97
+ */
98
+ async pollDevice(deviceId) {
99
+ const device = this.devices.get(deviceId);
100
+ if (!device || !this.running)
101
+ return;
102
+ try {
103
+ const dataPointIds = device.driver.dataPoints.map((dp) => dp.id);
104
+ const data = await device.driver.readDataPoints(dataPointIds);
105
+ // Reset failure count BEFORE callback - polling succeeded regardless of callback result
106
+ device.lastFailureCount = 0;
107
+ try {
108
+ this.onData(deviceId, data);
109
+ }
110
+ catch (callbackError) {
111
+ // Data callback threw - log but don't count as polling failure
112
+ console.error(`Error in data callback for device ${deviceId}:`, callbackError instanceof Error ? callbackError : new Error(String(callbackError)));
113
+ }
114
+ }
115
+ catch (error) {
116
+ // Increment failure count - scheduler owns this state
117
+ device.lastFailureCount++;
118
+ try {
119
+ this.onError(deviceId, error instanceof Error ? error : new Error(String(error)), device.lastFailureCount);
120
+ }
121
+ catch (callbackError) {
122
+ // Error callback threw - log but state already updated
123
+ console.error(`Error in error callback for device ${deviceId}:`, callbackError instanceof Error ? callbackError : new Error(String(callbackError)));
124
+ }
125
+ }
126
+ this.scheduleNextPoll(deviceId);
127
+ }
128
+ }
129
+ exports.PollingScheduler = PollingScheduler;
130
+ //# sourceMappingURL=polling-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polling-scheduler.js","sourceRoot":"","sources":["../../../src/polling-scheduler.ts"],"names":[],"mappings":";;;AAeA,MAAM,wBAAwB,GAAG,IAAI,CAAA,CAAC,YAAY;AAElD;;GAEG;AACH,MAAa,gBAAgB;IAI3B,YACU,MAAoB,EACpB,OAAsB;QADtB,WAAM,GAAN,MAAM,CAAc;QACpB,YAAO,GAAP,OAAO,CAAe;QALxB,YAAO,GAAG,IAAI,GAAG,EAA2B,CAAA;QAC5C,YAAO,GAAG,KAAK,CAAA;IAKpB,CAAC;IAEJ;;OAEG;IACH,cAAc,CAAC,QAAgB,EAAE,MAAoB,EAAE,MAAoB;QACzE,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,EAAE,QAAQ,IAAI,wBAAwB,CAAA;QAErE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE;YACzB,MAAM;YACN,MAAM;YACN,QAAQ;YACR,gBAAgB,EAAE,CAAC;SACpB,CAAC,CAAA;QAEF,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAA;QACnC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,gBAAgB,CAAC,QAAgB;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACzC,IAAI,MAAM,EAAE,KAAK,EAAE,CAAC;YAClB,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC5B,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC/B,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QAEnB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;YAC3C,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAA;QACnC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,IAAI;QACF,IAAI,CAAC,OAAO,GAAG,KAAK,CAAA;QAEpB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3C,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;gBACjB,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;gBAC1B,OAAO,MAAM,CAAC,KAAK,CAAA;YACrB,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,QAAgB;QAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACnC,CAAC;IAED;;OAEG;IACK,kBAAkB,CAAC,QAAgB;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACzC,IAAI,CAAC,MAAM;YAAE,OAAM;QAEnB,yCAAyC;QACzC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IACjC,CAAC;IAED;;OAEG;IACK,gBAAgB,CAAC,QAAgB;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACzC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAM;QAEpC,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,IAAI,CAAC,CAAA;QACzD,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAA;QAE/E,4CAA4C;QAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,gBAAgB,IAAI,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAA;QAEpF,MAAM,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC7B,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YAChD,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,OAAO;gBAAE,OAAM;YAE3C,KAAK,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;QAChC,CAAC,EAAE,KAAK,CAAC,CAAA;IACX,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,UAAU,CAAC,QAAgB;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACzC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAM;QAEpC,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;YAChE,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,YAAY,CAAC,CAAA;YAE7D,wFAAwF;YACxF,MAAM,CAAC,gBAAgB,GAAG,CAAC,CAAA;YAE3B,IAAI,CAAC;gBACH,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;YAC7B,CAAC;YAAC,OAAO,aAAa,EAAE,CAAC;gBACvB,+DAA+D;gBAC/D,OAAO,CAAC,KAAK,CACX,qCAAqC,QAAQ,GAAG,EAChD,aAAa,YAAY,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAClF,CAAA;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,sDAAsD;YACtD,MAAM,CAAC,gBAAgB,EAAE,CAAA;YAEzB,IAAI,CAAC;gBACH,IAAI,CAAC,OAAO,CACV,QAAQ,EACR,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EACzD,MAAM,CAAC,gBAAgB,CACxB,CAAA;YACH,CAAC;YAAC,OAAO,aAAa,EAAE,CAAC;gBACvB,uDAAuD;gBACvD,OAAO,CAAC,KAAK,CACX,sCAAsC,QAAQ,GAAG,EACjD,aAAa,YAAY,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAClF,CAAA;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IACjC,CAAC;CACF;AAlJD,4CAkJC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-validator.d.ts","sourceRoot":"","sources":["../../../../src/utils/config-validator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AA4BnD,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,IAAI,gBAAgB,CAOlF"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateConfig = validateConfig;
4
+ const zod_1 = require("zod");
5
+ const mqttConfigSchema = zod_1.z.object({
6
+ url: zod_1.z
7
+ .string()
8
+ .url()
9
+ .regex(/^(mqtt|mqtts|ws|wss):\/\//, {
10
+ message: 'URL must start with mqtt://, mqtts://, ws://, or wss://',
11
+ }),
12
+ clientId: zod_1.z.string().optional(),
13
+ username: zod_1.z.string().optional(),
14
+ password: zod_1.z.string().optional(),
15
+ reconnectPeriod: zod_1.z.number().int().positive().optional(),
16
+ });
17
+ const mqttBridgeConfigSchema = zod_1.z.object({
18
+ mqtt: mqttConfigSchema,
19
+ stateDir: zod_1.z.string().optional(),
20
+ topicPrefix: zod_1.z
21
+ .string()
22
+ // eslint-disable-next-line no-control-regex
23
+ .regex(/^[^+#/$\x00]+$/, {
24
+ message: 'Topic prefix must not contain MQTT special characters (+, #, /, $) or null character',
25
+ })
26
+ .optional(),
27
+ });
28
+ function validateConfig(config) {
29
+ const result = mqttBridgeConfigSchema.safeParse(config);
30
+ if (!result.success) {
31
+ const errors = result.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
32
+ throw new Error(`Invalid configuration: ${errors}`);
33
+ }
34
+ }
35
+ //# sourceMappingURL=config-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-validator.js","sourceRoot":"","sources":["../../../../src/utils/config-validator.ts"],"names":[],"mappings":";;AA8BA,wCAOC;AArCD,6BAAuB;AAIvB,MAAM,gBAAgB,GAAG,OAAC,CAAC,MAAM,CAAC;IAChC,GAAG,EAAE,OAAC;SACH,MAAM,EAAE;SACR,GAAG,EAAE;SACL,KAAK,CAAC,2BAA2B,EAAE;QAClC,OAAO,EAAE,yDAAyD;KACnE,CAAC;IACJ,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,eAAe,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACxD,CAAC,CAAA;AAEF,MAAM,sBAAsB,GAAG,OAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,gBAAgB;IACtB,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,WAAW,EAAE,OAAC;SACX,MAAM,EAAE;QACT,4CAA4C;SAC3C,KAAK,CAAC,gBAAgB,EAAE;QACvB,OAAO,EACL,sFAAsF;KACzF,CAAC;SACD,QAAQ,EAAE;CACd,CAAC,CAAA;AAEF,SAAgB,cAAc,CAAC,MAAe;IAC5C,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAEvD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC7F,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAA;IACrD,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/utils/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AA6BnD,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA4B9E"}
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadConfig = loadConfig;
4
+ const promises_1 = require("node:fs/promises");
5
+ const zod_1 = require("zod");
6
+ const mqttConfigSchema = zod_1.z.object({
7
+ url: zod_1.z
8
+ .string()
9
+ .url()
10
+ .regex(/^(mqtt|mqtts|ws|wss):\/\//, {
11
+ message: 'URL must start with mqtt://, mqtts://, ws://, or wss://',
12
+ })
13
+ .default('mqtt://localhost:1883'),
14
+ clientId: zod_1.z.string().optional(),
15
+ username: zod_1.z.string().optional(),
16
+ password: zod_1.z.string().optional(),
17
+ reconnectPeriod: zod_1.z.number().int().positive().optional(),
18
+ });
19
+ const mqttBridgeConfigSchema = zod_1.z.object({
20
+ mqtt: mqttConfigSchema.default({ url: 'mqtt://localhost:1883' }),
21
+ stateDir: zod_1.z.string().optional(),
22
+ topicPrefix: zod_1.z
23
+ .string()
24
+ // eslint-disable-next-line no-control-regex
25
+ .regex(/^[^+#/$\x00]+$/, {
26
+ message: 'Topic prefix must not contain MQTT special characters (+, #, /, $) or null character',
27
+ })
28
+ .optional(),
29
+ });
30
+ async function loadConfig(configPath) {
31
+ const content = await (0, promises_1.readFile)(configPath, 'utf-8');
32
+ const json = JSON.parse(content);
33
+ /* istanbul ignore next - defensive: JSON.parse never returns null */
34
+ const result = mqttBridgeConfigSchema.safeParse(json === null ? {} : json);
35
+ if (!result.success) {
36
+ const errors = result.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
37
+ throw new Error(`Invalid configuration: ${errors}`);
38
+ }
39
+ // Convert Zod output (with T | undefined) to MqttBridgeConfig (with T?)
40
+ const config = {
41
+ mqtt: {
42
+ url: result.data.mqtt.url,
43
+ ...(result.data.mqtt.clientId !== undefined && { clientId: result.data.mqtt.clientId }),
44
+ ...(result.data.mqtt.username !== undefined && { username: result.data.mqtt.username }),
45
+ ...(result.data.mqtt.password !== undefined && { password: result.data.mqtt.password }),
46
+ ...(result.data.mqtt.reconnectPeriod !== undefined && {
47
+ reconnectPeriod: result.data.mqtt.reconnectPeriod,
48
+ }),
49
+ },
50
+ ...(result.data.stateDir !== undefined && { stateDir: result.data.stateDir }),
51
+ ...(result.data.topicPrefix !== undefined && { topicPrefix: result.data.topicPrefix }),
52
+ };
53
+ return config;
54
+ }
55
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../src/utils/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-validation.d.ts","sourceRoot":"","sources":["../../../../src/utils/device-validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA+DvB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAM7B,CAAA;AAEF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAO1D"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deviceConfigSchema = void 0;
4
+ exports.validateDeviceConfig = validateDeviceConfig;
5
+ const zod_1 = require("zod");
6
+ // MQTT topic segment requirements:
7
+ // - Cannot contain null character
8
+ // - Cannot contain + or # (MQTT wildcards)
9
+ // - Cannot start with $ (reserved for system topics)
10
+ // - Cannot contain / (topic level separator)
11
+ // eslint-disable-next-line no-control-regex
12
+ const mqttTopicSegmentRegex = /^[^+#$/\u0000]+$/;
13
+ const deviceIdSchema = zod_1.z
14
+ .string()
15
+ .min(1, 'Device ID must not be empty')
16
+ .regex(mqttTopicSegmentRegex, {
17
+ message: 'Device ID must not contain MQTT special characters (+, #, /, $) or null character',
18
+ });
19
+ const driverNameSchema = zod_1.z
20
+ .string()
21
+ .min(1, 'Driver name must not be empty')
22
+ .regex(/^(@ya-modbus\/driver-[a-z0-9-]+|ya-modbus-driver-[a-z0-9-]+)$/, {
23
+ message: 'Driver must be @ya-modbus/driver-<name> or ya-modbus-driver-<name> with only lowercase letters, numbers, and hyphens',
24
+ })
25
+ .refine((name) => !name.includes('..') && !name.includes('\\'), {
26
+ message: 'Driver name cannot contain path traversal sequences',
27
+ });
28
+ const rtuConnectionSchema = zod_1.z.object({
29
+ type: zod_1.z.literal('rtu'),
30
+ port: zod_1.z.string().min(1),
31
+ baudRate: zod_1.z.number().positive(),
32
+ slaveId: zod_1.z.number().int().min(0).max(247),
33
+ parity: zod_1.z.enum(['none', 'even', 'odd']).optional(),
34
+ dataBits: zod_1.z.union([zod_1.z.literal(7), zod_1.z.literal(8)]).optional(),
35
+ stopBits: zod_1.z.union([zod_1.z.literal(1), zod_1.z.literal(2)]).optional(),
36
+ });
37
+ const tcpConnectionSchema = zod_1.z.object({
38
+ type: zod_1.z.literal('tcp'),
39
+ host: zod_1.z.string().min(1),
40
+ port: zod_1.z.number().int().positive().max(65535),
41
+ slaveId: zod_1.z.number().int().min(0).max(247),
42
+ });
43
+ const pollingConfigSchema = zod_1.z
44
+ .object({
45
+ interval: zod_1.z
46
+ .number()
47
+ .int()
48
+ .positive()
49
+ .min(100, 'Polling interval must be at least 100ms')
50
+ .max(86400000, 'Polling interval must not exceed 24 hours'),
51
+ maxRetries: zod_1.z
52
+ .number()
53
+ .int()
54
+ .nonnegative()
55
+ .max(100, 'Max retries should not exceed 100')
56
+ .optional(),
57
+ retryBackoff: zod_1.z.number().int().positive().optional(),
58
+ })
59
+ .optional();
60
+ exports.deviceConfigSchema = zod_1.z.object({
61
+ deviceId: deviceIdSchema,
62
+ driver: driverNameSchema,
63
+ connection: zod_1.z.union([rtuConnectionSchema, tcpConnectionSchema]),
64
+ enabled: zod_1.z.boolean().optional(),
65
+ polling: pollingConfigSchema,
66
+ });
67
+ function validateDeviceConfig(config) {
68
+ const result = exports.deviceConfigSchema.safeParse(config);
69
+ if (!result.success) {
70
+ const errors = result.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
71
+ throw new Error(`Invalid device configuration: ${errors}`);
72
+ }
73
+ }
74
+ //# sourceMappingURL=device-validation.js.map