@uns-kit/core 3.0.34 → 3.0.36

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 (37) hide show
  1. package/MIGRATIONS.md +41 -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 +49 -0
  6. package/dist/runtime/mqtt-handover-state.d.ts.map +1 -0
  7. package/dist/runtime/mqtt-handover-state.js +63 -0
  8. package/dist/runtime/mqtt-handover-state.js.map +1 -0
  9. package/dist/uns/handover-manager.d.ts +10 -1
  10. package/dist/uns/handover-manager.d.ts.map +1 -1
  11. package/dist/uns/handover-manager.js +113 -5
  12. package/dist/uns/handover-manager.js.map +1 -1
  13. package/dist/uns/uns-interfaces.d.ts +4 -0
  14. package/dist/uns/uns-interfaces.d.ts.map +1 -1
  15. package/dist/uns/uns-interfaces.js.map +1 -1
  16. package/dist/uns/uns-proxy-process.d.ts +3 -0
  17. package/dist/uns/uns-proxy-process.d.ts.map +1 -1
  18. package/dist/uns/uns-proxy-process.js +8 -2
  19. package/dist/uns/uns-proxy-process.js.map +1 -1
  20. package/dist/uns-config/config-schema.d.ts +4 -1
  21. package/dist/uns-config/config-schema.d.ts.map +1 -1
  22. package/dist/uns-config/uns-core-schema.d.ts +4 -1
  23. package/dist/uns-config/uns-core-schema.d.ts.map +1 -1
  24. package/dist/uns-config/uns-core-schema.js +1 -1
  25. package/dist/uns-config/uns-core-schema.js.map +1 -1
  26. package/dist/uns-mqtt/mqtt-worker.d.ts.map +1 -1
  27. package/dist/uns-mqtt/mqtt-worker.js +13 -1
  28. package/dist/uns-mqtt/mqtt-worker.js.map +1 -1
  29. package/dist/uns-mqtt/throttled-queue.d.ts +8 -1
  30. package/dist/uns-mqtt/throttled-queue.d.ts.map +1 -1
  31. package/dist/uns-mqtt/throttled-queue.js +32 -4
  32. package/dist/uns-mqtt/throttled-queue.js.map +1 -1
  33. package/dist/uns-mqtt/uns-mqtt-proxy.d.ts +20 -2
  34. package/dist/uns-mqtt/uns-mqtt-proxy.d.ts.map +1 -1
  35. package/dist/uns-mqtt/uns-mqtt-proxy.js +68 -6
  36. package/dist/uns-mqtt/uns-mqtt-proxy.js.map +1 -1
  37. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"throttled-queue.js","sourceRoot":"","sources":["../../src/uns-mqtt/throttled-queue.ts"],"names":[],"mappings":"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","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":"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","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;
@@ -88,17 +90,33 @@ export default class UnsMqttProxy extends UnsProxy {
88
90
  * @param batchSize - Optional batch size.
89
91
  * @param referenceHash - Optional reference hash.
90
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;
91
109
  setSubscriberActive(batchSize?: number, referenceHash?: string): void;
92
110
  /**
93
111
  * Sets the subscriber to passive mode.
94
112
  * @returns A promise that resolves when the subscriber is set to passive.
95
113
  */
96
- setSubscriberPassive(): Promise<UnsEvents["mqttWorker"]>;
114
+ setSubscriberPassive(strict?: boolean): Promise<UnsEvents["mqttWorker"]>;
97
115
  /**
98
116
  * Sets the subscriber to passive mode and allows the publisher to run
99
117
  * until the queue is empty (all messages are processed).
100
118
  */
101
- setSubscriberPassiveAndDrainQueue(): Promise<UnsEvents["mqttWorker"]>;
119
+ setSubscriberPassiveAndDrainQueue(strict?: boolean): Promise<UnsEvents["mqttWorker"]>;
102
120
  drainPublishes(timeoutMs?: number): Promise<void>;
103
121
  flush(timeoutMs?: number): Promise<void>;
104
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":"AAAA,OAAO,EAA4D,KAAK,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAG1I,OAAO,EAAoD,KAAK,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAE5H,OAAO,EAAE,qBAAqB,EAAE,MAAM,MAAM,CAAC;AAM7C,OAAO,EAAE,mBAAmB,EAAsC,UAAU,EAAE,cAAc,EAAmG,SAAS,EAAa,MAAM,0BAA0B,CAAC;AAOtP,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAa3C,oBAAY,WAAW;IACrB,GAAG,QAAQ,CAAM,iCAAiC;IAClD;;;OAGG;IACH,KAAK,UAAU,CAAE,8BAA8B;IAC/C;;;OAGG;IACH,IAAI,SAAS;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAwCD,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,QAAQ;IAChD,gBAAuB,wBAAwB,SAAU;IACzD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqB;IACxD,OAAO,CAAC,UAAU,CAA0F;IAC5G,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,eAAe,CAAmF;IAC1G,OAAO,CAAC,yBAAyB,CAAqB;IACtD,OAAO,CAAC,aAAa,CAAiB;IACtC,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,iBAAiB,CAAkC;IAC3D,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,0BAA0B,CAAS;IAC3C,OAAO,CAAC,YAAY,CAA8F;IAClH,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,cAAc,CAAyC;IAC/D,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,eAAe,CAAS;IAChC,sFAAsF;IAC/E,sBAAsB,IAAI,OAAO;IACxC,yFAAyF;IAClF,iBAAiB,IAAI,OAAO;IACnC,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,kBAAkB,CAAC,CAAyB;IACpD,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,aAAa,CAAoH;gBAIvI,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,aAAa,CAAC,EAAE,cAAc,EAC9B,eAAe,GAAE,OAAe,EAChC,gBAAgB,GAAE,OAAe;IA6DnC;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAkC7B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAIhC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA2FxB;;;;;;;OAOG;YACW,2BAA2B;IAoCzC;;;;;OAKG;IACI,kBAAkB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IAI3E;;;OAGG;IACI,mBAAmB,IAAI,OAAO,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAc9D;;;;;OAKG;IACI,wBAAwB,IAAI;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE;IAK3E,mBAAmB,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,GAAG,sBAAsB;IAQjG,qBAAqB,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI;IAQ1D,mBAAmB,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI;IASxD,qBAAqB,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1F,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9C,OAAO,CAAC,iBAAiB;IAYlB,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IAI5E;;;OAGG;IACI,oBAAoB,CAAC,MAAM,UAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAe7E;;;OAGG;IACU,iCAAiC,CAAC,MAAM,UAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAOnF,cAAc,CAAC,SAAS,GAAE,MAA8C,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBxF,KAAK,CAAC,SAAS,GAAE,MAA8C,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5F;;;;;;OAMG;IACU,kBAAkB,CAAC,WAAW,EAAE,mBAAmB,GAAG,IAAI,EAAE,IAAI,GAAE,WAA6B;IA4I5G,OAAO,CAAC,0BAA0B;IAelC,OAAO,CAAC,uBAAuB;IAQ/B;;;;;;OAMG;IACI,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrG;;;;;OAKG;IACI,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAI7D;;;;OAIG;IACI,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAItD;;;;OAIG;IACI,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI;IAI/C;;;;;;;OAOG;YACW,wBAAwB;IAkHtC;;OAEG;IACI,IAAI,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,IAAI,CAAC;YAanD,WAAW;IAuCzB,8EAA8E;IACvE,aAAa,IAAI,IAAI;IAErB,qBAAqB,IAAI,sBAAsB,GAAG,SAAS;IAIlE,OAAO,CAAC,mBAAmB;IAc3B,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,0BAA0B;IAiBlC,OAAO,CAAC,kBAAkB;IAuB1B,OAAO,CAAC,iBAAiB;CAI1B"}
@@ -1,5 +1,6 @@
1
+ import { MQTT_HANDOVER_STATE_FORMAT, 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();
@@ -63,6 +65,16 @@ export default class UnsMqttProxy extends UnsProxy {
63
65
  constructor(mqttHost, processName, instanceName, unsParameters, publisherActive = false, subscriberActive = false) {
64
66
  super();
65
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
+ stateFormat: MQTT_HANDOVER_STATE_FORMAT, 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;
66
78
  // Create the topic builder using packageJson values and the processName.
67
79
  this.topicBuilder = new MqttTopicBuilder(`uns-infra/${MqttTopicBuilder.sanitizeTopicPart(packageJson.name)}/${MqttTopicBuilder.sanitizeTopicPart(packageJson.version)}/${MqttTopicBuilder.sanitizeTopicPart(processName)}/`);
68
80
  // Generate the processStatusTopic using the builder.
@@ -330,6 +342,56 @@ export default class UnsMqttProxy extends UnsProxy {
330
342
  * @param batchSize - Optional batch size.
331
343
  * @param referenceHash - Optional reference hash.
332
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
+ }
333
395
  setSubscriberActive(batchSize, referenceHash) {
334
396
  this.worker.postMessage({ command: "setSubscriberActive", batchSize, referenceHash });
335
397
  }
@@ -337,8 +399,8 @@ export default class UnsMqttProxy extends UnsProxy {
337
399
  * Sets the subscriber to passive mode.
338
400
  * @returns A promise that resolves when the subscriber is set to passive.
339
401
  */
340
- setSubscriberPassive() {
341
- this.worker.postMessage({ command: "setSubscriberPassive" });
402
+ setSubscriberPassive(strict = false) {
403
+ this.worker.postMessage({ command: "setSubscriberPassive", strict });
342
404
  return new Promise((resolve) => {
343
405
  const handler = (msg) => {
344
406
  if (msg.command === "handover_subscriber") {
@@ -354,8 +416,8 @@ export default class UnsMqttProxy extends UnsProxy {
354
416
  * Sets the subscriber to passive mode and allows the publisher to run
355
417
  * until the queue is empty (all messages are processed).
356
418
  */
357
- async setSubscriberPassiveAndDrainQueue() {
358
- const mqttWorkerData = await this.setSubscriberPassive();
419
+ async setSubscriberPassiveAndDrainQueue(strict = false) {
420
+ const mqttWorkerData = await this.setSubscriberPassive(strict);
359
421
  await this.flush();
360
422
  logger.info(`${this.instanceNameWithSuffix} - Subscriber set to passive and queue drained.`);
361
423
  return mqttWorkerData;
@@ -643,7 +705,7 @@ export default class UnsMqttProxy extends UnsProxy {
643
705
  const newUom = msg.packet.message.data.uom;
644
706
  const lastValueEntry = this.lastValues.get(publishTopic);
645
707
  const currentTime = new Date(msg.packet.message.data.time);
646
- if (lastValueEntry) {
708
+ if (lastValueEntry && lastValueEntry.uom === newUom && typeof lastValueEntry.value === typeof newValue) {
647
709
  const intervalBetweenMessages = currentTime.getTime() - lastValueEntry.timestamp.getTime();
648
710
  const lastValue = lastValueEntry.value;
649
711
  this.lastValues.set(publishTopic, { value: newValue, uom: newUom, timestamp: currentTime });