UNPKG

@alteriom/mqtt-schema

Version:

Alteriom MQTT v1 schemas, TypeScript types, and validation helpers for web integration

85 lines (84 loc) 4.37 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.validators = void 0; exports.validateMessage = validateMessage; exports.classifyAndValidate = classifyAndValidate; const _2020_js_1 = __importDefault(require("ajv/dist/2020.js")); const ajv_formats_1 = __importDefault(require("ajv-formats")); // Schemas embedded via generated schema_data.ts (copy-schemas.cjs) to avoid filesystem dependency const schema_data_js_1 = require("./schema_data.js"); // Load JSON schemas via createRequire so it works in both CJS and ESM builds without import assertions. // Bind embedded schema objects for Ajv consumption const envelope = schema_data_js_1.envelope_schema; const sensorData = schema_data_js_1.sensor_data_schema; const sensorHeartbeat = schema_data_js_1.sensor_heartbeat_schema; const sensorStatus = schema_data_js_1.sensor_status_schema; const gatewayInfo = schema_data_js_1.gateway_info_schema; const gatewayMetrics = schema_data_js_1.gateway_metrics_schema; const firmwareStatus = schema_data_js_1.firmware_status_schema; const controlResponse = schema_data_js_1.control_response_schema; // Lazy singleton Ajv instance so consumers can optionally supply their own if needed. let _ajv = null; function getAjv(opts) { if (_ajv) return _ajv; _ajv = new _2020_js_1.default({ strict: false, allErrors: true, allowUnionTypes: true, ...opts }); (0, ajv_formats_1.default)(_ajv); // Add base schema so $ref works for those referencing envelope _ajv.addSchema(envelope, 'envelope.schema.json'); return _ajv; } function toResult(v, data) { const valid = v(data); if (valid) return { valid: true }; return { valid: false, errors: (v.errors || []).map((e) => `${e.instancePath || '/'} ${e.message || ''}`.trim()) }; } // Pre-compile validators (they are small; compilation cost negligible for typical web usage) const ajv = getAjv(); const sensorDataValidate = ajv.compile(sensorData); const sensorHeartbeatValidate = ajv.compile(sensorHeartbeat); const sensorStatusValidate = ajv.compile(sensorStatus); const gatewayInfoValidate = ajv.compile(gatewayInfo); const gatewayMetricsValidate = ajv.compile(gatewayMetrics); const firmwareStatusValidate = ajv.compile(firmwareStatus); const controlResponseValidate = ajv.compile(controlResponse); exports.validators = { sensorData: (d) => toResult(sensorDataValidate, d), sensorHeartbeat: (d) => toResult(sensorHeartbeatValidate, d), sensorStatus: (d) => toResult(sensorStatusValidate, d), gatewayInfo: (d) => toResult(gatewayInfoValidate, d), gatewayMetrics: (d) => toResult(gatewayMetricsValidate, d), firmwareStatus: (d) => toResult(firmwareStatusValidate, d), controlResponse: (d) => toResult(controlResponseValidate, d) }; function validateMessage(kind, data) { return exports.validators[kind](data); } // Classifier using lightweight heuristics to pick a schema validator. function classifyAndValidate(data) { if (!data || typeof data !== 'object') return { result: { valid: false, errors: ['Not an object'] } }; if (data.metrics) return { kind: 'gatewayMetrics', result: exports.validators.gatewayMetrics(data) }; if (data.sensors) return { kind: 'sensorData', result: exports.validators.sensorData(data) }; if (data.progress_pct !== undefined || (data.status && ['pending', 'downloading', 'flashing', 'verifying', 'rebooting', 'completed', 'failed'].includes(data.status))) return { kind: 'firmwareStatus', result: exports.validators.firmwareStatus(data) }; if (data.status && ['online', 'offline', 'updating', 'error'].includes(data.status) && data.device_type === 'sensor') return { kind: 'sensorStatus', result: exports.validators.sensorStatus(data) }; if (data.status && ['ok', 'error'].includes(data.status)) return { kind: 'controlResponse', result: exports.validators.controlResponse(data) }; if (data.device_type === 'gateway') return { kind: 'gatewayInfo', result: exports.validators.gatewayInfo(data) }; // Fallback treat as heartbeat attempt return { kind: 'sensorHeartbeat', result: exports.validators.sensorHeartbeat(data) }; }