function createRuntimeState() { const state = { startedAt: new Date().toISOString(), startedAtMs: Date.now(), db: { healthy: false, lastCheckAt: null, lastError: null, }, mqtt: { connected: false, topicFilter: null, lastConnectedAt: null, lastDisconnectedAt: null, lastError: null, lastMessageAt: null, messagesReceived: 0, messagesAccepted: 0, messagesRejected: 0, lastRejectReason: null, queueBacklog: 0, }, jobs: { offlineScannerRuns: 0, offlineScannerLastRunAt: null, offlineScannerLastError: null, }, }; return { markDbHealthy() { state.db.healthy = true; state.db.lastCheckAt = new Date().toISOString(); state.db.lastError = null; }, markDbError(error) { state.db.healthy = false; state.db.lastCheckAt = new Date().toISOString(); state.db.lastError = { at: new Date().toISOString(), message: error && error.message ? error.message : String(error), }; }, markMqttConnected(topicFilter) { state.mqtt.connected = true; state.mqtt.topicFilter = topicFilter; state.mqtt.lastConnectedAt = new Date().toISOString(); }, markMqttDisconnected() { state.mqtt.connected = false; state.mqtt.lastDisconnectedAt = new Date().toISOString(); }, markMqttError(error) { state.mqtt.lastError = { at: new Date().toISOString(), message: error && error.message ? error.message : String(error), }; }, markMqttMessageReceived() { state.mqtt.messagesReceived += 1; state.mqtt.lastMessageAt = new Date().toISOString(); }, markMqttMessageAccepted() { state.mqtt.messagesAccepted += 1; state.mqtt.lastRejectReason = null; }, markMqttMessageRejected(reason) { state.mqtt.messagesRejected += 1; state.mqtt.lastRejectReason = reason; }, setMqttQueueBacklog(size) { state.mqtt.queueBacklog = size; }, markOfflineScanRun() { state.jobs.offlineScannerRuns += 1; state.jobs.offlineScannerLastRunAt = new Date().toISOString(); state.jobs.offlineScannerLastError = null; }, markOfflineScanError(error) { state.jobs.offlineScannerRuns += 1; state.jobs.offlineScannerLastRunAt = new Date().toISOString(); state.jobs.offlineScannerLastError = { at: new Date().toISOString(), message: error && error.message ? error.message : String(error), }; }, snapshot() { return { startedAt: state.startedAt, uptimeSeconds: Math.floor((Date.now() - state.startedAtMs) / 1000), db: { ...state.db }, mqtt: { ...state.mqtt }, jobs: { ...state.jobs }, }; }, }; } module.exports = { createRuntimeState, };