UNPKG

react-mqtt-hooks

Version:
275 lines (265 loc) 8.61 kB
"use strict"; "use client"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { MqttConnector: () => MqttConnector, _cache: () => _cache, useMqttClient: () => useMqttClient, useTopic: () => useTopic, useTopics: () => useTopics }); module.exports = __toCommonJS(index_exports); // src/contexts/mqtt-connector.tsx var import_mqtt = __toESM(require("mqtt"), 1); var import_react2 = require("react"); // src/internals/utils.ts function parseMessage(message) { try { return JSON.parse(message.toString()); } catch { return message.toString(); } } // src/internals/mqtt-cache.ts var MqttCache = class _MqttCache { static instance; cache = /* @__PURE__ */ new Map(); mqttClient = null; customParser = void 0; activeSubscriptions = /* @__PURE__ */ new Set(); static getInstance() { if (!_MqttCache.instance) { _MqttCache.instance = new _MqttCache(); } return _MqttCache.instance; } setClient(client) { const previousClient = this.mqttClient; this.mqttClient = client; if (previousClient) { previousClient.removeAllListeners("message"); } if (client) { this.setupMessageListener(); this.resubscribeToActiveTopics(); client.on("connect", () => { this.resubscribeToActiveTopics(); }); } } setCustomParser(parser) { this.customParser = parser; } setupMessageListener() { if (!this.mqttClient) { return; } this.mqttClient.on("message", (topic, message) => { const parsedMsg = this.customParser ? this.customParser(message) : parseMessage(message); this.setData(topic, parsedMsg); this.notifySubscribers(topic, parsedMsg); }); } resubscribeToActiveTopics() { if (!this.mqttClient || !this.mqttClient.connected) return; this.activeSubscriptions.forEach((topic) => { this.mqttClient?.subscribe(topic); }); } notifySubscribers(topic, data) { const cacheItem = this.cache.get(topic); if (cacheItem && cacheItem.subscribers.size > 0) { cacheItem.subscribers.forEach((callback) => callback(data)); } } getData(topic) { return this.cache.get(topic)?.data; } setData(topic, data) { const item = this.cache.get(topic); if (item) { item.data = data; item.lastUpdated = (/* @__PURE__ */ new Date()).toLocaleString(); } else { this.cache.set(topic, { data, subscribers: /* @__PURE__ */ new Map(), lastUpdated: (/* @__PURE__ */ new Date()).toLocaleString() }); } } subscribe(topic, callback, subscriberId) { if (!this.cache.has(topic)) { this.cache.set(topic, { data: void 0, subscribers: /* @__PURE__ */ new Map(), lastUpdated: null }); } const item = this.cache.get(topic); item.subscribers.set(subscriberId, callback); if (item.subscribers.size === 1) { this.activeSubscriptions.add(topic); if (this.mqttClient?.connected) { this.mqttClient.subscribe(topic); } } return item.subscribers.size; } unsubscribe(topic, subscriberId) { const item = this.cache.get(topic); if (!item) return false; item.subscribers.delete(subscriberId); if (item.subscribers.size === 0) { this.activeSubscriptions.delete(topic); if (this.mqttClient?.connected) { this.mqttClient.unsubscribe(topic); return true; } } return false; } }; // src/contexts/mqtt-context.tsx var import_react = require("react"); var MqttContext = (0, import_react.createContext)(null); // src/contexts/mqtt-connector.tsx var import_jsx_runtime = ( // disable this rule to make it compatible with React 18 // eslint-disable-next-line react/no-context-provider require("react/jsx-runtime") ); function MqttConnector({ children, url, options, customParser }) { const [mqttClient, setMqttClient] = (0, import_react2.useState)(null); const cache = MqttCache.getInstance(); (0, import_react2.useEffect)(() => { const client = import_mqtt.default.connect(url, options); setMqttClient(client); client.on("connect", () => { cache.setClient(client); cache.setCustomParser(customParser); }); return () => { cache.setClient(null); client.end(); }; }, [url, options, cache, customParser]); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MqttContext.Provider, { value: mqttClient, children }); } // src/hooks/use-mqtt-client.ts var import_react3 = require("react"); function useMqttClient() { const mqttClient = (0, import_react3.useContext)(MqttContext); return mqttClient; } // src/hooks/use-topic.ts var import_react4 = require("react"); function useTopic(topic) { const mqttClient = useMqttClient(); const cache = MqttCache.getInstance(); const subscriberId = (0, import_react4.useId)(); const [data, setData] = (0, import_react4.useState)(() => { return topic ? cache.getData(topic) : void 0; }); (0, import_react4.useEffect)(() => { if (!topic) return; const handleDataUpdate = (newData) => { setData(newData); }; cache.subscribe(topic, handleDataUpdate, subscriberId); const cachedData = cache.getData(topic); if (cachedData !== void 0) { setData(cachedData); } return () => { cache.unsubscribe(topic, subscriberId); }; }, [mqttClient, topic, cache, subscriberId]); return data; } // src/hooks/use-topics.ts var import_react5 = require("react"); function useTopics(topics) { const mqttClient = useMqttClient(); const cache = MqttCache.getInstance(); const baseSubscriberId = (0, import_react5.useId)(); const normalizedTopics = (0, import_react5.useMemo)(() => [...new Set(topics)].sort(), [topics]); const [dataMap, setDataMap] = (0, import_react5.useState)(() => { return normalizedTopics.reduce((acc, topic) => { const cachedData = cache.getData(topic); if (cachedData !== void 0) { acc[topic] = cachedData; } return acc; }, {}); }); (0, import_react5.useEffect)(() => { if (normalizedTopics.length === 0) return; const initialData = normalizedTopics.reduce((acc, topic) => { const cachedData = cache.getData(topic); if (cachedData !== void 0) { acc[topic] = cachedData; } return acc; }, {}); if (Object.keys(initialData).length > 0) { setDataMap((prev) => ({ ...prev, ...initialData })); } normalizedTopics.forEach((topic) => { const observerId = `${baseSubscriberId}-${topic}`; const handleDataUpdate = (newData) => { setDataMap((prev) => ({ ...prev, [topic]: newData })); }; cache.subscribe(topic, handleDataUpdate, observerId); }); return () => { normalizedTopics.forEach((topic) => { cache.unsubscribe(topic, `${baseSubscriberId}-${topic}`); }); }; }, [mqttClient, normalizedTopics, cache, baseSubscriberId]); return dataMap; } // src/index.ts var _cache = MqttCache.getInstance(); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { MqttConnector, _cache, useMqttClient, useTopic, useTopics }); //# sourceMappingURL=index.cjs.map