diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 7406675ad..000000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/* -test/ \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 5934a6ebe..000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,63 +0,0 @@ -module.exports = { - env: { - 'jest/globals': true, - es6: true, - node: true, - }, - extends: ['eslint:recommended', 'plugin:jest/recommended', 'plugin:jest/style', 'prettier'], - parserOptions: { - ecmaVersion: 2018, - sourceType: 'module', - }, - rules: { - 'require-jsdoc': 'off', - 'no-prototype-builtins': 'off', - '@typescript-eslint/no-floating-promises': 'error', - }, - plugins: ['jest', 'perfectionist'], - overrides: [ - { - files: ['*.ts'], - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint'], - extends: ['plugin:@typescript-eslint/recommended'], - parserOptions: { - project: './tsconfig.json', - }, - rules: { - '@typescript-eslint/await-thenable': 'error', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/explicit-function-return-type': 'error', - '@typescript-eslint/no-empty-function': 'off', - '@typescript-eslint/no-explicit-any': 'error', - '@typescript-eslint/no-floating-promises': 'error', - '@typescript-eslint/no-unused-vars': 'error', - 'no-return-await': 'error', - 'perfectionist/sort-imports': [ - 'error', - { - groups: [ - 'type', - ['builtin', 'external'], - 'internal-type', - 'internal', - ['parent-type', 'sibling-type', 'index-type'], - ['parent', 'sibling', 'index'], - 'object', - 'unknown', - ], - customGroups: { - value: {}, - type: {}, - }, - newlinesBetween: 'always', - internalPattern: ['~/**'], - type: 'natural', - order: 'asc', - ignoreCase: false, - }, - ], - }, - }, - ], -}; diff --git a/.github/workflows/update_deps.yml b/.github/workflows/update_deps.yml index 33eb759ee..ef98dd4a6 100644 --- a/.github/workflows/update_deps.yml +++ b/.github/workflows/update_deps.yml @@ -23,8 +23,7 @@ jobs: node-version: 20 cache: npm # connect-gzip-static@4.0.0 requires Node 20 >= - # eslint: https://github.com/typescript-eslint/typescript-eslint/issues/8211 - - run: npx npm-check-updates -u -x connect-gzip-static -x eslint + - run: npx npm-check-updates -u -x connect-gzip-static - run: rm -f package-lock.json - run: npm install - uses: peter-evans/create-pull-request@v7 diff --git a/.prettierrc b/.prettierrc index 0267e1af9..0c99705e7 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,5 +5,23 @@ "printWidth": 150, "bracketSpacing": false, "endOfLine": "lf", - "tabWidth": 4 + "tabWidth": 4, + "importOrder": [ + "", + "^(node:)", + "", + "", + "", + "^[.]", + "", + "", + "", + "", + "", + "^zigbee", + "", + "^[.]" + ], + "importOrderParserPlugins": ["typescript", "decorators"], + "plugins": ["@ianvs/prettier-plugin-sort-imports"] } diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 000000000..1e26a89f4 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,32 @@ +// @ts-check + +import eslint from '@eslint/js'; +import eslintConfigPrettier from 'eslint-config-prettier'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, + { + languageOptions: { + parserOptions: { + project: true, + }, + }, + rules: { + '@typescript-eslint/await-thenable': 'error', + '@typescript-eslint/ban-ts-comment': 'error', + '@typescript-eslint/explicit-function-return-type': 'error', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-unused-vars': 'error', + 'array-bracket-spacing': ['error', 'never'], + 'no-return-await': 'error', + 'object-curly-spacing': ['error', 'never'], + '@typescript-eslint/no-floating-promises': 'error', + }, + }, + { + ignores: ['dist/', '**/*.js', '**/*.mjs'], + }, + eslintConfigPrettier, +); diff --git a/lib/controller.ts b/lib/controller.ts index cfa293f7c..c46482e3f 100644 --- a/lib/controller.ts +++ b/lib/controller.ts @@ -1,6 +1,10 @@ +import type * as SdNotify from 'sd-notify'; + import assert from 'assert'; + import bind from 'bind-decorator'; import stringify from 'json-stable-stringify-without-jsonify'; + import {setLogger as zhSetLogger} from 'zigbee-herdsman'; import {setLogger as zhcSetLogger} from 'zigbee-herdsman-converters'; @@ -31,6 +35,8 @@ import * as settings from './util/settings'; import utils from './util/utils'; import Zigbee from './zigbee'; +type SdNotifyType = typeof SdNotify; + const AllExtensions = [ ExtensionPublish, ExtensionReceive, @@ -63,15 +69,6 @@ type ExtensionArgs = [ addExtension: (extension: Extension) => Promise, ]; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -let sdNotify: any = null; -try { - // eslint-disable-next-line @typescript-eslint/no-require-imports - sdNotify = process.env.NOTIFY_SOCKET ? require('sd-notify') : null; -} catch { - // sd-notify is optional -} - export class Controller { private eventBus: EventBus; private zigbee: Zigbee; @@ -81,6 +78,7 @@ export class Controller { private exitCallback: (code: number, restart: boolean) => Promise; private extensions: Extension[]; private extensionArgs: ExtensionArgs; + private sdNotify: SdNotifyType | undefined; constructor(restartCallback: () => Promise, exitCallback: (code: number, restart: boolean) => Promise) { logger.init(); @@ -149,6 +147,14 @@ export class Controller { const info = await utils.getZigbee2MQTTVersion(); logger.info(`Starting Zigbee2MQTT version ${info.version} (commit #${info.commitHash})`); + try { + this.sdNotify = process.env.NOTIFY_SOCKET ? await import('sd-notify') : undefined; + logger.debug('sd-notify loaded'); + } catch { + // istanbul ignore next + logger.debug('sd-notify is not installed'); + } + // Start zigbee let startResult; try { @@ -224,11 +230,11 @@ export class Controller { logger.info(`Zigbee2MQTT started!`); - const watchdogInterval = sdNotify?.watchdogInterval() || 0; + const watchdogInterval = this.sdNotify?.watchdogInterval() || 0; if (watchdogInterval > 0) { - sdNotify.startWatchdogMode(Math.floor(watchdogInterval / 2)); + this.sdNotify?.startWatchdogMode(Math.floor(watchdogInterval / 2)); } - sdNotify?.ready(); + this.sdNotify?.ready(); } @bind async enableDisableExtension(enable: boolean, name: string): Promise { @@ -253,7 +259,7 @@ export class Controller { } async stop(restart = false): Promise { - sdNotify?.stopping(); + this.sdNotify?.stopping(process.pid); // Call extensions await this.callExtensions('stop', this.extensions); @@ -272,7 +278,7 @@ export class Controller { code = 1; } - sdNotify?.stopWatchdogMode(); + this.sdNotify?.stopWatchdogMode(); return this.exit(code, restart); } diff --git a/lib/eventBus.ts b/lib/eventBus.ts index 27d7ede8d..027106c27 100644 --- a/lib/eventBus.ts +++ b/lib/eventBus.ts @@ -2,7 +2,6 @@ import events from 'events'; import logger from './util/logger'; -// eslint-disable-next-line type ListenerKey = object; interface EventBusMap { diff --git a/lib/extension/availability.ts b/lib/extension/availability.ts index 04a1b2682..f13552bd2 100644 --- a/lib/extension/availability.ts +++ b/lib/extension/availability.ts @@ -1,6 +1,8 @@ import assert from 'assert'; + import bind from 'bind-decorator'; import debounce from 'debounce'; + import * as zhc from 'zigbee-herdsman-converters'; import logger from '../util/logger'; diff --git a/lib/extension/bind.ts b/lib/extension/bind.ts index 5ae3f4b16..1ddbaeefc 100755 --- a/lib/extension/bind.ts +++ b/lib/extension/bind.ts @@ -1,7 +1,9 @@ import assert from 'assert'; + import bind from 'bind-decorator'; import debounce from 'debounce'; import stringify from 'json-stable-stringify-without-jsonify'; + import {Zcl} from 'zigbee-herdsman'; import {ClusterName} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype'; diff --git a/lib/extension/bridge.ts b/lib/extension/bridge.ts index 98cdd5fdb..cad29657a 100644 --- a/lib/extension/bridge.ts +++ b/lib/extension/bridge.ts @@ -1,14 +1,15 @@ -/* eslint-disable camelcase */ -import bind from 'bind-decorator'; import fs from 'fs'; + +import bind from 'bind-decorator'; import stringify from 'json-stable-stringify-without-jsonify'; import JSZip from 'jszip'; import objectAssignDeep from 'object-assign-deep'; import winston from 'winston'; import Transport from 'winston-transport'; -import {Clusters} from 'zigbee-herdsman/dist/zspec/zcl/definition/cluster'; -import {CustomClusters, ClusterDefinition, ClusterName} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype'; + import * as zhc from 'zigbee-herdsman-converters'; +import {Clusters} from 'zigbee-herdsman/dist/zspec/zcl/definition/cluster'; +import {ClusterDefinition, ClusterName, CustomClusters} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype'; import Device from '../model/device'; import Group from '../model/group'; @@ -275,12 +276,12 @@ export default class Bridge extends Extension { } @bind async groupAdd(message: string | KeyValue): Promise { - if (typeof message === 'object' && !message.hasOwnProperty('friendly_name')) { + if (typeof message === 'object' && message.friendly_name === undefined) { throw new Error(`Invalid payload`); } const friendlyName = typeof message === 'object' ? message.friendly_name : message; - const ID = typeof message === 'object' && message.hasOwnProperty('id') ? message.id : null; + const ID = typeof message === 'object' && message.id !== undefined ? message.id : null; const group = settings.addGroup(friendlyName, ID); this.zigbee.createGroup(group.ID); await this.publishGroups(); @@ -316,7 +317,7 @@ export default class Bridge extends Extension { } @bind async installCodeAdd(message: KeyValue | string): Promise { - if (typeof message === 'object' && !message.hasOwnProperty('value')) { + if (typeof message === 'object' && message.value === undefined) { throw new Error('Invalid payload'); } @@ -327,7 +328,7 @@ export default class Bridge extends Extension { } @bind async permitJoin(message: KeyValue | string): Promise { - if (typeof message === 'object' && !message.hasOwnProperty('value')) { + if (typeof message === 'object' && message.value === undefined) { throw new Error('Invalid payload'); } @@ -426,7 +427,7 @@ export default class Bridge extends Extension { } @bind async touchlinkIdentify(message: KeyValue | string): Promise { - if (typeof message !== 'object' || !message.hasOwnProperty('ieee_address') || !message.hasOwnProperty('channel')) { + if (typeof message !== 'object' || message.ieee_address === undefined || message.channel === undefined) { throw new Error('Invalid payload'); } @@ -438,7 +439,7 @@ export default class Bridge extends Extension { @bind async touchlinkFactoryReset(message: KeyValue | string): Promise { let result = false; const payload: {ieee_address?: string; channel?: number} = {}; - if (typeof message === 'object' && message.hasOwnProperty('ieee_address') && message.hasOwnProperty('channel')) { + if (typeof message === 'object' && message.ieee_address !== undefined && message.channel !== undefined) { logger.info(`Start Touchlink factory reset of '${message.ieee_address}' on channel ${message.channel}`); result = await this.zigbee.touchlinkFactoryReset(message.ieee_address, message.channel); payload.ieee_address = message.ieee_address; @@ -473,7 +474,7 @@ export default class Bridge extends Extension { getValue(message: KeyValue | string): string | boolean | number { if (typeof message === 'object') { - if (!message.hasOwnProperty('value')) { + if (message.value === undefined) { throw new Error('No value given'); } @@ -484,7 +485,7 @@ export default class Bridge extends Extension { } async changeEntityOptions(entityType: 'device' | 'group', message: KeyValue | string): Promise { - if (typeof message !== 'object' || !message.hasOwnProperty('id') || !message.hasOwnProperty('options')) { + if (typeof message !== 'object' || message.id === undefined || message.options === undefined) { throw new Error(`Invalid payload`); } @@ -514,12 +515,12 @@ export default class Bridge extends Extension { @bind async deviceConfigureReporting(message: string | KeyValue): Promise { if ( typeof message !== 'object' || - !message.hasOwnProperty('id') || - !message.hasOwnProperty('cluster') || - !message.hasOwnProperty('maximum_report_interval') || - !message.hasOwnProperty('minimum_report_interval') || - !message.hasOwnProperty('reportable_change') || - !message.hasOwnProperty('attribute') + message.id === undefined || + message.cluster === undefined || + message.maximum_report_interval === undefined || + message.minimum_report_interval === undefined || + message.reportable_change === undefined || + message.attribute === undefined ) { throw new Error(`Invalid payload`); } @@ -565,7 +566,7 @@ export default class Bridge extends Extension { } @bind async deviceInterview(message: string | KeyValue): Promise { - if (typeof message !== 'object' || !message.hasOwnProperty('id')) { + if (typeof message !== 'object' || message.id === undefined) { throw new Error(`Invalid payload`); } @@ -588,7 +589,7 @@ export default class Bridge extends Extension { } @bind async deviceGenerateExternalDefinition(message: string | KeyValue): Promise { - if (typeof message !== 'object' || !message.hasOwnProperty('id')) { + if (typeof message !== 'object' || message.id === undefined) { throw new Error(`Invalid payload`); } @@ -606,7 +607,7 @@ export default class Bridge extends Extension { async renameEntity(entityType: 'group' | 'device', message: string | KeyValue): Promise { const deviceAndHasLast = entityType === 'device' && typeof message === 'object' && message.last === true; - if (typeof message !== 'object' || (!message.hasOwnProperty('from') && !deviceAndHasLast) || !message.hasOwnProperty('to')) { + if (typeof message !== 'object' || (message.from === undefined && !deviceAndHasLast) || message.to === undefined) { throw new Error(`Invalid payload`); } @@ -616,7 +617,7 @@ export default class Bridge extends Extension { const from = deviceAndHasLast ? this.lastJoinedDeviceIeeeAddr : message.from; const to = message.to; - const homeAssisantRename = message.hasOwnProperty('homeassistant_rename') ? message.homeassistant_rename : false; + const homeAssisantRename = message.homeassistant_rename !== undefined ? message.homeassistant_rename : false; const entity = this.getEntity(entityType, from); const oldFriendlyName = entity.options.friendly_name; @@ -745,7 +746,7 @@ export default class Bridge extends Extension { ieee_address: this.zigbee.firstCoordinatorEndpoint().getDevice().ieeeAddr, ...this.coordinatorVersion, }, - network: utils.toSnakeCase(await this.zigbee.getNetworkParameters()), + network: utils.toSnakeCaseObject(await this.zigbee.getNetworkParameters()), log_level: logger.getLevel(), permit_join: this.zigbee.getPermitJoin(), permit_join_timeout: this.zigbee.getPermitJoinTimeout(), diff --git a/lib/extension/configure.ts b/lib/extension/configure.ts index 352badc9b..306890634 100644 --- a/lib/extension/configure.ts +++ b/lib/extension/configure.ts @@ -1,5 +1,6 @@ import bind from 'bind-decorator'; import stringify from 'json-stable-stringify-without-jsonify'; + import * as zhc from 'zigbee-herdsman-converters'; import Device from '../model/device'; @@ -19,7 +20,7 @@ export default class Configure extends Extension { @bind private async onReconfigure(data: eventdata.Reconfigure): Promise { // Disabling reporting unbinds some cluster which could be bound by configure, re-setup. - if (data.device.zh.meta?.hasOwnProperty('configured')) { + if (data.device.zh.meta?.configured !== undefined) { delete data.device.zh.meta.configured; data.device.zh.save(); } @@ -43,7 +44,7 @@ export default class Configure extends Extension { await this.configure(device, 'mqtt_message', true); } else if (data.topic === this.topic) { const message = utils.parseJSON(data.message, data.message); - const ID = typeof message === 'object' && message.hasOwnProperty('id') ? message.id : message; + const ID = typeof message === 'object' && message.id !== undefined ? message.id : message; let error: string | undefined; const device = this.zigbee.resolveEntity(ID); @@ -76,7 +77,7 @@ export default class Configure extends Extension { }); this.eventBus.onDeviceJoined(this, async (data) => { - if (data.device.zh.meta.hasOwnProperty('configured')) { + if (data.device.zh.meta.configured !== undefined) { delete data.device.zh.meta.configured; data.device.zh.save(); } @@ -104,7 +105,7 @@ export default class Configure extends Extension { return; } - if (device.zh.meta?.hasOwnProperty('configured')) { + if (device.zh.meta?.configured !== undefined) { return; } @@ -120,7 +121,7 @@ export default class Configure extends Extension { this.configuring.add(device.ieeeAddr); - if (!this.attempts.hasOwnProperty(device.ieeeAddr)) { + if (this.attempts[device.ieeeAddr] === undefined) { this.attempts[device.ieeeAddr] = 0; } diff --git a/lib/extension/externalExtension.ts b/lib/extension/externalExtension.ts index 9d406bba3..6df656855 100644 --- a/lib/extension/externalExtension.ts +++ b/lib/extension/externalExtension.ts @@ -1,8 +1,9 @@ -import bind from 'bind-decorator'; import fs from 'fs'; -import stringify from 'json-stable-stringify-without-jsonify'; import path from 'path'; +import bind from 'bind-decorator'; +import stringify from 'json-stable-stringify-without-jsonify'; + import * as settings from '../util/settings'; import utils from '../util/utils'; import data from './../util/data'; @@ -93,7 +94,7 @@ export default class ExternalExtension extends Extension { @bind private async loadExtension(ConstructorClass: typeof Extension): Promise { await this.enableDisableExtension(false, ConstructorClass.name); - // @ts-ignore + // @ts-expect-error `ConstructorClass` is the interface, not the actual passed class await this.addExtension(new ConstructorClass(this.zigbee, this.mqtt, this.state, this.publishEntityState, this.eventBus, settings, logger)); } diff --git a/lib/extension/frontend.ts b/lib/extension/frontend.ts index 985ec1689..a6efc18ad 100644 --- a/lib/extension/frontend.ts +++ b/lib/extension/frontend.ts @@ -1,14 +1,16 @@ import assert from 'assert'; -import bind from 'bind-decorator'; -import gzipStatic, {RequestHandler} from 'connect-gzip-static'; -import finalhandler from 'finalhandler'; import fs from 'fs'; import http from 'http'; import https from 'https'; -import stringify from 'json-stable-stringify-without-jsonify'; import net from 'net'; import url from 'url'; + +import bind from 'bind-decorator'; +import gzipStatic, {RequestHandler} from 'connect-gzip-static'; +import finalhandler from 'finalhandler'; +import stringify from 'json-stable-stringify-without-jsonify'; import WebSocket from 'ws'; + import frontend from 'zigbee2mqtt-frontend'; import logger from '../util/logger'; @@ -78,8 +80,7 @@ export default class Frontend extends Extension { /* istanbul ignore next */ const options = { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - setHeaders: (res: any, path: string): void => { + setHeaders: (res: http.ServerResponse, path: string): void => { if (path.endsWith('index.html')) { res.setHeader('Cache-Control', 'no-store'); } @@ -117,8 +118,7 @@ export default class Frontend extends Extension { } @bind private onRequest(request: http.IncomingMessage, response: http.ServerResponse): void { - // @ts-ignore - this.fileServer(request, response, finalhandler(request, response)); + this.fileServer?.(request, response, finalhandler(request, response)); } private authenticate(request: http.IncomingMessage, cb: (authenticate: boolean) => void): void { diff --git a/lib/extension/groups.ts b/lib/extension/groups.ts index 04de37758..4e834c2e1 100644 --- a/lib/extension/groups.ts +++ b/lib/extension/groups.ts @@ -1,7 +1,9 @@ import assert from 'assert'; + import bind from 'bind-decorator'; import equals from 'fast-deep-equal/es6'; import stringify from 'json-stable-stringify-without-jsonify'; + import * as zhc from 'zigbee-herdsman-converters'; import Device from '../model/device'; diff --git a/lib/extension/homeassistant.ts b/lib/extension/homeassistant.ts index b9dc7ea74..962a8cae1 100644 --- a/lib/extension/homeassistant.ts +++ b/lib/extension/homeassistant.ts @@ -1,11 +1,13 @@ import assert from 'assert'; + import bind from 'bind-decorator'; import stringify from 'json-stable-stringify-without-jsonify'; + import * as zhc from 'zigbee-herdsman-converters'; import logger from '../util/logger'; import * as settings from '../util/settings'; -import utils, {isNumericExpose, isBinaryExpose, isEnumExpose, assertBinaryExpose, assertNumericExpose, assertEnumExpose} from '../util/utils'; +import utils, {assertBinaryExpose, assertEnumExpose, assertNumericExpose, isBinaryExpose, isEnumExpose, isNumericExpose} from '../util/utils'; import Extension from './extension'; interface MockProperty { @@ -13,7 +15,6 @@ interface MockProperty { value: KeyValue | string | null; } -// eslint-disable-next-line camelcase interface DiscoveryEntry { mockProperties: MockProperty[]; type: string; @@ -379,7 +380,6 @@ class Bridge { homeassistant?: KeyValue; }; - /* eslint-disable brace-style */ get ID(): string { return this.coordinatorIeeeAddress; } @@ -417,7 +417,6 @@ class Bridge { isGroup(): this is Group { return false; } - /* eslint-enable brace-style */ } /** @@ -1528,7 +1527,7 @@ export default class HomeAssistant extends Extension { }); }); - if (isDevice && entity.options.hasOwnProperty('legacy') && !entity.options.legacy) { + if (isDevice && entity.options.legacy !== undefined && !entity.options.legacy) { configs = configs.filter((c) => c !== SENSOR_CLICK); } @@ -1541,7 +1540,7 @@ export default class HomeAssistant extends Extension { if (entity.options.homeassistant) { const s = entity.options.homeassistant; - configs = configs.filter((config) => !s.hasOwnProperty(config.object_id) || s[config.object_id] != null); + configs = configs.filter((config) => s[config.object_id] === undefined || s[config.object_id] != null); configs.forEach((config) => { const configOverride = s[config.object_id]; if (configOverride) { @@ -1563,7 +1562,7 @@ export default class HomeAssistant extends Extension { return; } else if ( isDevice && - (!entity.definition || entity.zh.interviewing || (entity.options.hasOwnProperty('homeassistant') && !entity.options.homeassistant)) + (!entity.definition || entity.zh.interviewing || (entity.options.homeassistant !== undefined && !entity.options.homeassistant)) ) { return; } @@ -1582,11 +1581,11 @@ export default class HomeAssistant extends Extension { delete payload.state_topic_postfix; } - if (!payload.hasOwnProperty('state_topic') || payload.state_topic) { + if (payload.state_topic === undefined || payload.state_topic) { payload.state_topic = stateTopic; } else { /* istanbul ignore else */ - if (payload.hasOwnProperty('state_topic')) { + if (payload.state_topic !== undefined) { delete payload.state_topic; } } @@ -1626,7 +1625,7 @@ export default class HomeAssistant extends Extension { payload.origin = this.discoveryOrigin; // Availability payload (can be disabled by setting `payload.availability = false`). - if (!payload.hasOwnProperty('availability') || payload.availability) { + if (payload.availability === undefined || payload.availability) { payload.availability = [{topic: `${settings.get().mqtt.base_topic}/bridge/state`}]; if (isDevice || isGroup) { @@ -1941,17 +1940,17 @@ export default class HomeAssistant extends Extension { override adjustMessageBeforePublish(entity: Device | Group | Bridge, message: KeyValue): void { this.getDiscovered(entity).mockProperties.forEach((mockProperty) => { - if (!message.hasOwnProperty(mockProperty.property)) { + if (message[mockProperty.property] === undefined) { message[mockProperty.property] = mockProperty.value; } }); // Copy hue -> h, saturation -> s to make homeassistant happy - if (message.hasOwnProperty('color')) { - if (message.color.hasOwnProperty('hue')) { + if (message.color !== undefined) { + if (message.color.hue !== undefined) { message.color.h = message.color.hue; } - if (message.color.hasOwnProperty('saturation')) { + if (message.color.saturation !== undefined) { message.color.s = message.color.saturation; } } @@ -1977,8 +1976,8 @@ export default class HomeAssistant extends Extension { private async publishDeviceTriggerDiscover(device: Device, key: string, value: string, force = false): Promise { const haConfig = device.options.homeassistant; if ( - device.options.hasOwnProperty('homeassistant') && - (haConfig == null || (haConfig.hasOwnProperty('device_automation') && typeof haConfig === 'object' && haConfig.device_automation == null)) + device.options.homeassistant !== undefined && + (haConfig == null || (haConfig.device_automation !== undefined && typeof haConfig === 'object' && haConfig.device_automation == null)) ) { return; } diff --git a/lib/extension/legacy/bridgeLegacy.ts b/lib/extension/legacy/bridgeLegacy.ts index 2eb4fd238..4bf05934d 100644 --- a/lib/extension/legacy/bridgeLegacy.ts +++ b/lib/extension/legacy/bridgeLegacy.ts @@ -1,4 +1,5 @@ import assert from 'assert'; + import bind from 'bind-decorator'; import stringify from 'json-stable-stringify-without-jsonify'; @@ -67,7 +68,7 @@ export default class BridgeLegacy extends Extension { return; } - if (!json.hasOwnProperty('friendly_name') || !json.hasOwnProperty('options')) { + if (json.friendly_name === undefined || json.options === undefined) { logger.error('Invalid JSON message, should contain "friendly_name" and "options"'); return; } @@ -227,11 +228,11 @@ export default class BridgeLegacy extends Extension { try { // json payload with id and friendly_name const json = JSON.parse(message); - if (json.hasOwnProperty('id')) { + if (json.id !== undefined) { id = json.id; name = `group_${id}`; } - if (json.hasOwnProperty('friendly_name')) { + if (json.friendly_name !== undefined) { name = json.friendly_name; } } catch { @@ -321,7 +322,7 @@ export default class BridgeLegacy extends Extension { await cleanup(); } catch (error) { logger.error(`Failed to ${lookup[action][2]} ${entity.name} (${error})`); - // eslint-disable-next-line + logger.error(`See https://www.zigbee2mqtt.io/guide/usage/mqtt_topics_and_messages.html#zigbee2mqtt-bridge-request for more info`); await this.mqtt.publish('bridge/log', stringify({type: `device_${lookup[action][0]}_failed`, message})); @@ -342,7 +343,7 @@ export default class BridgeLegacy extends Extension { const option = match[1]; - if (!this.supportedOptions.hasOwnProperty(option)) { + if (this.supportedOptions[option] === undefined) { return; } diff --git a/lib/extension/legacy/deviceGroupMembership.ts b/lib/extension/legacy/deviceGroupMembership.ts index 8024b3e68..f3b618e7d 100644 --- a/lib/extension/legacy/deviceGroupMembership.ts +++ b/lib/extension/legacy/deviceGroupMembership.ts @@ -1,5 +1,7 @@ /* istanbul ignore file */ + import assert from 'assert'; + import bind from 'bind-decorator'; import Device from '../../model/device'; diff --git a/lib/extension/legacy/report.ts b/lib/extension/legacy/report.ts index f7b0abc40..5e0132807 100644 --- a/lib/extension/legacy/report.ts +++ b/lib/extension/legacy/report.ts @@ -172,11 +172,11 @@ export default class Report extends Extension { // Gledopto devices don't support reporting. if (devicesNotSupportingReporting.includes(device.definition) || device.definition.vendor === 'Gledopto') return false; - if (this.enabled && device.zh.meta.hasOwnProperty('reporting') && device.zh.meta.reporting === reportKey) { + if (this.enabled && device.zh.meta.reporting !== undefined && device.zh.meta.reporting === reportKey) { return false; } - if (!this.enabled && !device.zh.meta.hasOwnProperty('reporting')) { + if (!this.enabled && device.zh.meta.reporting === undefined) { return false; } diff --git a/lib/extension/legacy/softReset.ts b/lib/extension/legacy/softReset.ts index 7df9d0e2e..c58ce3dd4 100644 --- a/lib/extension/legacy/softReset.ts +++ b/lib/extension/legacy/softReset.ts @@ -1,4 +1,5 @@ /* istanbul ignore file */ + import logger from '../../util/logger'; // DEPRECATED import * as settings from '../../util/settings'; diff --git a/lib/extension/networkMap.ts b/lib/extension/networkMap.ts index 414b8425b..c7012ff8e 100644 --- a/lib/extension/networkMap.ts +++ b/lib/extension/networkMap.ts @@ -55,7 +55,7 @@ export default class NetworkMap extends Extension { @bind async onMQTTMessage(data: eventdata.MQTTMessage): Promise { /* istanbul ignore else */ if (this.legacyApi) { - if ((data.topic === this.legacyTopic || data.topic === this.legacyTopicRoutes) && this.supportedFormats.hasOwnProperty(data.message)) { + if ((data.topic === this.legacyTopic || data.topic === this.legacyTopicRoutes) && this.supportedFormats[data.message] !== undefined) { const includeRoutes = data.topic === this.legacyTopicRoutes; const topology = await this.networkScan(includeRoutes); let converted = this.supportedFormats[data.message](topology); @@ -68,7 +68,7 @@ export default class NetworkMap extends Extension { const message = utils.parseJSON(data.message, data.message); try { const type = typeof message === 'object' ? message.type : message; - if (!this.supportedFormats.hasOwnProperty(type)) { + if (this.supportedFormats[type] === undefined) { throw new Error(`Type '${type}' not supported, allowed are: ${Object.keys(this.supportedFormats)}`); } diff --git a/lib/extension/otaUpdate.ts b/lib/extension/otaUpdate.ts index a2d408d23..85587ab41 100644 --- a/lib/extension/otaUpdate.ts +++ b/lib/extension/otaUpdate.ts @@ -1,8 +1,10 @@ import assert from 'assert'; +import path from 'path'; + import bind from 'bind-decorator'; import stringify from 'json-stable-stringify-without-jsonify'; -import path from 'path'; import * as URI from 'uri-js'; + import {Zcl} from 'zigbee-herdsman'; import * as zhc from 'zigbee-herdsman-converters'; @@ -27,7 +29,7 @@ function isValidUrl(url: string): boolean { type UpdateState = 'updating' | 'idle' | 'available'; interface UpdatePayload { update_available?: boolean; - // eslint-disable-next-line camelcase + update: { progress?: number; remaining?: number; @@ -93,9 +95,10 @@ export default class OTAUpdate extends Extension { // with only 10 - 60 seconds inbetween. It doesn't make sense to check for a new update // each time, so this interval can be set by the user. The default is 1,440 minutes (one day). const updateCheckInterval = settings.get().ota.update_check_interval * 1000 * 60; - const check = this.lastChecked.hasOwnProperty(data.device.ieeeAddr) - ? Date.now() - this.lastChecked[data.device.ieeeAddr] > updateCheckInterval - : true; + const check = + this.lastChecked[data.device.ieeeAddr] !== undefined + ? Date.now() - this.lastChecked[data.device.ieeeAddr] > updateCheckInterval + : true; if (!check) return; this.lastChecked[data.device.ieeeAddr] = Date.now(); @@ -185,10 +188,10 @@ export default class OTAUpdate extends Extension { } const message = utils.parseJSON(data.message, data.message); - const ID = (typeof message === 'object' && message.hasOwnProperty('id') ? message.id : message) as string; + const ID = (typeof message === 'object' && message['id'] !== undefined ? message.id : message) as string; const device = this.zigbee.resolveEntity(ID); const type = data.topic.substring(data.topic.lastIndexOf('/') + 1); - const responseData: {id: string; updateAvailable?: boolean; from?: string; to?: string} = {id: ID}; + const responseData: {id: string; updateAvailable?: boolean; from?: KeyValue | null; to?: KeyValue | null} = {id: ID}; let error: string | undefined; let errorStack: string | undefined; @@ -288,8 +291,8 @@ export default class OTAUpdate extends Extension { const to = await this.readSoftwareBuildIDAndDateCode(device); const [fromS, toS] = [stringify(from_), stringify(to)]; logger.info(`Device '${device.name}' was updated from '${fromS}' to '${toS}'`); - responseData.from = from_ ? utils.toSnakeCase(from_) : null; - responseData.to = to ? utils.toSnakeCase(to) : null; + responseData.from = from_ ? utils.toSnakeCaseObject(from_) : null; + responseData.to = to ? utils.toSnakeCaseObject(to) : null; /** * Re-configure after reading software build ID and date code, some devices use a * custom attribute for this (e.g. Develco SMSZB-120) diff --git a/lib/extension/publish.ts b/lib/extension/publish.ts index 44c1a14f3..2c25083d8 100644 --- a/lib/extension/publish.ts +++ b/lib/extension/publish.ts @@ -1,6 +1,8 @@ import assert from 'assert'; + import bind from 'bind-decorator'; import stringify from 'json-stable-stringify-without-jsonify'; + import * as zhc from 'zigbee-herdsman-converters'; import * as philips from 'zigbee-herdsman-converters/lib/philips'; @@ -117,7 +119,7 @@ export default class Publish extends Extension { // ever issue a read here, as we assume the device will properly report changes. // Only do this when the retrieve_state option is enabled for this device. // retrieve_state == deprecated - if (re instanceof Device && result && result.hasOwnProperty('readAfterWriteTime') && re.options.retrieve_state) { + if (re instanceof Device && result && result.readAfterWriteTime !== undefined && re.options.retrieve_state) { const convertGet = converter.convertGet; assert(convertGet !== undefined, 'Converter has `readAfterWriteTime` but no `convertGet`'); setTimeout(() => convertGet(target, key, meta), result.readAfterWriteTime); @@ -131,9 +133,9 @@ export default class Publish extends Extension { * (state) is probably unnecessary. */ if (settings.get().homeassistant) { - const hasColorTemp = message.hasOwnProperty('color_temp'); - const hasColor = message.hasOwnProperty('color'); - const hasBrightness = message.hasOwnProperty('brightness'); + const hasColorTemp = message.color_temp !== undefined; + const hasColor = message.color !== undefined; + const hasBrightness = message.brightness !== undefined; const isOn = entityState.state === 'ON' ? true : false; if (isOn && (hasColorTemp || hasColor) && !hasBrightness) { delete message.state; @@ -254,7 +256,7 @@ export default class Publish extends Extension { endpointOrGroupID = localTarget.ID; } - if (!usedConverters.hasOwnProperty(endpointOrGroupID)) usedConverters[endpointOrGroupID] = []; + if (usedConverters[endpointOrGroupID] === undefined) usedConverters[endpointOrGroupID] = []; /* istanbul ignore next */ // Match any key if the toZigbee converter defines no key. const converter = converters.find((c) => (!c.key || c.key.includes(key)) && (!c.endpoint || c.endpoint == endpointName)); @@ -302,7 +304,7 @@ export default class Publish extends Extension { if (parsedTopic.type === 'set' && converter.convertSet) { logger.debug(`Publishing '${parsedTopic.type}' '${key}' to '${re.name}'`); const result = await converter.convertSet(localTarget, key, value, meta); - const optimistic = !entitySettings.hasOwnProperty('optimistic') || entitySettings.optimistic; + const optimistic = entitySettings.optimistic === undefined || entitySettings.optimistic; if (result && result.state && optimistic) { const msg = result.state; diff --git a/lib/extension/receive.ts b/lib/extension/receive.ts index 29812d113..b3f35df3d 100755 --- a/lib/extension/receive.ts +++ b/lib/extension/receive.ts @@ -1,7 +1,9 @@ import assert from 'assert'; + import bind from 'bind-decorator'; import debounce from 'debounce'; import stringify from 'json-stable-stringify-without-jsonify'; + import * as zhc from 'zigbee-herdsman-converters'; import logger from '../util/logger'; diff --git a/lib/model/device.ts b/lib/model/device.ts index bacc809b1..e995c9f39 100644 --- a/lib/model/device.ts +++ b/lib/model/device.ts @@ -1,7 +1,7 @@ -/* eslint-disable brace-style */ import assert from 'assert'; -import {CustomClusters} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype'; + import * as zhc from 'zigbee-herdsman-converters'; +import {CustomClusters} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype'; import * as settings from '../util/settings'; diff --git a/lib/mqtt.ts b/lib/mqtt.ts index 56b14edd2..bfafe2588 100644 --- a/lib/mqtt.ts +++ b/lib/mqtt.ts @@ -1,7 +1,8 @@ import type {QoS} from 'mqtt-packet'; -import bind from 'bind-decorator'; import fs from 'fs'; + +import bind from 'bind-decorator'; import * as mqtt from 'mqtt'; import logger from './util/logger'; @@ -74,14 +75,14 @@ export default class MQTT { options.clientId = mqttSettings.client_id; } - if (mqttSettings.hasOwnProperty('reject_unauthorized') && !mqttSettings.reject_unauthorized) { + if (mqttSettings.reject_unauthorized !== undefined && !mqttSettings.reject_unauthorized) { logger.debug(`MQTT reject_unauthorized set false, ignoring certificate warnings.`); options.rejectUnauthorized = false; } return new Promise((resolve, reject) => { this.client = mqtt.connect(mqttSettings.server, options); - // @ts-ignore https://github.com/Koenkk/zigbee2mqtt/issues/9822 + // https://github.com/Koenkk/zigbee2mqtt/issues/9822 this.client.stream.setMaxListeners(0); this.eventBus.onPublishAvailability(this, this.publishStateOnline); diff --git a/lib/state.ts b/lib/state.ts index 5532cf134..84f38ae79 100644 --- a/lib/state.ts +++ b/lib/state.ts @@ -1,4 +1,5 @@ import fs from 'fs'; + import objectAssignDeep from 'object-assign-deep'; import data from './util/data'; @@ -88,7 +89,7 @@ class State { } exists(entity: Device | Group): boolean { - return this.state.hasOwnProperty(entity.ID); + return this.state[entity.ID] !== undefined; } get(entity: Group | Device): KeyValue { diff --git a/lib/types/types.d.ts b/lib/types/types.d.ts index 19cac675c..97bc1f52f 100644 --- a/lib/types/types.d.ts +++ b/lib/types/types.d.ts @@ -1,4 +1,3 @@ -/* eslint-disable camelcase */ import type TypeEventBus from 'lib/eventBus'; import type TypeExtension from 'lib/extension/extension'; import type TypeDevice from 'lib/model/device'; @@ -7,17 +6,17 @@ import type TypeMQTT from 'lib/mqtt'; import type TypeState from 'lib/state'; import type TypeZigbee from 'lib/zigbee'; import type {QoS} from 'mqtt-packet'; +import type * as zhc from 'zigbee-herdsman-converters'; import type { - NetworkParameters as ZHNetworkParameters, CoordinatorVersion as ZHCoordinatorVersion, LQI as ZHLQI, + NetworkParameters as ZHNetworkParameters, RoutingTable as ZHRoutingTable, RoutingTableEntry as ZHRoutingTableEntry, } from 'zigbee-herdsman/dist/adapter/tstype'; import type * as ZHEvents from 'zigbee-herdsman/dist/controller/events'; -import type {Device as ZHDevice, Group as ZHGroup, Endpoint as ZHEndpoint} from 'zigbee-herdsman/dist/controller/model'; +import type {Device as ZHDevice, Endpoint as ZHEndpoint, Group as ZHGroup} from 'zigbee-herdsman/dist/controller/model'; import type {Cluster as ZHCluster, FrameControl as ZHFrameControl} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype'; -import type * as zhc from 'zigbee-herdsman-converters'; import {LogLevel} from 'lib/util/settings'; @@ -34,8 +33,7 @@ declare global { type Extension = TypeExtension; // Types - // eslint-disable-next-line @typescript-eslint/no-explicit-any - type ExternalDefinition = zhc.Definition & {homeassistant: any}; + type ExternalDefinition = zhc.Definition & {homeassistant: unknown}; interface MQTTResponse { data: KeyValue; status: 'error' | 'ok'; @@ -114,7 +112,6 @@ declare global { } // Settings - // eslint-disable camelcase interface Settings { homeassistant?: { discovery_topic: string; diff --git a/lib/types/zigbee2mqtt-frontend.d.ts b/lib/types/zigbee2mqtt-frontend.d.ts index c0e3ff84e..02bb80f0b 100644 --- a/lib/types/zigbee2mqtt-frontend.d.ts +++ b/lib/types/zigbee2mqtt-frontend.d.ts @@ -3,7 +3,7 @@ declare module 'zigbee2mqtt-frontend' { } declare module 'connect-gzip-static' { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - export type RequestHandler = (req: any, res: any) => void; + import {IncomingMessage, ServerResponse} from 'http'; + export type RequestHandler = (req: IncomingMessage, res: ServerResponse, finalhandler: (err: unknown) => void) => void; export default function gzipStatic(root: string, options?: Record): RequestHandler; } diff --git a/lib/util/logger.ts b/lib/util/logger.ts index 032bb3dbe..055972a88 100644 --- a/lib/util/logger.ts +++ b/lib/util/logger.ts @@ -1,8 +1,9 @@ import assert from 'assert'; import fs from 'fs'; +import path from 'path'; + import fx from 'mkdir-recursive'; import moment from 'moment'; -import path from 'path'; import {rimrafSync} from 'rimraf'; import winston from 'winston'; @@ -108,7 +109,7 @@ class Logger { /* istanbul ignore next */ if (this.output.includes('syslog')) { logging += `, syslog`; - // eslint-disable-next-line + // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unused-expressions require('winston-syslog').Syslog; const options: KeyValue = { @@ -117,7 +118,7 @@ class Logger { ...settings.get().advanced.log_syslog, }; - if (options.hasOwnProperty('type')) { + if (options['type'] !== undefined) { options.type = options.type.toString(); } diff --git a/lib/util/settings.ts b/lib/util/settings.ts index 872b51c4f..41f9f0451 100644 --- a/lib/util/settings.ts +++ b/lib/util/settings.ts @@ -1,11 +1,13 @@ +import path from 'path'; + import Ajv, {ValidateFunction} from 'ajv'; import objectAssignDeep from 'object-assign-deep'; -import path from 'path'; import data from './data'; import schemaJson from './settings.schema.json'; import utils from './utils'; import yaml, {YAMLFileException} from './yaml'; + export let schema: KeyValue = schemaJson; schema = {}; @@ -143,63 +145,66 @@ function loadSettingsWithDefaults(): void { 'homeassistant_legacy_entity_attributes', 'homeassistant_status_topic', ]) { - // @ts-expect-error + // @ts-expect-error ignore typing if (_settingsWithDefaults.advanced[key] !== undefined) { - // @ts-expect-error + // @ts-expect-error ignore typing sLegacy[key.replace('homeassistant_', '')] = _settingsWithDefaults.advanced[key]; } } } const s = typeof _settingsWithDefaults.homeassistant === 'object' ? _settingsWithDefaults.homeassistant : {}; - // @ts-expect-error + // @ts-expect-error ignore typing _settingsWithDefaults.homeassistant = {}; - // @ts-expect-error + // @ts-expect-error ignore typing objectAssignDeep(_settingsWithDefaults.homeassistant, defaults, sLegacy, s); } if (_settingsWithDefaults.availability || _settingsWithDefaults.advanced?.availability_timeout) { const defaults = {}; const s = typeof _settingsWithDefaults.availability === 'object' ? _settingsWithDefaults.availability : {}; - // @ts-expect-error + // @ts-expect-error ignore typing _settingsWithDefaults.availability = {}; - // @ts-expect-error + // @ts-expect-error ignore typing objectAssignDeep(_settingsWithDefaults.availability, defaults, s); } if (_settingsWithDefaults.frontend) { const defaults = {port: 8080, auth_token: false}; const s = typeof _settingsWithDefaults.frontend === 'object' ? _settingsWithDefaults.frontend : {}; - // @ts-expect-error + // @ts-expect-error ignore typing _settingsWithDefaults.frontend = {}; - // @ts-expect-error + // @ts-expect-error ignore typing objectAssignDeep(_settingsWithDefaults.frontend, defaults, s); } - if (_settings.advanced?.hasOwnProperty('baudrate') && _settings.serial?.baudrate == null) { - // @ts-expect-error + // @ts-expect-error ignore typing + if (_settings.advanced?.baudrate !== undefined && _settings.serial?.baudrate == null) { + // @ts-expect-error ignore typing _settingsWithDefaults.serial.baudrate = _settings.advanced.baudrate; } - if (_settings.advanced?.hasOwnProperty('rtscts') && _settings.serial?.rtscts == null) { - // @ts-expect-error + // @ts-expect-error ignore typing + if (_settings.advanced?.rtscts !== undefined && _settings.serial?.rtscts == null) { + // @ts-expect-error ignore typing _settingsWithDefaults.serial.rtscts = _settings.advanced.rtscts; } - if (_settings.advanced?.hasOwnProperty('ikea_ota_use_test_url') && _settings.ota?.ikea_ota_use_test_url == null) { - // @ts-expect-error + // @ts-expect-error ignore typing + if (_settings.advanced?.ikea_ota_use_test_url !== undefined && _settings.ota?.ikea_ota_use_test_url == null) { + // @ts-expect-error ignore typing _settingsWithDefaults.ota.ikea_ota_use_test_url = _settings.advanced.ikea_ota_use_test_url; } - // @ts-expect-error - if (_settings.experimental?.hasOwnProperty('transmit_power') && _settings.advanced?.transmit_power == null) { - // @ts-expect-error + // @ts-expect-error ignore typing + if (_settings.experimental?.transmit_power !== undefined && _settings.advanced?.transmit_power == null) { + // @ts-expect-error ignore typing _settingsWithDefaults.advanced.transmit_power = _settings.experimental.transmit_power; } - // @ts-expect-error - if (_settings.experimental?.hasOwnProperty('output') && _settings.advanced?.output == null) { - // @ts-expect-error + // @ts-expect-error ignore typing + if (_settings.experimental?.output !== undefined && _settings.advanced?.output == null) { + // @ts-expect-error ignore typing _settingsWithDefaults.advanced.output = _settings.experimental.output; } @@ -207,15 +212,15 @@ function loadSettingsWithDefaults(): void { _settingsWithDefaults.advanced.log_level = 'warning'; } - // @ts-expect-error + // @ts-expect-error ignore typing if (_settingsWithDefaults.ban) { - // @ts-expect-error + // @ts-expect-error ignore typing _settingsWithDefaults.blocklist.push(..._settingsWithDefaults.ban); } - // @ts-expect-error + // @ts-expect-error ignore typing if (_settingsWithDefaults.whitelist) { - // @ts-expect-error + // @ts-expect-error ignore typing _settingsWithDefaults.passlist.push(..._settingsWithDefaults.whitelist); } } @@ -377,14 +382,14 @@ function read(): Settings { applyEnvironmentVariables(s); // Read !secret MQTT username and password if set - // eslint-disable-next-line - const interpretValue = (value: any): any => { - const ref = parseValueRef(value); - if (ref) { - return yaml.read(data.joinPath(ref.filename))[ref.key]; - } else { - return value; + const interpretValue = (value: T): T => { + if (typeof value === 'string') { + const ref = parseValueRef(value); + if (ref) { + return yaml.read(data.joinPath(ref.filename))[ref.key]; + } } + return value; }; if (s.mqtt?.user) { @@ -410,7 +415,6 @@ function read(): Settings { // Read devices/groups configuration from separate file if specified. const readDevicesOrGroups = (type: 'devices' | 'groups'): void => { if (typeof s[type] === 'string' || (Array.isArray(s[type]) && Array(s[type]).length > 0)) { - /* eslint-disable-line */ const files: string[] = Array.isArray(s[type]) ? s[type] : [s[type]]; s[type] = {}; for (const file of files) { @@ -439,30 +443,30 @@ function applyEnvironmentVariables(settings: Partial): void { if (envVariable) { const setting = path.reduce((acc, val) => { - // @ts-expect-error + // @ts-expect-error ignore typing acc[val] = acc[val] || {}; - // @ts-expect-error + // @ts-expect-error ignore typing return acc[val]; }, settings); if (type.indexOf('object') >= 0 || type.indexOf('array') >= 0) { try { - // @ts-expect-error + // @ts-expect-error ignore typing setting[key] = JSON.parse(envVariable); } catch { - // @ts-expect-error + // @ts-expect-error ignore typing setting[key] = envVariable; } } else if (type.indexOf('number') >= 0) { - // @ts-expect-error + // @ts-expect-error ignore typing setting[key] = (envVariable as unknown as number) * 1; } else if (type.indexOf('boolean') >= 0) { - // @ts-expect-error + // @ts-expect-error ignore typing setting[key] = envVariable.toLowerCase() === 'true'; } else { /* istanbul ignore else */ if (type.indexOf('string') >= 0) { - // @ts-expect-error + // @ts-expect-error ignore typing setting[key] = envVariable; } } @@ -523,7 +527,7 @@ export function set(path: string[], value: string | number | boolean | KeyValue) export function apply(settings: Record): boolean { getInternalSettings(); // Ensure _settings is initialized. - // @ts-expect-error + // @ts-expect-error ignore typing const newSettings = objectAssignDeep.noMutate(_settings, settings); utils.removeNullPropertiesFromObject(newSettings, NULLABLE_SETTINGS); ajvSetting(newSettings); @@ -678,14 +682,14 @@ export function addGroup(name: string, ID?: string): GroupOptions { // look for free ID ID = '1'; - while (settings.groups.hasOwnProperty(ID)) { + while (settings.groups[ID]) { ID = (Number.parseInt(ID) + 1).toString(); } } else { // ensure provided ID is not in use ID = ID.toString(); - if (settings.groups.hasOwnProperty(ID)) { + if (settings.groups[ID]) { throw new Error(`Group ID '${ID}' is already in use`); } } diff --git a/lib/util/utils.ts b/lib/util/utils.ts index f599f61cb..9513ab5a3 100644 --- a/lib/util/utils.ts +++ b/lib/util/utils.ts @@ -1,12 +1,13 @@ import type * as zhc from 'zigbee-herdsman-converters'; import assert from 'assert'; -import equals from 'fast-deep-equal/es6'; import fs from 'fs'; -import humanizeDuration from 'humanize-duration'; import path from 'path'; import vm from 'vm'; +import equals from 'fast-deep-equal/es6'; +import humanizeDuration from 'humanize-duration'; + import data from './data'; // construct a local ISO8601 string (instead of UTC-based) @@ -101,7 +102,7 @@ function objectIsEmpty(object: object): boolean { function objectHasProperties(object: {[s: string]: unknown}, properties: string[]): boolean { for (const property of properties) { - if (!object.hasOwnProperty(property)) { + if (object[property] === undefined) { return false; } } @@ -120,7 +121,7 @@ function equalsPartial(object: KeyValue, expected: KeyValue): boolean { } function getObjectProperty(object: KeyValue, key: string, defaultValue: unknown): unknown { - return object && object.hasOwnProperty(key) ? object[key] : defaultValue; + return object && object[key] !== undefined ? object[key] : defaultValue; } function getResponse(request: KeyValue | string, data: KeyValue, error?: string): MQTTResponse { @@ -130,7 +131,7 @@ function getResponse(request: KeyValue | string, data: KeyValue, error?: string) response.error = error; } - if (typeof request === 'object' && request.hasOwnProperty('transaction')) { + if (typeof request === 'object' && request['transaction'] !== undefined) { response.transaction = request.transaction; } @@ -210,24 +211,24 @@ function toNetworkAddressHex(value: number): string { return `0x${'0'.repeat(4 - hex.length)}${hex}`; } -// eslint-disable-next-line -function toSnakeCase(value: string | KeyValue): any { - if (typeof value === 'object') { - value = {...value}; - for (const key of Object.keys(value)) { - const keySnakeCase = toSnakeCase(key); - if (key !== keySnakeCase) { - value[keySnakeCase] = value[key]; - delete value[key]; - } +function toSnakeCaseObject(value: KeyValue): KeyValue { + value = {...value}; + for (const key of Object.keys(value)) { + const keySnakeCase = toSnakeCaseString(key); + assert(typeof keySnakeCase === 'string'); + if (key !== keySnakeCase) { + value[keySnakeCase] = value[key]; + delete value[key]; } - return value; - } else { - return value - .replace(/\.?([A-Z])/g, (x, y) => '_' + y.toLowerCase()) - .replace(/^_/, '') - .replace('_i_d', '_id'); } + return value; +} + +function toSnakeCaseString(value: string): string { + return value + .replace(/\.?([A-Z])/g, (x, y) => '_' + y.toLowerCase()) + .replace(/^_/, '') + .replace('_i_d', '_id'); } function charRange(start: string, stop: string): number[] { @@ -447,7 +448,8 @@ export default { loadModuleFromFile, removeNullPropertiesFromObject, toNetworkAddressHex, - toSnakeCase, + toSnakeCaseString, + toSnakeCaseObject, isZHEndpoint, isZHGroup, hours, diff --git a/lib/util/yaml.ts b/lib/util/yaml.ts index 1b2fb4386..2c77db2c4 100644 --- a/lib/util/yaml.ts +++ b/lib/util/yaml.ts @@ -1,5 +1,6 @@ -import equals from 'fast-deep-equal/es6'; import fs from 'fs'; + +import equals from 'fast-deep-equal/es6'; import yaml, {YAMLException} from 'js-yaml'; export class YAMLFileException extends YAMLException { diff --git a/lib/zigbee.ts b/lib/zigbee.ts index ea15cdaa9..8ad537d56 100644 --- a/lib/zigbee.ts +++ b/lib/zigbee.ts @@ -1,6 +1,8 @@ -import bind from 'bind-decorator'; import {randomInt} from 'crypto'; + +import bind from 'bind-decorator'; import stringify from 'json-stable-stringify-without-jsonify'; + import {Controller} from 'zigbee-herdsman'; import * as ZHEvents from 'zigbee-herdsman/dist/controller/events'; @@ -114,7 +116,7 @@ export default class Zigbee { logger.debug(() => `Received Zigbee message from '${device.name}', type '${data.type}', ` + `cluster '${data.cluster}', data '${stringify(data.data)}' from endpoint ${data.endpoint.ID}` + - (data.hasOwnProperty('groupID') ? ` with groupID ${data.groupID}` : ``) + + (data['groupID'] !== undefined ? ` with groupID ${data.groupID}` : ``) + (device.zh.type === 'Coordinator' ? `, ignoring since it is from coordinator` : ``), ); if (device.zh.type === 'Coordinator') return; diff --git a/package-lock.json b/package-lock.json index 2805cd5fa..a1e5f7586 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "zigbee2mqtt", - "version": "1.40.1", + "version": "1.40.1-dev", "license": "GPL-3.0", "dependencies": { "ajv": "^8.17.1", @@ -14,7 +14,7 @@ "connect-gzip-static": "3.0.1", "debounce": "^2.1.0", "fast-deep-equal": "^3.1.3", - "finalhandler": "^1.2.0", + "finalhandler": "^1.3.0", "git-last-commit": "^1.0.1", "humanize-duration": "^3.32.1", "js-yaml": "^4.1.0", @@ -33,7 +33,7 @@ "winston-transport": "^4.7.1", "ws": "^8.18.0", "zigbee-herdsman": "0.57.3", - "zigbee-herdsman-converters": "20.12.1", + "zigbee-herdsman-converters": "20.13.0", "zigbee2mqtt-frontend": "0.7.4" }, "bin": { @@ -44,25 +44,26 @@ "@babel/plugin-proposal-decorators": "^7.24.7", "@babel/preset-env": "^7.25.4", "@babel/preset-typescript": "^7.24.7", + "@eslint/js": "^9.10.0", + "@ianvs/prettier-plugin-sort-imports": "^4.3.1", + "@types/eslint__js": "^8.42.3", "@types/finalhandler": "^1.2.3", "@types/humanize-duration": "^3.27.4", "@types/jest": "^29.5.12", "@types/js-yaml": "^4.0.9", - "@types/node": "^22.5.1", + "@types/node": "^22.5.4", "@types/object-assign-deep": "^0.4.3", "@types/readable-stream": "4.0.15", + "@types/sd-notify": "^2.8.2", "@types/ws": "8.5.12", - "@typescript-eslint/eslint-plugin": "^8.3.0", - "@typescript-eslint/parser": "^8.3.0", "babel-jest": "^29.7.0", - "eslint": "^8.57.0", + "eslint": "^9.9.1", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-jest": "^28.8.1", - "eslint-plugin-perfectionist": "^3.3.0", "jest": "^29.7.0", "prettier": "^3.3.3", "tmp": "^0.2.3", - "typescript": "^5.5.4" + "typescript": "^5.5.4", + "typescript-eslint": "^8.4.0" }, "engines": { "node": "^18 || ^20 || ^22" @@ -2059,6 +2060,19 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", @@ -2069,17 +2083,32 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -2087,7 +2116,7 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -2110,28 +2139,14 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2144,40 +2159,37 @@ "dev": true, "license": "MIT" }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz", + "integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==", "dev": true, "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz", + "integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@folder/readdir": { @@ -2194,46 +2206,6 @@ "node": ">=10" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -2248,13 +2220,43 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", "dev": true, - "license": "BSD-3-Clause" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@ianvs/prettier-plugin-sort-imports": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@ianvs/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.1.tgz", + "integrity": "sha512-ZHwbyjkANZOjaBm3ZosADD2OUYGFzQGxfy67HmGZU94mHqe7g1LCMA7YYKB1Cq+UTPCBqlAYapY0KXAjKEw8Sg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/core": "^7.24.0", + "@babel/generator": "^7.23.6", + "@babel/parser": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0", + "semver": "^7.5.2" + }, + "peerDependencies": { + "@vue/compiler-sfc": "2.7.x || 3.x", + "prettier": "2 || 3" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + } + } }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -3236,6 +3238,12 @@ } } }, + "node_modules/@serialport/bindings-cpp/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" + }, "node_modules/@serialport/bindings-interface": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.2.tgz", @@ -3317,6 +3325,12 @@ } } }, + "node_modules/@serialport/stream/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -3389,6 +3403,34 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint__js": { + "version": "8.42.3", + "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz", + "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/finalhandler": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@types/finalhandler/-/finalhandler-1.2.3.tgz", @@ -3461,10 +3503,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { - "version": "22.5.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.1.tgz", - "integrity": "sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==", + "version": "22.5.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -3487,6 +3536,13 @@ "safe-buffer": "~5.1.1" } }, + "node_modules/@types/sd-notify": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/@types/sd-notify/-/sd-notify-2.8.2.tgz", + "integrity": "sha512-LVWtuGvzso9z3N89NISzseq8RVHkEeg2h275370yQYx8/CoNaV2NnG17TTjDavy2FrmcUBFaR6OymlPQjqfb2g==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -3527,17 +3583,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.3.0.tgz", - "integrity": "sha512-FLAIn63G5KH+adZosDYiutqkOkYEx0nvcwNNfJAf+c7Ae/H35qWwTYvPZUKFj5AS+WfHG/WJJfWnDnyNUlp8UA==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.4.0.tgz", + "integrity": "sha512-rg8LGdv7ri3oAlenMACk9e+AR4wUV0yrrG+XKsGKOK0EVgeEDqurkXMPILG2836fW4ibokTB5v4b6Z9+GYQDEw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.3.0", - "@typescript-eslint/type-utils": "8.3.0", - "@typescript-eslint/utils": "8.3.0", - "@typescript-eslint/visitor-keys": "8.3.0", + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/type-utils": "8.4.0", + "@typescript-eslint/utils": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3561,16 +3617,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.3.0.tgz", - "integrity": "sha512-h53RhVyLu6AtpUzVCYLPhZGL5jzTD9fZL+SYf/+hYOx2bDkyQXztXSc4tbvKYHzfMXExMLiL9CWqJmVz6+78IQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.4.0.tgz", + "integrity": "sha512-NHgWmKSgJk5K9N16GIhQ4jSobBoJwrmURaLErad0qlLjrpP5bECYg+wxVTGlGZmJbU03jj/dfnb6V9bw+5icsA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.3.0", - "@typescript-eslint/types": "8.3.0", - "@typescript-eslint/typescript-estree": "8.3.0", - "@typescript-eslint/visitor-keys": "8.3.0", + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/typescript-estree": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "debug": "^4.3.4" }, "engines": { @@ -3590,14 +3646,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.3.0.tgz", - "integrity": "sha512-mz2X8WcN2nVu5Hodku+IR8GgCOl4C0G/Z1ruaWN4dgec64kDBabuXyPAr+/RgJtumv8EEkqIzf3X2U5DUKB2eg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.4.0.tgz", + "integrity": "sha512-n2jFxLeY0JmKfUqy3P70rs6vdoPjHK8P/w+zJcV3fk0b0BwRXC/zxRTEnAsgYT7MwdQDt/ZEbtdzdVC+hcpF0A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.3.0", - "@typescript-eslint/visitor-keys": "8.3.0" + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3608,14 +3664,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.3.0.tgz", - "integrity": "sha512-wrV6qh//nLbfXZQoj32EXKmwHf4b7L+xXLrP3FZ0GOUU72gSvLjeWUl5J5Ue5IwRxIV1TfF73j/eaBapxx99Lg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.4.0.tgz", + "integrity": "sha512-pu2PAmNrl9KX6TtirVOrbLPLwDmASpZhK/XU7WvoKoCUkdtq9zF7qQ7gna0GBZFN0hci0vHaSusiL2WpsQk37A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.3.0", - "@typescript-eslint/utils": "8.3.0", + "@typescript-eslint/typescript-estree": "8.4.0", + "@typescript-eslint/utils": "8.4.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -3633,9 +3689,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.3.0.tgz", - "integrity": "sha512-y6sSEeK+facMaAyixM36dQ5NVXTnKWunfD1Ft4xraYqxP0lC0POJmIaL/mw72CUMqjY9qfyVfXafMeaUj0noWw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.4.0.tgz", + "integrity": "sha512-T1RB3KQdskh9t3v/qv7niK6P8yvn7ja1mS7QK7XfRVL6wtZ8/mFs/FHf4fKvTA0rKnqnYxl/uHFNbnEt0phgbw==", "dev": true, "license": "MIT", "engines": { @@ -3647,14 +3703,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.3.0.tgz", - "integrity": "sha512-Mq7FTHl0R36EmWlCJWojIC1qn/ZWo2YiWYc1XVtasJ7FIgjo0MVv9rZWXEE7IK2CGrtwe1dVOxWwqXUdNgfRCA==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.4.0.tgz", + "integrity": "sha512-kJ2OIP4dQw5gdI4uXsaxUZHRwWAGpREJ9Zq6D5L0BweyOrWsL6Sz0YcAZGWhvKnH7fm1J5YFE1JrQL0c9dd53A==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.3.0", - "@typescript-eslint/visitor-keys": "8.3.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3675,17 +3731,43 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/utils": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.3.0.tgz", - "integrity": "sha512-F77WwqxIi/qGkIGOGXNBLV7nykwfjLsdauRB/DOFPdv6LTF3BHHkBpq81/b5iMPSF055oO2BiivDJV4ChvNtXA==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.4.0.tgz", + "integrity": "sha512-swULW8n1IKLjRAgciCkTCafyTHHfwVQFt8DovmaF69sKbOxTSFMmIZaSHjqO9i/RV0wIblaawhzvtva8Nmm7lQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.3.0", - "@typescript-eslint/types": "8.3.0", - "@typescript-eslint/typescript-estree": "8.3.0" + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/typescript-estree": "8.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3699,13 +3781,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.3.0.tgz", - "integrity": "sha512-RmZwrTbQ9QveF15m/Cl28n0LXD6ea2CjkhH5rQ55ewz3H24w+AMCJHPVYaZ8/0HoG8Z3cLLFFycRXxeO2tz9FA==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.4.0.tgz", + "integrity": "sha512-zTQD6WLNTre1hj5wp09nBIDiOc2U5r/qmzo7wxPn4ZgAjHql09EofqhF9WF+fZHzL5aCyaIpPcT2hyxl73kr9A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.3.0", + "@typescript-eslint/types": "8.4.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -3716,12 +3798,18 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "ISC" + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, "node_modules/abort-controller": { "version": "3.0.0", @@ -4217,12 +4305,14 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { @@ -4341,9 +4431,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001655", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz", - "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==", + "version": "1.0.30001658", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001658.tgz", + "integrity": "sha512-N2YVqWbJELVdrnsW5p+apoQyYt51aBMSsBZki1XZEfeBCexcM/sf4xiAHcXQBkuOwJBXtWF7aW1sYX6tKebPHw==", "dev": true, "funding": [ { @@ -4403,9 +4493,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.0.tgz", - "integrity": "sha512-N1NGmowPlGBLsOZLPvm48StN04V4YvQRL0i6b7ctrVY3epjP/ct7hFLOItz6pDIvRjwpfPxi52a2UWV2ziir8g==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", "dev": true, "license": "MIT" }, @@ -4706,12 +4796,12 @@ } }, "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -4814,19 +4904,6 @@ "node": ">=6" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4840,9 +4917,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", - "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==", + "version": "1.5.18", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.18.tgz", + "integrity": "sha512-1OfuVACu+zKlmjsNdcJuVQuVE61sZOLbNM4JAQ1Rvh6EOj0/EUKhMJjRH73InPlXSh8HIJk1cVZ8pyOV/FMdUQ==", "dev": true, "license": "ISC" }, @@ -4917,44 +4994,40 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz", + "integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.10.0", + "@eslint/plugin-kit": "^0.1.0", "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.0.2", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.1.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", @@ -4966,10 +5039,18 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-config-prettier": { @@ -4985,89 +5066,10 @@ "eslint": ">=7.0.0" } }, - "node_modules/eslint-plugin-jest": { - "version": "28.8.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.8.1.tgz", - "integrity": "sha512-G46XMyYu6PtSNJUkQ0hsPjzXYpzq/O4vpCciMizTKRJG8kNsRreGoMRDG6H9FIB/xVgfFuclVnuX4XRvFUzrZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "engines": { - "node": "^16.10.0 || ^18.12.0 || >=20.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0", - "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", - "jest": "*" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-perfectionist": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-3.3.0.tgz", - "integrity": "sha512-sGgShkEqDBqIZ3WlenGHwLe1cl3vHKTfeh9b1XXAamaxSC7AY4Os0jdNCXnGJW4l0TlpismT5t2r7CXY7sfKlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "^8.3.0", - "@typescript-eslint/utils": "^8.3.0", - "minimatch": "^10.0.1", - "natural-compare-lite": "^1.4.0" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "astro-eslint-parser": "^1.0.2", - "eslint": ">=8.0.0", - "svelte": ">=3.0.0", - "svelte-eslint-parser": "^0.41.0", - "vue-eslint-parser": ">=9.0.0" - }, - "peerDependenciesMeta": { - "astro-eslint-parser": { - "optional": true - }, - "svelte": { - "optional": true - }, - "svelte-eslint-parser": { - "optional": true - }, - "vue-eslint-parser": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-perfectionist/node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", + "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5075,20 +5077,20 @@ "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -5127,17 +5129,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5205,22 +5196,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -5254,19 +5229,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/eslint/node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", @@ -5296,32 +5258,19 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", + "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -5566,16 +5515,16 @@ "license": "MIT" }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/file-uri-to-path": { @@ -5599,9 +5548,9 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.0.tgz", + "integrity": "sha512-bmwQPHFq/qiWp9CbNbCQU73klT+i5qwP/0tah3MGHp26vUt2YV4WkdtXRqOZo+H+4m38k8epFHOvO4BRuAuohw==", "license": "MIT", "dependencies": { "debug": "2.6.9", @@ -5646,35 +5595,17 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=16" } }, "node_modules/flatted": { @@ -5691,9 +5622,9 @@ "license": "MIT" }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", @@ -5877,30 +5808,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -8350,19 +8257,16 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, "node_modules/minimist": { @@ -8505,9 +8409,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/multicast-dns": { @@ -8537,13 +8441,6 @@ "dev": true, "license": "MIT" }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true, - "license": "MIT" - }, "node_modules/node-addon-api": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz", @@ -8843,18 +8740,18 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz", - "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", + "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==", "license": "ISC", "engines": { "node": "20 || >=22" } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "dev": true, "license": "ISC" }, @@ -9255,6 +9152,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/rimraf/node_modules/glob": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", @@ -9408,12 +9314,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", @@ -9759,30 +9659,6 @@ "node": ">=8" } }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/text-decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", @@ -9944,6 +9820,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.4.0.tgz", + "integrity": "sha512-67qoc3zQZe3CAkO0ua17+7aCLI0dU+sSQd1eKPGq06QE4rfQjstVXR6woHO5qQvGUa550NfGckT4tzh3b3c8Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.4.0", + "@typescript-eslint/parser": "8.4.0", + "@typescript-eslint/utils": "8.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", @@ -10455,9 +10355,9 @@ } }, "node_modules/zigbee-herdsman-converters": { - "version": "20.12.1", - "resolved": "https://registry.npmjs.org/zigbee-herdsman-converters/-/zigbee-herdsman-converters-20.12.1.tgz", - "integrity": "sha512-ylV6hfbe/+AIl5cVlGd3KI4s5OE7xZ1IEsEftxb/0atwS+ldToDqfB31dq256XMFNZU/Atet1H90OAwXS2IHrA==", + "version": "20.13.0", + "resolved": "https://registry.npmjs.org/zigbee-herdsman-converters/-/zigbee-herdsman-converters-20.13.0.tgz", + "integrity": "sha512-eswCRUi2yZ/FRpfFxOJcljT2cpscyV6FmI7vEfyOKD4DmD+2oNVdq81tck6Q3zC+EwtrA5daUxCvmFdT4ssZlw==", "license": "MIT", "dependencies": { "axios": "^1.7.7", diff --git a/package.json b/package.json index 28f498340..eb11f7773 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "scripts": { "build": "tsc && node index.js writehash", "build-watch": "tsc --watch", - "eslint": "eslint lib/ --max-warnings=0", + "eslint": "eslint --max-warnings=0", "pretty:write": "prettier --write .", "pretty:check": "prettier --check .", "start": "node index.js", @@ -42,7 +42,7 @@ "connect-gzip-static": "3.0.1", "debounce": "^2.1.0", "fast-deep-equal": "^3.1.3", - "finalhandler": "^1.2.0", + "finalhandler": "^1.3.0", "git-last-commit": "^1.0.1", "humanize-duration": "^3.32.1", "js-yaml": "^4.1.0", @@ -61,7 +61,7 @@ "winston-transport": "^4.7.1", "ws": "^8.18.0", "zigbee-herdsman": "0.57.3", - "zigbee-herdsman-converters": "20.12.1", + "zigbee-herdsman-converters": "20.13.0", "zigbee2mqtt-frontend": "0.7.4" }, "devDependencies": { @@ -69,25 +69,26 @@ "@babel/plugin-proposal-decorators": "^7.24.7", "@babel/preset-env": "^7.25.4", "@babel/preset-typescript": "^7.24.7", + "@eslint/js": "^9.10.0", + "@ianvs/prettier-plugin-sort-imports": "^4.3.1", + "@types/eslint__js": "^8.42.3", "@types/finalhandler": "^1.2.3", "@types/humanize-duration": "^3.27.4", "@types/jest": "^29.5.12", "@types/js-yaml": "^4.0.9", - "@types/node": "^22.5.1", + "@types/node": "^22.5.4", "@types/object-assign-deep": "^0.4.3", "@types/readable-stream": "4.0.15", + "@types/sd-notify": "^2.8.2", "@types/ws": "8.5.12", - "@typescript-eslint/eslint-plugin": "^8.3.0", - "@typescript-eslint/parser": "^8.3.0", "babel-jest": "^29.7.0", - "eslint": "^8.57.0", + "eslint": "^9.9.1", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-jest": "^28.8.1", - "eslint-plugin-perfectionist": "^3.3.0", "jest": "^29.7.0", "prettier": "^3.3.3", "tmp": "^0.2.3", - "typescript": "^5.5.4" + "typescript": "^5.5.4", + "typescript-eslint": "^8.4.0" }, "overrides": { "zigbee-herdsman-converters": { diff --git a/test/availability.test.js b/test/availability.test.js index 3294a88f6..118f1badc 100644 --- a/test/availability.test.js +++ b/test/availability.test.js @@ -1,14 +1,13 @@ -import data from './stub/data'; -import logger from './stub/logger'; -import MQTT from './stub/mqtt'; -import zigbeeHerdsman from './stub/zigbeeHerdsman'; - -import utils from '../lib/util/utils'; -import * as settings from '../lib/util/settings'; -import Controller from '../lib/controller'; -import Availability from '../lib/extension/availability'; -import flushPromises from './lib/flushPromises'; -import stringify from 'json-stable-stringify-without-jsonify'; +const data = require('./stub/data'); +const logger = require('./stub/logger'); +const zigbeeHerdsman = require('./stub/zigbeeHerdsman'); +const MQTT = require('./stub/mqtt'); +const settings = require('../lib/util/settings'); +const Controller = require('../lib/controller'); +const flushPromises = require('./lib/flushPromises'); +const Availability = require('../lib/extension/availability').default; +const stringify = require('json-stable-stringify-without-jsonify'); +const utils = require('../lib/util/utils').default; const mocks = [MQTT.publish, logger.warning, logger.info]; const devices = zigbeeHerdsman.devices;