hb-lib-tools
Version:
homebridge-lib Command-Line Tools`
169 lines (157 loc) • 5.16 kB
JavaScript
// hb-lib-tools/index.js
//
// Library for Homebridge plugins.
// Copyright © 2017-2025 Erik Baauw. All rights reserved.
import { isIPv6 } from 'node:net'
import { chalk } from 'hb-lib-tools/chalk'
import { OptionParser } from 'hb-lib-tools/OptionParser'
/** Library for Homebridge plugins.
* see the {@tutorial hb-lib-tools} tutorial.
*
* Homebridge Lib provides:
* - A base class to building command-line tools:
* {@link CommandLineTool}.
* - A series of helper classes for building homebridge plugins (of any type)
* and/or command-line utilities:
* {@link Colour},
* {@link CommandLineParser},
* {@link HttpClient},
* {@link JsonFormatter},
* {@link OptionParser},
* {@link SystemInfo}, and
* {@link UpnpClient}.
* - A series of command-line utilities for troubleshooting Homebridge setups:
* `hap`, `json`, `sysinfo`, `upnp`.
* For more information on these, start the tool from the command-line
* with `-h` or `--help`.
*
* To access the classes provided by Homebridge Lib from your module,
* simply load it by:
* ```javascript
* import hb-lib-tools from 'hb-lib-tools'
* ```
*
* Note that each class provided by Homebridge Lib is implemented as a
* separate Javascript module, that is loaded lazily on first use.
* Due to the way NodeJS deals with circular module dependencies, these modules
* might not yet be initialised while your module is loading.
*
* @module hb-lib-tools
*/
// Check of e is a JavaScript runtime error.
function isJavaScriptError (e) {
return [
'AssertionError',
'EvalError',
'RangeError',
'ReferenceError',
'SyntaxError',
'TypeError',
'URIError'
].includes(e.constructor.name)
}
// Check if e is a NodeJs runtime error.
function isNodejsError (e) {
return typeof e.code === 'string' && e.code.startsWith('ERR_')
}
/** Convert Error to string.
*
* Include the stack trace only for programming errors (JavaScript and NodeJS
* runtime errors).
* Translate system errors into more readable messages.
* @param {Error} e - The error.
* @param {boolean} [useChalk=false] - Use chalk to grey out the stack trace.
* @returns {string} - The error as string.
* @memberof module:hb-lib-tools
*/
function formatError (e, useChalk = false) {
if (isJavaScriptError(e) || isNodejsError(e)) {
if (useChalk) {
const lines = e.stack.split('\n')
const firstLine = lines.shift()
return firstLine + '\n' + chalk.reset.grey(lines.join('\n'))
}
return e.stack
}
if (e.errno != null) { // SystemError
let label = ''
if (e.path != null) {
label = e.path
} else if (e.dest != null) {
label = e.dest
} else if (e.address != null) {
label = e.address
if (isIPv6(label)) {
label = '[' + label + ']'
}
if (e.port != null) {
label += ':' + e.port
}
} else if (e.port != null) {
label = '' + e.port
} else if (e.hostname != null) {
label = e.hostname
}
let message = ''
const a = /[A-Z0-9_-]*:( .*),/.exec(e.message)
if (a?.[1] != null) {
message = a[1]
}
if (label != null && message != null) {
return `${label}: cannot ${e.syscall}: ${e.code}${message}`
}
}
if (e.cmd != null && e.message.slice(-1) === '\n') { // exec error
return e.message.slice(0, e.message.length - 1)
}
return e.message
}
/** Return the recommended version of NodeJS from package.json.
* This is the version used to develop and test the software,
* typically the latest LTS version.
* @param {string} packageJson - The contents of package.json
* @returns {string} - The recommended version of NodeJS.
* @memberof module:hb-lib-tools
*/
function recommendedNodeVersion (packageJson) {
return packageJson?.engines?.node?.split('||')?.[0] ?? process.version.slice(1)
}
/** Resolve after given period, delaying execution.
*
* E.g. to delay execution for 1.5 seconds, issue:
* ```javascript
* import { timeout } from 'hb-lib-tools'
*
* await timeout(1500)
* ```
*
* @param {integer} msec - Period (in msec) to wait.
* @throws {TypeError} On invalid parameter type.
* @throws {RangeError} On invalid parameter value.
* @memberof module:hb-lib-tools
*/
async function timeout (msec) {
msec = OptionParser.toInt('msec', msec, 0)
return new Promise((resolve, reject) => {
setTimeout(() => { resolve() }, msec)
})
}
const zeroes = '00000000000000000000000000000000'
/** Convert integer or Buffer to hex string.
* @param {integer|Buffer} i - The integer or Buffer.
* @param {?integer} length - The (minimum) number of digits in the hex string.
* The hex string is left padded with `0`s, to reach the length.
* @returns {string} - The hex string.
* @memberof module:hb-lib-tools
*/
function toHexString (i, length) {
if (Buffer.isBuffer(i)) {
return i.toString('hex').toUpperCase().replace(/..\B/g, '$&:')
}
const s = i.toString(16).toUpperCase()
if (length == null || s.length >= length) {
return s
}
return (zeroes + s).slice(-length)
}
export { formatError, recommendedNodeVersion, timeout, toHexString }