diff --git a/package.json b/package.json index 4b486fd..ab372fc 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "ink": "^6.6.0", "ink-big-text": "^2.0.0", "ink-spinner": "^5.0.0", + "node-ble": "^1.13.0", "react": "^19.2.4", "sharp": "^0.34.5" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a7f0595..5e4a1ff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: ink-spinner: specifier: ^5.0.0 version: 5.0.0(ink@6.6.0(@types/react@19.2.10)(react@19.2.4))(react@19.2.4) + node-ble: + specifier: ^1.13.0 + version: 1.13.0 react: specifier: ^19.2.4 version: 19.2.4 @@ -244,89 +247,105 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -364,6 +383,10 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@nornagon/put@0.0.8': + resolution: {integrity: sha512-ugvXJjwF5ldtUpa7D95kruNJ41yFQDEKyF5CW4TgKJnh+W/zmlBzXXeKTyqIgwMFrkePN2JqOBqcF0M0oOunow==} + engines: {node: '>=0.3.0'} + '@rollup/rollup-android-arm-eabi@4.57.1': resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==} cpu: [arm] @@ -398,66 +421,79 @@ packages: resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.57.1': resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.57.1': resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.57.1': resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.57.1': resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.57.1': resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.57.1': resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.57.1': resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.57.1': resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.57.1': resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.57.1': resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.57.1': resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.57.1': resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.57.1': resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==} @@ -611,15 +647,25 @@ packages: '@yarnpkg/lockfile@1.1.0': resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + ansi-escapes@7.2.0: resolution: {integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==} engines: {node: '>=18'} + ansi-regex@2.1.1: + resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} + engines: {node: '>=0.10.0'} + ansi-regex@6.2.2: resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} @@ -635,6 +681,20 @@ packages: any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + aproba@1.2.0: + resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==} + + are-we-there-yet@1.1.7: + resolution: {integrity: sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==} + deprecated: This package is no longer supported. + + asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + + assert-plus@1.0.0: + resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} + engines: {node: '>=0.8'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -642,10 +702,31 @@ packages: async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + auto-bind@5.0.1: resolution: {integrity: sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + aws-sign2@0.7.0: + resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} + + aws4@1.13.2: + resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + brace-expansion@1.1.15: + resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -672,6 +753,9 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} + caseless@0.12.0: + resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} + cfonts@3.3.1: resolution: {integrity: sha512-ZGEmN3W9mViWEDjsuPo4nK4h39sfh6YtoneFYp9WLPI/rw8BaSSrfQC6jkrGW3JMvV3ZnExJB/AEqXc/nHYxkw==} engines: {node: '>=10'} @@ -697,6 +781,10 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} @@ -721,6 +809,10 @@ packages: resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + code-point-at@1.1.0: + resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} + engines: {node: '>=0.10.0'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -728,6 +820,10 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + commander@14.0.3: resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} @@ -736,6 +832,9 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} @@ -743,10 +842,19 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + convert-to-spaces@2.0.1: resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + core-util-is@1.0.2: + resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -754,6 +862,13 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + dashdash@1.14.1: + resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} + engines: {node: '>=0.10'} + + dbus-next@0.10.2: + resolution: {integrity: sha512-kLNQoadPstLgKKGIXKrnRsMgtAK/o+ix3ZmcfTfvBHzghiO9yHXpoKImGnB50EXwnfSFaSAullW/7UrSkAISSQ==} + debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -784,6 +899,13 @@ packages: resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} engines: {node: '>=0.10.0'} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -792,9 +914,19 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + ecc-jsbn@0.1.2: + resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} + emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + environment@1.1.0: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} @@ -829,10 +961,26 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + event-stream@3.3.4: + resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + extsprintf@1.3.0: + resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} + engines: {'0': node >=0.6.0} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -842,6 +990,9 @@ packages: picomatch: optional: true + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -852,10 +1003,27 @@ packages: fix-dts-default-cjs-exports@1.0.1: resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + forever-agent@0.6.1: + resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} + + form-data@2.3.3: + resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} + engines: {node: '>= 0.12'} + + from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -864,6 +1032,10 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + gauge@2.7.4: + resolution: {integrity: sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==} + deprecated: This package is no longer supported. + get-east-asian-width@1.4.0: resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} engines: {node: '>=18'} @@ -879,6 +1051,13 @@ packages: get-tsconfig@4.13.1: resolution: {integrity: sha512-EoY1N2xCn44xU6750Sx7OjOIT59FkmstNc3X6y5xpz7D5cBtZRe/3pSlTkDJgqsOk3WwZPkWfonhhUJfttQo3w==} + getpass@0.1.7: + resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -886,6 +1065,15 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + har-schema@2.0.0: + resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} + engines: {node: '>=4'} + + har-validator@5.1.5: + resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==} + engines: {node: '>=6'} + deprecated: this library is no longer supported + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -897,14 +1085,32 @@ packages: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hexy@0.2.11: + resolution: {integrity: sha512-ciq6hFsSG/Bpt2DmrZJtv+56zpPdnq+NQ4ijEFrveKN0ZG1mhl/LdT1NQZ9se6ty1fACcI4d4vYqC9v8EYpH2A==} + hasBin: true + + http-signature@1.2.0: + resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} + engines: {node: '>=0.8', npm: '>=1.3.7'} + indent-string@5.0.0: resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} engines: {node: '>=12'} + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ink-big-text@2.0.0: resolution: {integrity: sha512-Juzqv+rIOLGuhMJiE50VtS6dg6olWfzFdL7wsU/EARSL5Eaa5JNXMogMBm9AkjgzO2Y3UwWCOh87jbhSn8aNdw==} engines: {node: '>=14.16'} @@ -952,6 +1158,10 @@ packages: engines: {node: '>=8'} hasBin: true + is-fullwidth-code-point@1.0.0: + resolution: {integrity: sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==} + engines: {node: '>=0.10.0'} + is-fullwidth-code-point@5.1.0: resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} engines: {node: '>=18'} @@ -969,16 +1179,25 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isstream@0.1.2: + resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} + joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -989,16 +1208,35 @@ packages: js-tokens@9.0.1: resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + jsbi@2.0.5: + resolution: {integrity: sha512-TzO/62Hxeb26QMb4IGlI/5X+QLr9Uqp1FPkwp2+KOICW+Q+vSuFj61c8pkT6wAns4WcK56X7CmSHhJeDGWOqxQ==} + + jsbn@0.1.1: + resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + json-stable-stringify@1.3.0: resolution: {integrity: sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==} engines: {node: '>= 0.4'} + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + jsonfile@6.2.0: resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} jsonify@0.0.1: resolution: {integrity: sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==} + jsprim@1.4.2: + resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} + engines: {node: '>=0.6.0'} + kind-of@3.2.2: resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} engines: {node: '>=0.10.0'} @@ -1017,6 +1255,9 @@ packages: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + long@4.0.0: + resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -1027,6 +1268,9 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + map-stream@0.1.0: + resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -1035,13 +1279,41 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} @@ -1054,6 +1326,9 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nan@2.27.0: + resolution: {integrity: sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1066,6 +1341,9 @@ packages: resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==} engines: {node: ^18 || ^20 || >= 21} + node-ble@1.13.0: + resolution: {integrity: sha512-jg8GmyZwowUcze6t/GEEunYvAcASR9hMUpRxs2jehmgdalDQ0xXtkmDfdAQO4Rq3b1gBxBRo43E/ASzbPf3d/A==} + node-gyp-build@4.6.0: resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} hasBin: true @@ -1074,6 +1352,27 @@ packages: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true + node-gyp@7.1.2: + resolution: {integrity: sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ==} + engines: {node: '>= 10.12.0'} + hasBin: true + + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + + npmlog@4.1.2: + resolution: {integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==} + deprecated: This package is no longer supported. + + number-is-nan@1.0.1: + resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} + engines: {node: '>=0.10.0'} + + oauth-sign@0.9.0: + resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -1082,6 +1381,9 @@ packages: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -1099,6 +1401,10 @@ packages: engines: {node: '>=14', npm: '>5'} hasBin: true + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -1110,6 +1416,12 @@ packages: resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} engines: {node: '>= 14.16'} + pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + + performance-now@2.1.0: + resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1150,9 +1462,23 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.5.5: + resolution: {integrity: sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==} + engines: {node: '>=0.6'} + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -1166,10 +1492,18 @@ packages: resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} engines: {node: '>=0.10.0'} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + request@2.88.2: + resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} + engines: {node: '>= 6'} + deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 + resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} @@ -1181,11 +1515,29 @@ packages: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rollup@4.57.1: resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -1198,6 +1550,9 @@ packages: resolution: {integrity: sha512-AmH3D9hHPFmnF/oq/rvigfiAouAKyK/TjnrkwZRYSFZxNggJxwvbAbfYrLeuvq7ktUdhuHdVdSjj852Z55R+uA==} engines: {node: '>=16.0.0'} + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -1236,6 +1591,14 @@ packages: resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} engines: {node: '>= 12'} + split@0.3.3: + resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + + sshpk@1.18.0: + resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} + engines: {node: '>=0.10.0'} + hasBin: true + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -1246,6 +1609,13 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + stream-combiner@0.0.4: + resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + + string-width@1.0.2: + resolution: {integrity: sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==} + engines: {node: '>=0.10.0'} + string-width@7.2.0: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} @@ -1254,6 +1624,13 @@ packages: resolution: {integrity: sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw==} engines: {node: '>=20'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + strip-ansi@3.0.1: + resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} + engines: {node: '>=0.10.0'} + strip-ansi@7.1.2: resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} @@ -1274,6 +1651,11 @@ packages: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -1281,6 +1663,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -1311,6 +1696,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + tough-cookie@2.5.0: + resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} + engines: {node: '>=0.8'} + tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -1345,6 +1734,12 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + + tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} @@ -1364,10 +1759,28 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + usb@2.17.0: resolution: {integrity: sha512-UuFgrlglgDn5ll6d5l7kl3nDb2Yx43qLUGcDq+7UNLZLtbNug0HZBb2Xodhgx2JZB1LqvU+dOGqLEeYUeZqsHg==} engines: {node: '>=12.22.0 <13.0 || >=14.17.0'} + usocket@0.3.0: + resolution: {integrity: sha512-V/H02RNiaOCJZuPoKont/y12VJaImC6C5xW7OzPFjYu9qnig0yv9hyp9E7Wqjm6d8yZuZouH3NAfDATVMgh2SQ==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuid@3.4.0: + resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + + verror@1.10.0: + resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} + engines: {'0': node >=0.6.0} + vite-node@3.2.4: resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -1451,6 +1864,9 @@ packages: engines: {node: '>=8'} hasBin: true + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + widest-line@5.0.0: resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} engines: {node: '>=18'} @@ -1464,6 +1880,9 @@ packages: resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.19.0: resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} engines: {node: '>=10.0.0'} @@ -1476,6 +1895,17 @@ packages: utf-8-validate: optional: true + xml2js@0.4.23: + resolution: {integrity: sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==} + engines: {node: '>=4.0.0'} + + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yaml@2.8.2: resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} engines: {node: '>= 14.6'} @@ -1684,6 +2114,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@nornagon/put@0.0.8': {} + '@rollup/rollup-android-arm-eabi@4.57.1': optional: true @@ -1918,12 +2350,26 @@ snapshots: '@yarnpkg/lockfile@1.1.0': optional: true + abbrev@1.1.1: + optional: true + acorn@8.15.0: {} + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + optional: true + ansi-escapes@7.2.0: dependencies: environment: 1.1.0 + ansi-regex@2.1.1: + optional: true + ansi-regex@6.2.2: {} ansi-styles@4.3.0: @@ -1935,13 +2381,58 @@ snapshots: any-promise@1.3.0: {} + aproba@1.2.0: + optional: true + + are-we-there-yet@1.1.7: + dependencies: + delegates: 1.0.0 + readable-stream: 2.3.8 + optional: true + + asn1@0.2.6: + dependencies: + safer-buffer: 2.1.2 + optional: true + + assert-plus@1.0.0: + optional: true + assertion-error@2.0.1: {} async@3.2.6: optional: true + asynckit@0.4.0: + optional: true + auto-bind@5.0.1: {} + aws-sign2@0.7.0: + optional: true + + aws4@1.13.2: + optional: true + + balanced-match@1.0.2: + optional: true + + bcrypt-pbkdf@1.0.2: + dependencies: + tweetnacl: 0.14.5 + optional: true + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + optional: true + + brace-expansion@1.1.15: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + optional: true + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -1974,6 +2465,9 @@ snapshots: get-intrinsic: 1.3.0 optional: true + caseless@0.12.0: + optional: true + cfonts@3.3.1: dependencies: supports-color: 8.1.1 @@ -2001,6 +2495,9 @@ snapshots: dependencies: readdirp: 4.1.2 + chownr@2.0.0: + optional: true + ci-info@3.9.0: optional: true @@ -2021,6 +2518,9 @@ snapshots: dependencies: convert-to-spaces: 2.0.1 + code-point-at@1.1.0: + optional: true + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -2029,16 +2529,33 @@ snapshots: color-name@1.1.4: optional: true + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + optional: true + commander@14.0.3: {} commander@4.1.1: {} + concat-map@0.0.1: + optional: true + confbox@0.1.8: {} consola@3.4.2: {} + console-control-strings@1.1.0: + optional: true + convert-to-spaces@2.0.1: {} + core-util-is@1.0.2: + optional: true + + core-util-is@1.0.3: + optional: true + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -2048,6 +2565,23 @@ snapshots: csstype@3.2.3: {} + dashdash@1.14.1: + dependencies: + assert-plus: 1.0.0 + optional: true + + dbus-next@0.10.2: + dependencies: + '@nornagon/put': 0.0.8 + event-stream: 3.3.4 + hexy: 0.2.11 + jsbi: 2.0.5 + long: 4.0.0 + safe-buffer: 5.2.1 + xml2js: 0.4.23 + optionalDependencies: + usocket: 0.3.0 + debug@4.3.4: dependencies: ms: 2.1.2 @@ -2070,6 +2604,12 @@ snapshots: dependencies: is-descriptor: 1.0.3 + delayed-stream@1.0.0: + optional: true + + delegates@1.0.0: + optional: true + detect-libc@2.1.2: {} dunder-proto@1.0.1: @@ -2079,8 +2619,19 @@ snapshots: gopd: 1.2.0 optional: true + duplexer@0.1.2: {} + + ecc-jsbn@0.1.2: + dependencies: + jsbn: 0.1.1 + safer-buffer: 2.1.2 + optional: true + emoji-regex@10.6.0: {} + env-paths@2.2.1: + optional: true + environment@1.1.0: {} es-define-property@1.0.1: @@ -2133,12 +2684,37 @@ snapshots: dependencies: '@types/estree': 1.0.8 + event-stream@3.3.4: + dependencies: + duplexer: 0.1.2 + from: 0.1.7 + map-stream: 0.1.0 + pause-stream: 0.0.11 + split: 0.3.3 + stream-combiner: 0.0.4 + through: 2.3.8 + expect-type@1.3.0: {} + extend@3.0.2: + optional: true + + extsprintf@1.3.0: + optional: true + + fast-deep-equal@3.1.3: + optional: true + + fast-json-stable-stringify@2.1.0: + optional: true + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 + file-uri-to-path@1.0.0: + optional: true + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -2155,6 +2731,18 @@ snapshots: mlly: 1.8.0 rollup: 4.57.1 + forever-agent@0.6.1: + optional: true + + form-data@2.3.3: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + optional: true + + from@0.1.7: {} + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 @@ -2162,11 +2750,31 @@ snapshots: universalify: 2.0.1 optional: true + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + optional: true + + fs.realpath@1.0.0: + optional: true + fsevents@2.3.3: optional: true function-bind@1.1.2: {} + gauge@2.7.4: + dependencies: + aproba: 1.2.0 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 1.0.2 + strip-ansi: 3.0.1 + wide-align: 1.1.5 + optional: true + get-east-asian-width@1.4.0: {} get-intrinsic@1.3.0: @@ -2193,12 +2801,36 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + getpass@0.1.7: + dependencies: + assert-plus: 1.0.0 + optional: true + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + optional: true + gopd@1.2.0: optional: true graceful-fs@4.2.11: optional: true + har-schema@2.0.0: + optional: true + + har-validator@5.1.5: + dependencies: + ajv: 6.15.0 + har-schema: 2.0.0 + optional: true + has-flag@4.0.0: {} has-property-descriptors@1.0.2: @@ -2209,12 +2841,33 @@ snapshots: has-symbols@1.1.0: optional: true + has-unicode@2.0.1: + optional: true + hasown@2.0.2: dependencies: function-bind: 1.1.2 + hexy@0.2.11: {} + + http-signature@1.2.0: + dependencies: + assert-plus: 1.0.0 + jsprim: 1.4.2 + sshpk: 1.18.0 + optional: true + indent-string@5.0.0: {} + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + optional: true + + inherits@2.0.4: + optional: true + ink-big-text@2.0.0(ink@6.6.0(@types/react@19.2.10)(react@19.2.4))(react@19.2.4): dependencies: cfonts: 3.3.1 @@ -2278,6 +2931,11 @@ snapshots: is-docker@2.2.1: optional: true + is-fullwidth-code-point@1.0.0: + dependencies: + number-is-nan: 1.0.1 + optional: true + is-fullwidth-code-point@5.1.0: dependencies: get-east-asian-width: 1.4.0 @@ -2291,23 +2949,43 @@ snapshots: is-number@7.0.0: optional: true + is-typedarray@1.0.0: + optional: true + is-wsl@2.2.0: dependencies: is-docker: 2.2.1 optional: true + isarray@1.0.0: + optional: true + isarray@2.0.5: optional: true isexe@2.0.0: optional: true + isstream@0.1.2: + optional: true + joycon@3.1.1: {} js-tokens@4.0.0: {} js-tokens@9.0.1: {} + jsbi@2.0.5: {} + + jsbn@0.1.1: + optional: true + + json-schema-traverse@0.4.1: + optional: true + + json-schema@0.4.0: + optional: true + json-stable-stringify@1.3.0: dependencies: call-bind: 1.0.8 @@ -2317,6 +2995,9 @@ snapshots: object-keys: 1.1.1 optional: true + json-stringify-safe@5.0.1: + optional: true + jsonfile@6.2.0: dependencies: universalify: 2.0.1 @@ -2327,6 +3008,14 @@ snapshots: jsonify@0.0.1: optional: true + jsprim@1.4.2: + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.4.0 + verror: 1.10.0 + optional: true + kind-of@3.2.2: dependencies: is-buffer: 1.1.6 @@ -2342,6 +3031,8 @@ snapshots: load-tsconfig@0.2.5: {} + long@4.0.0: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -2352,6 +3043,8 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + map-stream@0.1.0: {} + math-intrinsics@1.1.0: optional: true @@ -2361,11 +3054,41 @@ snapshots: picomatch: 2.3.1 optional: true + mime-db@1.52.0: + optional: true + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + optional: true + mimic-fn@2.1.0: {} + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.15 + optional: true + minimist@1.2.8: optional: true + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + optional: true + + minipass@5.0.0: + optional: true + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + optional: true + + mkdirp@1.0.4: + optional: true + mlly@1.8.0: dependencies: acorn: 8.15.0 @@ -2384,6 +3107,9 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 + nan@2.27.0: + optional: true + nanoid@3.3.11: {} node-addon-api@7.0.0: @@ -2391,16 +3117,58 @@ snapshots: node-addon-api@8.5.0: {} + node-ble@1.13.0: + dependencies: + dbus-next: 0.10.2 + node-gyp-build@4.6.0: optional: true node-gyp-build@4.8.4: {} + node-gyp@7.1.2: + dependencies: + env-paths: 2.2.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + nopt: 5.0.0 + npmlog: 4.1.2 + request: 2.88.2 + rimraf: 3.0.2 + semver: 7.7.3 + tar: 6.2.1 + which: 2.0.2 + optional: true + + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + optional: true + + npmlog@4.1.2: + dependencies: + are-we-there-yet: 1.1.7 + console-control-strings: 1.1.0 + gauge: 2.7.4 + set-blocking: 2.0.0 + optional: true + + number-is-nan@1.0.1: + optional: true + + oauth-sign@0.9.0: + optional: true + object-assign@4.1.1: {} object-keys@1.1.1: optional: true + once@1.4.0: + dependencies: + wrappy: 1.0.2 + optional: true + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -2431,6 +3199,9 @@ snapshots: yaml: 2.8.2 optional: true + path-is-absolute@1.0.1: + optional: true + path-key@3.1.1: optional: true @@ -2438,6 +3209,13 @@ snapshots: pathval@2.0.1: {} + pause-stream@0.0.11: + dependencies: + through: 2.3.8 + + performance-now@2.1.0: + optional: true + picocolors@1.1.1: {} picomatch@2.3.1: @@ -2467,12 +3245,26 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + process-nextick-args@2.0.1: + optional: true + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 + psl@1.15.0: + dependencies: + punycode: 2.3.1 + optional: true + + punycode@2.3.1: + optional: true + + qs@6.5.5: + optional: true + react-is@16.13.1: {} react-reconciler@0.33.0(react@19.2.4): @@ -2482,8 +3274,43 @@ snapshots: react@19.2.4: {} + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + optional: true + readdirp@4.1.2: {} + request@2.88.2: + dependencies: + aws-sign2: 0.7.0 + aws4: 1.13.2 + caseless: 0.12.0 + combined-stream: 1.0.8 + extend: 3.0.2 + forever-agent: 0.6.1 + form-data: 2.3.3 + har-validator: 5.1.5 + http-signature: 1.2.0 + is-typedarray: 1.0.0 + isstream: 0.1.2 + json-stringify-safe: 5.0.1 + mime-types: 2.1.35 + oauth-sign: 0.9.0 + performance-now: 2.1.0 + qs: 6.5.5 + safe-buffer: 5.2.1 + tough-cookie: 2.5.0 + tunnel-agent: 0.6.0 + uuid: 3.4.0 + optional: true + resolve-from@5.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -2493,6 +3320,11 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + optional: true + rollup@4.57.1: dependencies: '@types/estree': 1.0.8 @@ -2524,6 +3356,16 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.57.1 fsevents: 2.3.3 + safe-buffer@5.1.2: + optional: true + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: + optional: true + + sax@1.6.0: {} + scheduler@0.27.0: {} semver@7.7.3: {} @@ -2548,6 +3390,9 @@ snapshots: - supports-color optional: true + set-blocking@2.0.0: + optional: true + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -2613,6 +3458,23 @@ snapshots: source-map@0.7.6: {} + split@0.3.3: + dependencies: + through: 2.3.8 + + sshpk@1.18.0: + dependencies: + asn1: 0.2.6 + assert-plus: 1.0.0 + bcrypt-pbkdf: 1.0.2 + dashdash: 1.14.1 + ecc-jsbn: 0.1.2 + getpass: 0.1.7 + jsbn: 0.1.1 + safer-buffer: 2.1.2 + tweetnacl: 0.14.5 + optional: true + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -2621,6 +3483,17 @@ snapshots: std-env@3.10.0: {} + stream-combiner@0.0.4: + dependencies: + duplexer: 0.1.2 + + string-width@1.0.2: + dependencies: + code-point-at: 1.1.0 + is-fullwidth-code-point: 1.0.0 + strip-ansi: 3.0.1 + optional: true + string-width@7.2.0: dependencies: emoji-regex: 10.6.0 @@ -2632,6 +3505,16 @@ snapshots: get-east-asian-width: 1.4.0 strip-ansi: 7.1.2 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + optional: true + + strip-ansi@3.0.1: + dependencies: + ansi-regex: 2.1.1 + optional: true + strip-ansi@7.1.2: dependencies: ansi-regex: 6.2.2 @@ -2659,6 +3542,16 @@ snapshots: dependencies: has-flag: 4.0.0 + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + optional: true + thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -2667,6 +3560,8 @@ snapshots: dependencies: any-promise: 1.3.0 + through@2.3.8: {} + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -2690,6 +3585,12 @@ snapshots: is-number: 7.0.0 optional: true + tough-cookie@2.5.0: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + optional: true + tree-kill@1.2.2: {} ts-interface-checker@0.1.13: {} @@ -2732,6 +3633,14 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + optional: true + + tweetnacl@0.14.5: + optional: true + type-fest@4.41.0: {} typescript@5.9.3: {} @@ -2743,6 +3652,11 @@ snapshots: universalify@2.0.1: optional: true + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + optional: true + usb@2.17.0: dependencies: '@types/w3c-web-usb': 1.0.13 @@ -2750,6 +3664,26 @@ snapshots: node-gyp-build: 4.8.4 optional: true + usocket@0.3.0: + dependencies: + bindings: 1.5.0 + nan: 2.27.0 + node-gyp: 7.1.2 + optional: true + + util-deprecate@1.0.2: + optional: true + + uuid@3.4.0: + optional: true + + verror@1.10.0: + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.3.0 + optional: true + vite-node@3.2.4(@types/node@22.19.8)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 @@ -2836,6 +3770,11 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + wide-align@1.1.5: + dependencies: + string-width: 1.0.2 + optional: true + widest-line@5.0.0: dependencies: string-width: 7.2.0 @@ -2851,8 +3790,21 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.1.2 + wrappy@1.0.2: + optional: true + ws@8.19.0: {} + xml2js@0.4.23: + dependencies: + sax: 1.6.0 + xmlbuilder: 11.0.1 + + xmlbuilder@11.0.1: {} + + yallist@4.0.0: + optional: true + yaml@2.8.2: optional: true diff --git a/src/lib/ble/backend.ts b/src/lib/ble/backend.ts new file mode 100644 index 0000000..a629495 --- /dev/null +++ b/src/lib/ble/backend.ts @@ -0,0 +1,51 @@ +export interface DiscoveredDevice { + address: string; + name: string | null; +} + +export interface BleCharacteristic { + uuid: string; + write(data: Buffer, withoutResponse: boolean): Promise; + subscribe(onData: (data: Buffer) => void): Promise; + unsubscribe(): Promise; +} + +export interface BleBackend { + readonly name: string; + + init(): Promise; + + scanFor( + matcher: (device: DiscoveredDevice) => boolean, + timeoutMs: number, + ): Promise; + + stopScan(): Promise; + + connect(address: string): Promise; + + onDisconnect(callback: () => void): void; + + discoverCharacteristics( + writeUUID: string, + notifyUUID: string, + normalizeUUID: (uuid: string) => string, + ): Promise<{ write: BleCharacteristic; notify: BleCharacteristic }>; + + disconnect(): Promise; +} + +export type BleBackendType = "noble" | "dbus"; + +/** + * Uses dbus on Linux and noble on other platforms. + * You can override with BEAMBOXCTL_BLE_BACKEND=noble|dbus. + */ +export function resolveBackendType(): BleBackendType { + const override = process.env.BEAMBOXCTL_BLE_BACKEND?.toLowerCase().trim(); + if (override === "noble" || override === "dbus") { + return override; + } + + return process.platform === "linux" ? "dbus" : "noble"; +} diff --git a/src/lib/ble/backends/dbus-backend.ts b/src/lib/ble/backends/dbus-backend.ts new file mode 100644 index 0000000..74c961f --- /dev/null +++ b/src/lib/ble/backends/dbus-backend.ts @@ -0,0 +1,244 @@ +import { createBluetooth } from "node-ble"; +import type NodeBle from "node-ble"; +import { ConnectionError } from "../../utils/errors.ts"; +import { logger } from "../../utils/logger.ts"; +import type { + BleBackend, + BleCharacteristic, + DiscoveredDevice, +} from "../backend.ts"; + +const POLL_INTERVAL_MS = 1000; + +class DBusCharacteristic implements BleCharacteristic { + private valueChangedListener: ((buffer: Buffer) => void) | null = null; + + constructor( + private characteristic: NodeBle.GattCharacteristic, + public readonly uuid: string, + ) {} + + async write(data: Buffer, withoutResponse: boolean): Promise { + if (withoutResponse) { + await this.characteristic.writeValueWithoutResponse(data); + } else { + await this.characteristic.writeValueWithResponse(data); + } + } + + async subscribe(onData: (data: Buffer) => void): Promise { + this.valueChangedListener = onData; + this.characteristic.on("valuechanged", onData); + await this.characteristic.startNotifications(); + } + + async unsubscribe(): Promise { + if (this.valueChangedListener) { + this.characteristic.removeListener( + "valuechanged", + this.valueChangedListener, + ); + this.valueChangedListener = null; + } + await this.characteristic.stopNotifications().catch(() => {}); + } +} + +export class DBusBackend implements BleBackend { + readonly name = "dbus"; + + private bluetooth: NodeBle.Bluetooth | null = null; + private destroy: (() => void) | null = null; + private adapter: NodeBle.Adapter | null = null; + private device: NodeBle.Device | null = null; + private scanning = false; + + async init(): Promise { + if (this.bluetooth) { + return; + } + + const { bluetooth, destroy } = createBluetooth(); + this.bluetooth = bluetooth; + this.destroy = destroy; + + try { + this.adapter = await bluetooth.defaultAdapter(); + } catch { + throw new ConnectionError( + "No Bluetooth adapter found (is bluetoothd running?)", + ); + } + + if (!(await this.adapter.isPowered())) { + throw new ConnectionError("Bluetooth adapter is not powered on"); + } + } + + private async ensureDiscovering(): Promise { + if (!this.adapter) { + throw new ConnectionError("Bluetooth adapter not initialized"); + } + if (!(await this.adapter.isDiscovering())) { + await this.adapter.startDiscovery(); + } + this.scanning = true; + } + + async scanFor( + matcher: (device: DiscoveredDevice) => boolean, + timeoutMs: number, + ): Promise { + if (!this.adapter) { + throw new ConnectionError("Bluetooth adapter not initialized"); + } + + await this.ensureDiscovering(); + + const adapter = this.adapter; + + // Keep re-checking unnamed devices on every poll instead of giving up on first sight. + // Not sure if this is the best way to handle this. + + const named = new Map(); + const deadline = Date.now() + timeoutMs; + + while (Date.now() < deadline) { + const addresses = await adapter.devices(); + + for (const address of addresses) { + if (named.get(address)) { + continue; + } + + let name: string | null = null; + try { + const remoteDevice = await adapter.getDevice(address); + name = await remoteDevice.getName().catch(async () => { + return await remoteDevice.getAlias().catch(() => null); + }); + } catch { + // device vanished mid-scan?? + } + + if (name && named.get(address) !== name) { + logger.debug(`Discovered device: ${name} (${address})`); + } + named.set(address, name); + + const candidate: DiscoveredDevice = { address, name }; + if (matcher(candidate)) { + await this.stopScan(); + this.device = await adapter.getDevice(address); + return candidate; + } + } + + await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS)); + } + + await this.stopScan(); + return null; + } + + async stopScan(): Promise { + if (this.adapter && this.scanning) { + await this.adapter.stopDiscovery().catch(() => {}); + this.scanning = false; + } + } + + async connect(address: string): Promise { + if (!this.adapter) { + throw new ConnectionError("Bluetooth adapter not initialized"); + } + + if (!this.device) { + this.device = await this.adapter.getDevice(address); + } + + if (await this.device.isConnected().catch(() => false)) { + await this.device.disconnect().catch(() => {}); + await new Promise((resolve) => setTimeout(resolve, 500)); + } + + await this.device.connect(); + } + + onDisconnect(callback: () => void): void { + this.device?.once("disconnect", callback); + } + + async discoverCharacteristics( + writeUUID: string, + notifyUUID: string, + normalizeUUID: (uuid: string) => string, + ): Promise<{ write: BleCharacteristic; notify: BleCharacteristic }> { + if (!this.device) { + throw new ConnectionError("Not connected to device"); + } + + const gattServer = await this.device.gatt(); + const serviceUuids = await gattServer.services(); + + const normalizedWriteUuid = normalizeUUID(writeUUID); + const normalizedNotifyUuid = normalizeUUID(notifyUUID); + + let writeChar: { char: NodeBle.GattCharacteristic; uuid: string } | null = + null; + let notifyChar: { char: NodeBle.GattCharacteristic; uuid: string } | null = + null; + + for (const serviceUuid of serviceUuids) { + const service = await gattServer.getPrimaryService(serviceUuid); + const charUuids = await service.characteristics(); + + for (const charUuid of charUuids) { + const normalizedCharUuid = normalizeUUID(charUuid); + + if (normalizedCharUuid === normalizedWriteUuid && !writeChar) { + writeChar = { + char: await service.getCharacteristic(charUuid), + uuid: charUuid, + }; + } + if (normalizedCharUuid === normalizedNotifyUuid && !notifyChar) { + notifyChar = { + char: await service.getCharacteristic(charUuid), + uuid: charUuid, + }; + } + } + } + + if (!writeChar || !notifyChar) { + throw new ConnectionError("Could not find required characteristics"); + } + + return { + write: new DBusCharacteristic(writeChar.char, writeChar.uuid), + notify: new DBusCharacteristic(notifyChar.char, notifyChar.uuid), + }; + } + + async disconnect(): Promise { + try { + await this.stopScan(); + + if (this.device) { + this.device.removeAllListeners(); + if (await this.device.isConnected().catch(() => false)) { + await this.device.disconnect().catch(() => {}); + } + this.device = null; + } + + this.destroy?.(); + this.destroy = null; + this.bluetooth = null; + this.adapter = null; + } catch { + // best-effort cleanup + } + } +} diff --git a/src/lib/ble/backends/noble-backend.ts b/src/lib/ble/backends/noble-backend.ts new file mode 100644 index 0000000..419f73c --- /dev/null +++ b/src/lib/ble/backends/noble-backend.ts @@ -0,0 +1,202 @@ +import noble from "@stoprocent/noble"; +import type { Peripheral, Characteristic } from "@stoprocent/noble"; +import { ConnectionError } from "../../utils/errors.ts"; +import { logger } from "../../utils/logger.ts"; +import type { + BleBackend, + BleCharacteristic, + DiscoveredDevice, +} from "../backend.ts"; + +class NobleCharacteristic implements BleCharacteristic { + constructor(private characteristic: Characteristic) {} + + get uuid(): string { + return this.characteristic.uuid; + } + + async write(data: Buffer, withoutResponse: boolean): Promise { + await this.characteristic.writeAsync(data, withoutResponse); + } + + async subscribe(onData: (data: Buffer) => void): Promise { + await this.characteristic.subscribeAsync(); + this.characteristic.on("data", onData); + } + + async unsubscribe(): Promise { + this.characteristic.removeAllListeners(); + await this.characteristic.unsubscribeAsync().catch(() => {}); + } +} + +export class NobleBackend implements BleBackend { + readonly name = "noble"; + + private peripheral: Peripheral | null = null; + private initialized = false; + + async init(): Promise { + if (this.initialized) { + return; + } + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + reject(new ConnectionError("Bluetooth adapter initialization timeout")); + }, 10000); + + const checkState = (state: string) => { + if (state === "poweredOn") { + clearTimeout(timeout); + this.initialized = true; + resolve(); + } else if (state === "poweredOff") { + clearTimeout(timeout); + reject(new ConnectionError("Bluetooth adapter is not powered on")); + } else if (state === "unsupported") { + clearTimeout(timeout); + reject( + new ConnectionError("Bluetooth is not supported on this device"), + ); + } else if (state === "unauthorized") { + clearTimeout(timeout); + reject(new ConnectionError("Bluetooth access not authorized")); + } + }; + + if ((noble as any).state === "poweredOn") { + clearTimeout(timeout); + this.initialized = true; + resolve(); + return; + } + + noble.on("stateChange", checkState); + }); + } + + async scanFor( + matcher: (device: DiscoveredDevice) => boolean, + timeoutMs: number, + ): Promise { + return new Promise((resolve) => { + let found = false; + + const timeoutId = setTimeout(async () => { + if (!found) { + await this.stopScan(); + noble.removeListener("discover", onDiscover); + resolve(null); + } + }, timeoutMs); + + const onDiscover = async (peripheral: Peripheral) => { + const device: DiscoveredDevice = { + address: peripheral.address || peripheral.id, + name: peripheral.advertisement.localName ?? null, + }; + + logger.debug( + `Discovered device: ${device.name ?? "(unnamed)"} (${device.address})`, + ); + + if (matcher(device)) { + found = true; + clearTimeout(timeoutId); + await this.stopScan(); + noble.removeListener("discover", onDiscover); + this.peripheral = peripheral; + resolve(device); + } + }; + + noble.on("discover", onDiscover); + + noble.startScanningAsync([], false).catch((err: Error) => { + clearTimeout(timeoutId); + noble.removeListener("discover", onDiscover); + resolve(null); + }); + }); + } + + async stopScan(): Promise { + await noble.stopScanningAsync().catch(() => {}); + } + + async connect(address: string): Promise { + if (!this.peripheral) { + throw new ConnectionError(`No discovered peripheral for ${address}`); + } + + if ( + this.peripheral.state === "connected" || + this.peripheral.state === "connecting" + ) { + await this.peripheral.disconnectAsync().catch(() => {}); + await new Promise((resolve) => setTimeout(resolve, 500)); + } + + await this.peripheral.connectAsync(); + } + + onDisconnect(callback: () => void): void { + this.peripheral?.once("disconnect", callback); + } + + async discoverCharacteristics( + writeUUID: string, + notifyUUID: string, + normalizeUUID: (uuid: string) => string, + ): Promise<{ write: BleCharacteristic; notify: BleCharacteristic }> { + if (!this.peripheral) { + throw new ConnectionError("Not connected to device"); + } + + const { services } = + await this.peripheral.discoverAllServicesAndCharacteristicsAsync(); + + const normalizedWriteUuid = normalizeUUID(writeUUID); + const normalizedNotifyUuid = normalizeUUID(notifyUUID); + + let writeChar: Characteristic | null = null; + let notifyChar: Characteristic | null = null; + + for (const service of services) { + for (const char of service.characteristics) { + const normalizedCharUuid = normalizeUUID(char.uuid); + if (normalizedCharUuid === normalizedWriteUuid) { + writeChar = char; + } + if (normalizedCharUuid === normalizedNotifyUuid) { + notifyChar = char; + } + } + } + + if (!writeChar || !notifyChar) { + throw new ConnectionError("Could not find required characteristics"); + } + + return { + write: new NobleCharacteristic(writeChar), + notify: new NobleCharacteristic(notifyChar), + }; + } + + async disconnect(): Promise { + try { + if (this.peripheral) { + this.peripheral.removeAllListeners(); + await this.peripheral.disconnectAsync().catch(() => {}); + this.peripheral = null; + } + + noble.removeAllListeners(); + await noble.stopScanningAsync().catch(() => {}); + } catch { + // best-effort cleanup + } + } +} diff --git a/src/lib/ble/ble-client.ts b/src/lib/ble/ble-client.ts index 87bf279..25bd71f 100644 --- a/src/lib/ble/ble-client.ts +++ b/src/lib/ble/ble-client.ts @@ -1,13 +1,20 @@ import { EventEmitter } from "node:events"; EventEmitter.defaultMaxListeners = 20; -import noble from "@stoprocent/noble"; -import type { Peripheral, Characteristic } from "@stoprocent/noble"; import type { BLEConfig, ProtocolConfig } from "../protocol/index.ts"; import { PacketType } from "../protocol/index.ts"; import { DeviceNotFoundError, ConnectionError } from "../utils/errors.ts"; import { PayloadBuilder, ResponseParser } from "../protocol/index.ts"; import { logger, LogEventType } from "../utils/logger.ts"; +import type { BleBackend, BleCharacteristic } from "./backend.ts"; +import { resolveBackendType } from "./backend.ts"; +import { NobleBackend } from "./backends/noble-backend.ts"; +import { DBusBackend } from "./backends/dbus-backend.ts"; + +function createBackend(): BleBackend { + const type = resolveBackendType(); + return type === "dbus" ? new DBusBackend() : new NobleBackend(); +} /** * Handles notifications from the BeamBox device @@ -214,14 +221,15 @@ class NotificationHandler { * Manages BLE connection and data transfer to BeamBox device */ export class BleUploader { - private peripheral: Peripheral | null = null; - private writeCharacteristic: Characteristic | null = null; - private notifyCharacteristic: Characteristic | null = null; + private backend: BleBackend; + private writeCharacteristic: BleCharacteristic | null = null; + private notifyCharacteristic: BleCharacteristic | null = null; private notificationHandler: NotificationHandler; private payloadBuilder: PayloadBuilder; private chunkDelay: number; private verbose: boolean = false; private isInitialized: boolean = false; + private connectedAddress: string | null = null; constructor( private deviceAddress: string | null, @@ -234,50 +242,20 @@ export class BleUploader { this.verbose = verbose; this.notificationHandler = new NotificationHandler(verbose); this.payloadBuilder = new PayloadBuilder(protocolConfig); + this.backend = createBackend(); + logger.debug(`Using BLE backend: ${this.backend.name}`); } /** - * Initialize Bluetooth adapter and wait for powered on state + * Initialize Bluetooth adapter and wait for it to be ready */ private async initBluetooth(): Promise { if (this.isInitialized) { return; } - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - reject(new ConnectionError("Bluetooth adapter initialization timeout")); - }, 10000); - - const checkState = (state: string) => { - if (state === "poweredOn") { - clearTimeout(timeout); - this.isInitialized = true; - resolve(); - } else if (state === "poweredOff") { - clearTimeout(timeout); - reject(new ConnectionError("Bluetooth adapter is not powered on")); - } else if (state === "unsupported") { - clearTimeout(timeout); - reject( - new ConnectionError("Bluetooth is not supported on this device"), - ); - } else if (state === "unauthorized") { - clearTimeout(timeout); - reject(new ConnectionError("Bluetooth access not authorized")); - } - }; - - // Check current state first - if ((noble as any).state === "poweredOn") { - clearTimeout(timeout); - this.isInitialized = true; - resolve(); - return; - } - - noble.on("stateChange", checkState); - }); + await this.backend.init(); + this.isInitialized = true; } /** @@ -315,50 +293,26 @@ export class BleUploader { logger.info("Starting device scan...", LogEventType.SCAN_START); - return new Promise((resolve) => { - const timeout = this.bleConfig.scanTimeout * 1000; - let found = false; + const targetName = this.bleConfig.deviceName.toLowerCase(); + const timeout = this.bleConfig.scanTimeout * 1000; - const timeoutId = setTimeout(async () => { - if (!found) { - await noble.stopScanningAsync(); - noble.removeListener("discover", onDiscover); - resolve(null); - } - }, timeout); + const found = await this.backend.scanFor((device) => { + return ( + !!device.name && device.name.toLowerCase().includes(targetName) + ); + }, timeout); - const onDiscover = async (peripheral: Peripheral) => { - const name = peripheral.advertisement.localName; + if (!found) { + return null; + } - if ( - name && - name.toLowerCase().includes(this.bleConfig.deviceName.toLowerCase()) - ) { - found = true; - clearTimeout(timeoutId); - await noble.stopScanningAsync(); - noble.removeListener("discover", onDiscover); + logger.info( + `Found device: ${found.name} (${found.address})`, + LogEventType.DEVICE_FOUND, + { name: found.name, address: found.address }, + ); - const address = peripheral.address || peripheral.id; - logger.info( - `Found device: ${name} (${address})`, - LogEventType.DEVICE_FOUND, - { name, address }, - ); - - this.peripheral = peripheral; - resolve(address); - } - }; - - noble.on("discover", onDiscover); - - noble.startScanningAsync([], false).catch((err: Error) => { - clearTimeout(timeoutId); - logger.error(`Scan error: ${err}`); - resolve(null); - }); - }); + return found.address; } /** @@ -369,7 +323,7 @@ export class BleUploader { try { await this.initBluetooth(); - if (!this.peripheral) { + if (!this.connectedAddress) { if (!this.deviceAddress) { logger.info("Scanning for device...", LogEventType.SCAN_START); const address = await this.findDevice(); @@ -385,74 +339,30 @@ export class BleUploader { LogEventType.SCAN_START, ); - const foundPeripheral = await new Promise( - (resolve) => { - const timeout = this.bleConfig.scanTimeout * 1000; - let found = false; + const targetAddress = this.deviceAddress.toLowerCase(); + const timeout = this.bleConfig.scanTimeout * 1000; - const timeoutId = setTimeout(async () => { - if (!found) { - await noble.stopScanningAsync(); - noble.removeListener("discover", onDiscover); - resolve(null); - } - }, timeout); - - const onDiscover = async (peripheral: Peripheral) => { - const address = peripheral.address || peripheral.id; - if ( - address.toLowerCase() === this.deviceAddress?.toLowerCase() - ) { - found = true; - clearTimeout(timeoutId); - await noble.stopScanningAsync(); - noble.removeListener("discover", onDiscover); - resolve(peripheral); - } - }; - - noble.on("discover", onDiscover); - noble.startScanningAsync([], false).catch(() => { - clearTimeout(timeoutId); - resolve(null); - }); - }, + const found = await this.backend.scanFor( + (device) => device.address.toLowerCase() === targetAddress, + timeout, ); - if (!foundPeripheral) { + if (!found) { throw new DeviceNotFoundError( `Could not find device with address '${this.deviceAddress}'`, ); } - this.peripheral = foundPeripheral; } } - // Stop scanning before connecting - await noble.stopScanningAsync().catch(() => {}); - - // Check peripheral state before connecting - logger.debug( - `Peripheral state before connect: ${this.peripheral!.state}`, - ); - - // If already connected, disconnect first to ensure clean state - if ( - this.peripheral!.state === "connected" || - this.peripheral!.state === "connecting" - ) { - logger.debug( - "Device already connected/connecting, disconnecting first...", - ); - await this.peripheral!.disconnectAsync().catch(() => {}); - await this.sleep(500); // Brief delay after disconnect - } + await this.backend.stopScan(); // Connect to device logger.info("Connecting to device...", LogEventType.CONNECT_START); const connectStartTime = Date.now(); - await this.peripheral!.connectAsync(); + await this.backend.connect(this.deviceAddress!); + this.connectedAddress = this.deviceAddress; const connectDuration = Date.now() - connectStartTime; logger.info( @@ -461,9 +371,9 @@ export class BleUploader { ); // Handle disconnect events - this.peripheral!.once("disconnect", () => { + this.backend.onDisconnect(() => { logger.info("Device disconnected"); - this.peripheral = null; + this.connectedAddress = null; this.writeCharacteristic = null; this.notifyCharacteristic = null; }); @@ -473,10 +383,7 @@ export class BleUploader { // Setup notifications if (this.notifyCharacteristic) { - await this.notifyCharacteristic.subscribeAsync(); - - // Listen for notifications - this.notifyCharacteristic.on("data", (data: Buffer) => { + await this.notifyCharacteristic.subscribe((data: Buffer) => { this.notificationHandler.handleNotification(data); }); } @@ -509,13 +416,6 @@ export class BleUploader { * Discover required characteristics on the device */ private async discoverCharacteristics(): Promise { - if (!this.peripheral) { - throw new ConnectionError("Not connected to device"); - } - - const { services } = - await this.peripheral.discoverAllServicesAndCharacteristicsAsync(); - const normalizedWriteUuid = this.normalizeUUID( this.bleConfig.writeCharacteristicUUID, ); @@ -526,38 +426,25 @@ export class BleUploader { logger.debug(`Looking for write UUID: ${normalizedWriteUuid}`); logger.debug(`Looking for notify UUID: ${normalizedNotifyUuid}`); - // Iterate through services and their characteristics - for (const service of services) { - logger.debug(`Service: ${service.uuid}`); + const { write, notify } = await this.backend.discoverCharacteristics( + this.bleConfig.writeCharacteristicUUID, + this.bleConfig.notifyCharacteristicUUID, + (uuid) => this.normalizeUUID(uuid), + ); - for (const char of service.characteristics) { - const normalizedCharUuid = this.normalizeUUID(char.uuid); - logger.debug( - ` Characteristic: ${char.uuid} (normalized: ${normalizedCharUuid})`, - ); + this.writeCharacteristic = write; + this.notifyCharacteristic = notify; - if (normalizedCharUuid === normalizedWriteUuid) { - this.writeCharacteristic = char; - logger.debug( - `Found write characteristic: ${char.uuid}`, - LogEventType.DISCOVER_CHAR, - { type: "write", uuid: char.uuid }, - ); - } - if (normalizedCharUuid === normalizedNotifyUuid) { - this.notifyCharacteristic = char; - logger.debug( - `Found notify characteristic: ${char.uuid}`, - LogEventType.DISCOVER_CHAR, - { type: "notify", uuid: char.uuid }, - ); - } - } - } - - if (!this.writeCharacteristic || !this.notifyCharacteristic) { - throw new ConnectionError("Could not find required characteristics"); - } + logger.debug( + `Found write characteristic: ${write.uuid}`, + LogEventType.DISCOVER_CHAR, + { type: "write", uuid: write.uuid }, + ); + logger.debug( + `Found notify characteristic: ${notify.uuid}`, + LogEventType.DISCOVER_CHAR, + { type: "notify", uuid: notify.uuid }, + ); } /** @@ -566,16 +453,13 @@ export class BleUploader { public async disconnect(): Promise { try { if (this.notifyCharacteristic) { - this.notifyCharacteristic.removeAllListeners(); - await this.notifyCharacteristic.unsubscribeAsync().catch(() => {}); - } - if (this.peripheral) { - this.peripheral.removeAllListeners(); - await this.peripheral.disconnectAsync().catch(() => {}); + await this.notifyCharacteristic.unsubscribe(); } - noble.removeAllListeners(); - await noble.stopScanningAsync().catch(() => {}); + await this.backend.disconnect(); + this.connectedAddress = null; + this.writeCharacteristic = null; + this.notifyCharacteristic = null; } catch (error) { logger.warning(`Error during disconnect: ${error}`); } @@ -618,7 +502,7 @@ export class BleUploader { this.notificationHandler.logSentPacket(packet, "Image info packet"); // Write without response - await this.writeCharacteristic.writeAsync(packet, true); + await this.writeCharacteristic.write(packet, true); await this.sleep(this.protocolConfig.imageInfoDelay * 1000); } @@ -696,7 +580,7 @@ export class BleUploader { } // Write without response - await this.writeCharacteristic.writeAsync(packet, true); + await this.writeCharacteristic.write(packet, true); if (this.notificationHandler.errorFlag) { logger.error("Device error flag set; aborting send.");