@uns-kit/core 3.0.33 → 3.0.35

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 (62) hide show
  1. package/MIGRATIONS.md +50 -0
  2. package/dist/config/app-config.d.ts +1 -1
  3. package/dist/config/app-config.d.ts.map +1 -1
  4. package/dist/config/app-config.js.map +1 -1
  5. package/dist/runtime/mqtt-handover-state.d.ts +48 -0
  6. package/dist/runtime/mqtt-handover-state.d.ts.map +1 -0
  7. package/dist/runtime/mqtt-handover-state.js +61 -0
  8. package/dist/runtime/mqtt-handover-state.js.map +1 -0
  9. package/dist/runtime/process-group-authorization.d.ts +72 -0
  10. package/dist/runtime/process-group-authorization.d.ts.map +1 -1
  11. package/dist/runtime/process-group-authorization.js +31 -0
  12. package/dist/runtime/process-group-authorization.js.map +1 -1
  13. package/dist/runtime/process-group-contract.d.ts +1 -0
  14. package/dist/runtime/process-group-contract.d.ts.map +1 -1
  15. package/dist/runtime/process-group-contract.js +2 -1
  16. package/dist/runtime/process-group-contract.js.map +1 -1
  17. package/dist/runtime/process-group-direct-contract.d.ts +181 -0
  18. package/dist/runtime/process-group-direct-contract.d.ts.map +1 -0
  19. package/dist/runtime/process-group-direct-contract.js +151 -0
  20. package/dist/runtime/process-group-direct-contract.js.map +1 -0
  21. package/dist/runtime/process-group-direct-review.d.ts +203 -0
  22. package/dist/runtime/process-group-direct-review.d.ts.map +1 -0
  23. package/dist/runtime/process-group-direct-review.js +263 -0
  24. package/dist/runtime/process-group-direct-review.js.map +1 -0
  25. package/dist/runtime/process-group-direct-runtime.d.ts +18 -0
  26. package/dist/runtime/process-group-direct-runtime.d.ts.map +1 -0
  27. package/dist/runtime/process-group-direct-runtime.js +154 -0
  28. package/dist/runtime/process-group-direct-runtime.js.map +1 -0
  29. package/dist/runtime/process-group-snapshot.mjs +44 -0
  30. package/dist/uns/handover-manager.d.ts +10 -1
  31. package/dist/uns/handover-manager.d.ts.map +1 -1
  32. package/dist/uns/handover-manager.js +113 -5
  33. package/dist/uns/handover-manager.js.map +1 -1
  34. package/dist/uns/uns-interfaces.d.ts +4 -0
  35. package/dist/uns/uns-interfaces.d.ts.map +1 -1
  36. package/dist/uns/uns-interfaces.js.map +1 -1
  37. package/dist/uns/uns-proxy-process.d.ts +1 -0
  38. package/dist/uns/uns-proxy-process.d.ts.map +1 -1
  39. package/dist/uns/uns-proxy-process.js +6 -2
  40. package/dist/uns/uns-proxy-process.js.map +1 -1
  41. package/dist/uns-config/config-schema.d.ts +4 -1
  42. package/dist/uns-config/config-schema.d.ts.map +1 -1
  43. package/dist/uns-config/uns-core-schema.d.ts +4 -1
  44. package/dist/uns-config/uns-core-schema.d.ts.map +1 -1
  45. package/dist/uns-config/uns-core-schema.js +1 -1
  46. package/dist/uns-config/uns-core-schema.js.map +1 -1
  47. package/dist/uns-mqtt/mqtt-proxy.d.ts +1 -0
  48. package/dist/uns-mqtt/mqtt-proxy.d.ts.map +1 -1
  49. package/dist/uns-mqtt/mqtt-proxy.js +5 -0
  50. package/dist/uns-mqtt/mqtt-proxy.js.map +1 -1
  51. package/dist/uns-mqtt/mqtt-worker.d.ts.map +1 -1
  52. package/dist/uns-mqtt/mqtt-worker.js +13 -1
  53. package/dist/uns-mqtt/mqtt-worker.js.map +1 -1
  54. package/dist/uns-mqtt/throttled-queue.d.ts +8 -1
  55. package/dist/uns-mqtt/throttled-queue.d.ts.map +1 -1
  56. package/dist/uns-mqtt/throttled-queue.js +32 -4
  57. package/dist/uns-mqtt/throttled-queue.js.map +1 -1
  58. package/dist/uns-mqtt/uns-mqtt-proxy.d.ts +26 -2
  59. package/dist/uns-mqtt/uns-mqtt-proxy.d.ts.map +1 -1
  60. package/dist/uns-mqtt/uns-mqtt-proxy.js +82 -6
  61. package/dist/uns-mqtt/uns-mqtt-proxy.js.map +1 -1
  62. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"mqtt-worker.js","sourceRoot":"","sources":["../../src/uns-mqtt/mqtt-worker.ts"],"names":[],"mappings":"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","sourcesContent":["import { MqttPublishQueue } from \"./publish-queue.js\";\nimport { IClientPublishOptions } from \"mqtt\";\nimport { join } from \"path\";\nimport { parentPort } from \"worker_threads\";\n\nimport { basePath } from \"../base-path.js\";\nimport logger from \"../logger.js\";\nimport { IMqttWorkerData } from \"./mqtt-interfaces.js\";\nimport MqttProxy from \"./mqtt-proxy.js\";\nimport { ThrottledPublisher, ThrottledSubscriber } from \"./throttled-queue.js\";\n\nexport class MqttWorker {\n private mqttProxy: MqttProxy;\n protected publisher: ThrottledPublisher | MqttPublishQueue;\n protected subscriber: ThrottledSubscriber;\n private defaultPublishOptions?: IClientPublishOptions;\n\n constructor(workerData: IMqttWorkerData) {\n const publishThrottlingDelay = workerData.publishThrottlingDelay ?? 1;\n const publishConcurrency = workerData.publishConcurrency ?? 1;\n const maxPendingPublishes = workerData.maxPendingPublishes;\n const subscribeThrottlingDelay = workerData.subscribeThrottlingDelay ?? 1;\n const persistToDisk = workerData.persistToDisk ?? false;\n const mqttHost = workerData.mqttHost;\n const instanceName = workerData.instanceNameWithSuffix;\n const mqttParameters = workerData.mqttParameters;\n const publisherActive = workerData.publisherActive;\n const subscriberActive = workerData.subscriberActive;\n const defaultPublishOptions = workerData.defaultPublishOptions;\n this.defaultPublishOptions = defaultPublishOptions;\n\n // Initialize and start the MQTT proxy.\n this.mqttProxy = new MqttProxy(mqttHost, instanceName, mqttParameters, this);\n void this.mqttProxy.start()?.catch((): void => {});\n\n // Set up the event listener for incoming messages from the MQTT proxy.\n this.mqttProxy.event.on(\"input\", (event) => {\n this.subscriber.enqueue(event.topic, event.message.toString());\n });\n\n // Set up the event listener for status messages from the MQTT proxy.\n this.mqttProxy.event.on(\"mqttProxyStatus\", (event) => {\n parentPort?.postMessage({\n command: \"mqttProxyStatus\",\n event: event.event,\n value: event.value,\n statusTopic: event.statusTopic,\n uom: event.uom,\n });\n });\n\n // Define the publish function to be used by the ThrottledPublisher.\n const publishFunction = async (topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> => {\n const publishOptions = options ?? defaultPublishOptions;\n try {\n await this.mqttProxy.publish(topic, message, publishOptions);\n } catch (reason: any) {\n logger.error(`${instanceName} - Error publishing message to topic ${topic}: ${reason?.message ?? String(reason)}`);\n throw reason;\n }\n };\n\n // Create an instance of ThrottledPublisher.\n if (workerData.publishQueue) {\n if (publishConcurrency !== 1 || persistToDisk) throw Error(\"MQTT output persistence requires serial publishing and cannot combine with legacy persistToDisk\");\n if (!workerData.publishQueueIdentity) throw Error(\"MQTT outbox requires an explicit stable owner/destination identity\");\n this.publisher = new MqttPublishQueue(workerData.publishQueue, workerData.publishQueueIdentity,\n record => this.mqttProxy.publishConfirmed(record.topic, record.message, record.options,\n workerData.publishQueue.publishTimeoutMs ?? 10000), publisherActive,\n status => parentPort?.postMessage({ command: \"publishQueueStatus\", status }));\n } else {\n this.publisher = new ThrottledPublisher(\n publishThrottlingDelay,\n publishFunction,\n persistToDisk,\n join(basePath, \"/workerQueue/\", \"throttled-publisher-queue.json\"),\n instanceName,\n publisherActive,\n publishConcurrency,\n maxPendingPublishes,\n );\n }\n\n // Define the message handler for incoming messages.\n const messageHandler = async (topic: string, message: string): Promise<void> => {\n parentPort?.postMessage({\n command: \"input\",\n topic: topic,\n message: message,\n });\n };\n\n // Create an instance of ThrottledSubscriber.\n this.subscriber = new ThrottledSubscriber(\n subscribeThrottlingDelay,\n messageHandler,\n persistToDisk,\n join(basePath, \"/workerQueue/\", \"throttled-subscriber-queue.json\"),\n instanceName,\n subscriberActive,\n );\n\n // Set up the message listener for commands from the main thread.\n this.initializeMessageListener();\n }\n\n /**\n * Listen for incoming messages from the main thread and process them.\n */\n private initializeMessageListener(): void {\n parentPort?.on(\"message\", async (msg) => {\n if (msg && msg.command === \"enqueue\" && msg.id && msg.topic && msg.message !== undefined) {\n try {\n const publishPromise = this.publisher.enqueueOrThrow(msg.topic, msg.message, msg.id, msg.options ?? this.defaultPublishOptions);\n parentPort?.postMessage({\n command: \"enqueueAccepted\",\n id: msg.id,\n status: \"accepted\",\n });\n const { id, topic } = msg;\n void publishPromise\n .then(() => {\n parentPort?.postMessage({\n command: \"publishResult\",\n id,\n status: \"success\",\n topic,\n });\n })\n .catch((error: any) => {\n parentPort?.postMessage({\n command: \"publishResult\",\n id,\n status: \"error\",\n topic,\n error: error?.message ?? String(error),\n });\n });\n } catch (error: any) {\n parentPort?.postMessage({\n command: \"enqueueAccepted\",\n id: msg.id,\n status: \"error\",\n error: error?.message ?? String(error),\n });\n }\n } else if (msg?.command === \"flushPublishes\" && this.publisher instanceof MqttPublishQueue) {\n try {\n await this.publisher.waitForIdle(msg.timeoutMs);\n parentPort?.postMessage({ command: \"publishQueueReply\", id: msg.id, status: \"success\" });\n } catch {\n parentPort?.postMessage({ command: \"publishQueueReply\", id: msg.id, status: \"error\", error: \"MQTT queue drain failed or timed out; pending work retained\" });\n }\n } else if (msg?.command === \"closePublisher\" && this.publisher instanceof MqttPublishQueue) {\n try {\n await this.mqttProxy.stop({ force: true });\n await this.publisher.close();\n parentPort?.postMessage({ command: \"publishQueueReply\", id: msg.id, status: \"success\" });\n } catch {\n parentPort?.postMessage({ command: \"publishQueueReply\", id: msg.id, status: \"error\", error: \"MQTT output could not be safely persisted/closed; worker retained\" });\n }\n } else if (msg && msg.command === \"subscribeAsync\" && msg.topics) {\n this.mqttProxy.subscribeAsync(msg.topics);\n } else if (msg && msg.command === \"unsubscribeAsync\" && msg.topics) {\n this.mqttProxy.unsubscribeAsync(msg.topics);\n } else if (msg && msg.command === \"setPublisherActive\") {\n try { this.publisher.becomeActive({batchSize: msg?.batchSize, referenceHash: msg?.referenceHash}); }\n catch { parentPort?.postMessage({ command: \"publishQueueControlError\", error: \"Persistent output cannot resume using a legacy skip hash\" }); }\n } else if (msg && msg.command === \"setPublisherPassive\") {\n const snapshot = await this.publisher.becomePassive(3);\n parentPort?.postMessage({\n command: \"handover_publisher\",\n ...snapshot\n }); \n } else if (msg && msg.command === \"setSubscriberActive\") {\n this.subscriber.becomeActive({batchSize: msg?.batchSize, referenceHash: msg?.referenceHash});\n } else if (msg && msg.command === \"setSubscriberPassive\") {\n const snapshot = await this.subscriber.becomePassive(3);\n parentPort?.postMessage({\n command: \"handover_subscriber\",\n ...snapshot\n }); \n }\n });\n }\n\n /**\n * Get the state of the publisher.\n */\n public getPublisherState(): boolean {\n return this.publisher.getState();\n }\n /**\n * Get the state of the subscriber.\n */\n public getSubscriberState(): boolean {\n return this.subscriber.getState();\n }\n\n}\n"]}
1
+ {"version":3,"file":"mqtt-worker.js","sourceRoot":"","sources":["../../src/uns-mqtt/mqtt-worker.ts"],"names":[],"mappings":"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","sourcesContent":["import { MqttPublishQueue } from \"./publish-queue.js\";\nimport { IClientPublishOptions } from \"mqtt\";\nimport { join } from \"path\";\nimport { parentPort } from \"worker_threads\";\n\nimport { basePath } from \"../base-path.js\";\nimport logger from \"../logger.js\";\nimport { IMqttWorkerData } from \"./mqtt-interfaces.js\";\nimport MqttProxy from \"./mqtt-proxy.js\";\nimport { ThrottledPublisher, ThrottledSubscriber } from \"./throttled-queue.js\";\n\nexport class MqttWorker {\n private mqttProxy: MqttProxy;\n protected publisher: ThrottledPublisher | MqttPublishQueue;\n protected subscriber: ThrottledSubscriber;\n private defaultPublishOptions?: IClientPublishOptions;\n\n constructor(workerData: IMqttWorkerData) {\n const publishThrottlingDelay = workerData.publishThrottlingDelay ?? 1;\n const publishConcurrency = workerData.publishConcurrency ?? 1;\n const maxPendingPublishes = workerData.maxPendingPublishes;\n const subscribeThrottlingDelay = workerData.subscribeThrottlingDelay ?? 1;\n const persistToDisk = workerData.persistToDisk ?? false;\n const mqttHost = workerData.mqttHost;\n const instanceName = workerData.instanceNameWithSuffix;\n const mqttParameters = workerData.mqttParameters;\n const publisherActive = workerData.publisherActive;\n const subscriberActive = workerData.subscriberActive;\n const defaultPublishOptions = workerData.defaultPublishOptions;\n this.defaultPublishOptions = defaultPublishOptions;\n\n // Initialize and start the MQTT proxy.\n this.mqttProxy = new MqttProxy(mqttHost, instanceName, mqttParameters, this);\n void this.mqttProxy.start()?.catch((): void => {});\n\n // Set up the event listener for incoming messages from the MQTT proxy.\n this.mqttProxy.event.on(\"input\", (event) => {\n this.subscriber.enqueue(event.topic, event.message.toString());\n });\n\n // Set up the event listener for status messages from the MQTT proxy.\n this.mqttProxy.event.on(\"mqttProxyStatus\", (event) => {\n parentPort?.postMessage({\n command: \"mqttProxyStatus\",\n event: event.event,\n value: event.value,\n statusTopic: event.statusTopic,\n uom: event.uom,\n });\n });\n\n // Define the publish function to be used by the ThrottledPublisher.\n const publishFunction = async (topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> => {\n const publishOptions = options ?? defaultPublishOptions;\n try {\n await this.mqttProxy.publish(topic, message, publishOptions);\n } catch (reason: any) {\n logger.error(`${instanceName} - Error publishing message to topic ${topic}: ${reason?.message ?? String(reason)}`);\n throw reason;\n }\n };\n\n // Create an instance of ThrottledPublisher.\n if (workerData.publishQueue) {\n if (publishConcurrency !== 1 || persistToDisk) throw Error(\"MQTT output persistence requires serial publishing and cannot combine with legacy persistToDisk\");\n if (!workerData.publishQueueIdentity) throw Error(\"MQTT outbox requires an explicit stable owner/destination identity\");\n this.publisher = new MqttPublishQueue(workerData.publishQueue, workerData.publishQueueIdentity,\n record => this.mqttProxy.publishConfirmed(record.topic, record.message, record.options,\n workerData.publishQueue.publishTimeoutMs ?? 10000), publisherActive,\n status => parentPort?.postMessage({ command: \"publishQueueStatus\", status }));\n } else {\n this.publisher = new ThrottledPublisher(\n publishThrottlingDelay,\n publishFunction,\n persistToDisk,\n join(basePath, \"/workerQueue/\", \"throttled-publisher-queue.json\"),\n instanceName,\n publisherActive,\n publishConcurrency,\n maxPendingPublishes,\n );\n }\n\n // Define the message handler for incoming messages.\n const messageHandler = async (topic: string, message: string): Promise<void> => {\n parentPort?.postMessage({\n command: \"input\",\n topic: topic,\n message: message,\n });\n };\n\n // Create an instance of ThrottledSubscriber.\n this.subscriber = new ThrottledSubscriber(\n subscribeThrottlingDelay,\n messageHandler,\n persistToDisk,\n join(basePath, \"/workerQueue/\", \"throttled-subscriber-queue.json\"),\n instanceName,\n subscriberActive,\n );\n\n // Set up the message listener for commands from the main thread.\n this.initializeMessageListener();\n }\n\n /**\n * Listen for incoming messages from the main thread and process them.\n */\n private initializeMessageListener(): void {\n parentPort?.on(\"message\", async (msg) => {\n if (msg && msg.command === \"enqueue\" && msg.id && msg.topic && msg.message !== undefined) {\n try {\n const publishPromise = this.publisher.enqueueOrThrow(msg.topic, msg.message, msg.id, msg.options ?? this.defaultPublishOptions);\n parentPort?.postMessage({\n command: \"enqueueAccepted\",\n id: msg.id,\n status: \"accepted\",\n });\n const { id, topic } = msg;\n void publishPromise\n .then(() => {\n parentPort?.postMessage({\n command: \"publishResult\",\n id,\n status: \"success\",\n topic,\n });\n })\n .catch((error: any) => {\n parentPort?.postMessage({\n command: \"publishResult\",\n id,\n status: \"error\",\n topic,\n error: error?.message ?? String(error),\n });\n });\n } catch (error: any) {\n parentPort?.postMessage({\n command: \"enqueueAccepted\",\n id: msg.id,\n status: \"error\",\n error: error?.message ?? String(error),\n });\n }\n } else if (msg?.command === \"flushPublishes\" && this.publisher instanceof MqttPublishQueue) {\n try {\n await this.publisher.waitForIdle(msg.timeoutMs);\n parentPort?.postMessage({ command: \"publishQueueReply\", id: msg.id, status: \"success\" });\n } catch {\n parentPort?.postMessage({ command: \"publishQueueReply\", id: msg.id, status: \"error\", error: \"MQTT queue drain failed or timed out; pending work retained\" });\n }\n } else if (msg?.command === \"closePublisher\" && this.publisher instanceof MqttPublishQueue) {\n try {\n await this.mqttProxy.stop({ force: true });\n await this.publisher.close();\n parentPort?.postMessage({ command: \"publishQueueReply\", id: msg.id, status: \"success\" });\n } catch {\n parentPort?.postMessage({ command: \"publishQueueReply\", id: msg.id, status: \"error\", error: \"MQTT output could not be safely persisted/closed; worker retained\" });\n }\n } else if (msg && msg.command === \"subscribeAsync\" && msg.topics) {\n this.mqttProxy.subscribeAsync(msg.topics);\n } else if (msg && msg.command === \"unsubscribeAsync\" && msg.topics) {\n this.mqttProxy.unsubscribeAsync(msg.topics);\n } else if (msg?.command === \"prepareSubscriberResume\" || msg?.command === \"activatePreparedSubscriber\") {\n try {\n if (msg.command === \"prepareSubscriberResume\") this.subscriber.prepareResume(msg.cursor);\n else this.subscriber.activatePreparedResume();\n parentPort?.postMessage({ command: \"publishQueueReply\", id: msg.id, status: \"success\" });\n } catch {\n parentPort?.postMessage({ command: \"publishQueueReply\", id: msg.id, status: \"error\", error: \"MQTT subscriber resume not proven\" });\n }\n } else if (msg && msg.command === \"setPublisherActive\") {\n try { this.publisher.becomeActive({batchSize: msg?.batchSize, referenceHash: msg?.referenceHash}); }\n catch { parentPort?.postMessage({ command: \"publishQueueControlError\", error: \"Persistent output cannot resume using a legacy skip hash\" }); }\n } else if (msg && msg.command === \"setPublisherPassive\") {\n const snapshot = await this.publisher.becomePassive(3);\n parentPort?.postMessage({\n command: \"handover_publisher\",\n ...snapshot\n }); \n } else if (msg && msg.command === \"setSubscriberActive\") {\n this.subscriber.becomeActive({batchSize: msg?.batchSize, referenceHash: msg?.referenceHash});\n } else if (msg && msg.command === \"setSubscriberPassive\") {\n const snapshot = await this.subscriber.becomePassive(3, msg.strict === true);\n parentPort?.postMessage({\n command: \"handover_subscriber\",\n ...snapshot\n }); \n }\n });\n }\n\n /**\n * Get the state of the publisher.\n */\n public getPublisherState(): boolean {\n return this.publisher.getState();\n }\n /**\n * Get the state of the subscriber.\n */\n public getSubscriberState(): boolean {\n return this.subscriber.getState();\n }\n\n}\n"]}
@@ -162,13 +162,20 @@ export declare class ThrottledSubscriber extends ThrottledQueue<SubscriberQueueI
162
162
  /**
163
163
  * Switch the subscriber to a passive state and return a snapshot.
164
164
  */
165
- becomePassive(n: number): Promise<{
165
+ becomePassive(n: number, strict?: boolean): Promise<{
166
166
  referenceHash: string;
167
167
  batchSize: number;
168
168
  }>;
169
169
  /**
170
170
  * Switch the subscriber to an active state and resume processing.
171
171
  */
172
+ private preparedResume;
173
+ /** Validate a unique overlap without consuming input or activating this owner. */
174
+ prepareResume(snapshot: {
175
+ referenceHash: string;
176
+ batchSize: number;
177
+ }): void;
178
+ activatePreparedResume(): void;
172
179
  becomeActive(snapshot: {
173
180
  referenceHash: string;
174
181
  batchSize: number;
@@ -1 +1 @@
1
- {"version":3,"file":"throttled-queue.d.ts","sourceRoot":"","sources":["../../src/uns-mqtt/throttled-queue.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"throttled-queue.d.ts","sourceRoot":"","sources":["../../src/uns-mqtt/throttled-queue.ts"],"names":[],"mappings":"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"}
@@ -383,19 +383,21 @@ export class ThrottledSubscriber extends ThrottledQueue {
383
383
  /**
384
384
  * Computes a SHA‑256 hash for a given batch of subscriber items.
385
385
  */
386
- computeHashForBatch(messages) {
387
- const data = JSON.stringify(messages.map((item) => this.serializeItem(item)));
386
+ computeHashForBatch(messages, strict = false) {
387
+ const data = JSON.stringify(messages.map((item) => strict ? { topic: item.topic, message: item.message } : this.serializeItem(item)));
388
388
  return createHash("sha256").update(data).digest("hex");
389
389
  }
390
390
  /**
391
391
  * Switch the subscriber to a passive state and return a snapshot.
392
392
  */
393
- async becomePassive(n) {
393
+ async becomePassive(n, strict = false) {
394
394
  this.active = false;
395
395
  logger.info(`${this.instanceName} - Subscriber is passive.`);
396
+ while (this.inFlight > 0)
397
+ await new Promise(resolve => setTimeout(resolve, 1));
396
398
  const batch = this.lastProcessedItems.slice(-n);
397
399
  const batchSize = batch.length;
398
- const referenceHash = this.computeHashForBatch(batch);
400
+ const referenceHash = this.computeHashForBatch(batch, strict);
399
401
  if (batchSize > 0) {
400
402
  logger.info(`${this.instanceName} - Subscriber batch reference hash: ${referenceHash}, size: ${batchSize}`);
401
403
  }
@@ -404,6 +406,32 @@ export class ThrottledSubscriber extends ThrottledQueue {
404
406
  /**
405
407
  * Switch the subscriber to an active state and resume processing.
406
408
  */
409
+ preparedResume;
410
+ /** Validate a unique overlap without consuming input or activating this owner. */
411
+ prepareResume(snapshot) {
412
+ if (this.active)
413
+ throw Error("MQTT successor is not passive");
414
+ if (snapshot.batchSize === 0) {
415
+ this.preparedResume = 0;
416
+ return;
417
+ }
418
+ const matches = [];
419
+ for (let i = 0; i <= this.queue.length - snapshot.batchSize; i++) {
420
+ if (this.computeHashForBatch(this.queue.slice(i, i + snapshot.batchSize), true) === snapshot.referenceHash)
421
+ matches.push(i + snapshot.batchSize);
422
+ }
423
+ if (matches.length !== 1)
424
+ throw Error("MQTT input overlap is missing or ambiguous; successor stays passive");
425
+ this.preparedResume = matches[0];
426
+ }
427
+ activatePreparedResume() {
428
+ if (this.active || this.preparedResume === undefined)
429
+ throw Error("MQTT resume was not prepared");
430
+ this.queue = this.queue.slice(this.preparedResume);
431
+ this.preparedResume = undefined;
432
+ this.active = true;
433
+ void this.processQueue();
434
+ }
407
435
  becomeActive(snapshot) {
408
436
  // If snapshot is empty (batchSize is zero), start processing from the beginning.
409
437
  if (snapshot.batchSize === 0 || snapshot.batchSize === undefined || snapshot.referenceHash === undefined) {
@@ -1 +1 @@
1
- {"version":3,"file":"throttled-queue.js","sourceRoot":"","sources":["../../src/uns-mqtt/throttled-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAExE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,MAAM,MAAM,cAAc,CAAC;AAElC;;;GAGG;AACH,MAAe,cAAc;IACjB,KAAK,GAAQ,EAAE,CAAC;IAChB,kBAAkB,GAAQ,EAAE,CAAC;IAC7B,YAAY,GAAG,KAAK,CAAC;IACrB,QAAQ,GAAG,CAAC,CAAC;IACb,cAAc,CAAS;IAC1B,KAAK,CAAS;IACX,aAAa,CAAU;IACvB,mBAAmB,CAAS;IAC5B,kBAAkB,GAAG,CAAC,CAAC;IACvB,YAAY,CAAS;IACrB,MAAM,GAAY,IAAI,CAAC;IACzB,eAAe,GAAG,KAAK,CAAC;IAEhC,YAAY,KAAa,EAAE,aAAsB,EAAE,YAAoB,EAAE,iBAAyB,CAAC;QACjG,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;QAElD,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,CAAC;IACH,CAAC;IAED;;OAEG;IACO,KAAK,CAAC,YAAY;QAC1B,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO;QAE9B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;gBAC1B,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,+CAA+C,CAAC,CAAC;gBACjF,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;YAC9B,CAAC;YACD,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC1B,OAAO;QACT,CAAC;QACD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,kDAAkD;QAClD,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAE7B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpE,+BAA+B;gBAC/B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;oBACjB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,6BAA6B,CAAC,CAAC;oBAC/D,OAAO,CAAC,oCAAoC;gBAC9C,CAAC;gBAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBAChC,IAAI,CAAC,IAAI,EAAE,CAAC;oBACV,SAAS;gBACX,CAAC;gBAED,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;gBACnB,KAAK,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC;gBAE3C,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;oBACvB,IAAI,CAAC,eAAe,EAAE,CAAC;gBACzB,CAAC;gBAED,IAAI,CAAC,YAAY,EAAE,CAAC;gBAEpB,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;oBACnF,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;gBAClE,CAAC;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC1B,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;gBAChF,KAAK,IAAI,CAAC,YAAY,EAAE,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,0BAA0B,CAAC,IAAO;QAC9C,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAC7B,qCAAqC;YACrC,IAAI,IAAI,CAAC,kBAAkB,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;gBACzC,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC;YAClC,CAAC;YACD,gCAAgC;YAChC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,iEAAiE;YACjE,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,6BAA8B,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5F,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;YAE/C,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACvB,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,CAAC;YAED,IAAI,CAAC,YAAY,EAAE,CAAC;YAEpB,+BAA+B;YAC/B,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzC,KAAK,IAAI,CAAC,YAAY,EAAE,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACO,YAAY;QACpB,MAAM,SAAS,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;QAC3J,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC5C,IACE,YAAY,GAAG,CAAC;eACb,YAAY,GAAG,IAAI,CAAC,kBAAkB;eACtC,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAkB,GAAG,GAAG,CAAC,EAC7E,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,MAAM,SAAS,yBAAyB,YAAY,GAAG,CAAC,CAAC;YACzF,IAAI,CAAC,kBAAkB,GAAG,YAAY,CAAC;QACzC,CAAC;aAAM,IAAI,YAAY,KAAK,CAAC,IAAI,IAAI,CAAC,kBAAkB,GAAG,CAAC,EAAE,CAAC;YAC7D,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,MAAM,SAAS,kBAAkB,CAAC,CAAC;YACnE,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAES,eAAe;QACvB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC3C,CAAC;IAED;;OAEG;IACO,eAAe;QACvB,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;YACrE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC;YACjD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACtC,CAAC;YACD,aAAa,CAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC5E,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,yBAAyB,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,kCAAkC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;IAED;;OAEG;IACO,iBAAiB;QACzB,IAAI,CAAC;YACH,IAAI,UAAU,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC;gBACzC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,CAAC;gBAC7E,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;gBACtE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,4BAA4B,CAAC,CAAC;YAChE,CAAC;QACH,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,qCAAqC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACzF,CAAC;IACH,CAAC;CAgBF;AAcD;;;GAGG;AACH,MAAM,OAAO,kBAAmB,SAAQ,cAAkC;IAChE,eAAe,CAAiG;IAChH,mBAAmB,CAAU;IAErC;;;;;;OAMG;IACH,YACE,KAAa,EACb,eAA+G,EAC/G,gBAAyB,KAAK,EAC9B,sBAA8B,IAAI,CAAC,QAAQ,EAAE,kBAAkB,EAAE,gCAAgC,CAAC,EAClG,YAAoB,EACpB,MAAe,EACf,iBAAyB,CAAC,EAC1B,mBAA4B;QAE5B,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;QAC1D,IAAI,CAAC,mBAAmB,GAAG,mBAAmB,CAAC;QAC/C,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,mBAAmB,GAAG,mBAAmB,CAAC;QAC/C,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,yBAAyB,CAAC,CAAC;QAC7D,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,0BAA0B,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED;;OAEG;IACI,OAAO,CAAC,KAAa,EAAE,OAAe,EAAE,EAAU,EAAE,OAA+B;QACxF,IAAI,CAAC;YACH,IAAI,CAAC,sBAAsB,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED;;OAEG;IACI,cAAc,CAAC,KAAa,EAAE,OAAe,EAAE,EAAU,EAAE,OAA+B;QAC/F,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;IAClE,CAAC;IAEO,sBAAsB;QAC5B,IAAI,IAAI,CAAC,mBAAmB,KAAK,SAAS,IAAI,IAAI,CAAC,eAAe,EAAE,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACjG,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,+BAA+B,IAAI,CAAC,mBAAmB,IAAI,CAAC,CAAC;QACnG,CAAC;IACH,CAAC;IAEO,sBAAsB,CAAC,KAAa,EAAE,OAAe,EAAE,EAAU,EAAE,OAA+B;QACxG,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YAElE,gDAAgD;YAChD,IAAI,CAAC,YAAY,EAAE,CAAC;YAEpB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACvB,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,CAAC;YAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,mBAAmB,CAAC,QAAe;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACtC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzD,CAAC;IAED;;OAEG;IACI,YAAY,CAAC,QAAsD;QACxE,iFAAiF;QACjF,IAAI,QAAQ,CAAC,SAAS,KAAK,CAAC,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,IAAI,QAAQ,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YACzG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,6BAA6B,CAAC,CAAC;YAC/D,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QAED,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC;QAC9C,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC;QAErB,sCAAsC;QACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;YACxD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC;YACxD,MAAM,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC;YAC3D,IAAI,WAAW,KAAK,aAAa,EAAE,CAAC;gBAClC,WAAW,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,6CAA6C;gBAC1E,MAAM;YACR,CAAC;QACH,CAAC;QAED,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;YACvB,wEAAwE;YACxE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAC3C,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,0DAA0D,CAAC,CAAC;QAC9F,CAAC;aAAM,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,sEAAsE;YACtE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,oFAAoF,CAAC,CAAC;QACxH,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,6BAA6B,CAAC,CAAC;QAC/D,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,aAAa,CAAC,CAAS;QAClC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,8BAA8B,CAAC,CAAC;QAChE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC;QAC/B,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QACtD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,sCAAsC,aAAa,WAAW,SAAS,EAAE,CAAC,CAAC;QAC7G,CAAC;QACD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC;IACtC,CAAC;IAEM,QAAQ;QACb,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;OAEG;IACO,KAAK,CAAC,WAAW,CAAC,IAAwB;QAClD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAC5E,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED;;OAEG;IACO,aAAa,CAAC,IAAwB;QAC9C,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC;IACJ,CAAC;IAED;;OAEG;IACO,eAAe,CAAC,IAAS;QACjC,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC;YACjB,MAAM,EAAE,CAAC,GAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,4CAA4C,GAAG,CAAC,OAAO,EAAE,CAAC;SAClH,CAAC;IACJ,CAAC;CACF;AAWD;;;GAGG;AACH,MAAM,OAAO,mBAAoB,SAAQ,cAAmC;IAClE,cAAc,CAAoD;IAE1E;;;;;;OAMG;IACH,YACE,KAAa,EACb,cAAiE,EACjE,gBAAyB,KAAK,EAC9B,sBAA8B,IAAI,CAAC,QAAQ,EAAE,mBAAmB,EAAE,iCAAiC,CAAC,EACpG,YAAoB,EACpB,MAAe;QAEf,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,YAAY,CAAC,CAAC;QAC1C,IAAI,CAAC,mBAAmB,GAAG,mBAAmB,CAAC;QAC/C,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,0BAA0B,CAAC,CAAC;QAC9D,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,2BAA2B,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED;;OAEG;IACI,OAAO,CAAC,KAAa,EAAE,OAAe;QAC3C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAE3D,gDAAgD;QAChD,IAAI,CAAC,YAAY,EAAE,CAAC;QAEpB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,CAAC;IACH,CAAC;IAED;;OAEG;IACO,KAAK,CAAC,WAAW,CAAC,IAAyB;QACnD,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACtD,CAAC;IAED;;OAEG;IACO,aAAa,CAAC,IAAyB;QAC/C,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC;IACJ,CAAC;IAED;;OAEG;IACO,eAAe,CAAC,IAAS;QACjC,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC;IACJ,CAAC;IAED;;OAEG;IACI,QAAQ;QACb,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;OAEG;IACK,mBAAmB,CAAC,QAA+B;QACzD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9E,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzD,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,aAAa,CAAC,CAAS;QAClC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,2BAA2B,CAAC,CAAC;QAC7D,MAAM,KAAK,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC;QAC/B,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QACtD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,uCAAuC,aAAa,WAAW,SAAS,EAAE,CAAC,CAAC;QAC9G,CAAC;QACD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC;IACtC,CAAC;IAED;;OAEG;IACI,YAAY,CAAC,QAAsD;QACxE,iFAAiF;QACjF,IAAI,QAAQ,CAAC,SAAS,KAAK,CAAC,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,IAAI,QAAQ,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YACzG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,8BAA8B,CAAC,CAAC;YAChE,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QAED,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC;QAC9C,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC;QAErB,sCAAsC;QACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;YACxD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC;YACxD,MAAM,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC;YAC3D,IAAI,WAAW,KAAK,aAAa,EAAE,CAAC;gBAClC,WAAW,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,6CAA6C;gBAC1E,MAAM;YACR,CAAC;QACH,CAAC;QAED,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;YACvB,wEAAwE;YACxE,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YACjD,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,2DAA2D,CAAC,CAAC;QAC/F,CAAC;aAAM,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,sEAAsE;YACtE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,qFAAqF,CAAC,CAAC;QACzH,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,8BAA8B,CAAC,CAAC;QAChE,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;CACF","sourcesContent":["import { createHash } from \"crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { IClientPublishOptions } from \"mqtt\";\nimport { join } from \"path\";\n\nimport { basePath } from \"../base-path.js\";\nimport logger from \"../logger.js\";\n\n/**\n * Abstract base class that encapsulates common functionality for managing\n * a throttled in‑memory queue with an optional disk persistence.\n */\nabstract class ThrottledQueue<T> {\n protected queue: T[] = [];\n protected lastProcessedItems: T[] = [];\n protected isProcessing = false;\n protected inFlight = 0;\n protected maxConcurrency: number;\n public delay: number;\n protected persistToDisk: boolean;\n protected persistenceFilePath: string;\n protected previousLoggedSize = 0;\n protected instanceName: string;\n protected active: boolean = true;\n private inactiveLogSent = false;\n\n constructor(delay: number, persistToDisk: boolean, instanceName: string, maxConcurrency: number = 1) {\n this.delay = delay;\n this.persistToDisk = persistToDisk;\n this.instanceName = instanceName;\n this.maxConcurrency = Math.max(1, maxConcurrency);\n\n if (this.persistToDisk) {\n this.loadQueueFromDisk();\n }\n }\n\n /**\n * Process the queue items one at a time with a delay between each.\n */\n protected async processQueue(): Promise<void> {\n if (this.isProcessing) return;\n\n if (!this.active) {\n if (!this.inactiveLogSent) {\n logger.info(`${this.instanceName} - Queue is not active. Exiting processQueue.`);\n this.inactiveLogSent = true;\n }\n this.isProcessing = false;\n return;\n }\n this.isProcessing = true;\n // Reset the flag when processing actually starts.\n this.inactiveLogSent = false;\n\n try {\n while (this.queue.length > 0 && this.inFlight < this.maxConcurrency) {\n // Check if the queue is active\n if (!this.active) {\n logger.info(`${this.instanceName} - Queue processing paused.`);\n return; // Exit the loop and stop processing\n }\n\n const item = this.queue.shift();\n if (!item) {\n continue;\n }\n\n this.inFlight += 1;\n void this.processItemWithBookkeeping(item);\n\n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n this.logQueueSize();\n\n if (this.delay > 0 && this.queue.length > 0 && this.inFlight < this.maxConcurrency) {\n await new Promise((resolve) => setTimeout(resolve, this.delay));\n }\n }\n } finally {\n this.isProcessing = false;\n if (this.active && this.queue.length > 0 && this.inFlight < this.maxConcurrency) {\n void this.processQueue();\n }\n }\n }\n\n private async processItemWithBookkeeping(item: T): Promise<void> {\n try {\n await this.processItem(item);\n // Only store 10 last processed items\n if (this.lastProcessedItems.length >= 10) {\n this.lastProcessedItems.shift();\n }\n // Store the last processed item\n this.lastProcessedItems.push(item);\n } catch (error) {\n // Specific error handling can be done in the subclass if needed.\n logger.error(`${this.instanceName} - Error processing item: ${(error as Error).message}`);\n } finally {\n this.inFlight = Math.max(0, this.inFlight - 1);\n\n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n this.logQueueSize();\n\n // Check if the queue is active\n if (this.active && this.queue.length > 0) {\n void this.processQueue();\n }\n }\n }\n\n /**\n * Log changes to the queue size at significant thresholds.\n */\n protected logQueueSize(): void {\n const className = `${this.constructor.name.includes(\"Publisher\") ? \"Publisher\" : this.constructor.name.includes(\"Subscriber\") ? \"Subscriber\" : \"Unknown\"}`;\n const pendingCount = this.getPendingCount();\n if (\n pendingCount > 1\n && pendingCount > this.previousLoggedSize\n && Math.floor(pendingCount / 100) > Math.floor(this.previousLoggedSize / 100)\n ) {\n logger.info(`${this.instanceName} - ${className} queue size length is ${pendingCount}.`);\n this.previousLoggedSize = pendingCount;\n } else if (pendingCount === 0 && this.previousLoggedSize > 0) {\n logger.info(`${this.instanceName} - ${className} queue is empty.`);\n this.previousLoggedSize = 0;\n }\n }\n\n protected getPendingCount(): number {\n return this.queue.length + this.inFlight;\n }\n\n /**\n * Save the current queue to disk.\n */\n protected saveQueueToDisk(): void {\n try {\n const queueData = this.queue.map((item) => this.serializeItem(item));\n const dir = join(this.persistenceFilePath, \"..\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(this.persistenceFilePath, JSON.stringify(queueData, null, 2));\n logger.debug(`${this.instanceName} - Queue saved to disk.`);\n } catch (error: any) {\n logger.error(`${this.instanceName} - Error saving queue to disk: ${error.message}`);\n }\n }\n\n /**\n * Load the queue from disk.\n */\n protected loadQueueFromDisk(): void {\n try {\n if (existsSync(this.persistenceFilePath)) {\n const queueData = JSON.parse(readFileSync(this.persistenceFilePath, \"utf8\"));\n this.queue = queueData.map((data: any) => this.deserializeItem(data));\n logger.info(`${this.instanceName} - Queue loaded from disk.`);\n }\n } catch (error: any) {\n logger.error(`${this.instanceName} - Error loading queue from disk: ${error.message}`);\n }\n }\n\n /**\n * Process a single queue item.\n */\n protected abstract processItem(item: T): Promise<void>;\n\n /**\n * Serialize a queue item for persistence.\n */\n protected abstract serializeItem(item: T): any;\n\n /**\n * Deserialize persisted data into a queue item.\n */\n protected abstract deserializeItem(data: any): T;\n}\n\n/**\n * Interface representing a queued publish item.\n */\ninterface PublisherQueueItem {\n topic: string;\n message: string;\n id: string;\n options?: IClientPublishOptions;\n resolve: () => void;\n reject: (err: any) => void;\n}\n\n/**\n * ThrottledPublisher: manages a queue of outgoing publish requests,\n * sending one message at a time using a provided publish function.\n */\nexport class ThrottledPublisher extends ThrottledQueue<PublisherQueueItem> {\n private publishFunction: (topic: string, message: string, id: string, options?: IClientPublishOptions) => Promise<void>;\n private maxPendingPublishes?: number;\n\n /**\n * @param delay Delay between messages in milliseconds.\n * @param publishFunction Function to perform the actual publish.\n * @param persistToDisk Whether to persist the queue to disk.\n * @param persistenceFilePath File path for queue persistence.\n * @param instanceName Unique instance name for logging.\n */\n constructor(\n delay: number,\n publishFunction: (topic: string, message: string, id: string, options?: IClientPublishOptions) => Promise<void>,\n persistToDisk: boolean = false,\n persistenceFilePath: string = join(basePath, \"/publisherQueue/\", \"throttled-publisher-queue.json\"),\n instanceName: string,\n active: boolean,\n maxConcurrency: number = 1,\n maxPendingPublishes?: number,\n ) {\n super(delay, persistToDisk, instanceName, maxConcurrency);\n this.persistenceFilePath = persistenceFilePath;\n this.publishFunction = publishFunction;\n this.active = active;\n this.maxPendingPublishes = maxPendingPublishes;\n if (active) {\n logger.info(`${this.instanceName} - Publisher is active.`);\n } else {\n logger.info(`${this.instanceName} - Publisher is passive.`);\n }\n }\n\n /**\n * Enqueue a publish request.\n */\n public enqueue(topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> {\n try {\n this.assertCanAcceptPublish();\n return this.enqueueAcceptedPublish(topic, message, id, options);\n } catch (error) {\n return Promise.reject(error);\n }\n }\n\n /**\n * Enqueue a publish request and throw synchronously if the bounded queue is full.\n */\n public enqueueOrThrow(topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> {\n this.assertCanAcceptPublish();\n return this.enqueueAcceptedPublish(topic, message, id, options);\n }\n\n private assertCanAcceptPublish(): void {\n if (this.maxPendingPublishes !== undefined && this.getPendingCount() >= this.maxPendingPublishes) {\n throw new Error(`${this.instanceName} - Publisher queue is full (${this.maxPendingPublishes}).`);\n }\n }\n\n private enqueueAcceptedPublish(topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> {\n return new Promise((resolve, reject) => {\n this.queue.push({ topic, message, id, options, resolve, reject });\n\n // Trigger logging after a new message is added.\n this.logQueueSize(); \n\n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n if (this.active) {\n this.processQueue();\n }\n });\n }\n\n private computeHashForBatch(messages: any[]): string {\n const data = JSON.stringify(messages);\n return createHash(\"sha256\").update(data).digest(\"hex\");\n }\n\n /**\n * Switch the publisher to a passive state and return a snapshot\n */\n public becomeActive(snapshot: { referenceHash: string; batchSize: number }): void {\n // If snapshot is empty (batchSize is zero), start processing from the beginning.\n if (snapshot.batchSize === 0 || snapshot.batchSize === undefined || snapshot.referenceHash === undefined) {\n this.active = true;\n logger.info(`${this.instanceName} - Publisher became active.`);\n this.processQueue();\n return;\n }\n\n const { referenceHash, batchSize } = snapshot;\n let resumeIndex = -1;\n\n // Use batchSize as the window length.\n for (let i = 0; i <= this.queue.length - batchSize; i++) {\n const currentBatch = this.queue.slice(i, i + batchSize);\n const currentHash = this.computeHashForBatch(currentBatch);\n if (currentHash === referenceHash) {\n resumeIndex = i + batchSize; // resume immediately after the matched batch\n break;\n }\n }\n\n if (resumeIndex !== -1) {\n // Adjust the queue so that processing resumes after the matching batch.\n this.queue = this.queue.slice(resumeIndex);\n logger.info(`${this.instanceName} - Publisher resumed from the matched snapshot position.`);\n } else if (this.queue.length > 0) {\n // Log info message and process the entire queue if no match is found.\n logger.info(`${this.instanceName} - No matching batch found during resume; processing the entire publisher's queue.`);\n }\n\n this.active = true;\n logger.info(`${this.instanceName} - Publisher became active.`);\n this.processQueue();\n }\n\n /**\n * Switch the publisher to a passive state and return a snapshot\n */\n public async becomePassive(n: number): Promise<{ referenceHash: string; batchSize: number; }> {\n this.active = false;\n logger.info(`${this.instanceName} - Publisher became passive.`);\n const batch = this.queue.slice(-n);\n const batchSize = batch.length;\n const referenceHash = this.computeHashForBatch(batch);\n if (batchSize > 0) {\n logger.info(`${this.instanceName} - Publisher batch reference hash: ${referenceHash}, size: ${batchSize}`);\n }\n return { referenceHash, batchSize };\n }\n\n public getState(): boolean {\n return this.active;\n }\n\n /**\n * Process a single publish request.\n */\n protected async processItem(item: PublisherQueueItem): Promise<void> {\n try {\n await this.publishFunction(item.topic, item.message, item.id, item.options);\n item.resolve();\n } catch (error) {\n item.reject(error);\n }\n }\n\n /**\n * Serialize a publish request for persistence.\n */\n protected serializeItem(item: PublisherQueueItem): any {\n return {\n topic: item.topic,\n message: item.message,\n id: item.id,\n options: item.options,\n };\n }\n\n /**\n * Deserialize persisted data into a publish request.\n */\n protected deserializeItem(data: any): PublisherQueueItem {\n return {\n topic: data.topic,\n message: data.message,\n id: data.id,\n options: data.options,\n resolve: () => {},\n reject: (err: any) => logger.error(`${this.instanceName} - Error processing queued publish item: ${err.message}`),\n };\n }\n}\n\n/**\n * Interface representing a queued subscribe item.\n */\ninterface SubscriberQueueItem {\n topic: string;\n message: string;\n timestamp: number;\n}\n\n/**\n * ThrottledSubscriber: manages a queue of incoming messages and processes\n * them one at a time using a provided message handler.\n */\nexport class ThrottledSubscriber extends ThrottledQueue<SubscriberQueueItem> {\n private messageHandler: (topic: string, message: string) => Promise<void>;\n\n /**\n * @param delay Delay between processing messages in milliseconds.\n * @param messageHandler Function to process incoming messages.\n * @param persistToDisk Whether to persist the queue to disk.\n * @param persistenceFilePath File path for queue persistence.\n * @param instanceName Unique instance name for logging.\n */\n constructor(\n delay: number,\n messageHandler: (topic: string, message: string) => Promise<void>,\n persistToDisk: boolean = false,\n persistenceFilePath: string = join(basePath, \"/subscriberQueue/\", \"throttled-subscriber-queue.json\"),\n instanceName: string,\n active: boolean,\n ) {\n super(delay, persistToDisk, instanceName);\n this.persistenceFilePath = persistenceFilePath;\n this.messageHandler = messageHandler;\n this.active = active;\n if (active) {\n logger.info(`${this.instanceName} - Subscriber is active.`);\n } else {\n logger.info(`${this.instanceName} - Subscriber is passive.`);\n }\n }\n\n /**\n * Enqueue subscribe messages for processing.\n */\n public enqueue(topic: string, message: string): void {\n this.queue.push({ topic, message, timestamp: Date.now() });\n \n // Trigger logging after a new message is added.\n this.logQueueSize();\n \n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n if (this.active) {\n this.processQueue();\n }\n }\n\n /**\n * Process a single subscribe message.\n */\n protected async processItem(item: SubscriberQueueItem): Promise<void> {\n await this.messageHandler(item.topic, item.message);\n }\n\n /**\n * Serialize a subscribe message for persistence.\n */\n protected serializeItem(item: SubscriberQueueItem): any {\n return {\n message: item.message,\n };\n }\n\n /**\n * Deserialize persisted data into a subscribe message.\n */\n protected deserializeItem(data: any): SubscriberQueueItem {\n return {\n topic: data.topic,\n message: data.message,\n timestamp: data.timestamp,\n };\n }\n\n /**\n * Get the current state of the subscriber (active or passive).\n */\n public getState(): boolean {\n return this.active;\n }\n\n /**\n * Computes a SHA‑256 hash for a given batch of subscriber items.\n */\n private computeHashForBatch(messages: SubscriberQueueItem[]): string {\n const data = JSON.stringify(messages.map((item) => this.serializeItem(item)));\n return createHash(\"sha256\").update(data).digest(\"hex\");\n }\n\n /**\n * Switch the subscriber to a passive state and return a snapshot.\n */\n public async becomePassive(n: number): Promise<{ referenceHash: string; batchSize: number; }> {\n this.active = false;\n logger.info(`${this.instanceName} - Subscriber is passive.`);\n const batch = this.lastProcessedItems.slice(-n);\n const batchSize = batch.length;\n const referenceHash = this.computeHashForBatch(batch);\n if (batchSize > 0) {\n logger.info(`${this.instanceName} - Subscriber batch reference hash: ${referenceHash}, size: ${batchSize}`);\n }\n return { referenceHash, batchSize };\n }\n\n /**\n * Switch the subscriber to an active state and resume processing.\n */\n public becomeActive(snapshot: { referenceHash: string; batchSize: number }): void {\n // If snapshot is empty (batchSize is zero), start processing from the beginning.\n if (snapshot.batchSize === 0 || snapshot.batchSize === undefined || snapshot.referenceHash === undefined) {\n this.active = true;\n logger.info(`${this.instanceName} - Subscriber became active.`); \n this.processQueue();\n return;\n }\n\n const { referenceHash, batchSize } = snapshot;\n let resumeIndex = -1;\n\n // Use batchSize as the window length.\n for (let i = 0; i <= this.queue.length - batchSize; i++) {\n const currentBatch = this.queue.slice(i, i + batchSize);\n const currentHash = this.computeHashForBatch(currentBatch);\n if (currentHash === referenceHash) {\n resumeIndex = i + batchSize; // resume immediately after the matched batch\n break;\n }\n }\n\n if (resumeIndex !== -1) {\n // Adjust the queue so that processing resumes after the matching batch.\n const queueSlice = this.queue.slice(resumeIndex); \n this.queue = queueSlice;\n logger.info(`${this.instanceName} - Subscriber resumed from the matched snapshot position.`);\n } else if (this.queue.length > 0) {\n // Log info message and process the entire queue if no match is found.\n logger.info(`${this.instanceName} - No matching batch found during resume; processing the entire subscriber's queue.`);\n }\n\n this.active = true;\n logger.info(`${this.instanceName} - Subscriber became active.`); \n this.processQueue();\n }\n}\n"]}
1
+ {"version":3,"file":"throttled-queue.js","sourceRoot":"","sources":["../../src/uns-mqtt/throttled-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAExE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,MAAM,MAAM,cAAc,CAAC;AAElC;;;GAGG;AACH,MAAe,cAAc;IACjB,KAAK,GAAQ,EAAE,CAAC;IAChB,kBAAkB,GAAQ,EAAE,CAAC;IAC7B,YAAY,GAAG,KAAK,CAAC;IACrB,QAAQ,GAAG,CAAC,CAAC;IACb,cAAc,CAAS;IAC1B,KAAK,CAAS;IACX,aAAa,CAAU;IACvB,mBAAmB,CAAS;IAC5B,kBAAkB,GAAG,CAAC,CAAC;IACvB,YAAY,CAAS;IACrB,MAAM,GAAY,IAAI,CAAC;IACzB,eAAe,GAAG,KAAK,CAAC;IAEhC,YAAY,KAAa,EAAE,aAAsB,EAAE,YAAoB,EAAE,iBAAyB,CAAC;QACjG,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;QAElD,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,CAAC;IACH,CAAC;IAED;;OAEG;IACO,KAAK,CAAC,YAAY;QAC1B,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO;QAE9B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;gBAC1B,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,+CAA+C,CAAC,CAAC;gBACjF,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;YAC9B,CAAC;YACD,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC1B,OAAO;QACT,CAAC;QACD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,kDAAkD;QAClD,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAE7B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpE,+BAA+B;gBAC/B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;oBACjB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,6BAA6B,CAAC,CAAC;oBAC/D,OAAO,CAAC,oCAAoC;gBAC9C,CAAC;gBAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBAChC,IAAI,CAAC,IAAI,EAAE,CAAC;oBACV,SAAS;gBACX,CAAC;gBAED,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;gBACnB,KAAK,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC;gBAE3C,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;oBACvB,IAAI,CAAC,eAAe,EAAE,CAAC;gBACzB,CAAC;gBAED,IAAI,CAAC,YAAY,EAAE,CAAC;gBAEpB,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;oBACnF,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;gBAClE,CAAC;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC1B,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;gBAChF,KAAK,IAAI,CAAC,YAAY,EAAE,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,0BAA0B,CAAC,IAAO;QAC9C,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAC7B,qCAAqC;YACrC,IAAI,IAAI,CAAC,kBAAkB,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;gBACzC,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC;YAClC,CAAC;YACD,gCAAgC;YAChC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,iEAAiE;YACjE,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,6BAA8B,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5F,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;YAE/C,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACvB,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,CAAC;YAED,IAAI,CAAC,YAAY,EAAE,CAAC;YAEpB,+BAA+B;YAC/B,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzC,KAAK,IAAI,CAAC,YAAY,EAAE,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACO,YAAY;QACpB,MAAM,SAAS,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;QAC3J,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC5C,IACE,YAAY,GAAG,CAAC;eACb,YAAY,GAAG,IAAI,CAAC,kBAAkB;eACtC,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAkB,GAAG,GAAG,CAAC,EAC7E,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,MAAM,SAAS,yBAAyB,YAAY,GAAG,CAAC,CAAC;YACzF,IAAI,CAAC,kBAAkB,GAAG,YAAY,CAAC;QACzC,CAAC;aAAM,IAAI,YAAY,KAAK,CAAC,IAAI,IAAI,CAAC,kBAAkB,GAAG,CAAC,EAAE,CAAC;YAC7D,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,MAAM,SAAS,kBAAkB,CAAC,CAAC;YACnE,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAES,eAAe;QACvB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC3C,CAAC;IAED;;OAEG;IACO,eAAe;QACvB,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;YACrE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC;YACjD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACtC,CAAC;YACD,aAAa,CAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC5E,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,yBAAyB,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,kCAAkC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;IAED;;OAEG;IACO,iBAAiB;QACzB,IAAI,CAAC;YACH,IAAI,UAAU,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC;gBACzC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,CAAC;gBAC7E,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;gBACtE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,4BAA4B,CAAC,CAAC;YAChE,CAAC;QACH,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,qCAAqC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACzF,CAAC;IACH,CAAC;CAgBF;AAcD;;;GAGG;AACH,MAAM,OAAO,kBAAmB,SAAQ,cAAkC;IAChE,eAAe,CAAiG;IAChH,mBAAmB,CAAU;IAErC;;;;;;OAMG;IACH,YACE,KAAa,EACb,eAA+G,EAC/G,gBAAyB,KAAK,EAC9B,sBAA8B,IAAI,CAAC,QAAQ,EAAE,kBAAkB,EAAE,gCAAgC,CAAC,EAClG,YAAoB,EACpB,MAAe,EACf,iBAAyB,CAAC,EAC1B,mBAA4B;QAE5B,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;QAC1D,IAAI,CAAC,mBAAmB,GAAG,mBAAmB,CAAC;QAC/C,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,mBAAmB,GAAG,mBAAmB,CAAC;QAC/C,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,yBAAyB,CAAC,CAAC;QAC7D,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,0BAA0B,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED;;OAEG;IACI,OAAO,CAAC,KAAa,EAAE,OAAe,EAAE,EAAU,EAAE,OAA+B;QACxF,IAAI,CAAC;YACH,IAAI,CAAC,sBAAsB,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED;;OAEG;IACI,cAAc,CAAC,KAAa,EAAE,OAAe,EAAE,EAAU,EAAE,OAA+B;QAC/F,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;IAClE,CAAC;IAEO,sBAAsB;QAC5B,IAAI,IAAI,CAAC,mBAAmB,KAAK,SAAS,IAAI,IAAI,CAAC,eAAe,EAAE,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACjG,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,+BAA+B,IAAI,CAAC,mBAAmB,IAAI,CAAC,CAAC;QACnG,CAAC;IACH,CAAC;IAEO,sBAAsB,CAAC,KAAa,EAAE,OAAe,EAAE,EAAU,EAAE,OAA+B;QACxG,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YAElE,gDAAgD;YAChD,IAAI,CAAC,YAAY,EAAE,CAAC;YAEpB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACvB,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,CAAC;YAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,mBAAmB,CAAC,QAAe;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACtC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzD,CAAC;IAED;;OAEG;IACI,YAAY,CAAC,QAAsD;QACxE,iFAAiF;QACjF,IAAI,QAAQ,CAAC,SAAS,KAAK,CAAC,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,IAAI,QAAQ,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YACzG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,6BAA6B,CAAC,CAAC;YAC/D,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QAED,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC;QAC9C,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC;QAErB,sCAAsC;QACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;YACxD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC;YACxD,MAAM,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC;YAC3D,IAAI,WAAW,KAAK,aAAa,EAAE,CAAC;gBAClC,WAAW,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,6CAA6C;gBAC1E,MAAM;YACR,CAAC;QACH,CAAC;QAED,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;YACvB,wEAAwE;YACxE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAC3C,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,0DAA0D,CAAC,CAAC;QAC9F,CAAC;aAAM,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,sEAAsE;YACtE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,oFAAoF,CAAC,CAAC;QACxH,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,6BAA6B,CAAC,CAAC;QAC/D,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,aAAa,CAAC,CAAS;QAClC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,8BAA8B,CAAC,CAAC;QAChE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC;QAC/B,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QACtD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,sCAAsC,aAAa,WAAW,SAAS,EAAE,CAAC,CAAC;QAC7G,CAAC;QACD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC;IACtC,CAAC;IAEM,QAAQ;QACb,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;OAEG;IACO,KAAK,CAAC,WAAW,CAAC,IAAwB;QAClD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAC5E,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED;;OAEG;IACO,aAAa,CAAC,IAAwB;QAC9C,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC;IACJ,CAAC;IAED;;OAEG;IACO,eAAe,CAAC,IAAS;QACjC,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC;YACjB,MAAM,EAAE,CAAC,GAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY,4CAA4C,GAAG,CAAC,OAAO,EAAE,CAAC;SAClH,CAAC;IACJ,CAAC;CACF;AAWD;;;GAGG;AACH,MAAM,OAAO,mBAAoB,SAAQ,cAAmC;IAClE,cAAc,CAAoD;IAE1E;;;;;;OAMG;IACH,YACE,KAAa,EACb,cAAiE,EACjE,gBAAyB,KAAK,EAC9B,sBAA8B,IAAI,CAAC,QAAQ,EAAE,mBAAmB,EAAE,iCAAiC,CAAC,EACpG,YAAoB,EACpB,MAAe;QAEf,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,YAAY,CAAC,CAAC;QAC1C,IAAI,CAAC,mBAAmB,GAAG,mBAAmB,CAAC;QAC/C,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,0BAA0B,CAAC,CAAC;QAC9D,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,2BAA2B,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED;;OAEG;IACI,OAAO,CAAC,KAAa,EAAE,OAAe;QAC3C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAE3D,gDAAgD;QAChD,IAAI,CAAC,YAAY,EAAE,CAAC;QAEpB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,CAAC;IACH,CAAC;IAED;;OAEG;IACO,KAAK,CAAC,WAAW,CAAC,IAAyB;QACnD,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACtD,CAAC;IAED;;OAEG;IACO,aAAa,CAAC,IAAyB;QAC/C,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC;IACJ,CAAC;IAED;;OAEG;IACO,eAAe,CAAC,IAAS;QACjC,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC;IACJ,CAAC;IAED;;OAEG;IACI,QAAQ;QACb,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;OAEG;IACK,mBAAmB,CAAC,QAA+B,EAAE,MAAM,GAAG,KAAK;QACzE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtI,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzD,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,aAAa,CAAC,CAAS,EAAE,MAAM,GAAG,KAAK;QAClD,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,2BAA2B,CAAC,CAAC;QAC7D,OAAO,IAAI,CAAC,QAAQ,GAAG,CAAC;YAAE,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/E,MAAM,KAAK,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC;QAC/B,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC9D,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,uCAAuC,aAAa,WAAW,SAAS,EAAE,CAAC,CAAC;QAC9G,CAAC;QACD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC;IACtC,CAAC;IAED;;OAEG;IACK,cAAc,CAAqB;IAE3C,kFAAkF;IAC3E,aAAa,CAAC,QAAsD;QACzE,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC9D,IAAI,QAAQ,CAAC,SAAS,KAAK,CAAC,EAAE,CAAC;YAAC,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;YAAC,OAAO;QAAC,CAAC;QAClE,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;YACjE,IAAI,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,KAAK,QAAQ,CAAC,aAAa;gBACxG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC,qEAAqE,CAAC,CAAC;QAC7G,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IAEM,sBAAsB;QAC3B,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,cAAc,KAAK,SAAS;YAAE,MAAM,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAClG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACnD,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;QAChC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,KAAK,IAAI,CAAC,YAAY,EAAE,CAAC;IAC3B,CAAC;IAEM,YAAY,CAAC,QAAsD;QACxE,iFAAiF;QACjF,IAAI,QAAQ,CAAC,SAAS,KAAK,CAAC,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,IAAI,QAAQ,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YACzG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,8BAA8B,CAAC,CAAC;YAChE,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QAED,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC;QAC9C,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC;QAErB,sCAAsC;QACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;YACxD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC;YACxD,MAAM,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC;YAC3D,IAAI,WAAW,KAAK,aAAa,EAAE,CAAC;gBAClC,WAAW,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,6CAA6C;gBAC1E,MAAM;YACR,CAAC;QACH,CAAC;QAED,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;YACvB,wEAAwE;YACxE,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YACjD,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,2DAA2D,CAAC,CAAC;QAC/F,CAAC;aAAM,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,sEAAsE;YACtE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,qFAAqF,CAAC,CAAC;QACzH,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,8BAA8B,CAAC,CAAC;QAChE,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;CACF","sourcesContent":["import { createHash } from \"crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { IClientPublishOptions } from \"mqtt\";\nimport { join } from \"path\";\n\nimport { basePath } from \"../base-path.js\";\nimport logger from \"../logger.js\";\n\n/**\n * Abstract base class that encapsulates common functionality for managing\n * a throttled in‑memory queue with an optional disk persistence.\n */\nabstract class ThrottledQueue<T> {\n protected queue: T[] = [];\n protected lastProcessedItems: T[] = [];\n protected isProcessing = false;\n protected inFlight = 0;\n protected maxConcurrency: number;\n public delay: number;\n protected persistToDisk: boolean;\n protected persistenceFilePath: string;\n protected previousLoggedSize = 0;\n protected instanceName: string;\n protected active: boolean = true;\n private inactiveLogSent = false;\n\n constructor(delay: number, persistToDisk: boolean, instanceName: string, maxConcurrency: number = 1) {\n this.delay = delay;\n this.persistToDisk = persistToDisk;\n this.instanceName = instanceName;\n this.maxConcurrency = Math.max(1, maxConcurrency);\n\n if (this.persistToDisk) {\n this.loadQueueFromDisk();\n }\n }\n\n /**\n * Process the queue items one at a time with a delay between each.\n */\n protected async processQueue(): Promise<void> {\n if (this.isProcessing) return;\n\n if (!this.active) {\n if (!this.inactiveLogSent) {\n logger.info(`${this.instanceName} - Queue is not active. Exiting processQueue.`);\n this.inactiveLogSent = true;\n }\n this.isProcessing = false;\n return;\n }\n this.isProcessing = true;\n // Reset the flag when processing actually starts.\n this.inactiveLogSent = false;\n\n try {\n while (this.queue.length > 0 && this.inFlight < this.maxConcurrency) {\n // Check if the queue is active\n if (!this.active) {\n logger.info(`${this.instanceName} - Queue processing paused.`);\n return; // Exit the loop and stop processing\n }\n\n const item = this.queue.shift();\n if (!item) {\n continue;\n }\n\n this.inFlight += 1;\n void this.processItemWithBookkeeping(item);\n\n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n this.logQueueSize();\n\n if (this.delay > 0 && this.queue.length > 0 && this.inFlight < this.maxConcurrency) {\n await new Promise((resolve) => setTimeout(resolve, this.delay));\n }\n }\n } finally {\n this.isProcessing = false;\n if (this.active && this.queue.length > 0 && this.inFlight < this.maxConcurrency) {\n void this.processQueue();\n }\n }\n }\n\n private async processItemWithBookkeeping(item: T): Promise<void> {\n try {\n await this.processItem(item);\n // Only store 10 last processed items\n if (this.lastProcessedItems.length >= 10) {\n this.lastProcessedItems.shift();\n }\n // Store the last processed item\n this.lastProcessedItems.push(item);\n } catch (error) {\n // Specific error handling can be done in the subclass if needed.\n logger.error(`${this.instanceName} - Error processing item: ${(error as Error).message}`);\n } finally {\n this.inFlight = Math.max(0, this.inFlight - 1);\n\n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n this.logQueueSize();\n\n // Check if the queue is active\n if (this.active && this.queue.length > 0) {\n void this.processQueue();\n }\n }\n }\n\n /**\n * Log changes to the queue size at significant thresholds.\n */\n protected logQueueSize(): void {\n const className = `${this.constructor.name.includes(\"Publisher\") ? \"Publisher\" : this.constructor.name.includes(\"Subscriber\") ? \"Subscriber\" : \"Unknown\"}`;\n const pendingCount = this.getPendingCount();\n if (\n pendingCount > 1\n && pendingCount > this.previousLoggedSize\n && Math.floor(pendingCount / 100) > Math.floor(this.previousLoggedSize / 100)\n ) {\n logger.info(`${this.instanceName} - ${className} queue size length is ${pendingCount}.`);\n this.previousLoggedSize = pendingCount;\n } else if (pendingCount === 0 && this.previousLoggedSize > 0) {\n logger.info(`${this.instanceName} - ${className} queue is empty.`);\n this.previousLoggedSize = 0;\n }\n }\n\n protected getPendingCount(): number {\n return this.queue.length + this.inFlight;\n }\n\n /**\n * Save the current queue to disk.\n */\n protected saveQueueToDisk(): void {\n try {\n const queueData = this.queue.map((item) => this.serializeItem(item));\n const dir = join(this.persistenceFilePath, \"..\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(this.persistenceFilePath, JSON.stringify(queueData, null, 2));\n logger.debug(`${this.instanceName} - Queue saved to disk.`);\n } catch (error: any) {\n logger.error(`${this.instanceName} - Error saving queue to disk: ${error.message}`);\n }\n }\n\n /**\n * Load the queue from disk.\n */\n protected loadQueueFromDisk(): void {\n try {\n if (existsSync(this.persistenceFilePath)) {\n const queueData = JSON.parse(readFileSync(this.persistenceFilePath, \"utf8\"));\n this.queue = queueData.map((data: any) => this.deserializeItem(data));\n logger.info(`${this.instanceName} - Queue loaded from disk.`);\n }\n } catch (error: any) {\n logger.error(`${this.instanceName} - Error loading queue from disk: ${error.message}`);\n }\n }\n\n /**\n * Process a single queue item.\n */\n protected abstract processItem(item: T): Promise<void>;\n\n /**\n * Serialize a queue item for persistence.\n */\n protected abstract serializeItem(item: T): any;\n\n /**\n * Deserialize persisted data into a queue item.\n */\n protected abstract deserializeItem(data: any): T;\n}\n\n/**\n * Interface representing a queued publish item.\n */\ninterface PublisherQueueItem {\n topic: string;\n message: string;\n id: string;\n options?: IClientPublishOptions;\n resolve: () => void;\n reject: (err: any) => void;\n}\n\n/**\n * ThrottledPublisher: manages a queue of outgoing publish requests,\n * sending one message at a time using a provided publish function.\n */\nexport class ThrottledPublisher extends ThrottledQueue<PublisherQueueItem> {\n private publishFunction: (topic: string, message: string, id: string, options?: IClientPublishOptions) => Promise<void>;\n private maxPendingPublishes?: number;\n\n /**\n * @param delay Delay between messages in milliseconds.\n * @param publishFunction Function to perform the actual publish.\n * @param persistToDisk Whether to persist the queue to disk.\n * @param persistenceFilePath File path for queue persistence.\n * @param instanceName Unique instance name for logging.\n */\n constructor(\n delay: number,\n publishFunction: (topic: string, message: string, id: string, options?: IClientPublishOptions) => Promise<void>,\n persistToDisk: boolean = false,\n persistenceFilePath: string = join(basePath, \"/publisherQueue/\", \"throttled-publisher-queue.json\"),\n instanceName: string,\n active: boolean,\n maxConcurrency: number = 1,\n maxPendingPublishes?: number,\n ) {\n super(delay, persistToDisk, instanceName, maxConcurrency);\n this.persistenceFilePath = persistenceFilePath;\n this.publishFunction = publishFunction;\n this.active = active;\n this.maxPendingPublishes = maxPendingPublishes;\n if (active) {\n logger.info(`${this.instanceName} - Publisher is active.`);\n } else {\n logger.info(`${this.instanceName} - Publisher is passive.`);\n }\n }\n\n /**\n * Enqueue a publish request.\n */\n public enqueue(topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> {\n try {\n this.assertCanAcceptPublish();\n return this.enqueueAcceptedPublish(topic, message, id, options);\n } catch (error) {\n return Promise.reject(error);\n }\n }\n\n /**\n * Enqueue a publish request and throw synchronously if the bounded queue is full.\n */\n public enqueueOrThrow(topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> {\n this.assertCanAcceptPublish();\n return this.enqueueAcceptedPublish(topic, message, id, options);\n }\n\n private assertCanAcceptPublish(): void {\n if (this.maxPendingPublishes !== undefined && this.getPendingCount() >= this.maxPendingPublishes) {\n throw new Error(`${this.instanceName} - Publisher queue is full (${this.maxPendingPublishes}).`);\n }\n }\n\n private enqueueAcceptedPublish(topic: string, message: string, id: string, options?: IClientPublishOptions): Promise<void> {\n return new Promise((resolve, reject) => {\n this.queue.push({ topic, message, id, options, resolve, reject });\n\n // Trigger logging after a new message is added.\n this.logQueueSize(); \n\n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n if (this.active) {\n this.processQueue();\n }\n });\n }\n\n private computeHashForBatch(messages: any[]): string {\n const data = JSON.stringify(messages);\n return createHash(\"sha256\").update(data).digest(\"hex\");\n }\n\n /**\n * Switch the publisher to a passive state and return a snapshot\n */\n public becomeActive(snapshot: { referenceHash: string; batchSize: number }): void {\n // If snapshot is empty (batchSize is zero), start processing from the beginning.\n if (snapshot.batchSize === 0 || snapshot.batchSize === undefined || snapshot.referenceHash === undefined) {\n this.active = true;\n logger.info(`${this.instanceName} - Publisher became active.`);\n this.processQueue();\n return;\n }\n\n const { referenceHash, batchSize } = snapshot;\n let resumeIndex = -1;\n\n // Use batchSize as the window length.\n for (let i = 0; i <= this.queue.length - batchSize; i++) {\n const currentBatch = this.queue.slice(i, i + batchSize);\n const currentHash = this.computeHashForBatch(currentBatch);\n if (currentHash === referenceHash) {\n resumeIndex = i + batchSize; // resume immediately after the matched batch\n break;\n }\n }\n\n if (resumeIndex !== -1) {\n // Adjust the queue so that processing resumes after the matching batch.\n this.queue = this.queue.slice(resumeIndex);\n logger.info(`${this.instanceName} - Publisher resumed from the matched snapshot position.`);\n } else if (this.queue.length > 0) {\n // Log info message and process the entire queue if no match is found.\n logger.info(`${this.instanceName} - No matching batch found during resume; processing the entire publisher's queue.`);\n }\n\n this.active = true;\n logger.info(`${this.instanceName} - Publisher became active.`);\n this.processQueue();\n }\n\n /**\n * Switch the publisher to a passive state and return a snapshot\n */\n public async becomePassive(n: number): Promise<{ referenceHash: string; batchSize: number; }> {\n this.active = false;\n logger.info(`${this.instanceName} - Publisher became passive.`);\n const batch = this.queue.slice(-n);\n const batchSize = batch.length;\n const referenceHash = this.computeHashForBatch(batch);\n if (batchSize > 0) {\n logger.info(`${this.instanceName} - Publisher batch reference hash: ${referenceHash}, size: ${batchSize}`);\n }\n return { referenceHash, batchSize };\n }\n\n public getState(): boolean {\n return this.active;\n }\n\n /**\n * Process a single publish request.\n */\n protected async processItem(item: PublisherQueueItem): Promise<void> {\n try {\n await this.publishFunction(item.topic, item.message, item.id, item.options);\n item.resolve();\n } catch (error) {\n item.reject(error);\n }\n }\n\n /**\n * Serialize a publish request for persistence.\n */\n protected serializeItem(item: PublisherQueueItem): any {\n return {\n topic: item.topic,\n message: item.message,\n id: item.id,\n options: item.options,\n };\n }\n\n /**\n * Deserialize persisted data into a publish request.\n */\n protected deserializeItem(data: any): PublisherQueueItem {\n return {\n topic: data.topic,\n message: data.message,\n id: data.id,\n options: data.options,\n resolve: () => {},\n reject: (err: any) => logger.error(`${this.instanceName} - Error processing queued publish item: ${err.message}`),\n };\n }\n}\n\n/**\n * Interface representing a queued subscribe item.\n */\ninterface SubscriberQueueItem {\n topic: string;\n message: string;\n timestamp: number;\n}\n\n/**\n * ThrottledSubscriber: manages a queue of incoming messages and processes\n * them one at a time using a provided message handler.\n */\nexport class ThrottledSubscriber extends ThrottledQueue<SubscriberQueueItem> {\n private messageHandler: (topic: string, message: string) => Promise<void>;\n\n /**\n * @param delay Delay between processing messages in milliseconds.\n * @param messageHandler Function to process incoming messages.\n * @param persistToDisk Whether to persist the queue to disk.\n * @param persistenceFilePath File path for queue persistence.\n * @param instanceName Unique instance name for logging.\n */\n constructor(\n delay: number,\n messageHandler: (topic: string, message: string) => Promise<void>,\n persistToDisk: boolean = false,\n persistenceFilePath: string = join(basePath, \"/subscriberQueue/\", \"throttled-subscriber-queue.json\"),\n instanceName: string,\n active: boolean,\n ) {\n super(delay, persistToDisk, instanceName);\n this.persistenceFilePath = persistenceFilePath;\n this.messageHandler = messageHandler;\n this.active = active;\n if (active) {\n logger.info(`${this.instanceName} - Subscriber is active.`);\n } else {\n logger.info(`${this.instanceName} - Subscriber is passive.`);\n }\n }\n\n /**\n * Enqueue subscribe messages for processing.\n */\n public enqueue(topic: string, message: string): void {\n this.queue.push({ topic, message, timestamp: Date.now() });\n \n // Trigger logging after a new message is added.\n this.logQueueSize();\n \n if (this.persistToDisk) {\n this.saveQueueToDisk();\n }\n\n if (this.active) {\n this.processQueue();\n }\n }\n\n /**\n * Process a single subscribe message.\n */\n protected async processItem(item: SubscriberQueueItem): Promise<void> {\n await this.messageHandler(item.topic, item.message);\n }\n\n /**\n * Serialize a subscribe message for persistence.\n */\n protected serializeItem(item: SubscriberQueueItem): any {\n return {\n message: item.message,\n };\n }\n\n /**\n * Deserialize persisted data into a subscribe message.\n */\n protected deserializeItem(data: any): SubscriberQueueItem {\n return {\n topic: data.topic,\n message: data.message,\n timestamp: data.timestamp,\n };\n }\n\n /**\n * Get the current state of the subscriber (active or passive).\n */\n public getState(): boolean {\n return this.active;\n }\n\n /**\n * Computes a SHA‑256 hash for a given batch of subscriber items.\n */\n private computeHashForBatch(messages: SubscriberQueueItem[], strict = false): string {\n const data = JSON.stringify(messages.map((item) => strict ? { topic: item.topic, message: item.message } : this.serializeItem(item)));\n return createHash(\"sha256\").update(data).digest(\"hex\");\n }\n\n /**\n * Switch the subscriber to a passive state and return a snapshot.\n */\n public async becomePassive(n: number, strict = false): Promise<{ referenceHash: string; batchSize: number; }> {\n this.active = false;\n logger.info(`${this.instanceName} - Subscriber is passive.`);\n while (this.inFlight > 0) await new Promise(resolve => setTimeout(resolve, 1));\n const batch = this.lastProcessedItems.slice(-n);\n const batchSize = batch.length;\n const referenceHash = this.computeHashForBatch(batch, strict);\n if (batchSize > 0) {\n logger.info(`${this.instanceName} - Subscriber batch reference hash: ${referenceHash}, size: ${batchSize}`);\n }\n return { referenceHash, batchSize };\n }\n\n /**\n * Switch the subscriber to an active state and resume processing.\n */\n private preparedResume: number | undefined;\n\n /** Validate a unique overlap without consuming input or activating this owner. */\n public prepareResume(snapshot: { referenceHash: string; batchSize: number }): void {\n if (this.active) throw Error(\"MQTT successor is not passive\");\n if (snapshot.batchSize === 0) { this.preparedResume = 0; return; }\n const matches: number[] = [];\n for (let i = 0; i <= this.queue.length - snapshot.batchSize; i++) {\n if (this.computeHashForBatch(this.queue.slice(i, i + snapshot.batchSize), true) === snapshot.referenceHash)\n matches.push(i + snapshot.batchSize);\n }\n if (matches.length !== 1) throw Error(\"MQTT input overlap is missing or ambiguous; successor stays passive\");\n this.preparedResume = matches[0];\n }\n\n public activatePreparedResume(): void {\n if (this.active || this.preparedResume === undefined) throw Error(\"MQTT resume was not prepared\");\n this.queue = this.queue.slice(this.preparedResume);\n this.preparedResume = undefined;\n this.active = true;\n void this.processQueue();\n }\n\n public becomeActive(snapshot: { referenceHash: string; batchSize: number }): void {\n // If snapshot is empty (batchSize is zero), start processing from the beginning.\n if (snapshot.batchSize === 0 || snapshot.batchSize === undefined || snapshot.referenceHash === undefined) {\n this.active = true;\n logger.info(`${this.instanceName} - Subscriber became active.`); \n this.processQueue();\n return;\n }\n\n const { referenceHash, batchSize } = snapshot;\n let resumeIndex = -1;\n\n // Use batchSize as the window length.\n for (let i = 0; i <= this.queue.length - batchSize; i++) {\n const currentBatch = this.queue.slice(i, i + batchSize);\n const currentHash = this.computeHashForBatch(currentBatch);\n if (currentHash === referenceHash) {\n resumeIndex = i + batchSize; // resume immediately after the matched batch\n break;\n }\n }\n\n if (resumeIndex !== -1) {\n // Adjust the queue so that processing resumes after the matching batch.\n const queueSlice = this.queue.slice(resumeIndex); \n this.queue = queueSlice;\n logger.info(`${this.instanceName} - Subscriber resumed from the matched snapshot position.`);\n } else if (this.queue.length > 0) {\n // Log info message and process the entire queue if no match is found.\n logger.info(`${this.instanceName} - No matching batch found during resume; processing the entire subscriber's queue.`);\n }\n\n this.active = true;\n logger.info(`${this.instanceName} - Subscriber became active.`); \n this.processQueue();\n }\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { type MqttProxyHandoverState } from "../runtime/mqtt-handover-state.js";
1
2
  import { type MqttPublishQueueStatus } from "./publish-queue-contract.js";
2
3
  import { IClientPublishOptions } from "mqtt";
3
4
  import { IMqttPublishRequest, IUnsPacket, IUnsParameters, UnsEvents } from "../uns/uns-interfaces.js";
@@ -21,6 +22,7 @@ export interface UnsMqttProxyStopOptions {
21
22
  }
22
23
  export default class UnsMqttProxy extends UnsProxy {
23
24
  static readonly DEFAULT_DRAIN_TIMEOUT_MS = 30000;
25
+ private readonly stateCompatibility;
24
26
  private lastValues;
25
27
  private worker;
26
28
  private pendingEnqueues;
@@ -35,6 +37,12 @@ export default class UnsMqttProxy extends UnsProxy {
35
37
  private workerFailure;
36
38
  private lifecycleState;
37
39
  private stopPromise;
40
+ private subscriptionsReady;
41
+ private connectionReady;
42
+ /** Current worker transport connection; does not imply an archive/database commit. */
43
+ getConnectionReadiness(): boolean;
44
+ /** Latest broker SUBACK evidence; false before startup, disconnect or worker failure. */
45
+ getInputReadiness(): boolean;
38
46
  private expectedWorkerExit;
39
47
  private publishQueueStatus?;
40
48
  private publishesSealed;
@@ -82,17 +90,33 @@ export default class UnsMqttProxy extends UnsProxy {
82
90
  * @param batchSize - Optional batch size.
83
91
  * @param referenceHash - Optional reference hash.
84
92
  */
93
+ getHandoverCompatibility(): {
94
+ instanceName: string;
95
+ compatibility: string;
96
+ };
97
+ exportHandoverState(cursor: {
98
+ batchSize: number;
99
+ referenceHash: string;
100
+ }): MqttProxyHandoverState;
101
+ validateHandoverState(input: MqttProxyHandoverState): void;
102
+ importHandoverState(input: MqttProxyHandoverState): void;
103
+ prepareHandoverResume(cursor: {
104
+ batchSize: number;
105
+ referenceHash: string;
106
+ }): Promise<void>;
107
+ activateHandoverResume(): Promise<void>;
108
+ private subscriberControl;
85
109
  setSubscriberActive(batchSize?: number, referenceHash?: string): void;
86
110
  /**
87
111
  * Sets the subscriber to passive mode.
88
112
  * @returns A promise that resolves when the subscriber is set to passive.
89
113
  */
90
- setSubscriberPassive(): Promise<UnsEvents["mqttWorker"]>;
114
+ setSubscriberPassive(strict?: boolean): Promise<UnsEvents["mqttWorker"]>;
91
115
  /**
92
116
  * Sets the subscriber to passive mode and allows the publisher to run
93
117
  * until the queue is empty (all messages are processed).
94
118
  */
95
- setSubscriberPassiveAndDrainQueue(): Promise<UnsEvents["mqttWorker"]>;
119
+ setSubscriberPassiveAndDrainQueue(strict?: boolean): Promise<UnsEvents["mqttWorker"]>;
96
120
  drainPublishes(timeoutMs?: number): Promise<void>;
97
121
  flush(timeoutMs?: number): Promise<void>;
98
122
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"uns-mqtt-proxy.d.ts","sourceRoot":"","sources":["../../src/uns-mqtt/uns-mqtt-proxy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"uns-mqtt-proxy.d.ts","sourceRoot":"","sources":["../../src/uns-mqtt/uns-mqtt-proxy.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
+ import { mqttProxyHandoverStateSchema } from "../runtime/mqtt-handover-state.js";
1
2
  import { assertDisabledPublishQueueEmpty } from "./disabled-publish-queue.js";
2
- import { randomUUID } from "node:crypto";
3
+ import { randomUUID, createHash } from "node:crypto";
3
4
  import { mqttPublishQueueIdentity, mqttPublishQueueSchema } from "./publish-queue-contract.js";
4
5
  import { readFileSync } from "fs";
5
6
  import * as path from "path";
@@ -34,6 +35,7 @@ export var MessageMode;
34
35
  })(MessageMode || (MessageMode = {}));
35
36
  export default class UnsMqttProxy extends UnsProxy {
36
37
  static DEFAULT_DRAIN_TIMEOUT_MS = 30_000;
38
+ stateCompatibility;
37
39
  lastValues = new Map();
38
40
  worker;
39
41
  pendingEnqueues = new Map();
@@ -48,6 +50,12 @@ export default class UnsMqttProxy extends UnsProxy {
48
50
  workerFailure = null;
49
51
  lifecycleState = "running";
50
52
  stopPromise = null;
53
+ subscriptionsReady = false;
54
+ connectionReady = false;
55
+ /** Current worker transport connection; does not imply an archive/database commit. */
56
+ getConnectionReadiness() { return this.lifecycleState === "running" && this.connectionReady; }
57
+ /** Latest broker SUBACK evidence; false before startup, disconnect or worker failure. */
58
+ getInputReadiness() { return this.lifecycleState === "running" && this.subscriptionsReady; }
51
59
  expectedWorkerExit = false;
52
60
  publishQueueStatus;
53
61
  publishesSealed = false;
@@ -57,6 +65,16 @@ export default class UnsMqttProxy extends UnsProxy {
57
65
  constructor(mqttHost, processName, instanceName, unsParameters, publisherActive = false, subscriberActive = false) {
58
66
  super();
59
67
  this.instanceName = instanceName;
68
+ const contract = unsParameters?.handoverStateContract;
69
+ if (contract !== undefined && (typeof contract !== "string" || !contract.trim() || contract.length > 256))
70
+ throw Error("Invalid MQTT state compatibility contract");
71
+ // Bind state to semantic revision and actual endpoints/subscriptions, never credentials or client IDs.
72
+ this.stateCompatibility = contract ? createHash("sha256").update(JSON.stringify({
73
+ contract, host: mqttHost, ssl: unsParameters?.mqttSSL ?? false,
74
+ port: unsParameters?.port ?? null, protocol: unsParameters?.protocol ?? null,
75
+ hosts: unsParameters?.hosts ?? [], servers: unsParameters?.servers ?? [],
76
+ subscriptions: [unsParameters?.mqttSubToTopics ?? []].flat().slice().sort(),
77
+ })).digest("hex") : undefined;
60
78
  // Create the topic builder using packageJson values and the processName.
61
79
  this.topicBuilder = new MqttTopicBuilder(`uns-infra/${MqttTopicBuilder.sanitizeTopicPart(packageJson.name)}/${MqttTopicBuilder.sanitizeTopicPart(packageJson.version)}/${MqttTopicBuilder.sanitizeTopicPart(processName)}/`);
62
80
  // Generate the processStatusTopic using the builder.
@@ -212,10 +230,16 @@ export default class UnsMqttProxy extends UnsProxy {
212
230
  this.event.emit("mqttWorker", { command: msg.command, batchSize: msg.batchSize, referenceHash: msg.referenceHash, instanceName: this.instanceName });
213
231
  }
214
232
  else if (msg && msg.command === "mqttProxyStatus") {
233
+ if (msg.event === "mqtt-connected")
234
+ this.connectionReady = msg.value === 1;
235
+ if (msg.event === "mqtt-subscriptions-ready")
236
+ this.subscriptionsReady = msg.value === 1;
215
237
  this.event.emit("mqttProxyStatus", { event: msg.event, value: msg.value, uom: msg.uom, statusTopic: msg.statusTopic });
216
238
  }
217
239
  });
218
240
  this.worker.on("error", (err) => {
241
+ this.subscriptionsReady = false;
242
+ this.connectionReady = false;
219
243
  if (this.expectedWorkerExit) {
220
244
  return;
221
245
  }
@@ -224,6 +248,8 @@ export default class UnsMqttProxy extends UnsProxy {
224
248
  this.failPendingPublishes(reason);
225
249
  });
226
250
  this.worker.on("exit", (code) => {
251
+ this.subscriptionsReady = false;
252
+ this.connectionReady = false;
227
253
  if (this.expectedWorkerExit) {
228
254
  return;
229
255
  }
@@ -316,6 +342,56 @@ export default class UnsMqttProxy extends UnsProxy {
316
342
  * @param batchSize - Optional batch size.
317
343
  * @param referenceHash - Optional reference hash.
318
344
  */
345
+ getHandoverCompatibility() {
346
+ if (!this.stateCompatibility)
347
+ throw Error("MQTT semantic handover contract is missing");
348
+ return { instanceName: this.instanceName, compatibility: this.stateCompatibility };
349
+ }
350
+ exportHandoverState(cursor) {
351
+ return mqttProxyHandoverStateSchema.parse({
352
+ instanceName: this.instanceName, compatibility: this.stateCompatibility,
353
+ values: [...this.lastValues].map(([topic, entry]) => ({ topic, ...entry, timestamp: entry.timestamp.toISOString() })),
354
+ sequences: [...this.currentSequenceId], cursor,
355
+ });
356
+ }
357
+ validateHandoverState(input) {
358
+ const state = mqttProxyHandoverStateSchema.parse(input);
359
+ if (!this.stateCompatibility || state.compatibility !== this.stateCompatibility)
360
+ throw Error("MQTT destination, subscriptions or calculation-state contract changed; review migration compatibility");
361
+ if (state.instanceName !== this.instanceName || this.lastValues.size || this.currentSequenceId.size)
362
+ throw Error("MQTT successor state is not empty or belongs to another proxy");
363
+ }
364
+ importHandoverState(input) {
365
+ this.validateHandoverState(input);
366
+ const state = mqttProxyHandoverStateSchema.parse(input);
367
+ if (state.instanceName !== this.instanceName || this.lastValues.size || this.currentSequenceId.size)
368
+ throw Error("MQTT successor state is not empty or belongs to another proxy");
369
+ this.lastValues = new Map(state.values.map(entry => [entry.topic, { value: entry.value, uom: entry.uom, timestamp: new Date(entry.timestamp) }]));
370
+ this.currentSequenceId = new Map(state.sequences.map(([topic, sequence]) => [topic, sequence]));
371
+ }
372
+ prepareHandoverResume(cursor) {
373
+ return this.subscriberControl("prepareSubscriberResume", cursor);
374
+ }
375
+ activateHandoverResume() { return this.subscriberControl("activatePreparedSubscriber"); }
376
+ subscriberControl(command, cursor) {
377
+ if (this.workerFailure)
378
+ return Promise.reject(this.workerFailure);
379
+ if (this.queueRequests.size >= 64)
380
+ return Promise.reject(Error("Too many pending MQTT controls"));
381
+ const id = randomUUID();
382
+ return new Promise((resolve, reject) => {
383
+ const timer = setTimeout(() => { this.queueRequests.delete(id); reject(Error("MQTT subscriber control timed out")); }, 30_000);
384
+ this.queueRequests.set(id, { resolve, reject, timer });
385
+ try {
386
+ this.worker.postMessage({ command, id, cursor });
387
+ }
388
+ catch (error) {
389
+ clearTimeout(timer);
390
+ this.queueRequests.delete(id);
391
+ reject(error);
392
+ }
393
+ });
394
+ }
319
395
  setSubscriberActive(batchSize, referenceHash) {
320
396
  this.worker.postMessage({ command: "setSubscriberActive", batchSize, referenceHash });
321
397
  }
@@ -323,8 +399,8 @@ export default class UnsMqttProxy extends UnsProxy {
323
399
  * Sets the subscriber to passive mode.
324
400
  * @returns A promise that resolves when the subscriber is set to passive.
325
401
  */
326
- setSubscriberPassive() {
327
- this.worker.postMessage({ command: "setSubscriberPassive" });
402
+ setSubscriberPassive(strict = false) {
403
+ this.worker.postMessage({ command: "setSubscriberPassive", strict });
328
404
  return new Promise((resolve) => {
329
405
  const handler = (msg) => {
330
406
  if (msg.command === "handover_subscriber") {
@@ -340,8 +416,8 @@ export default class UnsMqttProxy extends UnsProxy {
340
416
  * Sets the subscriber to passive mode and allows the publisher to run
341
417
  * until the queue is empty (all messages are processed).
342
418
  */
343
- async setSubscriberPassiveAndDrainQueue() {
344
- const mqttWorkerData = await this.setSubscriberPassive();
419
+ async setSubscriberPassiveAndDrainQueue(strict = false) {
420
+ const mqttWorkerData = await this.setSubscriberPassive(strict);
345
421
  await this.flush();
346
422
  logger.info(`${this.instanceNameWithSuffix} - Subscriber set to passive and queue drained.`);
347
423
  return mqttWorkerData;
@@ -629,7 +705,7 @@ export default class UnsMqttProxy extends UnsProxy {
629
705
  const newUom = msg.packet.message.data.uom;
630
706
  const lastValueEntry = this.lastValues.get(publishTopic);
631
707
  const currentTime = new Date(msg.packet.message.data.time);
632
- if (lastValueEntry) {
708
+ if (lastValueEntry && lastValueEntry.uom === newUom && typeof lastValueEntry.value === typeof newValue) {
633
709
  const intervalBetweenMessages = currentTime.getTime() - lastValueEntry.timestamp.getTime();
634
710
  const lastValue = lastValueEntry.value;
635
711
  this.lastValues.set(publishTopic, { value: newValue, uom: newUom, timestamp: currentTime });