UNPKG

node-switchbot

Version:

The node-switchbot is a Node.js module which allows you to control your Switchbot Devices through Bluetooth (BLE).

254 lines 10.8 kB
import { Buffer } from 'node:buffer'; /* Copyright(C) 2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. * * parameter-checker.ts: Switchbot BLE API registration. */ import { EventEmitter } from 'node:events'; export class ParameterChecker extends EventEmitter { _error = null; /** * Emits a log event with the specified log level and message. * * @param level - The severity level of the log (e.g., 'info', 'warn', 'error'). * @param message - The log message to be emitted. */ async emitLog(level, message) { this.emit('log', { level, message }); } /** * Gets the current error object. * * @returns {ErrorObject | null} - The current error object or null if no error. */ get error() { return this._error; } /** * Checks if the value is specified (not undefined). * * @param {unknown} value - The value to check. * @returns {boolean} - True if the value is specified, false otherwise. */ isSpecified(value) { return value !== undefined; } /** * Checks if the specified object contains valid values based on the provided rules. * * @param {Record<string, unknown>} obj - Object including parameters you want to check. * @param {Record<string, Rule>} rules - Object including rules for the parameters. * @param {boolean} [required] - Flag whether the `obj` is required or not. * @returns {Promise<boolean>} - Resolves to true if the value is valid, false otherwise. */ async check(obj, rules, required = false) { this._error = null; this.emitLog('debug', `Using rules: ${JSON.stringify(rules)}`); if (required && !this.isSpecified(obj)) { this._error = { code: 'MISSING_REQUIRED', message: 'The first argument is missing.' }; return false; } if (!required && !obj) { return true; } if (!this.isObject(obj, {})) { this._error = { code: 'MISSING_REQUIRED', message: 'The first argument is missing.' }; return false; } for (const [name, rule] of Object.entries(rules)) { const value = obj[name]; if (!this.isSpecified(value)) { if (rule.required) { this._error = { code: 'MISSING_REQUIRED', message: `The \`${name}\` is required.` }; return false; } continue; } const typeCheckers = { float: this.isFloat.bind(this), integer: this.isInteger.bind(this), boolean: this.isBoolean.bind(this), array: this.isArray.bind(this), object: this.isObject.bind(this), string: this.isString.bind(this), }; const checker = rule.type && typeCheckers[rule.type]; if (checker) { if (!(await checker(value, rule, name))) { return false; } } else { this._error = { code: 'TYPE_UNKNOWN', message: `The rule specified for the \`${name}\` includes an unknown type: ${rule.type}` }; return false; } } this.emitLog('debug', 'All checks passed.'); return true; } /** * Checks if the value is a float. * * @param {unknown} value - The value to check. * @param {Rule} rule - The rule object containing validation criteria. * @param {string} [name] - The parameter name. * @returns {Promise<boolean>} - Resolves to true if the value is valid, false otherwise. */ async isFloat(value, rule, name = 'value') { this._error = null; if (!rule.required && !this.isSpecified(value)) { return true; } if (typeof value !== 'number') { this._error = { code: 'TYPE_INVALID', message: `The \`${name}\` must be a number (integer or float).` }; return false; } if (typeof rule.min === 'number' && value < rule.min) { this._error = { code: 'VALUE_UNDERFLOW', message: `The \`${name}\` must be greater than or equal to ${rule.min}.` }; return false; } if (typeof rule.max === 'number' && value > rule.max) { this._error = { code: 'VALUE_OVERFLOW', message: `The \`${name}\` must be less than or equal to ${rule.max}.` }; return false; } if (Array.isArray(rule.enum) && !rule.enum.includes(value)) { this._error = { code: 'ENUM_UNMATCH', message: `The \`${name}\` must be any one of ${JSON.stringify(rule.enum)}.` }; return false; } return true; } /** * Checks if the value is an integer. * * @param {unknown} value - The value to check. * @param {Rule} rule - The rule object containing validation criteria. * @param {string} [name] - The parameter name. * @returns {Promise<boolean>} - Resolves to true if the value is valid, false otherwise. */ async isInteger(value, rule, name = 'value') { this._error = null; if (!rule.required && !this.isSpecified(value)) { return true; } if (Number.isInteger(value)) { return true; } this._error = { code: 'TYPE_INVALID', message: `The \`${name}\` must be an integer.` }; return false; } /** * Checks if the value is a boolean. * * @param {unknown} value - The value to check. * @param {Rule} rule - The rule object containing validation criteria. * @param {string} [name] - The parameter name. * @returns {Promise<boolean>} - Resolves to true if the value is valid, false otherwise. */ async isBoolean(value, rule, name = 'value') { this._error = null; if (!rule.required && !this.isSpecified(value)) { return true; } if (typeof value !== 'boolean') { this._error = { code: 'TYPE_INVALID', message: `The \`${name}\` must be boolean.` }; return false; } return true; } /** * Checks if the value is an object. * * @param {unknown} value - The value to check. * @param {Rule} rule - The rule object containing validation criteria. * @param {string} [name] - The parameter name. * @returns {Promise<boolean>} - Resolves to true if the value is valid, false otherwise. */ async isObject(value, rule, name = 'value') { this._error = null; if (!rule.required && !this.isSpecified(value)) { return true; } if (typeof value !== 'object' || value === null || Array.isArray(value)) { this._error = { code: 'TYPE_INVALID', message: `The \`${name}\` must be an object.` }; return false; } return true; } /** * Checks if the value is an array. * * @param {unknown} value - The value to check. * @param {Rule} rule - The rule object containing validation criteria. * @param {string} [name] - The parameter name. * @returns {Promise<boolean>} - Resolves to true if the value is valid, false otherwise. */ async isArray(value, rule, name = 'value') { this._error = null; if (!rule.required && !this.isSpecified(value)) { return true; } if (!Array.isArray(value)) { this._error = { code: 'TYPE_INVALID', message: 'The value must be an array.' }; return false; } if (typeof rule.min === 'number' && value.length < rule.min) { this._error = { code: 'LENGTH_UNDERFLOW', message: `The number of elements in the \`${name}\` must be greater than or equal to ${rule.min}.` }; return false; } if (typeof rule.max === 'number' && value.length > rule.max) { this._error = { code: 'LENGTH_OVERFLOW', message: `The number of elements in the \`${name}\` must be less than or equal to ${rule.max}.` }; return false; } return true; } /** * Checks if the value is a string. * * @param {unknown} value - The value to check. * @param {Rule} rule - The rule object containing validation criteria. * @param {string} [name] - The parameter name. * @returns {Promise<boolean>} - Resolves to true if the value is valid, false otherwise. */ async isString(value, rule, name = 'value') { this._error = null; if (!rule.required && !this.isSpecified(value)) { return true; } if (typeof value !== 'string') { this._error = { code: 'TYPE_INVALID', message: 'The value must be a string.' }; return false; } if (typeof rule.min === 'number' && value.length < rule.min) { this._error = { code: 'LENGTH_UNDERFLOW', message: `The number of characters in the \`${name}\` must be greater than or equal to ${rule.min}.` }; return false; } if (typeof rule.max === 'number' && value.length > rule.max) { this._error = { code: 'LENGTH_OVERFLOW', message: `The number of characters in the \`${name}\` must be less than or equal to ${rule.max}.` }; return false; } if (typeof rule.minBytes === 'number') { const blen = Buffer.from(value, 'utf8').length; if (blen < rule.minBytes) { this._error = { code: 'LENGTH_UNDERFLOW', message: `The byte length of the \`${name}\` (${blen} bytes) must be greater than or equal to ${rule.minBytes} bytes.` }; return false; } } if (typeof rule.maxBytes === 'number') { const blen = Buffer.from(value, 'utf8').length; if (blen > rule.maxBytes) { this._error = { code: 'LENGTH_OVERFLOW', message: `The byte length of the \`${name}\` (${blen} bytes) must be less than or equal to ${rule.maxBytes} bytes.` }; return false; } } if (rule.pattern instanceof RegExp && !rule.pattern.test(value)) { this._error = { code: 'PATTERN_UNMATCH', message: `The \`${name}\` does not conform with the pattern.` }; return false; } if (Array.isArray(rule.enum) && !rule.enum.includes(value)) { this._error = { code: 'ENUM_UNMATCH', message: `The \`${name}\` must be any one of ${JSON.stringify(rule.enum)}.` }; return false; } return true; } } export const parameterChecker = new ParameterChecker(); //# sourceMappingURL=parameter-checker.js.map