From 426a80104213f704130e0e213a9f334eec4954cc Mon Sep 17 00:00:00 2001 From: Ben <43026681+bwp91@users.noreply.github.com> Date: Fri, 8 May 2026 20:29:11 +0100 Subject: [PATCH 01/14] chore: dependency updates --- CHANGELOG.md | 12 + package-lock.json | 808 ++++++++++++++++++++++++++-------------------- package.json | 22 +- 3 files changed, 482 insertions(+), 360 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5bf0a8e1..839dfd124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. This project tries to adhere to [Semantic Versioning](http://semver.org/). +## v2.1.7 (Pending Release) + +### Changes + +- chore: dependency updates + +### Homebridge Dependencies + +- `@homebridge/ciao` @ `v1.3.9` +- `@homebridge/dbus-native` @ `v0.7.6` +- `bonjour-hap` @ `v3.10.3` + ## v2.1.6 (2026-05-08) ### Changes diff --git a/package-lock.json b/package-lock.json index dc9d7d892..16f0e349a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,9 @@ "version": "2.1.6", "license": "Apache-2.0", "dependencies": { - "@homebridge/ciao": "^1.3.8", - "@homebridge/dbus-native": "^0.7.5", - "bonjour-hap": "^3.10.2", + "@homebridge/ciao": "^1.3.9", + "@homebridge/dbus-native": "^0.7.6", + "bonjour-hap": "^3.10.3", "debug": "^4.4.3", "fast-srp-hap": "^2.0.4", "futoin-hkdf": "^1.5.3", @@ -26,20 +26,20 @@ "@types/debug": "^4.1.13", "@types/escape-html": "^1.0.4", "@types/jest": "^30.0.0", - "@types/node": "^25.6.2", + "@types/node": "^25.9.1", "@types/plist": "^3.0.5", - "@typescript-eslint/eslint-plugin": "^8.59.2", - "@typescript-eslint/parser": "^8.59.2", - "axios": "^1.16.0", + "@typescript-eslint/eslint-plugin": "^8.59.4", + "@typescript-eslint/parser": "^8.59.4", + "axios": "^1.16.1", "commander": "^14.0.3", "escape-html": "^1.0.3", - "eslint": "^10.3.0", + "eslint": "^10.4.0", "http-parser-js": "^0.5.10", - "jest": "^30.4.1", + "jest": "^30.4.2", "rimraf": "^6.1.3", - "semver": "^7.7.4", + "semver": "^7.8.1", "simple-plist": "^1.4.0", - "ts-jest": "^29.4.9", + "ts-jest": "^29.4.11", "ts-node": "^10.9.2", "typedoc": "^0.28.19", "typescript": "^6.0.3" @@ -49,13 +49,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -64,9 +64,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", - "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -74,21 +74,21 @@ } }, "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -115,14 +115,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -132,14 +132,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -159,9 +159,9 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -169,29 +169,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -201,9 +201,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, "license": "MIT", "engines": { @@ -211,9 +211,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -221,9 +221,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -231,9 +231,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -241,27 +241,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", - "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -326,13 +326,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -368,13 +368,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -494,13 +494,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -510,33 +510,33 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { @@ -544,14 +544,14 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -706,9 +706,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", - "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -815,9 +815,9 @@ } }, "node_modules/@homebridge/ciao": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.3.8.tgz", - "integrity": "sha512-lNhpCsZVbdbjz2trFjQdzQ3cUIMZQMIMksi7wd3ntTIYgdaGLqT1Ms97DfVIJYHzRuduf56ISvgU8RRLTpK/ng==", + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.3.9.tgz", + "integrity": "sha512-TMy9zy173jDOpnFXDqL3BPIQn5lfcAkSsivYQatCCakoHk4fLGd7QjfAaNGYE3Ox+/ZI6Lq0e1gGcz1qdw/IbA==", "license": "MIT", "dependencies": { "debug": "^4.4.3", @@ -830,9 +830,9 @@ } }, "node_modules/@homebridge/dbus-native": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@homebridge/dbus-native/-/dbus-native-0.7.5.tgz", - "integrity": "sha512-SX4Mwg7JYED+o5WMz1+Y4FzGPGtXruDcwQt1SCkzfSqMTzrFj3uO5spEMn04Jj2kCqJVcrRQFWbqw+XW8suIXA==", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/@homebridge/dbus-native/-/dbus-native-0.7.6.tgz", + "integrity": "sha512-jvwmRYqe01+sP7XqOu7GdP2lHJhctI9MjRddPvgPb1KvsVKW/RrKjF/8CwTqv1vXkwcJHySpny7PYaiBGwtjRw==", "license": "MIT", "dependencies": { "event-stream": "^4.0.1", @@ -1066,9 +1066,9 @@ } }, "node_modules/@jest/core": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.4.1.tgz", - "integrity": "sha512-zNfBGtukVyc0ClmSzXgeP6eseumdekHfrqa++GsPK8ZUm9Hm3TY8X8LQvGfZVrp23RSz9ebbcruXnAv3no0Q+g==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.4.2.tgz", + "integrity": "sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1086,14 +1086,14 @@ "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.11", "jest-changed-files": "30.4.1", - "jest-config": "30.4.1", + "jest-config": "30.4.2", "jest-haste-map": "30.4.1", "jest-message-util": "30.4.1", "jest-regex-util": "30.4.0", "jest-resolve": "30.4.1", - "jest-resolve-dependencies": "30.4.1", - "jest-runner": "30.4.1", - "jest-runtime": "30.4.1", + "jest-resolve-dependencies": "30.4.2", + "jest-runner": "30.4.2", + "jest-runtime": "30.4.2", "jest-snapshot": "30.4.1", "jest-util": "30.4.1", "jest-validate": "30.4.1", @@ -1445,16 +1445,22 @@ "license": "MIT" }, "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, "node_modules/@pkgjs/parseargs": { @@ -1735,13 +1741,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.6.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.2.tgz", - "integrity": "sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw==", + "version": "25.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", + "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.19.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "node_modules/@types/plist": { @@ -1787,17 +1793,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", - "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.4.tgz", + "integrity": "sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/type-utils": "8.59.2", - "@typescript-eslint/utils": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/type-utils": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -1810,7 +1816,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.59.2", + "@typescript-eslint/parser": "^8.59.4", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -1826,16 +1832,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz", - "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.4.tgz", + "integrity": "sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", "debug": "^4.4.3" }, "engines": { @@ -1851,14 +1857,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", - "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.4.tgz", + "integrity": "sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.2", - "@typescript-eslint/types": "^8.59.2", + "@typescript-eslint/tsconfig-utils": "^8.59.4", + "@typescript-eslint/types": "^8.59.4", "debug": "^4.4.3" }, "engines": { @@ -1873,14 +1879,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", - "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.4.tgz", + "integrity": "sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2" + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1891,9 +1897,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", - "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.4.tgz", + "integrity": "sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==", "dev": true, "license": "MIT", "engines": { @@ -1908,15 +1914,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz", - "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.4.tgz", + "integrity": "sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2", - "@typescript-eslint/utils": "8.59.2", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1933,9 +1939,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", - "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.4.tgz", + "integrity": "sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==", "dev": true, "license": "MIT", "engines": { @@ -1947,16 +1953,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", - "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.4.tgz", + "integrity": "sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.2", - "@typescript-eslint/tsconfig-utils": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", + "@typescript-eslint/project-service": "8.59.4", + "@typescript-eslint/tsconfig-utils": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -2014,16 +2020,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", - "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.4.tgz", + "integrity": "sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2" + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2038,13 +2044,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", - "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.4.tgz", + "integrity": "sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/types": "8.59.4", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -2076,9 +2082,9 @@ "license": "ISC" }, "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", "cpu": [ "arm" ], @@ -2090,9 +2096,9 @@ ] }, "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", "cpu": [ "arm64" ], @@ -2104,9 +2110,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", "cpu": [ "arm64" ], @@ -2118,9 +2124,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", "cpu": [ "x64" ], @@ -2132,9 +2138,9 @@ ] }, "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", "cpu": [ "x64" ], @@ -2146,9 +2152,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", "cpu": [ "arm" ], @@ -2160,9 +2166,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", "cpu": [ "arm" ], @@ -2174,9 +2180,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", "cpu": [ "arm64" ], @@ -2191,9 +2197,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", "cpu": [ "arm64" ], @@ -2207,10 +2213,44 @@ "linux" ] }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", "cpu": [ "ppc64" ], @@ -2225,9 +2265,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", "cpu": [ "riscv64" ], @@ -2242,9 +2282,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", "cpu": [ "riscv64" ], @@ -2259,9 +2299,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", "cpu": [ "s390x" ], @@ -2276,9 +2316,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", "cpu": [ "x64" ], @@ -2293,9 +2333,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", "cpu": [ "x64" ], @@ -2309,10 +2349,24 @@ "linux" ] }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", "cpu": [ "wasm32" ], @@ -2320,16 +2374,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", "cpu": [ "arm64" ], @@ -2341,9 +2397,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", "cpu": [ "ia32" ], @@ -2355,9 +2411,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", "cpu": [ "x64" ], @@ -2414,6 +2470,19 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", @@ -2525,14 +2594,15 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", - "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", "dev": true, "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, @@ -2664,9 +2734,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.28", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.28.tgz", - "integrity": "sha512-Ic44hnOtFIgravCunj1ifSoQPSUrkNiJuH9Mf6jr2jjoA74icqV8wU0KuadXeOR8zuIJMOoTv0GuQjZ9ZYNMeA==", + "version": "2.10.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", + "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2687,9 +2757,9 @@ } }, "node_modules/bonjour-hap": { - "version": "3.10.2", - "resolved": "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.10.2.tgz", - "integrity": "sha512-37ZEbMSKZ/K4O8sK9YDVFKJ62ZqlK4OPXcBUYaBnVlqr6vQjYwIoFuOQL0ZhztkCNgyyiCExfN4FcZB5cS9t1A==", + "version": "3.10.3", + "resolved": "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.10.3.tgz", + "integrity": "sha512-KEaj40wsnI7diI44Bonr/XWzSzNoodVG4Uo40sQIsqvyEPxSjix3dyjdryDudrIlBFYJBAvfW57mZ6PRgsMImg==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -2829,9 +2899,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001792", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", - "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", "dev": true, "funding": [ { @@ -3194,9 +3264,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.352", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.352.tgz", - "integrity": "sha512-9wHk8x6dyuimoe18EdiDPWKExNdxYqo4fn4FwOVVper6RxT3cmpBwBkWWfSOCYJjQdIco/nPhJhNLmn4Ufg1Yg==", + "version": "1.5.361", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz", + "integrity": "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==", "dev": true, "license": "ISC" }, @@ -3264,9 +3334,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "dev": true, "license": "MIT", "dependencies": { @@ -3323,16 +3393,16 @@ } }, "node_modules/eslint": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", - "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz", + "integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.5.5", + "@eslint/config-helpers": "^0.6.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.1", "@humanfs/node": "^0.16.6", @@ -3973,9 +4043,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "license": "MIT", "dependencies": { @@ -4131,6 +4201,20 @@ "dev": true, "license": "MIT" }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -4375,16 +4459,16 @@ } }, "node_modules/jest": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.4.1.tgz", - "integrity": "sha512-ZXSQlP2bAgIq0XmJ49HNmrgXSoWoHEzciSw3YhPbOA3gVMl3CyLdHjbpV+dbR7ggOVwSEo4cl5OOaYwRrmWqEA==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.4.2.tgz", + "integrity": "sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.4.1", + "@jest/core": "30.4.2", "@jest/types": "30.4.1", "import-local": "^3.2.0", - "jest-cli": "30.4.1" + "jest-cli": "30.4.2" }, "bin": { "jest": "bin/jest.js" @@ -4417,9 +4501,9 @@ } }, "node_modules/jest-circus": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.1.tgz", - "integrity": "sha512-kcCeuPX8Kh6TSujMOIzaAXWvvr41LFlbhLyEYzcc8doXIuGdX+hOxSxbAH7sJItvi1H2ZOU5B3ujD3FLiX5e4g==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.2.tgz", + "integrity": "sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4435,7 +4519,7 @@ "jest-each": "30.4.1", "jest-matcher-utils": "30.4.1", "jest-message-util": "30.4.1", - "jest-runtime": "30.4.1", + "jest-runtime": "30.4.2", "jest-snapshot": "30.4.1", "jest-util": "30.4.1", "p-limit": "^3.1.0", @@ -4449,19 +4533,19 @@ } }, "node_modules/jest-cli": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.4.1.tgz", - "integrity": "sha512-wh86tmU2ak4aqaVg4Y+OwNys9Plrh4478+o8Zapeo8iz95uwW/WY+A4Yb3pd6C3ilHhY+Ue6V+yDM8G+zUDX+A==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.4.2.tgz", + "integrity": "sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.4.1", + "@jest/core": "30.4.2", "@jest/test-result": "30.4.1", "@jest/types": "30.4.1", "chalk": "^4.1.2", "exit-x": "^0.2.2", "import-local": "^3.2.0", - "jest-config": "30.4.1", + "jest-config": "30.4.2", "jest-util": "30.4.1", "jest-validate": "30.4.1", "yargs": "^17.7.2" @@ -4482,9 +4566,9 @@ } }, "node_modules/jest-config": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.4.1.tgz", - "integrity": "sha512-AHAI8llsQFfz3oE6/AcBrP7tJdVnqczmBvnXONO8RWRqKefLaxKmkIUq0otc+QTZ/0gxBP7wBLfh6caMmNZcLA==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.4.2.tgz", + "integrity": "sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==", "dev": true, "license": "MIT", "dependencies": { @@ -4499,12 +4583,12 @@ "deepmerge": "^4.3.1", "glob": "^10.5.0", "graceful-fs": "^4.2.11", - "jest-circus": "30.4.1", + "jest-circus": "30.4.2", "jest-docblock": "30.4.0", "jest-environment-node": "30.4.1", "jest-regex-util": "30.4.0", "jest-resolve": "30.4.1", - "jest-runner": "30.4.1", + "jest-runner": "30.4.2", "jest-util": "30.4.1", "jest-validate": "30.4.1", "parse-json": "^5.2.0", @@ -4738,9 +4822,9 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.4.1.tgz", - "integrity": "sha512-MstV4pRIfUBs9kuMHSzYHPMPYHqQGoknfRv6tEEpOX7755aaK4Hk5ICwTtOHyjCc3+hYMoxnKF3ENu3iayEIog==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.4.2.tgz", + "integrity": "sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4752,9 +4836,9 @@ } }, "node_modules/jest-runner": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.1.tgz", - "integrity": "sha512-NbStoXGdqMuYF8m7NEQ6FH2gH4eTvcSyz7BINLLuGacdAKtlsDa7PzPSZUtyiBfdMycO2Yeyn3ibfzUl2plRjA==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.2.tgz", + "integrity": "sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==", "dev": true, "license": "MIT", "dependencies": { @@ -4774,7 +4858,7 @@ "jest-leak-detector": "30.4.1", "jest-message-util": "30.4.1", "jest-resolve": "30.4.1", - "jest-runtime": "30.4.1", + "jest-runtime": "30.4.2", "jest-util": "30.4.1", "jest-watcher": "30.4.1", "jest-worker": "30.4.1", @@ -4797,9 +4881,9 @@ } }, "node_modules/jest-runtime": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.1.tgz", - "integrity": "sha512-Ityu3lzs8+it7ABsi7N52Px3Ic1az9w+sBkP/r1xK5MaIq1BdYkYonftpwtX5AtibDSFrKTNEW9KLUXAynXIcA==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.2.tgz", + "integrity": "sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5081,10 +5165,20 @@ "license": "MIT" }, "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz", + "integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "dependencies": { "uc.micro": "^2.0.0" @@ -5176,15 +5270,25 @@ "license": "MIT" }, "node_modules/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz", + "integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", - "linkify-it": "^5.0.0", + "linkify-it": "^5.0.1", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" @@ -5367,11 +5471,14 @@ } }, "node_modules/node-releases": { - "version": "2.0.38", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", - "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", + "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-path": { "version": "3.0.0", @@ -5919,9 +6026,9 @@ } }, "node_modules/rimraf/node_modules/lru-cache": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", - "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", + "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -5991,9 +6098,9 @@ } }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", "dev": true, "license": "ISC", "bin": { @@ -6432,9 +6539,9 @@ } }, "node_modules/ts-jest": { - "version": "29.4.9", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.9.tgz", - "integrity": "sha512-LTb9496gYPMCqjeDLdPrKuXtncudeV1yRZnF4Wo5l3SFi0RYEnYRNgMrFIdg+FHvfzjCyQk1cLncWVqiSX+EvQ==", + "version": "29.4.11", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.11.tgz", + "integrity": "sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==", "dev": true, "license": "MIT", "dependencies": { @@ -6444,7 +6551,7 @@ "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.7.4", + "semver": "^7.8.0", "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, @@ -6688,45 +6795,48 @@ } }, "node_modules/undici-types": { - "version": "7.19.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", - "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, "license": "MIT" }, "node_modules/unrs-resolver": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "napi-postinstall": "^0.3.0" + "napi-postinstall": "^0.3.4" }, "funding": { "url": "https://opencollective.com/unrs-resolver" }, "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" } }, "node_modules/update-browserslist-db": { @@ -7001,9 +7111,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.4.tgz", - "integrity": "sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "dev": true, "license": "ISC", "bin": { diff --git a/package.json b/package.json index 239033e7f..243c72d74 100644 --- a/package.json +++ b/package.json @@ -53,9 +53,9 @@ "@types" ], "dependencies": { - "@homebridge/ciao": "^1.3.8", - "@homebridge/dbus-native": "^0.7.5", - "bonjour-hap": "^3.10.2", + "@homebridge/ciao": "^1.3.9", + "@homebridge/dbus-native": "^0.7.6", + "bonjour-hap": "^3.10.3", "debug": "^4.4.3", "fast-srp-hap": "^2.0.4", "futoin-hkdf": "^1.5.3", @@ -70,20 +70,20 @@ "@types/debug": "^4.1.13", "@types/escape-html": "^1.0.4", "@types/jest": "^30.0.0", - "@types/node": "^25.6.2", + "@types/node": "^25.9.1", "@types/plist": "^3.0.5", - "@typescript-eslint/eslint-plugin": "^8.59.2", - "@typescript-eslint/parser": "^8.59.2", - "axios": "^1.16.0", + "@typescript-eslint/eslint-plugin": "^8.59.4", + "@typescript-eslint/parser": "^8.59.4", + "axios": "^1.16.1", "commander": "^14.0.3", "escape-html": "^1.0.3", - "eslint": "^10.3.0", + "eslint": "^10.4.0", "http-parser-js": "^0.5.10", - "jest": "^30.4.1", + "jest": "^30.4.2", "rimraf": "^6.1.3", - "semver": "^7.7.4", + "semver": "^7.8.1", "simple-plist": "^1.4.0", - "ts-jest": "^29.4.9", + "ts-jest": "^29.4.11", "ts-node": "^10.9.2", "typedoc": "^0.28.19", "typescript": "^6.0.3" From 4fda8c69d012052a02c997b3eb1c97fad89fd523 Mon Sep 17 00:00:00 2001 From: Ben <43026681+bwp91@users.noreply.github.com> Date: Fri, 8 May 2026 20:30:43 +0100 Subject: [PATCH 02/14] chore: drop local @types shims, use upstream bonjour-hap types --- @types/bonjour-hap.d.ts | 66 ---------------------------------------- @types/simple-plist.d.ts | 4 --- CHANGELOG.md | 1 + package.json | 3 +- src/lib/Advertiser.ts | 8 ++--- 5 files changed, 5 insertions(+), 77 deletions(-) delete mode 100644 @types/bonjour-hap.d.ts delete mode 100644 @types/simple-plist.d.ts diff --git a/@types/bonjour-hap.d.ts b/@types/bonjour-hap.d.ts deleted file mode 100644 index 9f152f845..000000000 --- a/@types/bonjour-hap.d.ts +++ /dev/null @@ -1,66 +0,0 @@ -declare module "bonjour-hap" { - - export const enum Protocols { - TCP = "tcp", - UDP = "udp", - } - - export type Nullable = T | null; - export type TxtRecord = Record; - - export class BonjourHAPService { - name: string; - type: string; - subtypes: Nullable; - protocol: Protocols; - host: string; - port: number; - fqdn: string; - txt: Nullable>; - published: boolean; - - start(): void; - stop(callback?: () => void): void; - destroy(): void; - updateTxt(txt: TxtRecord, silent?: boolean): void; - } - - export type PublishOptions = { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - category?: any, - host?: string; - name?: string; - pincode?: string; - port: number; - protocol?: Protocols; - subtypes?: string[]; - txt?: Record; - type?: string; - username?: string; - - addUnsafeServiceEnumerationRecord?: boolean, - - restrictedAddresses?: string[]; - disabledIpv6?: boolean; - }; - - export class BonjourHAP { - publish(options: PublishOptions): BonjourHAPService; - unpublishAll(callback: () => void): void; - destroy(): void; - } - - - export type MulticastOptions = { - multicast?: boolean; - interface?: string; - port?: number; - ip?: string; - ttl?: number; - loopback?: boolean; - reuseAddr?: boolean; - }; - function createWithOptions(options?: MulticastOptions): BonjourHAP; - - export default createWithOptions; -} diff --git a/@types/simple-plist.d.ts b/@types/simple-plist.d.ts deleted file mode 100644 index f71beace7..000000000 --- a/@types/simple-plist.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module "simple-plist" { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - export function readFileSync(path: string): Record; -} diff --git a/CHANGELOG.md b/CHANGELOG.md index 839dfd124..71ce5e742 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. ### Changes - chore: dependency updates +- chore: drop local @types shims, use upstream bonjour-hap types ### Homebridge Dependencies diff --git a/package.json b/package.json index 243c72d74..2282564c5 100644 --- a/package.json +++ b/package.json @@ -49,8 +49,7 @@ "files": [ "README.md", "LICENSE", - "dist", - "@types" + "dist" ], "dependencies": { "@homebridge/ciao": "^1.3.9", diff --git a/src/lib/Advertiser.ts b/src/lib/Advertiser.ts index 0beca2c0d..b54c3bb38 100644 --- a/src/lib/Advertiser.ts +++ b/src/lib/Advertiser.ts @@ -1,10 +1,8 @@ -// eslint-disable-next-line @typescript-eslint/triple-slash-reference -/// import ciao, { CiaoService, MDNSServerOptions, Responder, ServiceEvent, ServiceTxt, ServiceType } from "@homebridge/ciao"; import { InterfaceName, IPAddress } from "@homebridge/ciao/lib/NetworkManager"; import dbus, { DBusInterface, MessageBus } from "@homebridge/dbus-native"; import assert from "assert"; -import bonjour, { BonjourHAP, BonjourHAPService } from "bonjour-hap"; +import bonjour, { Advertisement, Bonjour } from "bonjour-hap"; import crypto from "crypto"; import createDebug from "debug"; import { EventEmitter } from "events"; @@ -205,8 +203,8 @@ export class BonjourHAPAdvertiser extends EventEmitter implements Advertiser { private readonly setupHash: string; private readonly serviceOptions?: ServiceNetworkOptions; - private bonjour: BonjourHAP; - private advertisement?: BonjourHAPService; + private bonjour: Bonjour; + private advertisement?: Advertisement; private port?: number; private destroyed = false; From 60d4906b74083702f81e0be322e5b93f991c1fe1 Mon Sep 17 00:00:00 2001 From: Ben <43026681+bwp91@users.noreply.github.com> Date: Sat, 9 May 2026 21:17:23 +0100 Subject: [PATCH 03/14] chore(ci): bump release workflow action versions --- .github/workflows/release.yml | 2 +- CHANGELOG.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a0fe64758..8fab71446 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,7 +53,7 @@ jobs: - name: Get Release Tag (Latest Only) if: steps.release-type.outputs.type == 'latest' id: get_version - uses: jannemattila/get-version-from-tag@v3 + uses: jannemattila/get-version-from-tag@v4 - name: Tag Info (Latest Only) if: steps.release-type.outputs.type == 'latest' diff --git a/CHANGELOG.md b/CHANGELOG.md index 71ce5e742..c5789cb64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. - chore: dependency updates - chore: drop local @types shims, use upstream bonjour-hap types +- chore(ci): bump release workflow action versions ### Homebridge Dependencies From c062449c843349189f863c5559783db167aa011b Mon Sep 17 00:00:00 2001 From: Ben <43026681+bwp91@users.noreply.github.com> Date: Fri, 8 May 2026 22:32:30 +0100 Subject: [PATCH 04/14] test: cover encrypted data length validation in pair handlers Adds four HAPServer integration tests verifying the guard from 0719059b: /pair-setup M5 and /pair-verify M3 must respond with M4 + TLVErrorCode.AUTHENTICATION when ENCRYPTED_DATA is missing or shorter than the 16-byte auth tag, rather than passing through to a Buffer.alloc with a negative length. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 1 + src/lib/HAPServer.spec.ts | 95 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5789cb64..57c759c9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. - chore: dependency updates - chore: drop local @types shims, use upstream bonjour-hap types - chore(ci): bump release workflow action versions +- test: cover encrypted data length validation in pair handlers ### Homebridge Dependencies diff --git a/src/lib/HAPServer.spec.ts b/src/lib/HAPServer.spec.ts index e705ac426..1bb691d7d 100644 --- a/src/lib/HAPServer.spec.ts +++ b/src/lib/HAPServer.spec.ts @@ -232,6 +232,101 @@ describe("HAPServer", () => { expect(objectsM4[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.AUTHENTICATION); }); + describe("encrypted data length validation (fix 0719059b)", () => { + test("/pair-setup M5 should reject when ENCRYPTED_DATA is missing", async () => { + server = new HAPServer(accessoryInfoUnpaired); + const [port] = await bindServer(server); + + const pairSetup = new PairSetupClient(port, httpAgent); + + // advance the connection state to M4 via real SRP M1 + M3 + const responseM1 = await pairSetup.sendM1(); + const M2 = pairSetup.parseM2(responseM1.data); + const M3 = await pairSetup.prepareM3(M2, accessoryInfoUnpaired.pincode); + const responseM3 = await pairSetup.sendM3(M3); + pairSetup.parseM4(responseM3.data, M3); + + // craft an M5 with no ENCRYPTED_DATA TLV + const response = await axios.post( + `http://localhost:${port}/pair-setup`, + tlv.encode(TLVValues.STATE, PairingStates.M5), + { httpAgent, responseType: "arraybuffer" }, + ); + + const objects = tlv.decode(response.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M4); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.AUTHENTICATION); + }); + + test("/pair-setup M5 should reject when ENCRYPTED_DATA is shorter than the auth tag (16 bytes)", async () => { + server = new HAPServer(accessoryInfoUnpaired); + const [port] = await bindServer(server); + + const pairSetup = new PairSetupClient(port, httpAgent); + const responseM1 = await pairSetup.sendM1(); + const M2 = pairSetup.parseM2(responseM1.data); + const M3 = await pairSetup.prepareM3(M2, accessoryInfoUnpaired.pincode); + const responseM3 = await pairSetup.sendM3(M3); + pairSetup.parseM4(responseM3.data, M3); + + // 8 bytes is below the 16-byte minimum; without the guard this would underflow Buffer.alloc() + const response = await axios.post( + `http://localhost:${port}/pair-setup`, + tlv.encode( + TLVValues.STATE, PairingStates.M5, + TLVValues.ENCRYPTED_DATA, Buffer.alloc(8), + ), + { httpAgent, responseType: "arraybuffer" }, + ); + + const objects = tlv.decode(response.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M4); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.AUTHENTICATION); + }); + + test("/pair-verify M3 should reject when ENCRYPTED_DATA is missing", async () => { + server = new HAPServer(accessoryInfoPaired); + const [port] = await bindServer(server); + + const pairVerify = new PairVerifyClient(port, httpAgent); + // advance connection state to M2 via M1 + const responseM1 = await pairVerify.sendM1(); + pairVerify.parseM2(responseM1.data, serverInfoPaired); + + const response = await axios.post( + `http://localhost:${port}/pair-verify`, + tlv.encode(TLVValues.STATE, PairingStates.M3), + { httpAgent, responseType: "arraybuffer" }, + ); + + const objects = tlv.decode(response.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M4); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.AUTHENTICATION); + }); + + test("/pair-verify M3 should reject when ENCRYPTED_DATA is shorter than the auth tag (16 bytes)", async () => { + server = new HAPServer(accessoryInfoPaired); + const [port] = await bindServer(server); + + const pairVerify = new PairVerifyClient(port, httpAgent); + const responseM1 = await pairVerify.sendM1(); + pairVerify.parseM2(responseM1.data, serverInfoPaired); + + const response = await axios.post( + `http://localhost:${port}/pair-verify`, + tlv.encode( + TLVValues.STATE, PairingStates.M3, + TLVValues.ENCRYPTED_DATA, Buffer.alloc(15), + ), + { httpAgent, responseType: "arraybuffer" }, + ); + + const objects = tlv.decode(response.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M4); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.AUTHENTICATION); + }); + }); + describe("tests with paired and pair-verified connection", () => { let port: number; let address: string; From b7f50dd8fd0373c9d00cdf8cb3d1dbebf66dc4ed Mon Sep 17 00:00:00 2001 From: Ben <43026681+bwp91@users.noreply.github.com> Date: Fri, 8 May 2026 22:36:32 +0100 Subject: [PATCH 05/14] test: cover required TLV field validation in pairing handlers Adds seven HAPServer integration tests verifying the guards from 58c24b92: - /pair-setup M5 rejects when the decrypted sub-TLV is missing IDENTIFIER - /pair-verify M3 rejects when the decrypted sub-TLV is missing PROOF - /pairings rejects requests missing METHOD or STATE - /pairings ADD_PAIRING rejects when IDENTIFIER or PUBLIC_KEY is missing - /pairings REMOVE_PAIRING rejects when IDENTIFIER is missing Without these guards the handlers would crash on a `Buffer.toString()` / indexed access of an undefined TLV entry. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 1 + src/lib/HAPServer.spec.ts | 136 +++++++++++++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57c759c9b..bd6b5a6f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. - chore: drop local @types shims, use upstream bonjour-hap types - chore(ci): bump release workflow action versions - test: cover encrypted data length validation in pair handlers +- test: cover required TLV field validation in pairing handlers ### Homebridge Dependencies diff --git a/src/lib/HAPServer.spec.ts b/src/lib/HAPServer.spec.ts index 1bb691d7d..c45bd85a0 100644 --- a/src/lib/HAPServer.spec.ts +++ b/src/lib/HAPServer.spec.ts @@ -2,7 +2,7 @@ import axios, { AxiosError, AxiosResponse } from "axios"; import crypto from "crypto"; import { Agent } from "http"; import tweetnacl from "tweetnacl"; -import { PairingStates, TLVValues } from "../internal-types"; +import { PairingStates, PairMethods, TLVValues } from "../internal-types"; import { HAPHTTPClient } from "../test-utils/HAPHTTPClient"; import { PairSetupClient } from "../test-utils/PairSetupClient"; import { PairVerifyClient } from "../test-utils/PairVerifyClient"; @@ -30,6 +30,7 @@ import { import { AccessoryInfo, PairingInformation, PermissionTypes } from "./model/AccessoryInfo"; import { Service } from "./Service"; import { HAPConnection, HAPEncryption } from "./util/eventedhttp"; +import * as hapCrypto from "./util/hapCrypto"; import { awaitEventOnce, PromiseTimeout } from "./util/promise-utils"; import * as tlv from "./util/tlv"; @@ -327,6 +328,82 @@ describe("HAPServer", () => { }); }); + describe("required TLV fields validation in pairing handlers (fix 58c24b92)", () => { + test("/pair-setup M5 should reject when decrypted payload is missing IDENTIFIER", async () => { + server = new HAPServer(accessoryInfoUnpaired); + const [port] = await bindServer(server); + + const pairSetup = new PairSetupClient(port, httpAgent); + + const responseM1 = await pairSetup.sendM1(); + const M2 = pairSetup.parseM2(responseM1.data); + const M3 = await pairSetup.prepareM3(M2, accessoryInfoUnpaired.pincode); + const responseM3 = await pairSetup.sendM3(M3); + const M4 = pairSetup.parseM4(responseM3.data, M3); + + // build a sub-TLV missing IDENTIFIER (only PUBLIC_KEY + SIGNATURE) + const malformedSubTLV = tlv.encode( + TLVValues.PUBLIC_KEY, clientInfo.publicKey, + TLVValues.SIGNATURE, Buffer.alloc(64), + ); + + const sessionKey = hapCrypto.HKDF( + "sha512", + Buffer.from("Pair-Setup-Encrypt-Salt"), + M4.sharedSecret, + Buffer.from("Pair-Setup-Encrypt-Info"), + 32, + ); + const encrypted = hapCrypto.chacha20_poly1305_encryptAndSeal( + sessionKey, Buffer.from("PS-Msg05"), null, malformedSubTLV, + ); + + const response = await axios.post( + `http://localhost:${port}/pair-setup`, + tlv.encode( + TLVValues.STATE, PairingStates.M5, + TLVValues.ENCRYPTED_DATA, Buffer.concat([encrypted.ciphertext, encrypted.authTag]), + ), + { httpAgent, responseType: "arraybuffer" }, + ); + + const objects = tlv.decode(response.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M4); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + }); + + test("/pair-verify M3 should reject when decrypted payload is missing PROOF", async () => { + server = new HAPServer(accessoryInfoPaired); + const [port] = await bindServer(server); + + const pairVerify = new PairVerifyClient(port, httpAgent); + const responseM1 = await pairVerify.sendM1(); + const M2 = pairVerify.parseM2(responseM1.data, serverInfoPaired); + + // build a sub-TLV missing PROOF/SIGNATURE (only IDENTIFIER) + const malformedSubTLV = tlv.encode( + TLVValues.IDENTIFIER, Buffer.from(clientInfo.username), + ); + + const encrypted = hapCrypto.chacha20_poly1305_encryptAndSeal( + M2.sessionKey, Buffer.from("PV-Msg03"), null, malformedSubTLV, + ); + + const response = await axios.post( + `http://localhost:${port}/pair-verify`, + tlv.encode( + TLVValues.STATE, PairingStates.M3, + TLVValues.ENCRYPTED_DATA, Buffer.concat([encrypted.ciphertext, encrypted.authTag]), + ), + { httpAgent, responseType: "arraybuffer" }, + ); + + const objects = tlv.decode(response.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M4); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + }); + }); + describe("tests with paired and pair-verified connection", () => { let port: number; let address: string; @@ -399,6 +476,63 @@ describe("HAPServer", () => { expect(response).toEqual(list); }); + describe("/pairings required TLV fields (fix 58c24b92)", () => { + test("should reject when METHOD is missing", async () => { + const malformed = tlv.encode(TLVValues.STATE, PairingStates.M1); + const httpResponse = await client.writeHTTPRequest("POST", "/pairings", malformed, "application/pairing+tlv8"); + expect(httpResponse.statusCode).toEqual(HAPPairingHTTPCode.OK); + const objects = tlv.decode(httpResponse.body); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + }); + + test("should reject when STATE is missing", async () => { + const malformed = tlv.encode(TLVValues.METHOD, PairMethods.LIST_PAIRINGS); + const httpResponse = await client.writeHTTPRequest("POST", "/pairings", malformed, "application/pairing+tlv8"); + expect(httpResponse.statusCode).toEqual(HAPPairingHTTPCode.OK); + const objects = tlv.decode(httpResponse.body); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + }); + + test("ADD_PAIRING should reject when IDENTIFIER is missing", async () => { + const malformed = tlv.encode( + TLVValues.METHOD, PairMethods.ADD_PAIRING, + TLVValues.STATE, PairingStates.M1, + TLVValues.PUBLIC_KEY, crypto.randomBytes(32), + TLVValues.PERMISSIONS, PermissionTypes.ADMIN, + ); + const httpResponse = await client.writeHTTPRequest("POST", "/pairings", malformed, "application/pairing+tlv8"); + const objects = tlv.decode(httpResponse.body); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + }); + + test("ADD_PAIRING should reject when PUBLIC_KEY is missing", async () => { + const malformed = tlv.encode( + TLVValues.METHOD, PairMethods.ADD_PAIRING, + TLVValues.STATE, PairingStates.M1, + TLVValues.IDENTIFIER, thirdUsername, + TLVValues.PERMISSIONS, PermissionTypes.ADMIN, + ); + const httpResponse = await client.writeHTTPRequest("POST", "/pairings", malformed, "application/pairing+tlv8"); + const objects = tlv.decode(httpResponse.body); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + }); + + test("REMOVE_PAIRING should reject when IDENTIFIER is missing", async () => { + const malformed = tlv.encode( + TLVValues.METHOD, PairMethods.REMOVE_PAIRING, + TLVValues.STATE, PairingStates.M1, + ); + const httpResponse = await client.writeHTTPRequest("POST", "/pairings", malformed, "application/pairing+tlv8"); + const objects = tlv.decode(httpResponse.body); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + }); + }); + test("test /accessories", async () => { const accessoryResponse: AccessoriesResponse = { accessories: [{ From 9d27bd2956a6f639290e6395c4aac420a5a2599f Mon Sep 17 00:00:00 2001 From: Ben <43026681+bwp91@users.noreply.github.com> Date: Fri, 8 May 2026 22:46:38 +0100 Subject: [PATCH 06/14] test: cover RTP proxy setup rejection handling Adds two RTPStreamManagement tests verifying the .catch handler from 0ce74c4e: when RTPProxy.setup() rejects (e.g. socket bind failure), handleSetupEndpoints must surface the error via the callback and write SetupEndpointsStatus.ERROR into setupEndpointsResponse. Without the catch this surfaced as an unhandled promise rejection. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 1 + src/lib/camera/RTPStreamManagement.spec.ts | 221 +++++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 src/lib/camera/RTPStreamManagement.spec.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index bd6b5a6f3..09e50b025 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. - chore(ci): bump release workflow action versions - test: cover encrypted data length validation in pair handlers - test: cover required TLV field validation in pairing handlers +- test: cover RTP proxy setup rejection handling ### Homebridge Dependencies diff --git a/src/lib/camera/RTPStreamManagement.spec.ts b/src/lib/camera/RTPStreamManagement.spec.ts new file mode 100644 index 000000000..7a8849fc2 --- /dev/null +++ b/src/lib/camera/RTPStreamManagement.spec.ts @@ -0,0 +1,221 @@ +import { + CameraController, + CameraControllerOptions, + CameraRecordingDelegate, + CameraStreamingDelegate, + PrepareStreamCallback, + SnapshotRequestCallback, + StreamRequestCallback, +} from "../controller"; +import { + AudioBitrate, + AudioRecordingCodecType, + AudioRecordingSamplerate, + AudioStreamingCodecType, + AudioStreamingSamplerate, + CameraRecordingConfiguration, + CameraRecordingOptions, + H264Level, + H264Profile, + MediaContainerType, + PrepareStreamRequest, + RecordingPacket, + SnapshotRequest, + StreamingRequest, + VideoCodecType, +} from "../camera"; +import { HDSProtocolSpecificErrorReason } from "../datastream"; +import "../definitions"; +import { HAPConnection } from "../util/eventedhttp"; +import * as tlv from "../util/tlv"; +import * as uuid from "../util/uuid"; +import RTPProxy from "./RTPProxy"; +import { CameraStreamingOptions, RTPStreamManagement, SRTPCryptoSuites } from "./RTPStreamManagement"; + +class MockDelegate implements CameraStreamingDelegate, CameraRecordingDelegate { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + handleSnapshotRequest(request: SnapshotRequest, callback: SnapshotRequestCallback): void { + throw Error("Unsupported!"); + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + handleStreamRequest(request: StreamingRequest, callback: StreamRequestCallback): void { + throw Error("Unsupported!"); + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + prepareStream(request: PrepareStreamRequest, callback: PrepareStreamCallback): void { + throw Error("Unsupported!"); + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async *handleRecordingStreamRequest(streamId: number): AsyncGenerator { + yield { data: Buffer.alloc(64, 0), isLast: true }; + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + closeRecordingStream(streamId: number, reason: HDSProtocolSpecificErrorReason): void {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars + updateRecordingActive(active: boolean): void {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars + updateRecordingConfiguration(configuration: CameraRecordingConfiguration | undefined): void {} +} + +const baseStreamingOptions: CameraStreamingOptions = { + supportedCryptoSuites: [SRTPCryptoSuites.AES_CM_128_HMAC_SHA1_80], + video: { + codec: { + profiles: [H264Profile.BASELINE, H264Profile.MAIN, H264Profile.HIGH], + levels: [H264Level.LEVEL3_1, H264Level.LEVEL3_2, H264Level.LEVEL4_0], + }, + resolutions: [ + [1920, 1080, 30], + [1280, 720, 30], + [320, 240, 15], + ], + }, + audio: { + twoWayAudio: true, + codecs: [{ + type: AudioStreamingCodecType.AAC_ELD, + samplerate: AudioStreamingSamplerate.KHZ_24, + }], + }, +}; + +const baseRecordingOptions: CameraRecordingOptions = { + prebufferLength: 4000, + mediaContainerConfiguration: [{ + type: MediaContainerType.FRAGMENTED_MP4, + fragmentLength: 8000, + }], + video: { + type: VideoCodecType.H264, + parameters: baseStreamingOptions.video.codec, + resolutions: baseStreamingOptions.video.resolutions, + }, + audio: { + codecs: [{ + type: AudioRecordingCodecType.AAC_ELD, + audioChannels: 1, + bitrateMode: AudioBitrate.VARIABLE, + samplerate: AudioRecordingSamplerate.KHZ_48, + }], + }, +}; + +function buildControllerOptions(streamingOptions: CameraStreamingOptions): CameraControllerOptions { + const delegate = new MockDelegate(); + return { + cameraStreamCount: 2, + delegate, + streamingOptions, + recording: { + options: baseRecordingOptions, + delegate, + }, + sensors: { motion: true, occupancy: true }, + }; +} + +// matches the private const enums in RTPStreamManagement.ts +const SetupEndpointsTypes = { + SESSION_ID: 0x01, + CONTROLLER_ADDRESS: 0x03, + VIDEO_SRTP_PARAMETERS: 0x04, + AUDIO_SRTP_PARAMETERS: 0x05, +} as const; +const AddressTypes = { + ADDRESS_VERSION: 0x01, + ADDRESS: 0x02, + VIDEO_RTP_PORT: 0x03, + AUDIO_RTP_PORT: 0x04, +} as const; +const SRTPParametersTypes = { + SRTP_CRYPTO_SUITE: 0x01, + MASTER_KEY: 0x02, + MASTER_SALT: 0x03, +} as const; + +function buildSetupEndpointsTlv(): string { + const addressTlv = tlv.encode( + AddressTypes.ADDRESS_VERSION, 0x00, // IPV4 + AddressTypes.ADDRESS, "127.0.0.1", + AddressTypes.VIDEO_RTP_PORT, tlv.writeUInt16(5000), + AddressTypes.AUDIO_RTP_PORT, tlv.writeUInt16(5001), + ); + const srtpTlv = tlv.encode( + SRTPParametersTypes.SRTP_CRYPTO_SUITE, SRTPCryptoSuites.AES_CM_128_HMAC_SHA1_80, + SRTPParametersTypes.MASTER_KEY, Buffer.alloc(16), + SRTPParametersTypes.MASTER_SALT, Buffer.alloc(14), + ); + const payload = tlv.encode( + SetupEndpointsTypes.SESSION_ID, uuid.write("11111111-1111-1111-1111-111111111111"), + SetupEndpointsTypes.CONTROLLER_ADDRESS, addressTlv, + SetupEndpointsTypes.VIDEO_SRTP_PARAMETERS, srtpTlv, + SetupEndpointsTypes.AUDIO_SRTP_PARAMETERS, srtpTlv, + ); + return payload.toString("base64"); +} + +function buildMockConnection(): HAPConnection { + return { + localAddress: "127.0.0.1", + getLocalAddress: jest.fn().mockReturnValue("127.0.0.1"), + setMaxListeners: jest.fn(), + getMaxListeners: jest.fn().mockReturnValue(10), + on: jest.fn(), + removeListener: jest.fn(), + } as unknown as HAPConnection; +} + +describe("RTPStreamManagement", () => { + describe("handleSetupEndpoints proxy rejection (fix 0ce74c4e)", () => { + let controller: CameraController; + let setupSpy: jest.SpyInstance; + + beforeEach(() => { + const proxyOptions: CameraStreamingOptions = { + ...baseStreamingOptions, + proxy: true, + }; + controller = new CameraController(buildControllerOptions(proxyOptions)); + controller.constructServices(); + controller.configureServices(); + }); + + afterEach(() => { + setupSpy?.mockRestore(); + controller.handleFactoryReset(); + }); + + test("should fail gracefully when RTPProxy.setup rejects", async () => { + setupSpy = jest.spyOn(RTPProxy.prototype, "setup") + .mockImplementation(() => Promise.reject(new Error("socket creation failed"))); + + const rtp = controller.streamManagements[0] as RTPStreamManagement; + const error = await new Promise((resolve) => { + // @ts-expect-error: private method access + rtp.handleSetupEndpoints(buildSetupEndpointsTlv(), (err) => { + resolve(err as Error | undefined); + }, buildMockConnection()); + }); + + expect(error).toBeInstanceOf(Error); + expect(error!.message).toBe("socket creation failed"); + }); + + test("should set setupEndpointsResponse to ERROR status when proxy setup rejects", async () => { + setupSpy = jest.spyOn(RTPProxy.prototype, "setup") + .mockImplementation(() => Promise.reject(new Error("bind failed"))); + + const rtp = controller.streamManagements[0] as RTPStreamManagement; + await new Promise((resolve) => { + // @ts-expect-error: private method access + rtp.handleSetupEndpoints(buildSetupEndpointsTlv(), () => resolve(), buildMockConnection()); + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const response = (rtp as any).setupEndpointsResponse as string; + const decoded = tlv.decode(Buffer.from(response, "base64")); + // SetupEndpointsResponseTypes.STATUS = 0x02; SetupEndpointsStatus.ERROR = 0x02 + expect(decoded[0x02].readUInt8(0)).toBe(0x02); + }); + }); +}); From f9e07e6608e9deee5fa4a596332a653cc4def89f Mon Sep 17 00:00:00 2001 From: Ben <43026681+bwp91@users.noreply.github.com> Date: Fri, 8 May 2026 22:47:39 +0100 Subject: [PATCH 07/14] test: cover SEQUENCE_NUM presence check in pair handlers Adds two HAPServer integration tests verifying the guard from ebe2ec67: /pair-setup and /pair-verify must reject with 400 BAD_REQUEST + STATE=M2 + UNKNOWN error when the request TLV is missing TLVValues.SEQUENCE_NUM, rather than crashing on an indexed read of an undefined entry. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 1 + src/lib/HAPServer.spec.ts | 44 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09e50b025..34ca74b7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. - test: cover encrypted data length validation in pair handlers - test: cover required TLV field validation in pairing handlers - test: cover RTP proxy setup rejection handling +- test: cover SEQUENCE_NUM presence check in pair handlers ### Homebridge Dependencies diff --git a/src/lib/HAPServer.spec.ts b/src/lib/HAPServer.spec.ts index c45bd85a0..36d42ca38 100644 --- a/src/lib/HAPServer.spec.ts +++ b/src/lib/HAPServer.spec.ts @@ -328,6 +328,50 @@ describe("HAPServer", () => { }); }); + describe("SEQUENCE_NUM check in pair handlers (fix ebe2ec67)", () => { + test("/pair-setup should reject when SEQUENCE_NUM TLV is missing", async () => { + server = new HAPServer(accessoryInfoUnpaired); + const [port] = await bindServer(server); + + // a TLV containing only METHOD (no SEQUENCE_NUM/STATE) — without the + // guard this would crash on `tlvData[SEQUENCE_NUM][0]` of `undefined`. + try { + await axios.post( + `http://localhost:${port}/pair-setup`, + tlv.encode(TLVValues.METHOD, PairMethods.PAIR_SETUP), + { httpAgent, responseType: "arraybuffer" }, + ); + fail("Expected BAD_REQUEST response"); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(HAPHTTPCode.BAD_REQUEST); + const objects = tlv.decode(error.response?.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + } + }); + + test("/pair-verify should reject when SEQUENCE_NUM TLV is missing", async () => { + server = new HAPServer(accessoryInfoPaired); + const [port] = await bindServer(server); + + try { + await axios.post( + `http://localhost:${port}/pair-verify`, + tlv.encode(TLVValues.PUBLIC_KEY, Buffer.alloc(32)), + { httpAgent, responseType: "arraybuffer" }, + ); + fail("Expected BAD_REQUEST response"); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(HAPHTTPCode.BAD_REQUEST); + const objects = tlv.decode(error.response?.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + } + }); + }); + describe("required TLV fields validation in pairing handlers (fix 58c24b92)", () => { test("/pair-setup M5 should reject when decrypted payload is missing IDENTIFIER", async () => { server = new HAPServer(accessoryInfoUnpaired); From 0416816e1f0561d7f1b418057d3686ea72a9bb8f Mon Sep 17 00:00:00 2001 From: Ben <43026681+bwp91@users.noreply.github.com> Date: Fri, 8 May 2026 22:48:51 +0100 Subject: [PATCH 08/14] test: cover M1 reset prevention in pair-setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds two HAPServer integration tests verifying the guard from d4c81be0: - a second M1 on a connection with in-progress pair-setup state is rejected with 400 BAD_REQUEST + STATE=M2 + UNKNOWN, rather than restarting setup and overwriting the in-progress SRP server. - a fresh connection can still start M1 normally — the guard does not leak across connections. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 1 + src/lib/HAPServer.spec.ts | 52 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34ca74b7c..515b4e7b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. - test: cover required TLV field validation in pairing handlers - test: cover RTP proxy setup rejection handling - test: cover SEQUENCE_NUM presence check in pair handlers +- test: cover M1 reset prevention in pair-setup ### Homebridge Dependencies diff --git a/src/lib/HAPServer.spec.ts b/src/lib/HAPServer.spec.ts index 36d42ca38..cd1df6a7d 100644 --- a/src/lib/HAPServer.spec.ts +++ b/src/lib/HAPServer.spec.ts @@ -328,6 +328,58 @@ describe("HAPServer", () => { }); }); + describe("M1 reset prevention (fix d4c81be0)", () => { + test("/pair-setup should reject a second M1 on a connection with in-progress state", async () => { + server = new HAPServer(accessoryInfoUnpaired); + const [port] = await bindServer(server); + + const pairSetup = new PairSetupClient(port, httpAgent); + + // first M1 succeeds → connection state advances to M2 + const firstResponse = await pairSetup.sendM1(); + const firstObjects = tlv.decode(firstResponse.data); + expect(firstObjects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(firstObjects[TLVValues.ERROR_CODE]).toBeUndefined(); + + // second M1 on the same connection — without the guard this would + // restart pair-setup and overwrite the in-progress SRP server + try { + await pairSetup.sendM1(); + fail("Expected BAD_REQUEST response"); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(HAPHTTPCode.BAD_REQUEST); + const objects = tlv.decode(error.response?.data); + // sequence + 1 = M2; UNKNOWN error + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + } + }); + + test("/pair-setup should still accept M1 on a fresh connection after a previous setup completed", async () => { + server = new HAPServer(accessoryInfoUnpaired); + const [port] = await bindServer(server); + + // first connection: do an M1 and abort + const firstAgent = new Agent({ keepAlive: true }); + const firstClient = new PairSetupClient(port, firstAgent); + await firstClient.sendM1(); + firstAgent.destroy(); + + // second connection: a fresh M1 should still succeed (no state pollution) + const secondAgent = new Agent({ keepAlive: true }); + try { + const secondClient = new PairSetupClient(port, secondAgent); + const response = await secondClient.sendM1(); + const objects = tlv.decode(response.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE]).toBeUndefined(); + } finally { + secondAgent.destroy(); + } + }); + }); + describe("SEQUENCE_NUM check in pair handlers (fix ebe2ec67)", () => { test("/pair-setup should reject when SEQUENCE_NUM TLV is missing", async () => { server = new HAPServer(accessoryInfoUnpaired); From 29daf53dbdec470e197cd1086af712b816268aaf Mon Sep 17 00:00:00 2001 From: Ben <43026681+bwp91@users.noreply.github.com> Date: Fri, 8 May 2026 22:53:56 +0100 Subject: [PATCH 09/14] test: cover safe accessory lookups in slow/timeout warnings Adds two Accessory tests verifying the fix from 03b49f9f: when a GET /characteristics or PUT /characteristics request includes an aid.iid that does not resolve to a known accessory or characteristic, the slow-read/slow-write and timeout warning timers must skip the entry rather than crash on `getAccessoryByAID(aid)!.getCharacteristicByIID(iid)!`. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 1 + src/lib/Accessory.spec.ts | 55 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 515b4e7b2..b7224f909 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. - test: cover RTP proxy setup rejection handling - test: cover SEQUENCE_NUM presence check in pair handlers - test: cover M1 reset prevention in pair-setup +- test: cover safe accessory lookups in slow/timeout warnings ### Homebridge Dependencies diff --git a/src/lib/Accessory.spec.ts b/src/lib/Accessory.spec.ts index 7a1d5fe68..86a0e6827 100644 --- a/src/lib/Accessory.spec.ts +++ b/src/lib/Accessory.spec.ts @@ -1203,6 +1203,61 @@ describe("Accessory", () => { }); }); + describe("non-null assertions in accessory lookups (fix 03b49f9f)", () => { + test("slow read / timeout warning should not crash for unknown aid.iid in request", async () => { + jest.useFakeTimers({ doNotFake: ["nextTick", "queueMicrotask"] }); + + try { + // @ts-expect-error: private access + accessory.handleGetCharacteristics(connection, { + ids: [ + { aid: 999, iid: 999 }, // unknown accessory — exercises `!accessory` + { aid: aid, iid: 9999 }, // known accessory, unknown iid — exercises `!characteristic` + ], + includeMeta: false, + includeEvent: false, + includeType: false, + includePerms: false, + }, callback); + + // 3s slow read warning fires; without the fix this would throw on + // `accessory!.getCharacteristicByIID(...)` or `characteristic!.displayName`. + jest.advanceTimersByTime(3000); + // 6s later the timeout warning fires and the response is emitted. + jest.advanceTimersByTime(6000); + + await callbackPromise; + expect(callback).toHaveBeenCalledTimes(1); + // unknown ids are skipped, so the response has no characteristics + expect(callback.mock.calls[0][1].characteristics).toEqual([]); + } finally { + jest.useRealTimers(); + } + }); + + test("slow write / timeout warning should not crash for unknown aid.iid in request", async () => { + jest.useFakeTimers({ doNotFake: ["nextTick", "queueMicrotask"] }); + + try { + // @ts-expect-error: private access + accessory.handleSetCharacteristics(connection, { + characteristics: [ + { aid: 999, iid: 999, value: true }, + { aid: aid, iid: 9999, value: true }, + ], + }, callback); + + jest.advanceTimersByTime(3000); + jest.advanceTimersByTime(6000); + + await callbackPromise; + expect(callback).toHaveBeenCalledTimes(1); + } finally { + jest.useRealTimers(); + } + }); + }); + describe("handleSetCharacteristic", () => { let consoleWarnSpy: jest.SpyInstance; From d87e20e8b287a40693340f099ecada3384bb19cb Mon Sep 17 00:00:00 2001 From: Ben <43026681+bwp91@users.noreply.github.com> Date: Fri, 8 May 2026 22:56:22 +0100 Subject: [PATCH 10/14] test: cover aid.iid format validation Adds tests verifying the guards from ec93c504: - HAPServer GET /characteristics rejects malformed `?id=` entries (non-numeric, missing dot, too many dots, empty parts) with 400 + HAPStatus.INVALID_VALUE_IN_REQUEST, both for a single bad entry and inside a comma-separated list. - Accessory.handleHAPConnectionClosed skips registered events that do not contain a dot separator, instead of feeding an undefined index to parseInt. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 1 + src/lib/Accessory.spec.ts | 27 +++++++++++++++++++++++++++ src/lib/HAPServer.spec.ts | 25 +++++++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7224f909..87c7e94b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. - test: cover SEQUENCE_NUM presence check in pair handlers - test: cover M1 reset prevention in pair-setup - test: cover safe accessory lookups in slow/timeout warnings +- test: cover aid.iid format validation ### Homebridge Dependencies diff --git a/src/lib/Accessory.spec.ts b/src/lib/Accessory.spec.ts index 86a0e6827..cba23244d 100644 --- a/src/lib/Accessory.spec.ts +++ b/src/lib/Accessory.spec.ts @@ -1203,6 +1203,33 @@ describe("Accessory", () => { }); }); + describe("aid.iid format validation in handleHAPConnectionClosed (fix ec93c504)", () => { + test("should skip registered events without a dot separator", () => { + const clearMock = jest.fn(); + const mockConnection = { + getRegisteredEvents: jest.fn().mockReturnValue(new Set([ + "malformed", // no dot — without the fix this triggers parseInt(undefined) + `${aid}.${iids.on}`, // valid + ])), + clearRegisteredEvents: clearMock, + } as unknown as HAPConnection; + + // @ts-expect-error: private access + expect(() => accessory.handleHAPConnectionClosed(mockConnection)).not.toThrow(); + expect(clearMock).toHaveBeenCalled(); + }); + + test("should skip empty registered event strings", () => { + const mockConnection = { + getRegisteredEvents: jest.fn().mockReturnValue(new Set(["", `${aid}.${iids.on}`])), + clearRegisteredEvents: jest.fn(), + } as unknown as HAPConnection; + + // @ts-expect-error: private access + expect(() => accessory.handleHAPConnectionClosed(mockConnection)).not.toThrow(); + }); + }); + describe("non-null assertions in accessory lookups (fix 03b49f9f)", () => { test("slow read / timeout warning should not crash for unknown aid.iid in request", async () => { jest.useFakeTimers({ doNotFake: ["nextTick", "queueMicrotask"] }); diff --git a/src/lib/HAPServer.spec.ts b/src/lib/HAPServer.spec.ts index cd1df6a7d..4b93703ff 100644 --- a/src/lib/HAPServer.spec.ts +++ b/src/lib/HAPServer.spec.ts @@ -629,6 +629,31 @@ describe("HAPServer", () => { }); }); + describe("GET /characteristics aid.iid format validation (fix ec93c504)", () => { + test.each([ + ["non-numeric aid", "abc.5"], + ["non-numeric iid", "1.xyz"], + ["missing dot", "1"], + ["too many dots", "1.2.3"], + ["empty parts", "."], + ["empty aid", ".5"], + ["empty iid", "1."], + ["whitespace", "1. 5"], + ])("should reject id=%s (%s) with INVALID_VALUE_IN_REQUEST", async (_label, idValue) => { + const httpResponse = await client.writeHTTPRequest("GET", `/characteristics?id=${encodeURIComponent(idValue)}`); + expect(httpResponse.statusCode).toBe(HAPHTTPCode.BAD_REQUEST); + const body = JSON.parse(httpResponse.body.toString()); + expect(body.status).toBe(HAPStatus.INVALID_VALUE_IN_REQUEST); + }); + + test("should reject when one entry in a comma-separated list is malformed", async () => { + const httpResponse = await client.writeHTTPRequest("GET", "/characteristics?id=1.5,abc.def"); + expect(httpResponse.statusCode).toBe(HAPHTTPCode.BAD_REQUEST); + const body = JSON.parse(httpResponse.body.toString()); + expect(body.status).toBe(HAPStatus.INVALID_VALUE_IN_REQUEST); + }); + }); + test("test /accessories", async () => { const accessoryResponse: AccessoriesResponse = { accessories: [{ From 72f52e0b441c93fb30e3fe80e13446b73b4e0e58 Mon Sep 17 00:00:00 2001 From: Ben <43026681+bwp91@users.noreply.github.com> Date: Fri, 8 May 2026 22:57:24 +0100 Subject: [PATCH 11/14] test: cover camera stream start TLV parsing guards Adds two RTPStreamManagement tests verifying the try/catch from 34056583: when a /selected-stream-configuration write contains malformed video/audio configuration TLVs (missing fields, undefined buffer reads), `_handleStartStream` must call back with HAPStatus.INVALID_VALUE_IN_REQUEST rather than throwing out of the characteristic write handler. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 1 + src/lib/camera/RTPStreamManagement.spec.ts | 47 ++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87c7e94b1..36f460460 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. - test: cover M1 reset prevention in pair-setup - test: cover safe accessory lookups in slow/timeout warnings - test: cover aid.iid format validation +- test: cover camera stream start TLV parsing guards ### Homebridge Dependencies diff --git a/src/lib/camera/RTPStreamManagement.spec.ts b/src/lib/camera/RTPStreamManagement.spec.ts index 7a8849fc2..8b143ec9f 100644 --- a/src/lib/camera/RTPStreamManagement.spec.ts +++ b/src/lib/camera/RTPStreamManagement.spec.ts @@ -26,6 +26,7 @@ import { } from "../camera"; import { HDSProtocolSpecificErrorReason } from "../datastream"; import "../definitions"; +import { HAPStatus } from "../HAPServer"; import { HAPConnection } from "../util/eventedhttp"; import * as tlv from "../util/tlv"; import * as uuid from "../util/uuid"; @@ -166,6 +167,52 @@ function buildMockConnection(): HAPConnection { } describe("RTPStreamManagement", () => { + describe("_handleStartStream malformed TLV (fix 34056583)", () => { + let controller: CameraController; + + beforeEach(() => { + controller = new CameraController(buildControllerOptions(baseStreamingOptions)); + controller.constructServices(); + controller.configureServices(); + }); + + afterEach(() => { + controller.handleFactoryReset(); + }); + + test("empty video configuration should call callback with INVALID_VALUE_IN_REQUEST", () => { + const rtp = controller.streamManagements[0] as RTPStreamManagement; + const callback = jest.fn(); + + // empty video/audio config — every indexed buffer access (e.g. + // `videoParameters[VideoCodecParametersTypes.PROFILE_ID][0]`) would + // throw without the try/catch from 34056583. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (rtp as any)._handleStartStream({}, {}, callback); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback.mock.calls[0][0]).toBe(HAPStatus.INVALID_VALUE_IN_REQUEST); + }); + + test("video configuration missing CODEC_PARAMETERS should not throw", () => { + const rtp = controller.streamManagements[0] as RTPStreamManagement; + const callback = jest.fn(); + + // SelectedVideoParametersTypes.CODEC_TYPE = 0x01 — provide that but + // omit CODEC_PARAMETERS / ATTRIBUTES / RTP_PARAMETERS so the inner + // `tlv.decode(undefined)` would have crashed without the guard. + const videoConfig = { 0x01: Buffer.from([0x00]) }; + + expect(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (rtp as any)._handleStartStream(videoConfig, {}, callback); + }).not.toThrow(); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback.mock.calls[0][0]).toBe(HAPStatus.INVALID_VALUE_IN_REQUEST); + }); + }); + describe("handleSetupEndpoints proxy rejection (fix 0ce74c4e)", () => { let controller: CameraController; let setupSpy: jest.SpyInstance; From 517e33b23fab5b20ba9d9d025a5beb07a50e89a9 Mon Sep 17 00:00:00 2001 From: Ben <43026681+bwp91@users.noreply.github.com> Date: Fri, 8 May 2026 23:04:30 +0100 Subject: [PATCH 12/14] test: cover error argument in pairing debug logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds two HAPServer integration tests verifying the fix from f12ed233: when M5 / pair-verify M3 decryption fails, the debug() call must receive the caught error as its second %s argument. Tests intercept the global debug.log function and assert the captured args include the username AND the error object — without the fix only the username was passed and %s rendered as a literal trailing \"undefined\" / blank. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 1 + src/lib/HAPServer.spec.ts | 98 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36f460460..57627e0d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. - test: cover safe accessory lookups in slow/timeout warnings - test: cover aid.iid format validation - test: cover camera stream start TLV parsing guards +- test: cover error argument in pairing debug logs ### Homebridge Dependencies diff --git a/src/lib/HAPServer.spec.ts b/src/lib/HAPServer.spec.ts index 4b93703ff..552e4b412 100644 --- a/src/lib/HAPServer.spec.ts +++ b/src/lib/HAPServer.spec.ts @@ -1,5 +1,6 @@ import axios, { AxiosError, AxiosResponse } from "axios"; import crypto from "crypto"; +import createDebug from "debug"; import { Agent } from "http"; import tweetnacl from "tweetnacl"; import { PairingStates, PairMethods, TLVValues } from "../internal-types"; @@ -328,6 +329,103 @@ describe("HAPServer", () => { }); }); + describe("error argument in pairing debug logs (fix f12ed233)", () => { + let originalEnable: string; + let originalLog: (...args: unknown[]) => void; + let captured: unknown[][]; + + beforeEach(() => { + captured = []; + // createDebug.disable() returns the namespaces it just disabled, so we + // can faithfully restore them via createDebug.enable() in afterEach. + originalEnable = createDebug.disable(); + // route all enabled debug output through our capture function + // eslint-disable-next-line @typescript-eslint/no-explicit-any + originalLog = (createDebug as any).log; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (createDebug as any).log = function(this: { namespace: string }, ...args: unknown[]) { + captured.push([this.namespace, ...args]); + }; + createDebug.enable("HAP-NodeJS:HAPServer"); + }); + + afterEach(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (createDebug as any).log = originalLog; + createDebug.disable(); + if (originalEnable) { + createDebug.enable(originalEnable); + } + }); + + test("M5 decrypt failure should pass the error to the debug logger", async () => { + server = new HAPServer(accessoryInfoUnpaired); + const [port] = await bindServer(server); + + const pairSetup = new PairSetupClient(port, httpAgent); + const responseM1 = await pairSetup.sendM1(); + const M2 = pairSetup.parseM2(responseM1.data); + const M3 = await pairSetup.prepareM3(M2, accessoryInfoUnpaired.pincode); + const responseM3 = await pairSetup.sendM3(M3); + pairSetup.parseM4(responseM3.data, M3); + + // craft an M5 with bogus ciphertext+tag (passes the >=16 length check + // from 0719059b but fails MAC verification) + await axios.post( + `http://localhost:${port}/pair-setup`, + tlv.encode( + TLVValues.STATE, PairingStates.M5, + TLVValues.ENCRYPTED_DATA, Buffer.alloc(48), // 32-byte payload + 16-byte tag, all zeros + ), + { httpAgent, responseType: "arraybuffer" }, + ); + + // captured rows: [namespace, formatString, ...formatArgs] + const m5Lines = captured.filter(line => typeof line[1] === "string" + && (line[1] as string).includes("Error while decrypting and verifying M5 subTlv")); + expect(m5Lines.length).toBeGreaterThan(0); + + // the format string contains two %s — the first is the username, the + // second is the error. Without the fix only the username was passed. + const line = m5Lines[0]; + expect(line[2]).toBe(accessoryInfoUnpaired.username); + // line[3] should be the error caught from decipher.final() — the + // exact constructor varies by Node version / openssl binding, so + // just assert it's a thrown error-like object with a message. + expect(line[3]).toBeDefined(); + expect((line[3] as Error).message).toEqual(expect.any(String)); + }); + + test("pair-verify M3 decrypt failure should pass the error to the debug logger", async () => { + server = new HAPServer(accessoryInfoPaired); + const [port] = await bindServer(server); + + const pairVerify = new PairVerifyClient(port, httpAgent); + const responseM1 = await pairVerify.sendM1(); + pairVerify.parseM2(responseM1.data, serverInfoPaired); + + await axios.post( + `http://localhost:${port}/pair-verify`, + tlv.encode( + TLVValues.STATE, PairingStates.M3, + TLVValues.ENCRYPTED_DATA, Buffer.alloc(48), + ), + { httpAgent, responseType: "arraybuffer" }, + ); + + const m3Lines = captured.filter(line => typeof line[1] === "string" + && (line[1] as string).includes("M3: Failed to decrypt and/or verify")); + expect(m3Lines.length).toBeGreaterThan(0); + const line = m3Lines[0]; + expect(line[2]).toBe(accessoryInfoPaired.username); + // line[3] should be the error caught from decipher.final() — the + // exact constructor varies by Node version / openssl binding, so + // just assert it's a thrown error-like object with a message. + expect(line[3]).toBeDefined(); + expect((line[3] as Error).message).toEqual(expect.any(String)); + }); + }); + describe("M1 reset prevention (fix d4c81be0)", () => { test("/pair-setup should reject a second M1 on a connection with in-progress state", async () => { server = new HAPServer(accessoryInfoUnpaired); From 1f72ff7f52e4eec257c1fdef1b0291a3e222091a Mon Sep 17 00:00:00 2001 From: Ben <43026681+bwp91@users.noreply.github.com> Date: Fri, 8 May 2026 23:08:29 +0100 Subject: [PATCH 13/14] test: cover constant-time pincode comparison MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds five HAPServer tests verifying the fix from 12aea013: the insecure PUT /characteristics and POST /resource paths must compare the authorization header against the configured pincode using crypto.timingSafeEqual with a length check first. Coverage: - missing authorization → CONNECTION_AUTHORIZATION_REQUIRED - wrong-length authorization → rejected (without the length guard timingSafeEqual would throw RangeError) - same-length wrong pincode → rejected - correct pincode → authorized - /resource endpoint also rejects wrong-length auth Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 1 + src/lib/HAPServer.spec.ts | 119 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57627e0d0..097f3a580 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. - test: cover aid.iid format validation - test: cover camera stream start TLV parsing guards - test: cover error argument in pairing debug logs +- test: cover constant-time pincode comparison ### Homebridge Dependencies diff --git a/src/lib/HAPServer.spec.ts b/src/lib/HAPServer.spec.ts index 552e4b412..f2e484097 100644 --- a/src/lib/HAPServer.spec.ts +++ b/src/lib/HAPServer.spec.ts @@ -329,6 +329,125 @@ describe("HAPServer", () => { }); }); + describe("constant-time pincode comparison (fix 12aea013)", () => { + test("PUT /characteristics with no authorization header should be rejected", async () => { + server = new HAPServer(accessoryInfoUnpaired); + server.allowInsecureRequest = true; + const [port] = await bindServer(server); + + try { + await axios.put( + `http://localhost:${port}/characteristics`, + { characteristics: [{ aid: 1, iid: 9, value: true }] }, + { httpAgent }, + ); + fail("Expected CONNECTION_AUTHORIZATION_REQUIRED response"); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED); + expect(error.response?.data).toEqual({ status: HAPStatus.INSUFFICIENT_PRIVILEGES }); + } + }); + + test("PUT /characteristics with wrong-length authorization should not crash timingSafeEqual", async () => { + server = new HAPServer(accessoryInfoUnpaired); + server.allowInsecureRequest = true; + const [port] = await bindServer(server); + + // 5 bytes — different length to the 11-byte pincode. Without the + // length guard added in 12aea013, crypto.timingSafeEqual throws + // RangeError and the connection would be killed. + try { + await axios.put( + `http://localhost:${port}/characteristics`, + { characteristics: [{ aid: 1, iid: 9, value: true }] }, + { httpAgent, headers: { authorization: "short" } }, + ); + fail("Expected CONNECTION_AUTHORIZATION_REQUIRED response"); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED); + } + }); + + test("PUT /characteristics with same-length wrong pincode should be rejected", async () => { + // use a pincode without leading whitespace — node's HTTP parser trims + // header values, so the fixture's " 031-45-154" can't round-trip and + // any wrong value with the same leading space hits the wrong-length + // branch on the server, not the constant-time same-length comparison. + const info = AccessoryInfo.create(serverUsername); + // @ts-expect-error: private access + info.setupID = Accessory._generateSetupID(); + info.displayName = "Outlet"; + info.category = 7; + info.pincode = "031-45-154"; + + server = new HAPServer(info); + server.allowInsecureRequest = true; + const [port] = await bindServer(server); + + const wrongPincode = "999-99-999"; // same length as "031-45-154", different content + try { + await axios.put( + `http://localhost:${port}/characteristics`, + { characteristics: [{ aid: 1, iid: 9, value: true }] }, + { httpAgent, headers: { authorization: wrongPincode } }, + ); + fail("Expected CONNECTION_AUTHORIZATION_REQUIRED response"); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED); + } + }); + + test("PUT /characteristics with correct pincode should be authorized", async () => { + // use a pincode without leading whitespace — HTTP header values get trimmed + // by node's parser so the global test fixture's " 031-45-154" can't round-trip. + const info = AccessoryInfo.create(serverUsername); + // @ts-expect-error: private access + info.setupID = Accessory._generateSetupID(); + info.displayName = "Outlet"; + info.category = 7; + info.pincode = "031-45-154"; + + server = new HAPServer(info); + server.allowInsecureRequest = true; + const [port] = await bindServer(server); + + server.on(HAPServerEventTypes.SET_CHARACTERISTICS, (connection, writeRequest, callback) => { + callback(undefined, { characteristics: writeRequest.characteristics.map(c => ({ aid: c.aid, iid: c.iid, status: HAPStatus.SUCCESS })) }); + }); + + const response = await axios.put( + `http://localhost:${port}/characteristics`, + { characteristics: [{ aid: 1, iid: 9, value: true }] }, + { httpAgent, headers: { authorization: info.pincode } }, + ); + // a successful insecure PUT returns 204 NO_CONTENT (or 200 if there's a response body) + expect(response.status).toBeGreaterThanOrEqual(200); + expect(response.status).toBeLessThan(300); + }); + + test("POST /resource with wrong-length authorization should not crash timingSafeEqual", async () => { + server = new HAPServer(accessoryInfoUnpaired); + server.allowInsecureRequest = true; + const [port] = await bindServer(server); + + try { + await axios.post( + `http://localhost:${port}/resource`, + { "resource-type": "image", "image-width": 100, "image-height": 100 }, + { httpAgent, headers: { authorization: "short" } }, + ); + fail("Expected CONNECTION_AUTHORIZATION_REQUIRED response"); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED); + expect(error.response?.data).toEqual({ status: HAPStatus.INSUFFICIENT_PRIVILEGES }); + } + }); + }); + describe("error argument in pairing debug logs (fix f12ed233)", () => { let originalEnable: string; let originalLog: (...args: unknown[]) => void; From 83524b19ebaa89ceb3f56f8329df7d51f1e8ba3e Mon Sep 17 00:00:00 2001 From: Ben <43026681+bwp91@users.noreply.github.com> Date: Fri, 8 May 2026 23:13:01 +0100 Subject: [PATCH 14/14] test: cover characteristic warning message for non-Error throws Adds three Characteristic tests verifying the fix from fe8c1b3a: when validateUserInput throws an object without a `message` property (e.g. a non-Error), setValue and updateValue must emit the characteristic warning with the message "Unknown error" instead of the literal string "undefined" produced by `error?.message + ""`. A real Error message is still preserved untouched. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 1 + src/lib/Characteristic.spec.ts | 66 ++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 097f3a580..f1f914436 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. - test: cover camera stream start TLV parsing guards - test: cover error argument in pairing debug logs - test: cover constant-time pincode comparison +- test: cover characteristic warning message for non-Error throws ### Homebridge Dependencies diff --git a/src/lib/Characteristic.spec.ts b/src/lib/Characteristic.spec.ts index 117788d0f..0556bc5a4 100644 --- a/src/lib/Characteristic.spec.ts +++ b/src/lib/Characteristic.spec.ts @@ -2199,4 +2199,70 @@ describe("Characteristic", () => { }); + describe("undefined string in characteristic error warnings (fix fe8c1b3a)", () => { + test("setValue should emit \"Unknown error\" when validateUserInput throws an object without a message", () => { + const characteristic = createCharacteristic(Formats.STRING); + + // throw a non-Error (no `message` property) — without the fix this + // produced `error?.message + ""` === "undefined" in the warning text + // eslint-disable-next-line @typescript-eslint/no-explicit-any + jest.spyOn(characteristic as any, "validateUserInput").mockImplementation(() => { + throw {}; + }); + + const warnings: { type: CharacteristicWarningType; message: string }[] = []; + characteristic.on(CharacteristicEventTypes.CHARACTERISTIC_WARNING, (type, message) => { + warnings.push({ type, message }); + }); + + characteristic.setValue("anything"); + + const errorWarnings = warnings.filter(w => w.type === CharacteristicWarningType.ERROR_MESSAGE); + expect(errorWarnings.length).toBe(1); + expect(errorWarnings[0].message).toBe("Unknown error"); + expect(errorWarnings[0].message).not.toBe("undefined"); + }); + + test("setValue should preserve a real Error message when validateUserInput throws an Error", () => { + const characteristic = createCharacteristic(Formats.STRING); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + jest.spyOn(characteristic as any, "validateUserInput").mockImplementation(() => { + throw new Error("validation failed: bad value"); + }); + + const warnings: { type: CharacteristicWarningType; message: string }[] = []; + characteristic.on(CharacteristicEventTypes.CHARACTERISTIC_WARNING, (type, message) => { + warnings.push({ type, message }); + }); + + characteristic.setValue("anything"); + + const errorWarnings = warnings.filter(w => w.type === CharacteristicWarningType.ERROR_MESSAGE); + expect(errorWarnings.length).toBe(1); + expect(errorWarnings[0].message).toBe("validation failed: bad value"); + }); + + test("updateValue should emit \"Unknown error\" when validateUserInput throws an object without a message", () => { + const characteristic = createCharacteristic(Formats.STRING); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + jest.spyOn(characteristic as any, "validateUserInput").mockImplementation(() => { + throw {}; + }); + + const warnings: { type: CharacteristicWarningType; message: string }[] = []; + characteristic.on(CharacteristicEventTypes.CHARACTERISTIC_WARNING, (type, message) => { + warnings.push({ type, message }); + }); + + characteristic.updateValue("anything"); + + const errorWarnings = warnings.filter(w => w.type === CharacteristicWarningType.ERROR_MESSAGE); + expect(errorWarnings.length).toBe(1); + expect(errorWarnings[0].message).toBe("Unknown error"); + expect(errorWarnings[0].message).not.toBe("undefined"); + }); + }); + });