From dd1c400e3526b1f8880a9ce91d3c50ab718d7f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Crist=C3=B3bal=20Contreras=20Rubio?= Date: Tue, 9 Jul 2024 14:05:22 +0200 Subject: [PATCH 1/6] Update deps of whole monorepo --- package-lock.json | 2887 ++++++++++++++++++--------------------------- package.json | 18 +- 2 files changed, 1170 insertions(+), 1735 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1d63b46..7e594e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,20 +12,20 @@ "packages/*" ], "devDependencies": { - "@types/node": "20.12.10", - "@vitest/coverage-v8": "1.6.0", - "chai": "5.1.0", + "@types/node": "20.14.10", + "@vitest/coverage-v8": "2.0.1", + "chai": "5.1.1", "deep-equal-in-any-order": "2.0.6", - "mocha": "10.4.0", - "node-red": "3.1.9", + "mocha": "10.6.0", + "node-red": "4.0.2", "node-red-node-test-helper": "0.3.4", "ts-standard": "12.0.2", - "tsup": "8.0.2", - "typescript": "5.4.5", - "vitest": "1.6.0" + "tsup": "8.1.0", + "typescript": "5.5.3", + "vitest": "2.0.1" }, "peerDependencies": { - "valibot": ">=0.30.0" + "valibot": ">=0.36.0" } }, "node_modules/@ampproject/remapping": { @@ -72,9 +72,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", - "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" @@ -143,10 +143,41 @@ "resolved": "packages/thelmabiotel-tblive-nodered", "link": true }, + "node_modules/@emnapi/core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.2.0.tgz", + "integrity": "sha512-E7Vgw78I93we4ZWdYCb4DGAwRROGkMIXk7/y87UmANR+J6qsWusmC3gLt0H+O0KOt5e6O38U8oJamgbudrES/w==", + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", + "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz", + "integrity": "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], @@ -160,9 +191,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -176,9 +207,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], @@ -192,9 +223,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -208,9 +239,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -224,9 +255,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -240,9 +271,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -256,9 +287,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], @@ -272,9 +303,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -288,9 +319,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -304,9 +335,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -320,9 +351,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], @@ -336,9 +367,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -352,9 +383,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], @@ -368,9 +399,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -384,9 +415,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -400,9 +431,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -416,9 +447,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -432,9 +463,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -448,9 +479,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], @@ -464,9 +495,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -480,9 +511,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], @@ -496,9 +527,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -783,6 +814,18 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -852,106 +895,71 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "dev": true, - "optional": true, - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.4.tgz", + "integrity": "sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==", "dev": true, "optional": true, "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "optional": true, - "bin": { - "semver": "bin/semver.js" + "@emnapi/core": "^1.1.0", + "@emnapi/runtime": "^1.1.0", + "@tybys/wasm-util": "^0.9.0" } }, "node_modules/@node-red/editor-api": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@node-red/editor-api/-/editor-api-3.1.9.tgz", - "integrity": "sha512-HHhFiwxmD8V5+U/xe+Gl9T0oAnwFeA7zisG8VW+Ruh3apGQvV9l5UoL9Yg00jEPDOhL99k/wqcXI42lakEkiKw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@node-red/editor-api/-/editor-api-4.0.2.tgz", + "integrity": "sha512-ZG0n1fpwAP5qhwa1vXdhHGhtVm6flmQKp1N++RPxqmF8uZlkI5wI0FzDrF3V/En7+gUD+o5ylcca4kq+I8GIdA==", "dev": true, "dependencies": { - "@node-red/editor-client": "3.1.9", - "@node-red/util": "3.1.9", + "@node-red/editor-client": "4.0.2", + "@node-red/util": "4.0.2", "bcryptjs": "2.4.3", "body-parser": "1.20.2", "clone": "2.1.2", "cors": "2.8.5", "express": "4.19.2", - "express-session": "1.17.3", + "express-session": "1.18.0", "memorystore": "1.6.7", "mime": "3.0.0", "multer": "1.4.5-lts.1", "mustache": "4.2.0", - "oauth2orize": "1.11.1", - "passport": "0.6.0", + "oauth2orize": "1.12.0", + "passport": "0.7.0", "passport-http-bearer": "1.0.1", "passport-oauth2-client-password": "0.1.2", - "ws": "7.5.6" + "ws": "7.5.10" }, "optionalDependencies": { - "bcrypt": "5.1.0" + "@node-rs/bcrypt": "1.10.4" } }, "node_modules/@node-red/editor-client": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@node-red/editor-client/-/editor-client-3.1.9.tgz", - "integrity": "sha512-k8ik9fqcUxwsjEL0bBywNRYoFk7VZxdcoXRKCtcB3H8T/KRgQBDZu4j27dtff/5WPqnvtmXOQBbdDrhluMO0ng==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@node-red/editor-client/-/editor-client-4.0.2.tgz", + "integrity": "sha512-B8UsqaxnTeIshgUFhMKj7hk/ddyxSXFTcRnuSw0uC39+ki3UUQ3pWJT8FLOr0NVypCFD4jq8qDzMundD0FVLFw==", "dev": true }, "node_modules/@node-red/nodes": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@node-red/nodes/-/nodes-3.1.9.tgz", - "integrity": "sha512-H0ZJjgmc7tbDBExF8WWIab7VJ1PBJxqExc6HWfb5FJQcOyA9mzCXwBduirWGxWAbQzZvq5GLgzy5ECzDJIjbSQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@node-red/nodes/-/nodes-4.0.2.tgz", + "integrity": "sha512-dK+S6WJ2S8RQ/WMH1kSzYKvubM0+5zyRq7oEvrAyjk3xqQCwkf7Mfb48zdtHh6zW73/qv1pquY0iikIyZOkU6Q==", "dev": true, "dependencies": { - "acorn": "8.8.2", - "acorn-walk": "8.2.0", - "ajv": "8.12.0", + "acorn": "8.11.3", + "acorn-walk": "8.3.2", + "ajv": "8.14.0", "body-parser": "1.20.2", "cheerio": "1.0.0-rc.10", "content-type": "1.0.5", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-parser": "1.4.6", "cors": "2.8.5", "cronosjs": "1.7.1", "denque": "2.1.0", "form-data": "4.0.0", - "fs-extra": "11.1.1", + "fs-extra": "11.2.0", "got": "12.6.0", "hash-sum": "2.0.0", "hpagent": "1.2.0", @@ -960,29 +968,29 @@ "is-utf8": "0.2.1", "js-yaml": "4.1.0", "media-typer": "1.1.0", - "mqtt": "4.3.7", + "mqtt": "5.7.0", "multer": "1.4.5-lts.1", "mustache": "4.2.0", "node-watch": "0.7.4", "on-headers": "1.0.2", "raw-body": "2.5.2", - "tough-cookie": "4.1.3", - "uuid": "9.0.0", - "ws": "7.5.6", + "tough-cookie": "4.1.4", + "uuid": "9.0.1", + "ws": "7.5.10", "xml2js": "0.6.2" } }, "node_modules/@node-red/registry": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@node-red/registry/-/registry-3.1.9.tgz", - "integrity": "sha512-lm1jNGO5ebax5kw5A2stOymMVQpuAGJ24M+3bfPYj3djzgBq4qKbNX6EAJLtyLHlCKecAybJoXDNpNcCnl7BXQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@node-red/registry/-/registry-4.0.2.tgz", + "integrity": "sha512-1R9DBH4EhWOH5UJffNZ8Wdemch1onc7ZwRPoylnCD0aFpRWT2q1nEqHJDthtBF2l9x9CZejB31/LBAmU1KCkTQ==", "dev": true, "dependencies": { - "@node-red/util": "3.1.9", + "@node-red/util": "4.0.2", "clone": "2.1.2", - "fs-extra": "11.1.1", + "fs-extra": "11.2.0", "semver": "7.5.4", - "tar": "6.2.1", + "tar": "7.2.0", "uglify-js": "3.17.4" } }, @@ -1020,33 +1028,288 @@ "dev": true }, "node_modules/@node-red/runtime": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@node-red/runtime/-/runtime-3.1.9.tgz", - "integrity": "sha512-tpuHE5gEqLx9OoRjSxsyh683yGCnBlAAwbjkVv5lonqYqLJwE3DCJnMuHYj1lPUDzSc0QzhE9efm+LIhAhBU4g==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@node-red/runtime/-/runtime-4.0.2.tgz", + "integrity": "sha512-wW1l8ZIeaLvSB8rKj/g7RsGBLq8Q6ladbCm3Zv4tTbuLmx61t04FZo2DNYTrVlMIR3O5R+vmc5UbzJco7WpVxA==", "dev": true, "dependencies": { - "@node-red/registry": "3.1.9", - "@node-red/util": "3.1.9", - "async-mutex": "0.4.0", + "@node-red/registry": "4.0.2", + "@node-red/util": "4.0.2", + "async-mutex": "0.5.0", "clone": "2.1.2", "express": "4.19.2", - "fs-extra": "11.1.1", - "json-stringify-safe": "5.0.1" + "fs-extra": "11.2.0", + "json-stringify-safe": "5.0.1", + "rfdc": "^1.3.1" } }, "node_modules/@node-red/util": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@node-red/util/-/util-3.1.9.tgz", - "integrity": "sha512-BT7mMds8MFrXwgGuNjmk/vY0X621hirLcqAOp5/ZrrFuzPVoK4PDgoNx5igYD/HVQbVcJTHfN1cRopSFPfdF2Q==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@node-red/util/-/util-4.0.2.tgz", + "integrity": "sha512-pnAyC3N0JeORyvQQBpW4WQjBJU8iFNC5eG6W6LLfzhW3FvXndOUr85rx4fFtpWXZs8wMtWG4dBw7J4F4QLNJAQ==", "dev": true, "dependencies": { - "fs-extra": "11.1.1", + "fs-extra": "11.2.0", "i18next": "21.10.0", "json-stringify-safe": "5.0.1", - "jsonata": "1.8.7", + "jsonata": "2.0.5", "lodash.clonedeep": "^4.5.0", - "moment": "2.29.4", - "moment-timezone": "0.5.43" + "moment": "2.30.1", + "moment-timezone": "0.5.45" + } + }, + "node_modules/@node-rs/bcrypt": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.10.4.tgz", + "integrity": "sha512-Kzs8HKt2eBeT5VnkeKgiz/QKTjOO3URcvSNEQZahNwZnL6dBeeJQTxxYisc/6969+5n6c3+gNwKvqJsZzmGe7g==", + "dev": true, + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@node-rs/bcrypt-android-arm-eabi": "1.10.4", + "@node-rs/bcrypt-android-arm64": "1.10.4", + "@node-rs/bcrypt-darwin-arm64": "1.10.4", + "@node-rs/bcrypt-darwin-x64": "1.10.4", + "@node-rs/bcrypt-freebsd-x64": "1.10.4", + "@node-rs/bcrypt-linux-arm-gnueabihf": "1.10.4", + "@node-rs/bcrypt-linux-arm64-gnu": "1.10.4", + "@node-rs/bcrypt-linux-arm64-musl": "1.10.4", + "@node-rs/bcrypt-linux-x64-gnu": "1.10.4", + "@node-rs/bcrypt-linux-x64-musl": "1.10.4", + "@node-rs/bcrypt-wasm32-wasi": "1.10.4", + "@node-rs/bcrypt-win32-arm64-msvc": "1.10.4", + "@node-rs/bcrypt-win32-ia32-msvc": "1.10.4", + "@node-rs/bcrypt-win32-x64-msvc": "1.10.4" + } + }, + "node_modules/@node-rs/bcrypt-android-arm-eabi": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.10.4.tgz", + "integrity": "sha512-55ajutuTdfK1hKseyliflnxzNtxszQQ/EoLtgJlgCe7rI24vGP9EEEZDznB/u9OaJ14/AYzZtIhkEOYdbIdw0A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-android-arm64": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.10.4.tgz", + "integrity": "sha512-dCgQT7nH65tORmJw2hQ6zQgFmmC+/JBYZUWtf7pPZI76AVAn5tc7cIUrxYoV4OT1+uD63b9Av+mS1fT2EPzWEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-darwin-arm64": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.10.4.tgz", + "integrity": "sha512-gmHdWikHL3YVZgqXAHT+X/PG+kqIyNlPeFAWKdby83RkDI8FUiPV4qqGilgNnBmVWKkobRae9/I1HDbc4Sbhyg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-darwin-x64": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.10.4.tgz", + "integrity": "sha512-WDzL1WKRtoyTkH6IMPx95Mkd6XaeN0VWJbSDMqQY6AFBOk03yJEj7YYXshCcF+Ur6KBBVSwRf6sdFJ15NI1Z3g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-freebsd-x64": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.10.4.tgz", + "integrity": "sha512-seSPJi+4MIUd1faL/n/wmDdDwaynd/FTkvTnb7qzCk8LBT+/dxi7MTz+uaD8KYDREcB9Wmhv+lwr0S9/jBTcjg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.10.4.tgz", + "integrity": "sha512-YcMLUtN9cGNTWKnaXslxGO1M0S5b4QN9KYhuyG6Kju27RfqvU5UbmpKElCsEUO2EIjxGwzvPu59T+Fyh6sVbwg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm64-gnu": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.10.4.tgz", + "integrity": "sha512-uYGUK/mO8SiftqmVSAePWxgK82vg+X/gtrVRJi95yq2iwp1+fYJX3ndxCyYPmeplBbd3NJ/F5lPT3FC/IHTTGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-arm64-musl": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.10.4.tgz", + "integrity": "sha512-rLvSMW/gVUBd2k2gAqQfuOReHWd9+jvz58E3i1TbkRE3a5ChvjOFc9qKPEmXuXuD9Mdj7gUwcYwpq8MdB5MtNw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-x64-gnu": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.10.4.tgz", + "integrity": "sha512-I++6bh+BIp70X/D/crlSgCq8K0s9nGvzmvAGFkqSG4h3LBtjJx4RKbygnoWvcBV9ErK1rvcjfMyjwZt1ukueFA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-linux-x64-musl": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.10.4.tgz", + "integrity": "sha512-f9RPl/5n2NS0mMJXB4IYbodKnq5HzOK5x1b9eKbcjsY0rw3mJC3K0XRFc8iaw1a5chA+xV1TPXz5mkykmr2CQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-wasm32-wasi": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.10.4.tgz", + "integrity": "sha512-VaDOf+wic0yoHFimMkC5VMa/33BNqg6ieD+C/ibb7Av3NnVW4/W9YpDpqAWMR2w3fA40uTLWZ7FZSrcFck27oA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@node-rs/bcrypt-win32-arm64-msvc": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.10.4.tgz", + "integrity": "sha512-M7sGnbKPvhYJ5b76ywXiEwR4mIs/JSDHjRrhm9fshKAvltQrwc3Mou22TJggvDN3gKOF1W85uPiM2OgGX/jxMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-win32-ia32-msvc": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.10.4.tgz", + "integrity": "sha512-zn/n4DYnuOfC2JgmVDa0JHP+5DUqAOTl2jmV3yrMrmN+StDT4Om5wtvWxvEmgv3CkeZAuAU3Y/fwjSXIpZ0Fhg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/bcrypt-win32-x64-msvc": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.10.4.tgz", + "integrity": "sha512-ynQokTTGbuLu/cckaD8dNcE+Zsfam1zElE+teNol8AxcL7Jv+ghJItSnRthPRV/vLxuycDF2DIICgpXG/p9jrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" } }, "node_modules/@nodelib/fs.scandir": { @@ -1384,6 +1647,16 @@ "node": ">=14.16" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", + "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -1415,20 +1688,39 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.12.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.10.tgz", - "integrity": "sha512-Eem5pH9pmWBHoGAT8Dr5fdc5rYA+4NAovdM4EktRPVAAiJhmWWfQrA0cFhAbOsQdSfIHjAud6YdkbL69+zSKjw==", + "version": "20.14.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", + "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, + "node_modules/@types/readable-stream": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.14.tgz", + "integrity": "sha512-xZn/AuUbCMShGsqH/ehZtGDwQtbx00M9rZ2ENLe4tOjFZ/JFeWMhEZkk2fEe1jAUqqEAURIkFJ7Az/go8mM1/w==", + "dev": true, + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", @@ -1624,164 +1916,67 @@ "dev": true }, "node_modules/@vitest/coverage-v8": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz", - "integrity": "sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.0.1.tgz", + "integrity": "sha512-ACcSlJtWlravv0QyJSCO9rvm06msj6x0HooXouB0NXKG6PGxUN5VX4X8QEATfTMGsJlZLqWvq0dEY9W1V0rcSw==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.2.1", + "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^0.2.3", - "debug": "^4.3.4", + "debug": "^4.3.5", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.4", - "istanbul-reports": "^3.1.6", - "magic-string": "^0.30.5", - "magicast": "^0.3.3", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "test-exclude": "^6.0.0" + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.10", + "magicast": "^0.3.4", + "picocolors": "^1.0.1", + "std-env": "^3.7.0", + "strip-literal": "^2.1.0", + "test-exclude": "^7.0.1" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.6.0" + "vitest": "2.0.1" } }, "node_modules/@vitest/expect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", - "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.1.tgz", + "integrity": "sha512-yw70WL3ZwzbI2O3MOXYP2Shf4vqVkS3q5FckLJ6lhT9VMMtDyWdofD53COZcoeuHwsBymdOZp99r5bOr5g+oeA==", "dev": true, "dependencies": { - "@vitest/spy": "1.6.0", - "@vitest/utils": "1.6.0", - "chai": "^4.3.10" + "@vitest/spy": "2.0.1", + "@vitest/utils": "2.0.1", + "chai": "^5.1.1" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/expect/node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "node_modules/@vitest/runner": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.0.1.tgz", + "integrity": "sha512-XfcSXOGGxgR2dQ466ZYqf0ZtDLLDx9mZeQcKjQDLQ9y6Cmk2Wl7wxMuhiYK4Fo1VxCtLcFEGW2XpcfMuiD1Maw==", "dev": true, - "engines": { - "node": "*" + "dependencies": { + "@vitest/utils": "2.0.1", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/expect/node_modules/chai": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", - "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "node_modules/@vitest/snapshot": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.0.1.tgz", + "integrity": "sha512-rst79a4Q+J5vrvHRapdfK4BdqpMH0eF58jVY1vYeBo/1be+nkyenGI5SCSohmjf6MkCkI20/yo5oG+0R8qrAnA==", "dev": true, "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.0.8" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@vitest/expect/node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@vitest/expect/node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@vitest/expect/node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.1" - } - }, - "node_modules/@vitest/expect/node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/@vitest/runner": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", - "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", - "dev": true, - "dependencies": { - "@vitest/utils": "1.6.0", - "p-limit": "^5.0.0", - "pathe": "^1.1.1" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner/node_modules/p-limit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", - "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vitest/runner/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vitest/snapshot": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", - "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", - "dev": true, - "dependencies": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", + "magic-string": "^0.30.10", + "pathe": "^1.1.2", "pretty-format": "^29.7.0" }, "funding": { @@ -1789,47 +1984,50 @@ } }, "node_modules/@vitest/spy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", - "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.1.tgz", + "integrity": "sha512-NLkdxbSefAtJN56GtCNcB4GiHFb5i9q1uh4V229lrlTZt2fnwsTyjLuWIli1xwK2fQspJJmHXHyWx0Of3KTXWA==", "dev": true, "dependencies": { - "tinyspy": "^2.2.0" + "tinyspy": "^3.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", - "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.1.tgz", + "integrity": "sha512-STH+2fHZxlveh1mpU4tKzNgRk7RZJyr6kFGJYCI5vocdfqfPsQrgVC6k7dBWHfin5QNB4TLvRS0Ckly3Dt1uWw==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", "estree-walker": "^3.0.3", - "loupe": "^2.3.7", + "loupe": "^3.1.1", "pretty-format": "^29.7.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/utils/node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.1" - } - }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -1844,9 +2042,9 @@ } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1865,9 +2063,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "dev": true, "engines": { "node": ">=0.4.0" @@ -1886,15 +2084,15 @@ } }, "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", + "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" }, "funding": { "type": "github", @@ -1902,9 +2100,9 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { "node": ">=6" @@ -1959,27 +2157,6 @@ "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", "dev": true }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true, - "optional": true - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "dev": true, - "optional": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2175,9 +2352,9 @@ } }, "node_modules/async-mutex": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.4.0.tgz", - "integrity": "sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", "dev": true, "dependencies": { "tslib": "^2.4.0" @@ -2205,9 +2382,9 @@ } }, "node_modules/axios": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.0.tgz", - "integrity": "sha512-IiB0wQeKyPRdsFVhBgIo31FbzOyf2M6wYl7/NVutFwFBRMiAbjNiydJIHKeLmPugF4kJLfA1uWZ82Is2QzqqFA==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", "dev": true, "dependencies": { "follow-redirects": "^1.15.6", @@ -2253,21 +2430,6 @@ "node": ">= 0.8" } }, - "node_modules/bcrypt": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", - "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.10", - "node-addon-api": "^5.0.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", @@ -2287,14 +2449,15 @@ } }, "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.13.tgz", + "integrity": "sha512-tMncAcpsyjZgAVbVFupVIaB2xud13xxT59fdHkuszY2jdZkqIWfpQdmII1fOe3kOGAz0mNLTIHEm+KxpYsQKKg==", "dev": true, "dependencies": { - "buffer": "^5.5.0", + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "readable-stream": "^4.2.0" } }, "node_modules/body-parser": { @@ -2382,10 +2545,10 @@ "dev": true }, "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "devOptional": true, "funding": [ { "type": "github", @@ -2402,7 +2565,7 @@ ], "dependencies": { "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "ieee754": "^1.2.1" } }, "node_modules/buffer-from": { @@ -2533,13 +2696,13 @@ } }, "node_modules/chai": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.0.tgz", - "integrity": "sha512-kDZ7MZyM6Q1DhR9jy7dalKohXQ2yrlXkk59CR52aRKxJrobmlBNqnFQxX9xOX8w+4mz8SYlKJa/7D7ddltFXCw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", + "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", "dev": true, "dependencies": { "assertion-error": "^2.0.1", - "check-error": "^2.0.0", + "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" @@ -2638,12 +2801,12 @@ } }, "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", "dev": true, "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/cli-table": { @@ -2696,16 +2859,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "optional": true, - "bin": { - "color-support": "bin.js" - } - }, "node_modules/colors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", @@ -2737,14 +2890,10 @@ } }, "node_modules/commist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", - "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", - "dev": true, - "dependencies": { - "leven": "^2.1.0", - "minimist": "^1.1.0" - } + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", + "dev": true }, "node_modules/component-emitter": { "version": "1.3.1", @@ -2776,18 +2925,19 @@ "typedarray": "^0.0.6" } }, - "node_modules/confbox": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", - "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", - "dev": true - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, - "optional": true + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } }, "node_modules/content-disposition": { "version": "0.5.4", @@ -2831,9 +2981,9 @@ } }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "dev": true, "engines": { "node": ">= 0.6" @@ -2995,9 +3145,9 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -3127,13 +3277,6 @@ "node": ">=0.4.0" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true, - "optional": true - }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -3162,16 +3305,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "dev": true, - "optional": true, - "engines": { - "node": ">=8" - } - }, "node_modules/dezalgo": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", @@ -3183,9 +3316,9 @@ } }, "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, "engines": { "node": ">=0.3.1" @@ -3279,18 +3412,6 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/duplexify": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.2" - } - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -3318,15 +3439,6 @@ "node": ">= 0.8" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/enquirer": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", @@ -3517,9 +3629,9 @@ } }, "node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "devOptional": true, "hasInstallScript": true, "bin": { @@ -3529,29 +3641,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/esbuild-runner": { @@ -4200,18 +4312,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/espree/node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -4272,6 +4372,24 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -4338,13 +4456,13 @@ } }, "node_modules/express-session": { - "version": "1.17.3", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", - "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz", + "integrity": "sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==", "dev": true, "dependencies": { - "cookie": "0.4.2", - "cookie-signature": "1.0.6", + "cookie": "0.6.0", + "cookie-signature": "1.0.7", "debug": "2.6.9", "depd": "~2.0.0", "on-headers": "~1.0.2", @@ -4356,14 +4474,11 @@ "node": ">= 0.8.0" } }, - "node_modules/express-session/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "dev": true }, "node_modules/express-session/node_modules/debug": { "version": "2.6.9", @@ -4400,15 +4515,6 @@ } ] }, - "node_modules/express/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -4484,6 +4590,19 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", "dev": true }, + "node_modules/fast-unique-numbers": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", + "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.23.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.1.0" + } + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -4708,9 +4827,9 @@ } }, "node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", @@ -4721,46 +4840,16 @@ "node": ">=14.14" } }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs-minipass/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -4807,27 +4896,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "dev": true, - "optional": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -5120,13 +5188,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true, - "optional": true - }, "node_modules/hash-sum": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", @@ -5155,56 +5216,10 @@ } }, "node_modules/help-me": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", - "integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==", - "dev": true, - "dependencies": { - "glob": "^7.1.6", - "readable-stream": "^3.6.0" - } - }, - "node_modules/help-me/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/help-me/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/help-me/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "dev": true }, "node_modules/hexoid": { "version": "1.0.0", @@ -5882,9 +5897,9 @@ } }, "node_modules/istanbul-lib-source-maps": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz", - "integrity": "sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.23", @@ -6018,9 +6033,9 @@ } }, "node_modules/jsonata": { - "version": "1.8.7", - "resolved": "https://registry.npmjs.org/jsonata/-/jsonata-1.8.7.tgz", - "integrity": "sha512-tOW2/hZ+nR2bcQZs+0T62LVe5CHaNa3laFFWb/262r39utN6whJGBF7IR2Wq1QXrDbhftolk5gggW8uUJYlBTQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/jsonata/-/jsonata-2.0.5.tgz", + "integrity": "sha512-wEse9+QLIIU5IaCgtJCPsFi/H4F3qcikWzF4bAELZiRz08ohfx3Q6CjDRf4ZPF5P/92RI3KIHtb7u3jqPaHXdQ==", "dev": true, "engines": { "node": ">= 8" @@ -6068,15 +6083,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -6129,22 +6135,6 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/local-pkg": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", - "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", - "dev": true, - "dependencies": { - "mlly": "^1.4.2", - "pkg-types": "^1.0.3" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -6437,107 +6427,136 @@ } }, "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.1.tgz", + "integrity": "sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==", "dev": true, "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "minipass": "^7.0.4", + "rimraf": "^5.0.5" }, "engines": { - "node": ">= 8" + "node": ">= 18" } }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/minizlib/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=8" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/minizlib/node_modules/jackspeak": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.2.tgz", + "integrity": "sha512-qH3nOSj8q/8+Eg8LUPOq3C+6HWkpUioIjDsq1+D4zY91oZvpPttw8GwtF1nReRYKXl+1AORyFqtm2f5Q1SB6/Q==", "dev": true, "dependencies": { - "minimist": "^1.2.6" + "@isaacs/cliui": "^8.0.2" }, - "bin": { - "mkdirp": "bin/cmd.js" + "engines": { + "node": "14 >=14.21 || 16 >=16.20 || >=18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/mlly": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.0.tgz", - "integrity": "sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==", + "node_modules/minizlib/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "acorn": "^8.11.3", - "pathe": "^1.1.2", - "pkg-types": "^1.1.0", - "ufo": "^1.5.3" + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mlly/node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "node_modules/minizlib/node_modules/rimraf": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.9.tgz", + "integrity": "sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==", "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, "bin": { - "acorn": "bin/acorn" + "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": ">=0.4.0" + "node": "14 >=14.20 || 16 >=16.20 || >=18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, "node_modules/mocha": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", - "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", - "dev": true, - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "8.1.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.6.0.tgz", + "integrity": "sha512-hxjt4+EEB0SA0ZDygSS015t65lJw/I2yRCS3Ae+SJ5FrbzrXgfYwJr96f0OvIXdj7h4lv/vLCrH3rkiuizFSvw==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", @@ -6547,6 +6566,18 @@ "node": ">= 14.0.0" } }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -6569,18 +6600,18 @@ } }, "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "dev": true, "engines": { "node": "*" } }, "node_modules/moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", + "version": "0.5.45", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", + "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", "dev": true, "dependencies": { "moment": "^2.29.4" @@ -6590,66 +6621,78 @@ } }, "node_modules/mqtt": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.3.7.tgz", - "integrity": "sha512-ew3qwG/TJRorTz47eW46vZ5oBw5MEYbQZVaEji44j5lAUSQSqIEoul7Kua/BatBW0H0kKQcC9kwUHa1qzaWHSw==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.7.0.tgz", + "integrity": "sha512-/o0CBYSjZzddmQDV2iglCafsA0xWKpqnS62tGbOLOliubBxszpXO1DAQPyfI7ZcPDG0b9ni7QITn+5FW1E2UTg==", "dev": true, "dependencies": { - "commist": "^1.0.0", + "@types/readable-stream": "^4.0.5", + "@types/ws": "^8.5.9", + "commist": "^3.2.0", "concat-stream": "^2.0.0", - "debug": "^4.1.1", - "duplexify": "^4.1.1", - "help-me": "^3.0.0", - "inherits": "^2.0.3", - "lru-cache": "^6.0.0", - "minimist": "^1.2.5", - "mqtt-packet": "^6.8.0", - "number-allocator": "^1.0.9", - "pump": "^3.0.0", - "readable-stream": "^3.6.0", + "debug": "^4.3.4", + "help-me": "^5.0.0", + "lru-cache": "^10.0.1", + "minimist": "^1.2.8", + "mqtt": "^5.2.0", + "mqtt-packet": "^9.0.0", + "number-allocator": "^1.0.14", + "readable-stream": "^4.4.2", "reinterval": "^1.1.0", "rfdc": "^1.3.0", - "split2": "^3.1.0", - "ws": "^7.5.5", - "xtend": "^4.0.2" + "split2": "^4.2.0", + "worker-timers": "^7.1.4", + "ws": "^8.14.2" }, "bin": { - "mqtt": "bin/mqtt.js", - "mqtt_pub": "bin/pub.js", - "mqtt_sub": "bin/sub.js" + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" }, "engines": { - "node": ">=10.0.0" + "node": ">=16.0.0" } }, "node_modules/mqtt-packet": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", - "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.0.tgz", + "integrity": "sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==", "dev": true, "dependencies": { - "bl": "^4.0.2", - "debug": "^4.1.1", + "bl": "^6.0.8", + "debug": "^4.3.4", "process-nextick-args": "^2.0.1" } }, "node_modules/mqtt/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.2.tgz", + "integrity": "sha512-voV4dDrdVZVNz84n39LFKDaRzfwhdzJ7akpyXfTMxCgRUp07U3lcJUXRlhTKP17rgt09sUzLi5iCitpEAr+6ug==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": "14 || 16 || 18 || 20 || >=22" } }, - "node_modules/mqtt/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/mqtt/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } }, "node_modules/ms": { "version": "2.1.2", @@ -6730,10 +6773,13 @@ } }, "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/mz": { "version": "2.7.0", @@ -6822,49 +6868,22 @@ "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", "dev": true }, - "node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", - "dev": true, - "optional": true - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "optional": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/node-red": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/node-red/-/node-red-3.1.9.tgz", - "integrity": "sha512-SNuXZoplH/UewibVbe/UPyMhsmeuoCGjDVmBmWo+Wj8arE14PF1cOoTKdnbv5F/vPc1kbUvd0+oWCm9kv7wfkw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/node-red/-/node-red-4.0.2.tgz", + "integrity": "sha512-19AC1zouBYxaobzkpPb5PdqQptgO1fI9NRcrasFAYoG6wB8S53khFeuy1qiNaggo2JFu6TSJk2zLR+y4wksU1w==", "dev": true, "dependencies": { - "@node-red/editor-api": "3.1.9", - "@node-red/nodes": "3.1.9", - "@node-red/runtime": "3.1.9", - "@node-red/util": "3.1.9", + "@node-red/editor-api": "4.0.2", + "@node-red/nodes": "4.0.2", + "@node-red/runtime": "4.0.2", + "@node-red/util": "4.0.2", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", + "cors": "2.8.5", "express": "4.19.2", - "fs-extra": "11.1.1", - "node-red-admin": "^3.1.3", + "fs-extra": "11.2.0", + "node-red-admin": "^4.0.0", "nopt": "5.0.0", "semver": "7.5.4" }, @@ -6873,65 +6892,41 @@ "node-red-pi": "bin/node-red-pi" }, "engines": { - "node": ">=14" + "node": ">=18.5" }, "optionalDependencies": { - "bcrypt": "5.1.0" + "@node-rs/bcrypt": "1.10.4" } }, "node_modules/node-red-admin": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/node-red-admin/-/node-red-admin-3.1.3.tgz", - "integrity": "sha512-RRkjwLjriCKW3bqiU21y3j+wpZ4bDf2EH3IEqxwP6hT4ccIwEK8Nt9dPZRWD6NyWGbEVDSTM5H0/whaRdFCqSw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/node-red-admin/-/node-red-admin-4.0.0.tgz", + "integrity": "sha512-OP2IE/5r+TCeZBj5x+8MfslEjsxqGc1Er5rI5IMG3D/A3++3r0EkW+tlc0pKnX8lmKs1batgAtMIQbh4XkMY3w==", "dev": true, "dependencies": { "ansi-colors": "^4.1.3", - "axios": "^1.6.8", + "axios": "^1.7.2", "bcryptjs": "^2.4.3", "cli-table": "^0.3.11", "enquirer": "^2.3.6", "minimist": "^1.2.8", "mustache": "^4.2.0", - "read": "^1.0.7" + "read": "^3.0.1" }, "bin": { "node-red-admin": "node-red-admin.js" }, "engines": { - "node": ">=14" + "node": ">=18" }, "optionalDependencies": { - "bcrypt": "5.1.1" + "@node-rs/bcrypt": "1.10.4" } }, - "node_modules/node-red-admin/node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/node-red-admin/node_modules/bcrypt": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", - "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.11", - "node-addon-api": "^5.0.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/node-red-node-test-helper": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/node-red-node-test-helper/-/node-red-node-test-helper-0.3.4.tgz", - "integrity": "sha512-OFXGEkKZpLkgoijAgpUIjzn5RF8QnbwPX9RjfI2LWXq1ACfeXkXcW0i1ioiphrokdE3MiWQJtH5omLNnSNdyaQ==", + "node_modules/node-red-node-test-helper": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/node-red-node-test-helper/-/node-red-node-test-helper-0.3.4.tgz", + "integrity": "sha512-OFXGEkKZpLkgoijAgpUIjzn5RF8QnbwPX9RjfI2LWXq1ACfeXkXcW0i1ioiphrokdE3MiWQJtH5omLNnSNdyaQ==", "dev": true, "dependencies": { "body-parser": "^1.20.2", @@ -7037,19 +7032,6 @@ "node": ">=8" } }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "dev": true, - "optional": true, - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -7073,9 +7055,9 @@ } }, "node_modules/oauth2orize": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/oauth2orize/-/oauth2orize-1.11.1.tgz", - "integrity": "sha512-9dSx/Gwm0J2Rvj4RH9+h7iXVnRXZ6biwWRgb2dCeQhCosODS0nYdM9I/G7BUGsjbgn0pHjGcn1zcCRtzj2SlRA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/oauth2orize/-/oauth2orize-1.12.0.tgz", + "integrity": "sha512-j4XtFDQUBsvUHPjUmvmNDUDMYed2MphMIJBhyxVVe8hGCjkuYnjIsW+D9qk8c5ciXRdnk6x6tEbiO6PLeOZdCQ==", "dev": true, "dependencies": { "debug": "2.x.x", @@ -7340,6 +7322,12 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7390,9 +7378,9 @@ } }, "node_modules/passport": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", - "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", "dev": true, "dependencies": { "passport-strategy": "1.x.x", @@ -7668,17 +7656,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-types": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.1.tgz", - "integrity": "sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==", - "dev": true, - "dependencies": { - "confbox": "^0.1.7", - "mlly": "^1.7.0", - "pathe": "^1.1.2" - } - }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -7689,9 +7666,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", "dev": true, "funding": [ { @@ -7709,7 +7686,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" }, "engines": { @@ -7792,6 +7769,15 @@ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -7840,16 +7826,6 @@ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7973,29 +7949,31 @@ "dev": true }, "node_modules/read": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/read/-/read-3.0.1.tgz", + "integrity": "sha512-SLBrDU/Srs/9EoWhU5GdbAoxG1GzpQHo/6qiGItaoLJ1thmYpcNIM1qISEUvyHBzfGlWIyd6p2DNi1oV1VmAuw==", "dev": true, "dependencies": { - "mute-stream": "~0.0.4" + "mute-stream": "^1.0.0" }, "engines": { - "node": ">=0.8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": ">= 6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/readdirp": { @@ -8155,9 +8133,9 @@ } }, "node_modules/rfdc": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", - "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true }, "node_modules/rimraf": { @@ -8323,9 +8301,9 @@ "dev": true }, "node_modules/sax": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", - "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "dev": true }, "node_modules/semver": { @@ -8398,9 +8376,9 @@ "dev": true }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "dependencies": { "randombytes": "^2.1.0" @@ -8421,13 +8399,6 @@ "node": ">= 0.8.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, - "optional": true - }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -8684,12 +8655,12 @@ } }, "node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "dev": true, - "dependencies": { - "readable-stream": "^3.0.0" + "engines": { + "node": ">= 10.x" } }, "node_modules/stackback": { @@ -8851,12 +8822,6 @@ "npm": ">=6" } }, - "node_modules/stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", - "dev": true - }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -9125,15 +9090,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/sucrase/node_modules/minipass": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", - "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/superagent": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", @@ -9204,94 +9160,111 @@ } }, "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.2.0.tgz", + "integrity": "sha512-hctwP0Nb4AB60bj8WQgRYaMOuJYRAPMGiQUAotms5igN8ppfQM+IvjQ5HcKu1MaZh2Wy2KWVTe563Yj8dfc14w==", "dev": true, "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.0", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", "dev": true, "bin": { - "mkdirp": "bin/cmd.js" + "mkdirp": "dist/cjs/src/bin.js" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "engines": { + "node": ">=18" + } }, "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", "dev": true, "dependencies": { "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "glob": "^10.4.1", + "minimatch": "^9.0.4" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/test-exclude/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/test-exclude/node_modules/jackspeak": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.2.tgz", + "integrity": "sha512-qH3nOSj8q/8+Eg8LUPOq3C+6HWkpUioIjDsq1+D4zY91oZvpPttw8GwtF1nReRYKXl+1AORyFqtm2f5Q1SB6/Q==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": "*" + "node": "14 >=14.21 || 16 >=16.20 || >=18" }, "funding": { "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/text-table": { @@ -9328,18 +9301,18 @@ "dev": true }, "node_modules/tinypool": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", - "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.0.tgz", + "integrity": "sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ==", "dev": true, "engines": { - "node": ">=14.0.0" + "node": "^18.0.0 || >=20.0.0" } }, "node_modules/tinyspy": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", - "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.0.tgz", + "integrity": "sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==", "dev": true, "engines": { "node": ">=14.0.0" @@ -9376,9 +9349,9 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", "dev": true, "dependencies": { "psl": "^1.1.33", @@ -9399,13 +9372,6 @@ "node": ">= 4.0.0" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, - "optional": true - }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -9463,22 +9429,22 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "dev": true }, "node_modules/tsup": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.0.2.tgz", - "integrity": "sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.1.0.tgz", + "integrity": "sha512-UFdfCAXukax+U6KzeTNO2kAARHcWxmKsnvSPXUcfA1D+kU05XDccCrkffCQpFaWDsZfV0jMyTsxU39VfCp6EOg==", "dev": true, "dependencies": { "bundle-require": "^4.0.0", "cac": "^6.7.12", "chokidar": "^3.5.1", "debug": "^4.3.1", - "esbuild": "^0.19.2", + "esbuild": "^0.21.4", "execa": "^5.0.0", "globby": "^11.0.3", "joycon": "^3.0.1", @@ -9682,9 +9648,9 @@ "dev": true }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -9694,12 +9660,6 @@ "node": ">=14.17" } }, - "node_modules/ufo": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", - "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", - "dev": true - }, "node_modules/uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", @@ -9804,18 +9764,22 @@ } }, "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/valibot": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.33.3.tgz", - "integrity": "sha512-/fuY1DlX8uiQ7aphlzrrI2DbG0YJk84JMgvz2qKpUIdXRNsS53varfo4voPjSrjUr5BSV2K0miSEJUOlA5fQFg==" + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.36.0.tgz", + "integrity": "sha512-CjF1XN4sUce8sBK9TixrDqFM7RwNkuXdJu174/AwmQUB62QbCQADg5lLe8ldBalFgtj1uKj+pKwDJiNo4Mn+eQ==" }, "node_modules/vary": { "version": "1.1.2", @@ -9827,13 +9791,13 @@ } }, "node_modules/vite": { - "version": "5.2.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz", - "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.3.tgz", + "integrity": "sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==", "dev": true, "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", + "esbuild": "^0.21.3", + "postcss": "^8.4.39", "rollup": "^4.13.0" }, "bin": { @@ -9882,15 +9846,15 @@ } }, "node_modules/vite-node": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", - "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.0.1.tgz", + "integrity": "sha512-nVd6kyhPAql0s+xIVJzuF+RSRH8ZimNrm6U8ZvTA4MXv8CHI17TFaQwRaFiK75YX6XeFqZD4IoAaAfi9OR1XvQ==", "dev": true, "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.4", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", + "debug": "^4.3.5", + "pathe": "^1.1.2", + "picocolors": "^1.0.1", "vite": "^5.0.0" }, "bin": { @@ -9903,570 +9867,102 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "aix" - ], + "node_modules/vitest": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.0.1.tgz", + "integrity": "sha512-PBPvNXRJiywtI9NmbnEqHIhcXlk8mB0aKf6REQIaYGY4JtWF1Pg8Am+N0vAuxdg/wUSlxPSVJr8QdjwcVxc2Hg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@vitest/expect": "2.0.1", + "@vitest/runner": "2.0.1", + "@vitest/snapshot": "2.0.1", + "@vitest/spy": "2.0.1", + "@vitest/utils": "2.0.1", + "chai": "^5.1.1", + "debug": "^4.3.5", + "execa": "^8.0.1", + "magic-string": "^0.30.10", + "pathe": "^1.1.2", + "picocolors": "^1.0.1", + "std-env": "^3.7.0", + "tinybench": "^2.8.0", + "tinypool": "^1.0.0", + "vite": "^5.0.0", + "vite-node": "2.0.1", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, "engines": { - "node": ">=12" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.0.1", + "@vitest/ui": "2.0.1", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", - "cpu": [ - "arm" - ], + "node_modules/vitest/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, "engines": { - "node": ">=12" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", - "cpu": [ - "arm64" - ], + "node_modules/vitest/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" - } - }, - "node_modules/vitest": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", - "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", - "dev": true, - "dependencies": { - "@vitest/expect": "1.6.0", - "@vitest/runner": "1.6.0", - "@vitest/snapshot": "1.6.0", - "@vitest/spy": "1.6.0", - "@vitest/utils": "1.6.0", - "acorn-walk": "^8.3.2", - "chai": "^4.3.10", - "debug": "^4.3.4", - "execa": "^8.0.1", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "tinybench": "^2.5.1", - "tinypool": "^0.8.3", - "vite": "^5.0.0", - "vite-node": "1.6.0", - "why-is-node-running": "^2.2.2" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.6.0", - "@vitest/ui": "1.6.0", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/vitest/node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/vitest/node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/vitest/node_modules/chai": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", - "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.0.8" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/vitest/node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/vitest/node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/vitest/node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/vitest/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/vitest/node_modules/human-signals": { @@ -10490,15 +9986,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/vitest/node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.1" - } - }, "node_modules/vitest/node_modules/mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", @@ -10553,15 +10040,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/vitest/node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/vitest/node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -10586,24 +10064,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, - "optional": true - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "optional": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -10699,9 +10159,9 @@ } }, "node_modules/why-is-node-running": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", - "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "dependencies": { "siginfo": "^2.0.0", @@ -10714,16 +10174,6 @@ "node": ">=8" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "optional": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -10733,10 +10183,44 @@ "node": ">=0.10.0" } }, + "node_modules/worker-timers": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz", + "integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2", + "worker-timers-broker": "^6.1.8", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-broker": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", + "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.24.5", + "fast-unique-numbers": "^8.0.13", + "tslib": "^2.6.2", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-worker": { + "version": "7.0.71", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", + "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2" + } + }, "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "node_modules/wrap-ansi": { @@ -10781,9 +10265,9 @@ "dev": true }, "node_modules/ws": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz", - "integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "engines": { "node": ">=8.3.0" @@ -10887,9 +10371,9 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "engines": { "node": ">=10" @@ -11014,31 +10498,6 @@ "node": ">=18.0.0" } }, - "packages/sbg-ecom/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true, - "peer": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "packages/sbg-ecom/node_modules/crc": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/crc/-/crc-4.3.2.tgz", @@ -11078,30 +10537,6 @@ "node": ">=18.0.0" } }, - "packages/septentrio-sbf/node_modules/buffer": { - "version": "6.0.3", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "packages/septentrio-sbf/node_modules/crc": { "version": "4.3.2", "license": "MIT", @@ -11122,8 +10557,8 @@ "version": "0.0.6", "license": "MIT", "dependencies": { - "@schemasjs/valibot-numbers": "^1.0.12", - "@schemasjs/validator": "^1.0.0" + "@schemasjs/valibot-numbers": "^1.0.13", + "@schemasjs/validator": "^1.0.1" }, "engines": { "node": ">= 18" diff --git a/package.json b/package.json index 153d636..799ed14 100644 --- a/package.json +++ b/package.json @@ -76,20 +76,20 @@ "thelmabiotel-tblive:nodered:test": "npm run test --workspace=@coremarine/thelmabiotel-tblive-nodered" }, "devDependencies": { - "@types/node": "20.12.10", - "@vitest/coverage-v8": "1.6.0", - "chai": "5.1.0", + "@types/node": "20.14.10", + "@vitest/coverage-v8": "2.0.1", + "chai": "5.1.1", "deep-equal-in-any-order": "2.0.6", - "mocha": "10.4.0", - "node-red": "3.1.9", + "mocha": "10.6.0", + "node-red": "4.0.2", "node-red-node-test-helper": "0.3.4", "ts-standard": "12.0.2", - "tsup": "8.0.2", - "typescript": "5.4.5", - "vitest": "1.6.0" + "tsup": "8.1.0", + "typescript": "5.5.3", + "vitest": "2.0.1" }, "peerDependencies": { - "valibot": ">=0.30.0" + "valibot": ">=0.36.0" }, "ts-standard": { "ignore": [ From c23bfe10ba16a691de7318c8f536ee8686608883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Crist=C3=B3bal=20Contreras=20Rubio?= Date: Tue, 16 Jul 2024 21:28:40 +0200 Subject: [PATCH 2/6] NMEA-PARSER: Checksum refactored --- packages/nmea-parser/src/checksum.ts | 15 ++++++--------- packages/nmea-parser/tests/checksum.test.ts | 4 ++-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/nmea-parser/src/checksum.ts b/packages/nmea-parser/src/checksum.ts index f5afcd0..7afc550 100644 --- a/packages/nmea-parser/src/checksum.ts +++ b/packages/nmea-parser/src/checksum.ts @@ -1,11 +1,8 @@ -export const getChecksum = (data: string): number => Array.from(data).reduce((acc, cur) => acc ^ cur.charCodeAt(0), 0) +export const calculateChecksum = (data: string): number => Array.from(data).reduce((acc, cur) => acc ^ cur.charCodeAt(0), 0) -export const stringChecksumToNumber = (checksum: string): number => parseInt(checksum, 16) +export const stringChecksumToNumber = (checksum: string): number => Number.parseInt(checksum, 16) -export const numberChecksumToString = (checksum: number): string => { - const num0 = checksum & 0b0000_1111 - const num1 = (checksum & 0b1111_0000) >>> 4 - const char0 = num0.toString(16).toUpperCase() - const char1 = num1.toString(16).toUpperCase() - return `${char1}${char0}` -} +export const numberChecksumToString = (checksum: number): string => checksum + .toString(16) + .padStart(2, '0') + .toUpperCase() diff --git a/packages/nmea-parser/tests/checksum.test.ts b/packages/nmea-parser/tests/checksum.test.ts index b5fea93..78ac2db 100644 --- a/packages/nmea-parser/tests/checksum.test.ts +++ b/packages/nmea-parser/tests/checksum.test.ts @@ -1,6 +1,6 @@ import { test, expect } from "vitest" import { CHECKSUM_LENGTH, DELIMITER, DELIMITER_LENGTH, END_FLAG_LENGTH} from '../src/constants' -import { getChecksum, numberChecksumToString, stringChecksumToNumber } from '../src/checksum' +import { calculateChecksum, numberChecksumToString, stringChecksumToNumber } from '../src/checksum' const TEST_SENTENCES = [ @@ -17,7 +17,7 @@ const EXPECTED_CHECKSUMS = [ 121, 48, 98, 122, 48, 97 ] test('getChecksum', () => { TEST_SENTENCES.forEach((sentence, index) => { const data = sentence.slice(1, - (DELIMITER_LENGTH + CHECKSUM_LENGTH + END_FLAG_LENGTH)) - const checksum = getChecksum(data) + const checksum = calculateChecksum(data) expect(checksum).toBe(EXPECTED_CHECKSUMS[index]) }) }) From 84a93bcb6d18ab3453a6a3f250575a560a66da3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Crist=C3=B3bal=20Contreras=20Rubio?= Date: Mon, 22 Jul 2024 13:02:13 +0200 Subject: [PATCH 3/6] NMEA-PARSER: move protocols files to another location --- packages/nmea-parser/protocols/nmea.yaml | 50 +-- packages/nmea-parser/protocols/norsub.yaml | 372 ++++++++++----------- 2 files changed, 211 insertions(+), 211 deletions(-) diff --git a/packages/nmea-parser/protocols/nmea.yaml b/packages/nmea-parser/protocols/nmea.yaml index 972142e..21af032 100644 --- a/packages/nmea-parser/protocols/nmea.yaml +++ b/packages/nmea-parser/protocols/nmea.yaml @@ -3,13 +3,13 @@ protocols: version: '3.1' standard: true sentences: - - sentence: AAM + - id: AAM description: Waypoint Arrival Alarm - fields: + payload: # 1 - name: status type: string - note: "BOOLEAN\n + description: "BOOLEAN\n A = arrival circle entered\n @@ -17,7 +17,7 @@ protocols: # 2 - name: status type: string - note: "BOOLEAN\n + description: "BOOLEAN\n A = perpendicular passed at waypoint\n @@ -32,9 +32,9 @@ protocols: # 5 - name: waypoint_id type: string - - sentence: GGA + - id: GGA description: Global Positioning System Fix Data - fields: + payload: # 1 - name: utc_position type: string @@ -46,7 +46,7 @@ protocols: # 3 - name: latitude_direction type: string - note: "N: North\n + description: "N: North\n S: South" # 4 - name: longitude @@ -55,12 +55,12 @@ protocols: # 5 - name: longitude_direction type: string - note: "E - East\n + description: "E - East\n W - West" # 6 - name: gps_quality type: int8 - note: "0: Fix not valid\n + description: "0: Fix not valid\n 1: GPS fix\n 2: Differential GPS fix (DGNSS), SBAS, OmniSTAR VBS, Beacon, RTX in GVBS mode\n 3: Not applicable\n @@ -79,7 +79,7 @@ protocols: - name: altitude type: float64 units: m - note: "Orthometric height Mean-Sea-Level (MSL reference)" + description: "Orthometric height Mean-Sea-Level (MSL reference)" # 10 - name: altitude_units type: string @@ -88,7 +88,7 @@ protocols: - name: geoid_separation type: float64 units: m - note: "Geoidal Separation: the difference between the WGS-84 earth ellipsoid surface and mean-sea-level (geoid) surface, \"-\" = mean-sea-level surface below WGS-84 ellipsoid surface." + description: "Geoidal Separation: the difference between the WGS-84 earth ellipsoid surface and mean-sea-level (geoid) surface, \"-\" = mean-sea-level surface below WGS-84 ellipsoid surface." # 12 - name: geoid_separation_units type: string @@ -97,11 +97,11 @@ protocols: - name: age_of_differential_gps_data type: uint32 units: sec - note: "Time in seconds since last SC104 Type 1 or 9 update, null field when DGPS is not used300" + description: "Time in seconds since last SC104 Type 1 or 9 update, null field when DGPS is not used300" # 14 - name: reference_station_id type: uint16 - note: "Reference station ID, range 0000 to 4095. A null field when any reference station ID is selected and no corrections are received. See table below for a description of the field values.\n + description: "Reference station ID, range 0000 to 4095. A null field when any reference station ID is selected and no corrections are received. See table below for a description of the field values.\n 0002 CenterPoint or ViewPoint RTX\n @@ -130,41 +130,41 @@ protocols: 1020 HP/G2 (GPS)\n 1021 HP/G2 (GPS/GLONASS)" - - sentence: HDT + - id: HDT description: Heading - True - fields: + payload: # 1 - name: heading type: float32 - note: "Heading, degrees True" + description: "Heading, degrees True" # 2 - name: "true" type: string - note: "T = True" - - sentence: ZDA + description: "T = True" + - id: ZDA description: Time & Date - UTC, day, month, year and local time zone - fields: + payload: # 1 - name: utc_time type: string - note: "UTC time (hours, minutes, seconds, may have fractional subseconds)" + description: "UTC time (hours, minutes, seconds, may have fractional subseconds)" # 2 - name: day type: int8 - note: "Day, 01 to 31" + description: "Day, 01 to 31" # 3 - name: month type: int8 - note: "Month, 01 to 12" + description: "Month, 01 to 12" # 4 - name: year type: int16 - note: "Year (4 digits)" + description: "Year (4 digits)" # 5 - name: local_zone_hours type: int8 - note: "Local zone description, 00 to +- 13 hours" + description: "Local zone description, 00 to +- 13 hours" # 6 - name: local_zone_minutes type: int8 - note: "Local zone minutes description, 00 to 59, apply same sign as local hours" + description: "Local zone minutes description, 00 to 59, apply same sign as local hours" diff --git a/packages/nmea-parser/protocols/norsub.yaml b/packages/nmea-parser/protocols/norsub.yaml index c833bb2..0e4a0e0 100644 --- a/packages/nmea-parser/protocols/norsub.yaml +++ b/packages/nmea-parser/protocols/norsub.yaml @@ -2,36 +2,36 @@ protocols: - protocol: GYROCOMPAS1 standard: false sentences: - - sentence: HEHDT - fields: + - id: HEHDT + payload: - name: heading - type: float + type: float32 units: deg - name: symbol type: string - - sentence: PHTRO - fields: + - id: PHTRO + payload: - name: pitch - type: float + type: float32 units: deg - name: pitch_direction type: string - note: M bow up, P bow down + description: M bow up, P bow down - name: roll - type: float + type: float32 units: deg - name: roll_direction type: string - note: M bow up, P bow down - - sentence: PHINF - fields: + description: M bow up, P bow down + - id: PHINF + payload: - name: status type: string - protocol: NORSUB standard: false sentences: - - sentence: PNORSUB - fields: + - id: PNORSUB + payload: - name: time type: uint32 units: ms @@ -39,29 +39,29 @@ protocols: type: uint32 units: ms - name: roll - type: double + type: float64 units: deg - name: pitch - type: double + type: float64 units: deg - name: heading - type: double + type: float64 units: deg - note: 0 - 360 + description: 0 - 360 - name: heave - type: double + type: float64 units: m - note: z-down + description: z-down - name: status type: uint32 - note: "0 - Error\n + description: "0 - Error\n 1 - No Error" - protocol: NORSUB2 standard: false sentences: - - sentence: PNORSUB2 - fields: + - id: PNORSUB2 + payload: - name: time type: uint32 units: ms @@ -69,33 +69,33 @@ protocols: type: uint32 units: ms - name: roll - type: double + type: float64 units: deg - name: pitch - type: double + type: float64 units: deg - name: heading - type: double + type: float64 units: deg - note: 0 - 360 + description: 0 - 360 - name: heave - type: double + type: float64 units: m - note: z-down + description: z-down - name: heave_velocity - type: double + type: float64 units: m/s - note: z-down + description: z-down - name: status type: uint32 - note: "0 - Error\n + description: "0 - Error\n 1 - No Error" - protocol: NORSUB6 standard: false sentences: - - sentence: PNORSUB6 - fields: + - id: PNORSUB6 + payload: - name: time type: uint32 units: ms @@ -103,63 +103,63 @@ protocols: type: uint32 units: ms - name: roll - type: double + type: float64 units: deg - name: pitch - type: double + type: float64 units: deg - name: heading - type: double + type: float64 units: deg - note: 0 - 360 + description: 0 - 360 - name: surge - type: double + type: float64 units: m - name: sway - type: double + type: float64 units: m - name: heave - type: double + type: float64 units: m - note: z-down + description: z-down - name: roll_rate - type: double + type: float64 units: deg/s - name: pitch_rate - type: double + type: float64 units: deg/s - name: yaw_rate - type: double + type: float64 units: deg/s - name: surge_velocity - type: double + type: float64 units: m/s - name: sway_velocity - type: double + type: float64 units: m/s - name: heave_velocity - type: double + type: float64 units: m/s - note: z-down + description: z-down - name: acceleration_x - type: double + type: float64 units: m/s2 - name: acceleration_y - type: double + type: float64 units: m/s2 - name: acceleration_z - type: double + type: float64 units: m/s2 - name: status type: uint32 - note: "0 - Error\n + description: "0 - Error\n 1 - No Error" - protocol: NORSUB7 standard: false sentences: - - sentence: PNORSUB7 - fields: + - id: PNORSUB7 + payload: - name: time type: uint32 units: ms @@ -167,78 +167,78 @@ protocols: type: uint32 units: ms - name: roll - type: double + type: float64 units: deg - name: pitch - type: double + type: float64 units: deg - name: heading - type: double + type: float64 units: deg - note: 0 - 360 + description: 0 - 360 - name: surge - type: double + type: float64 units: m - name: sway - type: double + type: float64 units: m - name: heave - type: double + type: float64 units: m - note: z-down + description: z-down - name: roll_rate - type: double + type: float64 units: deg/s - name: pitch_rate - type: double + type: float64 units: deg/s - name: yaw_rate - type: double + type: float64 units: deg/s - name: surge_velocity - type: double + type: float64 units: m/s - name: sway_velocity - type: double + type: float64 units: m/s - name: heave_velocity - type: double + type: float64 units: m/s - note: z-down + description: z-down - name: acceleration_x - type: double + type: float64 units: m/s2 - name: acceleration_y - type: double + type: float64 units: m/s2 - name: acceleration_z - type: double + type: float64 units: m/s2 - name: period_x - type: double + type: float64 units: s - name: period_y - type: double + type: float64 units: s - name: period_z - type: double + type: float64 units: s - name: amplitude_x - type: double + type: float64 units: m - name: amplitude_y - type: double + type: float64 units: m - name: amplitude_z - type: double + type: float64 units: m - name: status type: uint32 - protocol: NORSUB7b standard: false sentences: - - sentence: PNORSUB7b - fields: + - id: PNORSUB7b + payload: - name: time type: uint32 units: ms @@ -246,70 +246,70 @@ protocols: type: uint32 units: ms - name: roll - type: double + type: float64 units: deg - name: pitch - type: double + type: float64 units: deg - name: heading - type: double + type: float64 units: deg - note: 0 - 360 + description: 0 - 360 - name: surge - type: double + type: float64 units: m - name: sway - type: double + type: float64 units: m - name: heave - type: double + type: float64 units: m - note: z-down + description: z-down - name: roll_rate - type: double + type: float64 units: deg/s - name: pitch_rate - type: double + type: float64 units: deg/s - name: yaw_rate - type: double + type: float64 units: deg/s - name: surge_velocity - type: double + type: float64 units: m/s - name: sway_velocity - type: double + type: float64 units: m/s - name: heave_velocity - type: double + type: float64 units: m/s - note: z-down + description: z-down - name: acceleration_x - type: double + type: float64 units: m/s2 - name: acceleration_y - type: double + type: float64 units: m/s2 - name: acceleration_z - type: double + type: float64 units: m/s2 - name: period_x - type: double + type: float64 units: s - name: period_y - type: double + type: float64 units: s - name: period_z - type: double + type: float64 units: s - name: amplitude_x - type: double + type: float64 units: m - name: amplitude_y - type: double + type: float64 units: m - name: amplitude_z - type: double + type: float64 units: m - name: status_a type: uint16 @@ -318,9 +318,9 @@ protocols: - protocol: NORSUB8 standard: false sentences: - - sentence: PNORSUB8 + - id: PNORSUB8 description: The whole regular attitude information from the MRU - fields: + payload: - name: time type: uint32 units: us @@ -328,191 +328,191 @@ protocols: type: uint32 units: us - name: roll - type: double + type: float64 units: deg - name: pitch - type: double + type: float64 units: deg - name: heading - type: double + type: float64 units: deg - note: From 0 to 360 + description: From 0 to 360 - name: surge - type: double + type: float64 units: m - name: sway - type: double + type: float64 units: m - name: heave - type: double + type: float64 units: m - note: z-down + description: z-down - name: roll_rate - type: double + type: float64 units: deg/s - name: pitch_rate - type: double + type: float64 units: deg/s - name: yaw_rate - type: double + type: float64 units: deg/s - name: surge_velocity - type: double + type: float64 units: m/s - name: sway_velocity - type: double + type: float64 units: m/s - name: heave_velocity - type: double + type: float64 units: m/s - note: z-down + description: z-down - name: acceleration_x - type: double + type: float64 units: m/s2 - name: acceleration_y - type: double + type: float64 units: m/s2 - name: acceleration_z - type: double + type: float64 units: m/s2 - name: period_x - type: double + type: float64 units: s - name: period_y - type: double + type: float64 units: s - name: period_z - type: double + type: float64 units: s - name: amplitude_x - type: double + type: float64 units: m - name: amplitude_y - type: double + type: float64 units: m - name: amplitude_z - type: double + type: float64 units: m - name: status type: uint32 - protocol: NORSUB PRDID standard: false sentences: - - sentence: PRDID - fields: + - id: PRDID + payload: - name: pitch - type: double + type: float64 units: deg - name: roll - type: double + type: float64 units: deg - protocol: Tokimek PTVG standard: false sentences: - - sentence: PTVG - fields: + - id: PTVG + payload: - name: pitch - type: number + type: float64 units: deg - note: "Multiplied by 100, a [-] bow up / a [space] bow down" + description: "Multiplied by 100, a [-] bow up / a [space] bow down" - name: roll - type: number + type: float64 units: deg - note: "Multiplied by 100, a [-] bow up / a [space] bow down" + description: "Multiplied by 100, a [-] bow up / a [space] bow down" - name: heading - type: number + type: float64 units: deg - protocol: RDI ADCP standard: false sentences: - - sentence: PRDID - fields: + - id: PRDID + payload: - name: pitch - type: double + type: float64 units: deg - note: "s if [+] is bow up / s is [-] if bow down, leading zeros" + description: "s if [+] is bow up / s is [-] if bow down, leading zeros" - name: roll - type: double + type: float64 units: deg - note: "s if [+] is bow up / s is [-] if bow down, leading zeros" + description: "s if [+] is bow up / s is [-] if bow down, leading zeros" - name: heading - type: double + type: float64 units: deg - protocol: SMCA standard: false sentences: - - sentence: PSMCA - fields: + - id: PSMCA + payload: - name: pitch - type: double + type: float64 units: deg - note: "±100 degs, resolution 0.001 degs" + description: "±100 degs, resolution 0.001 degs" - name: roll - type: double + type: float64 units: deg - note: "±100 degs, resolution 0.001 degs" + description: "±100 degs, resolution 0.001 degs" - name: heading - type: double + type: float64 units: m - note: "±10 m, resolution 0.01 m" + description: "±10 m, resolution 0.01 m" - name: surge - type: double + type: float64 units: m - note: "±10 m, resolution 0.01 m" + description: "±10 m, resolution 0.01 m" - name: sway - type: double + type: float64 units: m - note: "±10 m, resolution 0.01 m" + description: "±10 m, resolution 0.01 m" - protocol: SMCC standard: false sentences: - - sentence: PSMCC - fields: + - id: PSMCC + payload: - name: pitch - type: double + type: float64 units: deg - note: "±100 degs, resolution 0.001 degs" + description: "±100 degs, resolution 0.001 degs" - name: roll - type: double + type: float64 units: deg - note: "±100 degs, resolution 0.001 degs" + description: "±100 degs, resolution 0.001 degs" - name: yaw - type: double + type: float64 units: deg - note: "0-359.9 degs, resolution 0.1 degs" + description: "0-359.9 degs, resolution 0.1 degs" - name: surge - type: double + type: float64 units: m - note: "±10 m, resolution 0.01 m" + description: "±10 m, resolution 0.01 m" - name: sway - type: double + type: float64 units: m - note: "±10 m, resolution 0.01 m" + description: "±10 m, resolution 0.01 m" - name: heave - type: double + type: float64 units: m - note: "±10 m, resolution 0.01 m" + description: "±10 m, resolution 0.01 m" - name: surge_velocity - type: double + type: float64 units: m/s - note: "±100 m/s, resolution 0.01 m/s" + description: "±100 m/s, resolution 0.01 m/s" - name: sway_velocity - type: double + type: float64 units: m/s - note: "±100 m/s, resolution 0.01 m/s" + description: "±100 m/s, resolution 0.01 m/s" - name: heave_velocity - type: double + type: float64 units: m/s - note: "±100 m/s, resolution 0.01 m/s" + description: "±100 m/s, resolution 0.01 m/s" - name: acceleration_x - type: double + type: float64 units: m/s2 - note: "±100 m/s2, resolution 0.01 m/s2" + description: "±100 m/s2, resolution 0.01 m/s2" - name: acceleration_y - type: double + type: float64 units: m/s2 - note: "±100 m/s2, resolution 0.01 m/s2" + description: "±100 m/s2, resolution 0.01 m/s2" - name: acceleration_z - type: double + type: float64 units: m/s2 - note: "±100 m/s2, resolution 0.01 m/s2" + description: "±100 m/s2, resolution 0.01 m/s2" From 4a1835c42c6b2fe4210a216c818ccd44bbe12e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Crist=C3=B3bal=20Contreras=20Rubio?= Date: Mon, 22 Jul 2024 13:08:16 +0200 Subject: [PATCH 4/6] NMEA-PARSER: Refactor whole parser to get a unified standard output --- packages/nmea-parser/src/checksum.ts | 4 + packages/nmea-parser/src/constants.ts | 231 ++-- packages/nmea-parser/src/nmea-sentences.ts | 171 +++ packages/nmea-parser/src/nmea.ts | 170 +-- packages/nmea-parser/src/parser.ts | 238 ++-- packages/nmea-parser/src/protocols.ts | 54 +- packages/nmea-parser/src/schemas.ts | 232 ++-- packages/nmea-parser/src/sentences.ts | 305 ++++-- packages/nmea-parser/src/types.ts | 88 +- packages/nmea-parser/src/utils.ts | 19 +- packages/nmea-parser/tests/index.test.ts | 253 ++--- packages/nmea-parser/tests/norsub.ts | 1028 +++++++++--------- packages/nmea-parser/tests/parser.test.ts | 286 ----- packages/nmea-parser/tests/protocols.test.ts | 203 +--- packages/nmea-parser/tests/sentences.test.ts | 532 ++++++--- 15 files changed, 1810 insertions(+), 2004 deletions(-) create mode 100644 packages/nmea-parser/src/nmea-sentences.ts delete mode 100644 packages/nmea-parser/tests/parser.test.ts diff --git a/packages/nmea-parser/src/checksum.ts b/packages/nmea-parser/src/checksum.ts index 7afc550..fd5eff7 100644 --- a/packages/nmea-parser/src/checksum.ts +++ b/packages/nmea-parser/src/checksum.ts @@ -1,3 +1,5 @@ +import { Checksum } from './types' + export const calculateChecksum = (data: string): number => Array.from(data).reduce((acc, cur) => acc ^ cur.charCodeAt(0), 0) export const stringChecksumToNumber = (checksum: string): number => Number.parseInt(checksum, 16) @@ -6,3 +8,5 @@ export const numberChecksumToString = (checksum: number): string => checksum .toString(16) .padStart(2, '0') .toUpperCase() + +export const getChecksum = (cs: string): Checksum => ({ sample: cs, value: stringChecksumToNumber(cs) }) diff --git a/packages/nmea-parser/src/constants.ts b/packages/nmea-parser/src/constants.ts index dffec01..b0b4f5a 100644 --- a/packages/nmea-parser/src/constants.ts +++ b/packages/nmea-parser/src/constants.ts @@ -1,3 +1,24 @@ +// COMMONS +export const FIELD_TYPES = [ + // Unsigned Integers + 'uint8', // 'char', + 'uint16', // 'unsigned short', + 'uint32', // 'unsigned int', + 'uint64', // 'unsigned long', + // Integers + 'int8', // 'signed char', + 'int16', // 'short', + 'int32', // 'int', + 'int64', // 'long', + // Floats + 'float32', // 'float', + 'float64', // 'double', + // Strings + 'string', + // Boolean + 'boolean' // 'bool' +] as const + // NMEA export const START_FLAG = '$' export const SEPARATOR = ',' @@ -15,106 +36,107 @@ export const NMEA_ID_LENGTH = 3 export const NMEA_TALKER_LENGTH = 2 export const NMEA_SENTENCE_LENGTH = NMEA_ID_LENGTH + NMEA_TALKER_LENGTH -export const TALKERS = new Map() -TALKERS.set('AB', 'Independent AIS Base Station') -TALKERS.set('AD', 'Dependent AIS Base Station') -TALKERS.set('AG', 'Autopilot - General') -TALKERS.set('AI', 'Mobile AIS Station') -TALKERS.set('AN', 'AIS Aid to Navigation') -TALKERS.set('AP', 'Autopilot - Magnetic') -TALKERS.set('AR', 'AIS Receiving Station') -TALKERS.set('AT', 'AIS Transmitting Station') -TALKERS.set('AX', 'AIS Simplex Repeater') -TALKERS.set('BD', 'BeiDou (China)') -TALKERS.set('BI', 'Bilge System') -TALKERS.set('BN', 'Bridge navigational watch alarm system') -TALKERS.set('CA', 'Central Alarm') -TALKERS.set('CC', 'Computer - Programmed Calculator (obsolete)') -TALKERS.set('CD', 'Communications - Digital Selective Calling (DSC)') -TALKERS.set('CM', 'Computer - Memory Data (obsolete)') -TALKERS.set('CR', 'Data Receiver') -TALKERS.set('CS', 'Communications - Satellite') -TALKERS.set('CT', 'Communications - Radio-Telephone (MF/HF)') -TALKERS.set('CV', 'Communications - Radio-Telephone (VHF)') -TALKERS.set('CX', 'Communications - Scanning Receiver') -TALKERS.set('DE', 'DECCA Navigation (obsolete)') -TALKERS.set('DF', 'Direction Finder') -TALKERS.set('DM', 'Velocity Sensor, Speed Log, Water, Magnetic') -TALKERS.set('DP', 'Dynamiv Position') -TALKERS.set('DU', 'Duplex repeater station') -TALKERS.set('EC', 'Electronic Chart Display & Information System (ECDIS)') -TALKERS.set('EP', 'Emergency Position Indicating Beacon (EPIRB)') -TALKERS.set('ER', 'Engine Room Monitoring Systems') -TALKERS.set('FD', 'Fire Door') -TALKERS.set('FS', 'Fire Sprinkler') -TALKERS.set('GA', 'Galileo Positioning System') -TALKERS.set('GB', 'BeiDou (China)') -TALKERS.set('GI', 'NavIC, IRNSS (India)') -TALKERS.set('GL', 'GLONASS, according to IEIC 61162-1') -TALKERS.set('GN', 'Combination of multiple satellite systems (NMEA 1083)') -TALKERS.set('GP', 'Global Positioning System receiver') -TALKERS.set('GQ', 'QZSS regional GPS augmentation system (Japan)') -TALKERS.set('HC', 'Heading - Magnetic Compass') -TALKERS.set('HD', 'Hull Door') -TALKERS.set('HE', 'Heading - North Seeking Gyro') -TALKERS.set('HF', 'Heading - Fluxgate') -TALKERS.set('HN', 'Heading - Non North Seeking Gyro') -TALKERS.set('HS', 'Hull Stress') -TALKERS.set('II', 'Integrated Instrumentation') -TALKERS.set('IN', 'Integrated Navigation') -TALKERS.set('JA', 'Alarm and Monitoring') -TALKERS.set('JB', 'Water Monitoring') -TALKERS.set('JC', 'Power Management') -TALKERS.set('JD', 'Propulsion Control') -TALKERS.set('JE', 'Engine Control') -TALKERS.set('JF', 'Propulsion Boiler') -TALKERS.set('JG', 'Aux Boiler') -TALKERS.set('JH', 'Engine Governor') -TALKERS.set('LA', 'Loran A (obsolete)') -TALKERS.set('LC', 'Loran C (obsolete)') -TALKERS.set('MP', 'Microwave Positioning System (obsolete)') -TALKERS.set('MX', 'Multiplexer') -TALKERS.set('NL', 'Navigation light controller') -TALKERS.set('OM', 'OMEGA Navigation System (obsolete)') -TALKERS.set('OS', 'Distress Alarm System (obsolete)') -TALKERS.set('QZ', 'QZSS regional GPS augmentation system (Japan)') -TALKERS.set('RA', 'RADAR and/or ARPA') -TALKERS.set('RB', 'Record Book') -TALKERS.set('RC', 'Propulsion Machinery') -TALKERS.set('RI', 'Rudder Angle Indicator') -TALKERS.set('SA', 'Physical Shore AUS Station') -TALKERS.set('SD', 'Depth Sounder') -TALKERS.set('SG', 'Steering Gear') -TALKERS.set('SN', 'Electronic Positioning System, other/general') -TALKERS.set('SS', 'Scanning Sounder') -TALKERS.set('ST', 'Skytraq debug output') -TALKERS.set('TC', 'Track Control') -TALKERS.set('TI', 'Turn Rate Indicator') -TALKERS.set('TR', 'TRANSIT Navigation System') -TALKERS.set('UP', 'Microprocessor controller') -TALKERS.set('VA', 'VHF Data Exchange System (VDES), ASM') -TALKERS.set('VD', 'Velocity Sensor, Doppler, other/general') -TALKERS.set('VM', 'Velocity Sensor, Speed Log, Water, Magnetic') -TALKERS.set('VR', 'Voyage Data recorder') -TALKERS.set('VS', 'VHF Data Exchange System (VDES), Satellite') -TALKERS.set('VT', 'VHF Data Exchange System (VDES), Terrestrial') -TALKERS.set('VW', 'Velocity Sensor, Speed Log, Water, Mechanical') -TALKERS.set('WD', 'Watertight Door') -TALKERS.set('WI', 'Weather Instruments') -TALKERS.set('WL', 'Water Level') -TALKERS.set('YC', 'Transducer - Temperature (obsolete)') -TALKERS.set('YD', 'Transducer - Displacement, Angular or Linear (obsolete)') -TALKERS.set('YF', 'Transducer - Frequency (obsolete)') -TALKERS.set('YL', 'Transducer - Level (obsolete)') -TALKERS.set('YP', 'Transducer - Pressure (obsolete)') -TALKERS.set('YR', 'Transducer - Flow Rate (obsolete)') -TALKERS.set('YT', 'Transducer - Tachometer (obsolete)') -TALKERS.set('YV', 'Transducer - Volume (obsolete)') -TALKERS.set('YX', 'Transducer') -TALKERS.set('ZA', 'Timekeeper - Atomic Clock') -TALKERS.set('ZC', 'Timekeeper - Chronometer') -TALKERS.set('ZQ', 'Timekeeper - Quartz') -TALKERS.set('ZV', 'Timekeeper - Radio Update, WWV or WWVH') +export const TALKERS = [ + ['AB', 'Independent AIS Base Station'], + ['AD', 'Dependent AIS Base Station'], + ['AG', 'Autopilot - General'], + ['AI', 'Mobile AIS Station'], + ['AN', 'AIS Aid to Navigation'], + ['AP', 'Autopilot - Magnetic'], + ['AR', 'AIS Receiving Station'], + ['AT', 'AIS Transmitting Station'], + ['AX', 'AIS Simplex Repeater'], + ['BD', 'BeiDou (China)'], + ['BI', 'Bilge System'], + ['BN', 'Bridge navigational watch alarm system'], + ['CA', 'Central Alarm'], + ['CC', 'Computer - Programmed Calculator (obsolete)'], + ['CD', 'Communications - Digital Selective Calling (DSC)'], + ['CM', 'Computer - Memory Data (obsolete)'], + ['CR', 'Data Receiver'], + ['CS', 'Communications - Satellite'], + ['CT', 'Communications - Radio-Telephone (MF/HF)'], + ['CV', 'Communications - Radio-Telephone (VHF)'], + ['CX', 'Communications - Scanning Receiver'], + ['DE', 'DECCA Navigation (obsolete)'], + ['DF', 'Direction Finder'], + ['DM', 'Velocity Sensor, Speed Log, Water, Magnetic'], + ['DP', 'Dynamiv Position'], + ['DU', 'Duplex repeater station'], + ['EC', 'Electronic Chart Display & Information System (ECDIS)'], + ['EP', 'Emergency Position Indicating Beacon (EPIRB)'], + ['ER', 'Engine Room Monitoring Systems'], + ['FD', 'Fire Door'], + ['FS', 'Fire Sprinkler'], + ['GA', 'Galileo Positioning System'], + ['GB', 'BeiDou (China)'], + ['GI', 'NavIC, IRNSS (India)'], + ['GL', 'GLONASS, according to IEIC 61162-1'], + ['GN', 'Combination of multiple satellite systems (NMEA 1083)'], + ['GP', 'Global Positioning System receiver'], + ['GQ', 'QZSS regional GPS augmentation system (Japan)'], + ['HC', 'Heading - Magnetic Compass'], + ['HD', 'Hull Door'], + ['HE', 'Heading - North Seeking Gyro'], + ['HF', 'Heading - Fluxgate'], + ['HN', 'Heading - Non North Seeking Gyro'], + ['HS', 'Hull Stress'], + ['II', 'Integrated Instrumentation'], + ['IN', 'Integrated Navigation'], + ['JA', 'Alarm and Monitoring'], + ['JB', 'Water Monitoring'], + ['JC', 'Power Management'], + ['JD', 'Propulsion Control'], + ['JE', 'Engine Control'], + ['JF', 'Propulsion Boiler'], + ['JG', 'Aux Boiler'], + ['JH', 'Engine Governor'], + ['LA', 'Loran A (obsolete)'], + ['LC', 'Loran C (obsolete)'], + ['MP', 'Microwave Positioning System (obsolete)'], + ['MX', 'Multiplexer'], + ['NL', 'Navigation light controller'], + ['OM', 'OMEGA Navigation System (obsolete)'], + ['OS', 'Distress Alarm System (obsolete)'], + ['QZ', 'QZSS regional GPS augmentation system (Japan)'], + ['RA', 'RADAR and/or ARPA'], + ['RB', 'Record Book'], + ['RC', 'Propulsion Machinery'], + ['RI', 'Rudder Angle Indicator'], + ['SA', 'Physical Shore AUS Station'], + ['SD', 'Depth Sounder'], + ['SG', 'Steering Gear'], + ['SN', 'Electronic Positioning System, other/general'], + ['SS', 'Scanning Sounder'], + ['ST', 'Skytraq debug output'], + ['TC', 'Track Control'], + ['TI', 'Turn Rate Indicator'], + ['TR', 'TRANSIT Navigation System'], + ['UP', 'Microprocessor controller'], + ['VA', 'VHF Data Exchange System (VDES), ASM'], + ['VD', 'Velocity Sensor, Doppler, other/general'], + ['VM', 'Velocity Sensor, Speed Log, Water, Magnetic'], + ['VR', 'Voyage Data recorder'], + ['VS', 'VHF Data Exchange System (VDES), Satellite'], + ['VT', 'VHF Data Exchange System (VDES), Terrestrial'], + ['VW', 'Velocity Sensor, Speed Log, Water, Mechanical'], + ['WD', 'Watertight Door'], + ['WI', 'Weather Instruments'], + ['WL', 'Water Level'], + ['YC', 'Transducer - Temperature (obsolete)'], + ['YD', 'Transducer - Displacement, Angular or Linear (obsolete)'], + ['YF', 'Transducer - Frequency (obsolete)'], + ['YL', 'Transducer - Level (obsolete)'], + ['YP', 'Transducer - Pressure (obsolete)'], + ['YR', 'Transducer - Flow Rate (obsolete)'], + ['YT', 'Transducer - Tachometer (obsolete)'], + ['YV', 'Transducer - Volume (obsolete)'], + ['YX', 'Transducer'], + ['ZA', 'Timekeeper - Atomic Clock'], + ['ZC', 'Timekeeper - Chronometer'], + ['ZQ', 'Timekeeper - Quartz'], + ['ZV', 'Timekeeper - Radio Update, WWV or WWVH'] +] as const export const TALKERS_SPECIAL = { P: 'Vendor specific', @@ -148,3 +170,12 @@ export const MIN_FLOAT = -999999999999999 // PARSER export const MAX_CHARACTERS = 1024 export const MAX_NMEA_CHARACTERS = 82 + +export const UNKNOWN_NMEA_SENTENCE_SCAFOLDING = { + id: 'unknown', + protocol: { + name: 'unknown', + standard: false + }, + description: 'unknown nmea sentence' +} diff --git a/packages/nmea-parser/src/nmea-sentences.ts b/packages/nmea-parser/src/nmea-sentences.ts new file mode 100644 index 0000000..05ec475 --- /dev/null +++ b/packages/nmea-parser/src/nmea-sentences.ts @@ -0,0 +1,171 @@ +import { ProtocolsFileContent } from './types' + +export const NMEA_SENTENCES: ProtocolsFileContent = { + protocols: [ + { + protocol: 'NMEA', + version: '3.1', + standard: true, + sentences: [ + { + id: 'AAM', + description: 'Waypoint Arrival Alarm', + payload: [ + { + name: 'status', + type: 'string', + description: 'BOOLEAN\n\nA = arrival circle entered\n\nV = arrival circle not passed' + }, + { + name: 'status', + type: 'string', + description: 'BOOLEAN\n\nA = perpendicular passed at waypoint\n\nV = perpendicular not passed' + }, + { + name: 'arrival_circle_radius', + type: 'float32' + }, + { + name: 'radius_units', + type: 'string', + units: 'nautic miles' + }, + { + name: 'waypoint_id', + type: 'string' + } + ] + }, + { + id: 'GGA', + description: 'Global Positioning System Fix Data', + payload: [ + { + name: 'utc_position', + type: 'string', + units: 'ms' + }, + { + name: 'latitude', + type: 'string', + units: 'deg' + }, + { + name: 'latitude_direction', + type: 'string', + description: 'N: North\n S: South' + }, + { + name: 'longitude', + type: 'string', + units: 'deg' + }, + { + name: 'longitude_direction', + type: 'string', + description: 'E - East\n W - West' + }, + { + name: 'gps_quality', + type: 'int8', + description: '0: Fix not valid\n 1: GPS fix\n 2: Differential GPS fix (DGNSS), SBAS, OmniSTAR VBS, Beacon, RTX in GVBS mode\n 3: Not applicable\n 4: RTK Fixed, xFill\n 5: RTK Float, OmniSTAR XP/HP, Location RTK, RTX\n 6: INS Dead reckoning\n 7: Manual Input Mode\n 8: Simulator Mode' + }, + { + name: 'satellites', + type: 'uint8' + }, + { + name: 'hdop', + type: 'float64' + }, + { + name: 'altitude', + type: 'float64', + units: 'm', + description: 'Orthometric height Mean-Sea-Level (MSL reference)' + }, + { + name: 'altitude_units', + type: 'string', + units: 'm' + }, + { + name: 'geoid_separation', + type: 'float64', + units: 'm', + description: 'Geoidal Separation: the difference between the WGS-84 earth ellipsoid surface and mean-sea-level (geoid) surface, "-" = mean-sea-level surface below WGS-84 ellipsoid surface.' + }, + { + name: 'geoid_separation_units', + type: 'string', + units: 'm' + }, + { + name: 'age_of_differential_gps_data', + type: 'uint32', + units: 'sec', + description: 'Time in seconds since last SC104 Type 1 or 9 update, null field when DGPS is not used300' + }, + { + name: 'reference_station_id', + type: 'uint16', + description: 'Reference station ID, range 0000 to 4095. A null field when any reference station ID is selected and no corrections are received. See table below for a description of the field values.\n\n0002 CenterPoint or ViewPoint RTX\n\n0005 RangePoint RTX\n\n0006 FieldPoint RTX\n\n0100 VBS\n\n1000 HP\n\n1001 HP/XP (Orbits)\n\n1002 HP/G2 (Orbits)\n\n1008 XP (GPS)\n\n1012 G2 (GPS)\n\n1013 G2 (GPS/GLONASS)\n\n1014 G2 (GLONASS)\n\n1016 HP/XP (GPS)\n\n1020 HP/G2 (GPS)\n\n1021 HP/G2 (GPS/GLONASS)' + } + ] + }, + { + id: 'HDT', + description: 'Heading - True', + payload: [ + { + name: 'heading', + type: 'float32', + description: 'Heading, degrees True' + }, + { + name: 'true', + type: 'string', + description: 'T = True' + } + ] + }, + { + id: 'ZDA', + description: 'Time & Date - UTC, day, month, year and local time zone', + payload: [ + { + name: 'utc_time', + type: 'string', + description: 'UTC time (hours, minutes, seconds, may have fractional subseconds)' + }, + { + name: 'day', + type: 'int8', + description: 'Day, 01 to 31' + }, + { + name: 'month', + type: 'int8', + description: 'Month, 01 to 12' + }, + { + name: 'year', + type: 'int16', + description: 'Year (4 digits)' + }, + { + name: 'local_zone_hours', + type: 'int8', + description: 'Local zone description, 00 to +- 13 hours' + }, + { + name: 'local_zone_minutes', + type: 'int8', + description: 'Local zone minutes description, 00 to 59, apply same sign as local hours' + } + ] + } + ] + } + ] +} diff --git a/packages/nmea-parser/src/nmea.ts b/packages/nmea-parser/src/nmea.ts index f8c058f..8b13789 100644 --- a/packages/nmea-parser/src/nmea.ts +++ b/packages/nmea-parser/src/nmea.ts @@ -1,169 +1 @@ -export const PROTOCOLS = { - protocols: [ - { - protocol: 'NMEA', - version: '3.1', - standard: true, - sentences: [ - { - sentence: 'AAM', - description: 'Waypoint Arrival Alarm', - fields: [ - { - name: 'status', - type: 'string', - note: 'BOOLEAN\n\nA = arrival circle entered\n\nV = arrival circle not passed' - }, - { - name: 'status', - type: 'string', - note: 'BOOLEAN\n\nA = perpendicular passed at waypoint\n\nV = perpendicular not passed' - }, - { - name: 'arrival_circle_radius', - type: 'float32' - }, - { - name: 'radius_units', - type: 'string', - units: 'nautic miles' - }, - { - name: 'waypoint_id', - type: 'string' - } - ] - }, - { - sentence: 'GGA', - description: 'Global Positioning System Fix Data', - fields: [ - { - name: 'utc_position', - type: 'string', - units: 'ms' - }, - { - name: 'latitude', - type: 'string', - units: 'deg' - }, - { - name: 'latitude_direction', - type: 'string', - note: 'N: North\n S: South' - }, - { - name: 'longitude', - type: 'string', - units: 'deg' - }, - { - name: 'longitude_direction', - type: 'string', - note: 'E - East\n W - West' - }, - { - name: 'gps_quality', - type: 'int8', - note: '0: Fix not valid\n 1: GPS fix\n 2: Differential GPS fix (DGNSS), SBAS, OmniSTAR VBS, Beacon, RTX in GVBS mode\n 3: Not applicable\n 4: RTK Fixed, xFill\n 5: RTK Float, OmniSTAR XP/HP, Location RTK, RTX\n 6: INS Dead reckoning\n 7: Manual Input Mode\n 8: Simulator Mode' - }, - { - name: 'satellites', - type: 'uint8' - }, - { - name: 'hdop', - type: 'float64' - }, - { - name: 'altitude', - type: 'float64', - units: 'm', - note: 'Orthometric height Mean-Sea-Level (MSL reference)' - }, - { - name: 'altitude_units', - type: 'string', - units: 'm' - }, - { - name: 'geoid_separation', - type: 'float64', - units: 'm', - note: 'Geoidal Separation: the difference between the WGS-84 earth ellipsoid surface and mean-sea-level (geoid) surface, "-" = mean-sea-level surface below WGS-84 ellipsoid surface.' - }, - { - name: 'geoid_separation_units', - type: 'string', - units: 'm' - }, - { - name: 'age_of_differential_gps_data', - type: 'uint32', - units: 'sec', - note: 'Time in seconds since last SC104 Type 1 or 9 update, null field when DGPS is not used300' - }, - { - name: 'reference_station_id', - type: 'uint16', - note: 'Reference station ID, range 0000 to 4095. A null field when any reference station ID is selected and no corrections are received. See table below for a description of the field values.\n\n0002 CenterPoint or ViewPoint RTX\n\n0005 RangePoint RTX\n\n0006 FieldPoint RTX\n\n0100 VBS\n\n1000 HP\n\n1001 HP/XP (Orbits)\n\n1002 HP/G2 (Orbits)\n\n1008 XP (GPS)\n\n1012 G2 (GPS)\n\n1013 G2 (GPS/GLONASS)\n\n1014 G2 (GLONASS)\n\n1016 HP/XP (GPS)\n\n1020 HP/G2 (GPS)\n\n1021 HP/G2 (GPS/GLONASS)' - } - ] - }, - { - sentence: 'HDT', - description: 'Heading - True', - fields: [ - { - name: 'heading', - type: 'float32', - note: 'Heading, degrees True' - }, - { - name: 'true', - type: 'string', - note: 'T = True' - } - ] - }, - { - sentence: 'ZDA', - description: 'Time & Date - UTC, day, month, year and local time zone', - fields: [ - { - name: 'utc_time', - type: 'string', - note: 'UTC time (hours, minutes, seconds, may have fractional subseconds)' - }, - { - name: 'day', - type: 'int8', - note: 'Day, 01 to 31' - }, - { - name: 'month', - type: 'int8', - note: 'Month, 01 to 12' - }, - { - name: 'year', - type: 'int16', - note: 'Year (4 digits)' - }, - { - name: 'local_zone_hours', - type: 'int8', - note: 'Local zone description, 00 to +- 13 hours' - }, - { - name: 'local_zone_minutes', - type: 'int8', - note: 'Local zone minutes description, 00 to 59, apply same sign as local hours' - } - ] - } - ] - } - ] -} + diff --git a/packages/nmea-parser/src/parser.ts b/packages/nmea-parser/src/parser.ts index 0359e6c..891c5a7 100644 --- a/packages/nmea-parser/src/parser.ts +++ b/packages/nmea-parser/src/parser.ts @@ -1,10 +1,10 @@ -import { END_FLAG, END_FLAG_LENGTH, MAX_CHARACTERS, NMEA_ID_LENGTH, START_FLAG, START_FLAG_LENGTH } from './constants' -import { PROTOCOLS } from './nmea' -import { getSentencesByProtocol, getStoreSentences, readProtocolsFile, readProtocolsString } from './protocols' -import { BooleanSchema, NMEALikeSchema, ProtocolsInputSchema, StringSchema, UnsignedIntegerSchema } from './schemas' -import { generateSentenceFromModel, getFakeSentence, getNMEAUnparsedSentence } from './sentences' -import type { Data, FieldType, FieldUnknown, NMEAKnownSentence, NMEALike, NMEAParser, NMEAPreParsed, NMEASentence, NMEAUknownSentence, ParserSentences, ProtocolOutput, ProtocolsFile, ProtocolsInput, Sentence, StoredSentences } from './types' -import { getTalker } from './utils' +import { getChecksum } from './checksum' +import { MAX_CHARACTERS, NMEA_ID_LENGTH } from './constants' +import { NMEA_SENTENCES } from './nmea-sentences' +import { getStoreSentences, readProtocolsYAMLFile, readProtocolsYAMLString } from './protocols' +import { BooleanSchema, ProtocolsInputSchema, StringSchema, UnsignedIntegerSchema } from './schemas' +import { createFakeSentence, getIdPayloadAndChecksum, getKnownNMEASentence, getTalker, getUnknowNMEASentence, getUnparsedNMEAFrames, lastUncompletedFrame } from './sentences' +import type { MapStoredSentences, NMEALike, NMEAParser, NMEASentence, ProtocolOutput, ProtocolsFileContent, ProtocolsInput, Sentence, StoredSentence } from './types' export class Parser implements NMEAParser { // Memory - Buffer @@ -16,7 +16,7 @@ export class Parser implements NMEAParser { get bufferLimit (): typeof this._bufferLength { return this._bufferLength } set bufferLimit (limit: number) { this._bufferLength = UnsignedIntegerSchema.parse(limit) } // Sentences - protected _sentences: StoredSentences = new Map() + protected _sentences: MapStoredSentences = new Map() // get sentences() { return this._sentences } constructor (memory: boolean = false, limit: number = MAX_CHARACTERS) { @@ -26,33 +26,19 @@ export class Parser implements NMEAParser { this.readInternalProtocols() } + // Mandatory -------------------------------------------------------------------------------------------------------- private readInternalProtocols (): void { - const parsed = ProtocolsInputSchema.parse(PROTOCOLS) + const parsed = ProtocolsInputSchema.parse(NMEA_SENTENCES) this.addProtocols(parsed) } - private readProtocols (input: ProtocolsInput): ProtocolsFile { - if (input.file !== undefined) return readProtocolsFile(input.file) - if (input.content !== undefined) return readProtocolsString(input.content) + private readProtocols (input: ProtocolsInput): ProtocolsFileContent { + if (input.file !== undefined) return readProtocolsYAMLFile(input.file) + if (input.content !== undefined) return readProtocolsYAMLString(input.content) if (input.protocols !== undefined) return { protocols: input.protocols } throw new Error('Invalid protocols to add') } - getProtocols (): ProtocolOutput[] { - return getSentencesByProtocol(this._sentences) - } - - getSentence (id: string): Sentence { - if (!StringSchema.is(id) || id.length < NMEA_ID_LENGTH) { return null } - const aux = this._sentences.get(id) ?? null - if (aux !== null) { return aux } - const [talk, sent] = [id.slice(0, id.length - NMEA_ID_LENGTH), id.slice(-NMEA_ID_LENGTH)] - const sentence = this._sentences.get(sent) - if (sentence === undefined) { return null } - const talker = getTalker(talk) - return { ...sentence, talker } - } - addProtocols (input: ProtocolsInput): void { if (!ProtocolsInputSchema.is(input)) { const error = 'Parser: invalid protocols to parse' @@ -67,159 +53,91 @@ export class Parser implements NMEAParser { this._sentences = new Map([...this._sentences, ...sentences]) } - getSentences (): ParserSentences { - return Object.fromEntries(this._sentences.entries()) - } - - getFakeSentenceByID (id: string): NMEALike | null { - if (!StringSchema.is(id) || id.length < NMEA_ID_LENGTH) { return null } - const aux = this._sentences.get(id) ?? null - if (aux !== null) { return generateSentenceFromModel(aux) } - // const [_, sent] = [id.slice(0, id.length - NMEA_ID_LENGTH), id.slice(-NMEA_ID_LENGTH)] - const sent = id.slice(-NMEA_ID_LENGTH) - const sentence = this._sentences.get(sent) - if (sentence === undefined) { return null } - const mockSentence = generateSentenceFromModel(sentence) - return getFakeSentence(mockSentence, id) - } - parseData (text: string): NMEASentence[] { if (!StringSchema.is(text)) return [] const data = (this.memory) ? this._buffer + text : text return this.getFrames(data) } - private getField (value: string, type: FieldType): Data { - if (value.length === 0) return null - - switch (type) { - case 'string': - return value - case 'bool': - case 'boolean': - return (Boolean(value)).valueOf() - case 'float': - case 'double': - case 'number': { - const number = parseFloat(value) - if (!Number.isNaN(number)) return number - throw new Error(`invalid float number -> ${value} it is not an ${type}`) + private getFrames (text: string): NMEASentence[] { + if (this._memory) { + const lastFrame = lastUncompletedFrame(text) + if (lastFrame !== null) { + this._buffer = lastFrame } } - - const number = parseInt(value) - if (Number.isInteger(number)) return number - throw new Error(`invalid integer -> ${value} it is not an ${type}`) + const unparsedFrames = getUnparsedNMEAFrames(text) + return unparsedFrames.map(frame => this.getFrame(frame)) } - private getUnknowFrame (sentence: NMEAPreParsed): NMEAUknownSentence { - const fields: FieldUnknown[] = sentence.data.map(value => ({ - name: 'unknown', type: 'string', data: value - })) - return { ...sentence, fields, protocol: { name: 'UNKNOWN' } } - } - - private getKnownFrame (preparsed: NMEAPreParsed): NMEAKnownSentence | null { - const storedSentence = this._sentences.get(preparsed.sentence) - if (storedSentence === undefined) return null - // Bad known frame - if (storedSentence.fields.length !== preparsed.data.length) { - console.debug(`Invalid ${preparsed.sentence} sentence -> it has to have ${storedSentence.fields.length} fields but it contains ${preparsed.data.length}`) - return null + private getFrame (text: NMEALike): NMEASentence { + const received = Date.now() + const { id: sentenceID, payload: pl, checksum: cs } = getIdPayloadAndChecksum(text) + const checksum = getChecksum(cs) + const sentence = this._sentences.get(sentenceID) + // Known NMEA sentence + if (sentence !== undefined) { + const response = getKnownNMEASentence({ received, sample: text, sentenceID, sentencePayload: pl, checksum, model: sentence }) + if (response !== null) { return response } } - try { - // @ts-expect-error the sentence will be completed with the foreach - const knownSentence: NMEAKnownSentence = { ...preparsed, ...storedSentence, data: [] as Data[] } - preparsed.data.forEach((value, index) => { - const type = knownSentence.fields[index].type - const data = this.getField(value, type) - knownSentence.fields[index].data = data - knownSentence.data.push(data) - }) - return knownSentence - } catch (error) { - if (error instanceof Error) { - console.debug(`Invalid NMEA Frame ${preparsed.sentence} -> ${preparsed.raw}\n\t${error.message}`) - } else { - console.error('Parser.getKnownFrame') - console.error(error) + // Known NMEA sentence with Talker + const talker = getTalker(sentenceID) + if (talker !== null) { + const id = sentenceID.replace(talker.value, '') + const talkerSentence = this._sentences.get(id) + if (talkerSentence !== undefined) { + const response = getKnownNMEASentence({ received, sample: text, sentenceID: id, sentencePayload: pl, checksum, model: talkerSentence }) + if (response !== null) { return { ...response, talker } } } } - return null - } - - private getKnownTalkerFrame (preparsed: NMEAPreParsed): NMEAKnownSentence | null { - const { sentence: aux } = preparsed - // Not valid NMEA sentence ID length - if (aux.length < NMEA_ID_LENGTH) { return null } - const [id, sentence] = [aux.slice(0, aux.length - NMEA_ID_LENGTH), aux.slice(-NMEA_ID_LENGTH)] - // Unknown NMEA frame - if (!this._sentences.has(sentence)) { return null } - // Knowing the frame - const knownSentence = this.getKnownFrame({ ...preparsed, sentence }) - if (knownSentence === null) { return null } - if (knownSentence.data.length !== preparsed.data.length) { return null } - // Knowing the talker - const talker = getTalker(id) - return { ...knownSentence, talker } - } - - private getFrame (text: string, timestamp: number): NMEASentence | null { - const unparsedSentence = getNMEAUnparsedSentence(text) - // Not valid NMEA sentence - if (unparsedSentence === null) { - console.debug(`Invalid NMEA frame -> ${text}`) - return null - } - const preparsedSentence: NMEAPreParsed = { ...unparsedSentence, timestamp, talker: null } - // Known NMEA sentence - if (this._sentences.has(preparsedSentence.sentence)) { - const sentence = this.getKnownFrame(preparsedSentence) - if (sentence !== null) return sentence - // Probably known sentence by talker ID - } else { - const sentence = this.getKnownTalkerFrame(preparsedSentence) - if (sentence !== null) return sentence - } // Unknown NMEA sentence - console.debug(`Unknown NMEA sentence -> ${text}`) - return this.getUnknowFrame(preparsedSentence) + const unknown = getUnknowNMEASentence({ received, sample: text, sentenceID, sentencePayload: pl, checksum }) + return (talker !== null) ? { ...unknown, talker } : unknown } - private getFrames (text: string): NMEASentence[] { - const timestamp = Date.now() - const frames: NMEASentence[] = [] - let pivot = 0 - - while (pivot < text.length) { - const start = text.indexOf(START_FLAG, pivot) - if (start === -1) { - this._buffer = '' - break - } - - const end = text.indexOf(END_FLAG, start + START_FLAG_LENGTH) - if (end === -1) { - if (this._memory) { this._buffer = text.slice(start) } - break - } - - const possibleFrame = text.slice(start, end + END_FLAG_LENGTH) + // Nice to have ----------------------------------------------------------------------------------------------------- + getSentences (): StoredSentence[] { + return Array.from(this._sentences.values()) + } - if (!NMEALikeSchema.is(possibleFrame)) { - pivot = start + START_FLAG_LENGTH - continue + getSentencesByProtocol (): ProtocolOutput { + const sentences = this.getSentences() + // return Object.groupBy(sentences, (sentence: StoredSentence) => sentence.protocol.name) + const response: ProtocolOutput = {} + sentences.forEach(sentence => { + const key = sentence.protocol.name + if (!(key in response)) { + response[key] = [sentence] } + response[key].push(sentence) + }) + return response + } - const frame = this.getFrame(possibleFrame, timestamp) - if (frame === null) { - pivot = start + START_FLAG_LENGTH - continue - } + getSentence (id: string): Sentence | null { + if (!StringSchema.is(id) || id.length < NMEA_ID_LENGTH) { return null } + const sentence = this._sentences.get(id) + if (sentence !== undefined) { return { ...sentence } } + const talker = getTalker(id) + if (talker === null) { return null } + const sentenceID = id.slice(talker.value.length) + const sent = this._sentences.get(sentenceID) + if (sent !== undefined) { return { ...sent, talker } } + return null + } - frames.push(frame) - pivot = end + END_FLAG_LENGTH + getFakeSentenceByID (id: string): NMEALike | null { + if (!StringSchema.is(id) || id.length < NMEA_ID_LENGTH) { return null } + // No Talker + const sentence = this._sentences.get(id) + if (sentence !== undefined) { return createFakeSentence(sentence) } + // Talker + const talker = getTalker(id) + if (talker !== null) { + const sentenceID = id.slice(talker.value.length) + const sent = this._sentences.get(sentenceID) + if (sent !== undefined) { return createFakeSentence(sent, talker.value) } } - return frames + return null } } diff --git a/packages/nmea-parser/src/protocols.ts b/packages/nmea-parser/src/protocols.ts index e94cce3..75669c2 100644 --- a/packages/nmea-parser/src/protocols.ts +++ b/packages/nmea-parser/src/protocols.ts @@ -1,56 +1,46 @@ - -import fs from 'node:fs' import yaml from 'js-yaml' -import { ProtocolsFileSchema, StringSchema } from './schemas' -import type { Protocol, ProtocolOutput, ProtocolsFile, StoredSentence, StoredSentences } from './types' +import fs from 'node:fs' +import { ProtocolsFileContentSchema, StringSchema } from './schemas' +import type { MapStoredSentences, Protocol, ProtocolsFileContent, StoredSentence } from './types' -export const readProtocolsString = (content: string): ProtocolsFile => { +export const readProtocolsYAMLString = (content: string): ProtocolsFileContent => { const fileData = yaml.load(content) - return ProtocolsFileSchema.parse(fileData) + const parsed = ProtocolsFileContentSchema.safeParse(fileData) + if (!parsed.success) { + throw new Error(parsed.errors?.toString()) + } + return parsed.data as ProtocolsFileContent + // Valibot version to debug + // const parsed = safeParse(ValibotProtocolsFileContentSchema, fileData) + // if (parsed.success) { return parsed.output } + // throw new Error(parsed.issues?.toString()) } -export const readProtocolsFile = (file: string): ProtocolsFile => { +export const readProtocolsYAMLFile = (file: string): ProtocolsFileContent => { const filename = StringSchema.parse(file) const content = fs.readFileSync(filename, 'utf-8') - return readProtocolsString(content) + return readProtocolsYAMLString(content) } -const getStoreSentencesFromProtocol = (protocol: Protocol): StoredSentences => { +const getStoreSentencesFromProtocol = (protocol: Protocol): MapStoredSentences => { const { protocol: name, standard, version, sentences } = protocol - const storedSentences: StoredSentences = new Map() + const storedSentences: MapStoredSentences = new Map() sentences.forEach(element => { const obj: StoredSentence = { - sentence: element.sentence, - fields: element.fields, + id: element.id, + payload: element.payload, protocol: { name, standard, version }, description: element?.description } - storedSentences.set(element.sentence, obj) + storedSentences.set(element.id, obj) }) return storedSentences } -export const getStoreSentences = ({ protocols }: ProtocolsFile): StoredSentences => { - let storedSentences: StoredSentences = new Map() +export const getStoreSentences = ({ protocols }: ProtocolsFileContent): MapStoredSentences => { + let storedSentences: MapStoredSentences = new Map() protocols.forEach(protocol => { storedSentences = new Map([...storedSentences, ...getStoreSentencesFromProtocol(protocol)]) }) return storedSentences } - -export const getSentencesByProtocol = (storedSentences: StoredSentences): ProtocolOutput[] => { - const mapProtocols = new Map() - storedSentences.forEach((value, key) => { - const mapKey = (value.protocol.version !== undefined) ? `${value.protocol.name}_${value.protocol?.version}` : value.protocol.name - const object = mapProtocols.get(mapKey) ?? { - protocol: value.protocol.name, - version: value.protocol?.version, - sentences: [key] - } - if (!object.sentences.includes(key)) { - object.sentences.push(key) - } - mapProtocols.set(mapKey, object) - }) - return Array.from(mapProtocols.values()) -} diff --git a/packages/nmea-parser/src/schemas.ts b/packages/nmea-parser/src/schemas.ts index e1c7228..4024f0f 100644 --- a/packages/nmea-parser/src/schemas.ts +++ b/packages/nmea-parser/src/schemas.ts @@ -1,18 +1,21 @@ -import * as v from 'valibot' -import { ValibotValidator } from '@schemasjs/validator' import { - IntegerSchema as ValibotIntegerSchema, - Int8Schema as ValibotInt8Schema, + Float32Schema as ValibotFloat32Schema, + Float64Schema as ValibotFloat64Schema, Int16Schema as ValibotInt16Schema, Int32Schema as ValibotInt32Schema, - UnsignedIntegerSchema as ValibotUnsignedIntegerSchema, - Uint8Schema as ValibotUint8Schema, + Int8Schema as ValibotInt8Schema, + IntegerSchema as ValibotIntegerSchema, Uint16Schema as ValibotUint16Schema, - Uint32Schema as ValibotUint32Schema + Uint32Schema as ValibotUint32Schema, + Uint8Schema as ValibotUint8Schema, + UnsignedIntegerSchema as ValibotUnsignedIntegerSchema } from '@schemasjs/valibot-numbers' -import { DELIMITER, END_FLAG, SEPARATOR, START_FLAG } from './constants' +import { ValibotValidator } from '@schemasjs/validator' +import * as v from 'valibot' +import { CHECKSUM_LENGTH, DELIMITER, END_FLAG, FIELD_TYPES, NMEA_SENTENCE_LENGTH, SEPARATOR, START_FLAG } from './constants' +import { stringChecksumToNumber } from './checksum' -// COMMONS +// COMMONS ------------------------------------------------------------------------------------------------------------ const ValibotStringSchema = v.string() export const StringSchema = ValibotValidator>(ValibotStringSchema) @@ -29,56 +32,36 @@ export const IntegerSchema = ValibotValidator>(ValibotInt8Schema) export const Int16Schema = ValibotValidator>(ValibotInt16Schema) export const Int32Schema = ValibotValidator>(ValibotInt32Schema) +const ValibotInt64Schema = v.bigint() +export const Int64Schema = ValibotValidator>(ValibotInt64Schema) export const UnsignedIntegerSchema = ValibotValidator>(ValibotUnsignedIntegerSchema) export const Uint8Schema = ValibotValidator>(ValibotUint8Schema) export const Uint16Schema = ValibotValidator>(ValibotUint16Schema) export const Uint32Schema = ValibotValidator>(ValibotUint32Schema) +const ValibotUint64Schema = v.pipe(v.bigint(), v.minValue(0n)) +export const Uint64Schema = ValibotValidator>(ValibotUint64Schema) -// PROTOCOLS -const ValibotFieldTypeSchema = v.union([ - // Numbers - v.literal('char'), v.literal('uint8'), - v.literal('signed char'), v.literal('int8'), - - v.literal('unsigned short'), v.literal('uint16'), - v.literal('short'), v.literal('int16'), - - v.literal('unsigned int'), v.literal('uint32'), - v.literal('int'), v.literal('int32'), - - // v.literal('unsigned long'), v.literal('uint64'), - // v.literal('long'), v.literal('int64'), - - v.literal('float'), v.literal('float32'), - v.literal('double'), v.literal('float64'), v.literal('number'), +export const Float32Schema = ValibotValidator>(ValibotFloat32Schema) +export const Float64Schema = ValibotValidator>(ValibotFloat64Schema) +// PROTOCOLS ---------------------------------------------------------------------------------------------------------- +const ValibotProtocolFieldTypeSchema = v.picklist(FIELD_TYPES, 'invalid type') +export const ProtocolFieldTypeSchema = ValibotValidator>(ValibotProtocolFieldTypeSchema) - // Strings - v.literal('string'), - // Boolean - v.literal('bool'), - v.literal('boolean') -]) -export const FieldTypeSchema = ValibotValidator>(ValibotFieldTypeSchema) - -const ValibotFieldSchema = v.object({ +const ValibotProtocolFieldSchema = v.object({ name: ValibotStringSchema, - type: ValibotFieldTypeSchema, + type: ValibotProtocolFieldTypeSchema, units: v.optional(ValibotStringSchema), - note: v.optional(ValibotStringSchema) + description: v.optional(ValibotStringSchema) }) -export const FieldSchema = ValibotValidator>(ValibotFieldSchema) +export const ProtocolFieldSchema = ValibotValidator>(ValibotProtocolFieldSchema) -const ValibotFieldUnknownSchema = v.object({ - name: v.literal('unknown'), - type: v.literal('string'), - data: ValibotStringSchema -}) -export const FieldUnknownSchema = ValibotValidator>(ValibotFieldUnknownSchema) +const ValibotProtocolSentencePayloadSchema = v.array(ValibotProtocolFieldSchema, 'invalid payload') +export const ProtocolSentencePayloadSchema = ValibotValidator>(ValibotProtocolSentencePayloadSchema) const ValibotProtocolSentenceSchema = v.object({ - sentence: ValibotStringSchema, - fields: v.array(ValibotFieldSchema), + id: ValibotStringSchema, + payload: ValibotProtocolSentencePayloadSchema, description: v.optional(ValibotStringSchema) }) export const ProtocolSentenceSchema = ValibotValidator>(ValibotProtocolSentenceSchema) @@ -113,123 +96,104 @@ export const VersionSchema = ValibotValidator>(ValibotProtocolSchema) +export const ProtocolSchema = ValibotValidator>(ValibotProtocolSchema) -const ValibotProtocolsFileSchema = v.object({ protocols: v.array(ValibotProtocolSchema) }) -export const ProtocolsFileSchema = ValibotValidator>(ValibotProtocolsFileSchema) +export const ValibotProtocolsFileContentSchema = v.object({ protocols: v.array(ValibotProtocolSchema) }) +export const ProtocolsFileContentSchema = ValibotValidator>(ValibotProtocolsFileContentSchema) const ValibotProtocolsInputSchema = v.object({ file: v.optional(ValibotStringSchema), content: v.optional(ValibotStringSchema), protocols: v.optional(v.array(ValibotProtocolSchema)) }) -export const ProtocolsInputSchema = ValibotValidator>(ValibotProtocolsInputSchema) +export const ProtocolsInputSchema = ValibotValidator>(ValibotProtocolsInputSchema) const ValibotStoredSentenceSchema = v.object({ - sentence: ValibotStringSchema, + id: ValibotStringSchema, protocol: v.object({ name: ValibotStringSchema, standard: v.optional(ValibotBooleanSchema, false), - version: v.optional(ValibotVersionSchema) + version: v.optional(ValibotStringSchema) }), - fields: v.array(ValibotFieldSchema), + payload: v.array(ValibotProtocolFieldSchema), description: v.optional(ValibotStringSchema) }) -export const StoredSentenceSchema = ValibotValidator>(ValibotStoredSentenceSchema) +export const StoredSentenceSchema = ValibotValidator>(ValibotStoredSentenceSchema) -const ValibotStoredSentencesSchema = v.map(ValibotStringSchema, ValibotStoredSentenceSchema) -export const StoredSentencesSchema = ValibotValidator>(ValibotStoredSentencesSchema) +const ValibotMapStoredSentencesSchema = v.map(ValibotStringSchema, ValibotStoredSentenceSchema) +export const MapStoredSentencesSchema = ValibotValidator>(ValibotMapStoredSentencesSchema) const ValibotJSONSchemaInputSchema = v.object({ path: v.optional(ValibotStringSchema), filename: v.optional(ValibotStringSchema, 'nmea_protocols_schema.json') }) export const JSONSchemaInputSchema = ValibotValidator>(ValibotJSONSchemaInputSchema) -// SENTENCES -const ValibotNMEALikeSchema = v.pipe( - v.string(), - v.startsWith(START_FLAG), - v.includes(SEPARATOR), - v.includes(DELIMITER), - v.endsWith(END_FLAG) -) -export const NMEALikeSchema = ValibotValidator>(ValibotNMEALikeSchema) +// SENTENCES ---------------------------------------------------------------------------------------------------------- +const ValibotChecksumSchema = v.object({ + sample: ValibotStringSchema, + value: ValibotUint8Schema +}) +export const ChecksumSchema = ValibotValidator>(ValibotChecksumSchema) + +const ValibotValueSchema = v.union([ValibotStringSchema, ValibotBooleanSchema, ValibotNumberSchema, v.bigint(), v.null()], 'invalid value') +export const ValueSchema = ValibotValidator>(ValibotValueSchema) const ValibotTalkerSchema = v.object({ - id: ValibotStringSchema, + value: ValibotStringSchema, description: ValibotStringSchema }) export const TalkerSchema = ValibotValidator>(ValibotTalkerSchema) -const ValibotNMEAUnparsedSentenceSchema = v.object({ - raw: ValibotStringSchema, - sentence: ValibotStringSchema, - checksum: ValibotUnsignedIntegerSchema, - data: ValibotStringArraySchema +const ValibotNMEALikeSchema = v.custom<`$${string}*${string}\r\n`>(input => { + if (typeof input !== 'string') { return false } + if (!input.startsWith(START_FLAG)) { return false } + if (!input.endsWith(END_FLAG)) { return false } + const parts = input.split(DELIMITER) + if (parts.length !== 2) { return false } + const [info, cs] = parts + if (cs.length !== CHECKSUM_LENGTH + END_FLAG.length) { return false } + const checksum = cs.slice(0, CHECKSUM_LENGTH) + const numChecksum = stringChecksumToNumber(checksum) + if (!v.safeParse(ValibotUint8Schema, numChecksum).success) { return false } + const data = info.slice(START_FLAG.length) + if (data.length < NMEA_SENTENCE_LENGTH) { return false } + return info.includes(SEPARATOR) +}) +export const NMEALikeSchema = ValibotValidator>(ValibotNMEALikeSchema) + +const ValibotNMEAParsedFieldSchema = v.object({ + name: v.optional(v.string('payload name bad'), 'unknown'), + sample: v.string('payload sample bad'), + value: ValibotValueSchema, + type: v.optional(v.union([v.picklist(FIELD_TYPES), v.literal('unknown')], 'payload type bad'), 'unknown'), + units: v.optional(v.string('payload units bad'), 'unknown'), + description: v.optional(v.string('payload description bad')), + metadata: v.optional(v.any()) +}) +export const NMEAParsedFieldchema = ValibotValidator>(ValibotNMEAParsedFieldSchema) + +const ValibotNMEAParsedPayloadSchema = v.array(ValibotNMEAParsedFieldSchema) +export const NMEAParsedPayloadSchema = ValibotValidator>(ValibotNMEAParsedPayloadSchema) + +const ValibotNMEASentenceSchema = v.object({ + received: ValibotUnsignedIntegerSchema, + sample: ValibotNMEALikeSchema, + id: ValibotStringSchema, + description: v.optional(ValibotStringSchema), + checksum: ValibotChecksumSchema, + payload: v.array(ValibotNMEAParsedFieldSchema), + protocol: v.optional( + v.object({ + name: ValibotStringSchema, + standard: ValibotBooleanSchema, + version: v.optional(ValibotStringSchema) + }), + { name: 'unknown', standard: false } + ), + talker: v.optional(ValibotTalkerSchema) }) -export const NMEAUnparsedSentenceSchema = ValibotValidator>(ValibotNMEAUnparsedSentenceSchema) - -const ValibotNMEAPreParsedSentenceSchema = v.intersect([ - ValibotNMEAUnparsedSentenceSchema, - v.object({ - timestamp: ValibotUnsignedIntegerSchema, - talker: v.optional(v.nullable(ValibotTalkerSchema), null) - }) -]) -export const NMEAPreParsedSentenceSchema = ValibotValidator>(ValibotNMEAPreParsedSentenceSchema) - -const ValibotDataSchema = v.nullable(v.union([ValibotStringSchema, ValibotNumberSchema, ValibotBooleanSchema])) -export const DataSchema = ValibotValidator>(ValibotDataSchema) - -const ValibotFieldParsedSchema = v.intersect([ - ValibotFieldSchema, - v.object({ - data: ValibotDataSchema - }) -]) -export const FieldParsedSchema = ValibotValidator>(ValibotFieldParsedSchema) - -const ValibotStoredSentenceDataSchema = v.intersect([ - ValibotStoredSentenceSchema, - v.object({ - fields: v.array(ValibotFieldParsedSchema), - data: v.array(ValibotDataSchema) - }) -]) -export const StoredSentenceDataSchema = ValibotValidator>(ValibotStoredSentenceDataSchema) - -const ValibotNMEAUknownSentenceSchema = v.intersect([ - ValibotNMEAPreParsedSentenceSchema, - v.object({ - protocol: v.object({ name: v.literal('UNKNOWN') }), - fields: v.array(ValibotFieldUnknownSchema) - }) -]) -export const NMEAUknownSentenceSchema = ValibotValidator>(ValibotNMEAUknownSentenceSchema) - -const ValibotNMEAKnownSentenceSchema = v.intersect([ - ValibotStoredSentenceDataSchema, - v.object({ - timestamp: ValibotUnsignedIntegerSchema, - talker: v.optional(v.nullable(ValibotTalkerSchema), null), - checksum: ValibotUnsignedIntegerSchema, - fields: v.array(ValibotFieldParsedSchema), - data: v.array(ValibotDataSchema) - }) -]) -export const NMEAKnownSentenceSchema = ValibotValidator>(ValibotNMEAKnownSentenceSchema) - -const ValibotNMEASentenceSchema = v.union([ValibotNMEAKnownSentenceSchema, ValibotNMEAUknownSentenceSchema]) -export const NMEASentenceSchema = ValibotValidator>(ValibotNMEASentenceSchema) - -const ValibotOutputSentenceSchema = v.intersect([ - ValibotStoredSentenceSchema, - v.object({ - talker: v.optional(ValibotTalkerSchema) - }) -]) -export const OutputSentenceSchema = ValibotValidator>(ValibotOutputSentenceSchema) +export const NMEASentenceSchema = ValibotValidator>(ValibotNMEASentenceSchema) diff --git a/packages/nmea-parser/src/sentences.ts b/packages/nmea-parser/src/sentences.ts index 14a48fa..c35a2b5 100644 --- a/packages/nmea-parser/src/sentences.ts +++ b/packages/nmea-parser/src/sentences.ts @@ -1,113 +1,236 @@ -import { getChecksum, numberChecksumToString, stringChecksumToNumber } from './checksum' -import { CHECKSUM_LENGTH, DELIMITER, END_FLAG, END_FLAG_LENGTH, MINIMAL_LENGTH, SEPARATOR, START_FLAG, START_FLAG_LENGTH } from './constants' -import { NMEALikeSchema, NMEAUknownSentenceSchema, NMEAUnparsedSentenceSchema } from './schemas' -import type { FieldType, FieldUnknown, NMEALike, NMEAPreParsed, NMEAUknownSentence, NMEAUnparsedSentence, StoredSentence } from './types' +import { calculateChecksum, numberChecksumToString, stringChecksumToNumber } from './checksum' +import { CHECKSUM_LENGTH, DELIMITER, END_FLAG, END_FLAG_LENGTH, MINIMAL_LENGTH, NMEA_ID_LENGTH, SEPARATOR, START_FLAG, TALKERS, TALKERS_SPECIAL } from './constants' +import { Float32Schema, Float64Schema, Int16Schema, Int32Schema, Int64Schema, Int8Schema, NMEASentenceSchema, Uint16Schema, Uint32Schema, Uint64Schema, Uint8Schema } from './schemas' +import type { Checksum, NMEALike, NMEASentence, Payload, ProtocolFieldType, StoredSentence, Talker, Value } from './types' import { isLowerCharASCII, isNumberCharASCII, isUpperCharASCII } from './utils' -// GET NMEA SENTENCE -export const isNMEAFrame = (text: string): boolean => { - // Not valid NMEA like - const parsed = NMEALikeSchema.safeParse(text) - if (!parsed.success) { - console.debug(`Error parsing frame -> ${(parsed.errors as string[])[0]}`) - return false - } - // More than one START Flag - if (text.lastIndexOf(START_FLAG) > 0) { - console.debug(`Invalid NMEA line -> it contains more than one ${START_FLAG} symbol -> ${text}`) - return false + +export const lastUncompletedFrame = (text: string): string | null => { + // Start of last possible frame + const lastStartIndex = text.lastIndexOf(START_FLAG) + if (lastStartIndex === -1) { return null } + // Last possible frame + const remainder = text.slice(lastStartIndex) + // Complete frame -> discard + if (remainder.includes(END_FLAG)) { return null } + // Incomplete frame + return remainder +} + +export const getUnparsedNMEAFrames = (text: string): NMEALike[] => { + // Includes all flags + if ([START_FLAG, SEPARATOR, DELIMITER, END_FLAG].some(flag => !text.includes(flag))) { return [] } + return text + // Split by END Flag + .split(END_FLAG) + // Remove empty or not enough string + .filter(str => str.length > MINIMAL_LENGTH) + // Contains START FLAG + .filter(str => str.includes(START_FLAG)) + // String from last START FLAG + .map(str => str.split(START_FLAG).at(-1) as string) + // Contains just one DELIMITER FLAG + .filter(str => { + const first = str.indexOf(DELIMITER) + const last = str.lastIndexOf(DELIMITER) + return (first !== -1) && (first === last) + }) + // Valid CHECKSUM + .filter(str => { + const [payload, checksum] = str.split(DELIMITER) as [string, string] + // Checksum has two length + if (checksum.length !== CHECKSUM_LENGTH) { + console.debug(`Invalid sentence: checksum has not two characters -> $${str}`) + return false + } + // Checksum hexadecimal + if (!/[0-9A-Fa-f]{2}/.test(checksum)) { + console.debug(`Invalid sentence: checksum is not a hexadecimal digit -> $${str}`) + return false + } + // Invalid checksum + const numChecksum = stringChecksumToNumber(checksum) + const computedChecksum = calculateChecksum(payload) + if (numChecksum !== computedChecksum) { + console.debug(`Invalid sentence: calculated checksum ${numberChecksumToString(computedChecksum)} is not equal to given checksum ${checksum} -> $${str}`) + return false + } + // ok + return true + }) + // PAYLOAD contains valid characters + .filter(str => { + const payload = str.split(DELIMITER).at(0) as string + if (!payload.includes(SEPARATOR)) { + console.debug(`Invalid sentence: payload has not separator character "${SEPARATOR}" -> $${str}`) + return false + } + // Payload valid characters -> TODO: with regex + if (['\r', '\n'].some(char => payload.includes(char))) { + console.debug(`Invalid sentence: payload has invalid characters -> $${str}`) + return false + } + // ok + return true + }) + // Return NMEA frames + .map(str => `${START_FLAG}${str}${END_FLAG}` as NMEALike) +} + +export const getIdPayloadAndChecksum = (frame: NMEALike): { id: string, payload: string, checksum: string } => { + const [info, checksum] = frame.slice(START_FLAG.length, -END_FLAG_LENGTH).split(DELIMITER) + const id = info.split(SEPARATOR)[0] + const payload = info.slice(id.length + 1) + return { id, payload, checksum } +} + +export const hasSameNumberOfFields = (payload: string, sentence: StoredSentence): boolean => payload.split(SEPARATOR).length === sentence.payload.length + +const parseNumber = (value: string, type: ProtocolFieldType): Value | undefined => { + // Integers + if (type === 'int8') return Int8Schema.safeParse(Number(value)).data + if (type === 'int16') return Int16Schema.safeParse(Number(value)).data + if (type === 'int32') return Int32Schema.safeParse(Number(value)).data + if (type === 'int64') return Int64Schema.safeParse(BigInt(value)).data + // Unsigned Integers + if (type === 'uint8') return Uint8Schema.safeParse(Number(value)).data + if (type === 'uint16') return Uint16Schema.safeParse(Number(value)).data + if (type === 'uint32') return Uint32Schema.safeParse(Number(value)).data + if (type === 'uint64') return Uint64Schema.safeParse(BigInt(value)).data + // Floats + if (type === 'float32') return Float32Schema.safeParse(Number(value)).data + if (type === 'float64') return Float64Schema.safeParse(Number(value)).data +} +const parseBoolean = (value: string): Value | undefined => { + if (value.toLowerCase() === 'false' || value === '0') return false + if (value.toLowerCase() === 'true' || value === '1') return true +} +export const parseValue = (value: string, type: ProtocolFieldType): Value => { + try { + // String + if (type === 'string') { return value } + // Boolean + if (type === 'boolean') { + const b = parseBoolean(value) + if (b !== undefined) { return b } + } + // Number + const num = parseNumber(value, type) + if (num !== undefined) { return num } + } catch (error) { + console.debug(`Error parsing value: ${value} is not ${type}`) } - // Not enough characters - const data = parsed.data as string - if (data.length < MINIMAL_LENGTH) { - console.debug('Invalid NMEA line -> it doesn\'t contain any data') - return false + return null +} + +export const getKnownNMEASentence = ( + { received, sample, sentenceID, sentencePayload, checksum, model }: { received: number, sample: NMEALike, sentenceID: string, sentencePayload: string, checksum: Checksum, model: StoredSentence } +): NMEASentence | null => { + // Invalid sentence + if (!hasSameNumberOfFields(sentencePayload, model)) return null + // Valid sentence + const fields = sentencePayload.split(SEPARATOR) + const payload: Payload = model.payload.map(({ name, type, units, description }, index) => { + const sample = fields[index] + const value = parseValue(sample, type) + return { name, sample, value, type, units: units ?? 'unknown', description } + }) + const { protocol } = model + // TODO: Metada -> GGA Latitude-Longitude degrees + const sent: NMEASentence = { + received, + sample, + id: sentenceID, + checksum, + payload, + protocol } - // Not one DELIMITER - const frameParts = data.slice(START_FLAG_LENGTH, -END_FLAG_LENGTH).split(DELIMITER) - if (frameParts.length !== 2) { - console.debug(`Invalid NMEA line -> it doesn't contain just one DELIMITER ${DELIMITER}`) - return false + return NMEASentenceSchema.parse(sent) +} + +export const getTalker = (sentenceID: string): Talker | null => { + if (sentenceID.length <= NMEA_ID_LENGTH) return null + // Known Talker + const talker = TALKERS.filter(([talkerID, _talkerDescription]) => sentenceID.startsWith(talkerID)) + if (talker.length === 1) { + const value = talker[0][0] + return { value, description: talker[0][1] } } - // Check checksum length and value - const [frame, checksum] = frameParts - if (checksum.length !== CHECKSUM_LENGTH) { - console.debug(`Invalid NMEA line -> checksum has not just two characters => CHECKSUM = ${checksum}`) - return false + // Special Talker U# + if (sentenceID.startsWith('U') && !isNaN(Number(sentenceID[1]))) { + const value = sentenceID.slice(0, 2) + return { value, description: TALKERS_SPECIAL.U } } - const frameChecksumNumber = getChecksum(frame) - const checksumNumber = stringChecksumToNumber(checksum) - if (frameChecksumNumber !== checksumNumber) { - console.debug(`Invalid NMEA line -> calculated checksum ${frameChecksumNumber} != frame checksum ${checksumNumber}`) - return false + // Special Talker Pxxx -> Propietary + if (sentenceID.startsWith('P')) { + return { value: sentenceID, description: TALKERS_SPECIAL.P } } - return true -} - -export const getNMEAUnparsedSentence = (text: string): NMEAUnparsedSentence | null => { - if (!isNMEAFrame(text)) return null - const raw = text - const [info, cs] = raw.slice(1, -END_FLAG_LENGTH).split(DELIMITER) - const checksum = stringChecksumToNumber(cs) - const [sentence, ...data] = info.split(SEPARATOR) - const parsed = NMEAUnparsedSentenceSchema.safeParse({ raw, sentence, checksum, data }) - if (parsed.success) { return (parsed.data as NMEAUnparsedSentence) } - console.debug(`Error parsing sentence -> ${raw}`) - console.debug((parsed.errors as string[])[0]) + // Unknown talker return null } -export const getUnknownSentence = (sentence: NMEAPreParsed): NMEAUknownSentence => { - const fields: FieldUnknown[] = sentence.data.map(value => ({ name: 'unknown', type: 'string', data: value })) - const unknowFrame = { ...sentence, protocol: { name: 'UNKNOWN' }, fields } - const parsed = NMEAUknownSentenceSchema.safeParse(unknowFrame) - if (parsed.success) return parsed.data as NMEAUknownSentence - throw new Error((parsed.errors as string[])[0]) +export const getUnknowNMEASentence = ( + { received, sample, sentenceID, sentencePayload, checksum }: { received: number, sample: NMEALike, sentenceID: string, sentencePayload: string, checksum: Checksum } +): NMEASentence => { + const fields = sentencePayload.split(SEPARATOR) + const response: Payload = fields.map(field => ({ + name: 'unknown', sample: field, value: field, type: 'string', units: 'unknown' + })) + const sent = NMEASentenceSchema.parse({ + received, + sample, + id: sentenceID, + checksum, + payload: response, + description: 'unknown nmea sentence' + }) + return sent } // TESTING - GENERATE -export const getNumberValue = (type: FieldType): number => { +const createNumberValue = (type: ProtocolFieldType): number | bigint | null => { const sign = ((Math.random() < 0.5) ? -1 : 1) + // Unsigned integer const useed = Math.round(Math.random() * (Number.MAX_SAFE_INTEGER - Number.MIN_SAFE_INTEGER) + Number.MIN_SAFE_INTEGER) + // Signed integer const seed = useed * sign + // Float const fseed = Math.random() * sign + // Big Int + const uint64 = new BigUint64Array([0n]) + crypto.getRandomValues(uint64) + const biguintseed = uint64[0] + const int64 = new BigInt64Array([0n]) + crypto.getRandomValues(int64) + const bigintseed = int64[0] switch (type) { + // Unsigned Integers case 'uint8': - case 'char': return (new Uint8Array([useed]))[0] - case 'uint16': - case 'unsigned short': return (new Uint16Array([useed]))[0] - case 'uint32': - case 'unsigned int': return (new Uint32Array([useed]))[0] - + case 'uint64': + return biguintseed + // Signed Integers case 'int8': - case 'signed char': return (new Int8Array([seed]))[0] - case 'int16': - case 'short': return (new Int16Array([seed]))[0] - case 'int32': - case 'int': return (new Int32Array([seed]))[0] - + case 'int64': + return bigintseed + // Floats case 'float32': - case 'float': return (new Float32Array([fseed]))[0] - case 'float64': - case 'double': - case 'number': return (new Float64Array([fseed]))[0] } - throw Error('invalid type') + return null } -export const getStringValue = (): string => { +const createStringValue = (): string => { const text = Buffer.from(Math.random().toString(36).substring(2)).toString('ascii') const array = Array.from(text).map(letter => { if (isLowerCharASCII(letter) || isUpperCharASCII(letter) || isNumberCharASCII(letter)) { return letter } @@ -116,33 +239,29 @@ export const getStringValue = (): string => { return array.join('') } -export const getValue = (type: FieldType): string | number | boolean => { +export const createValue = (type: ProtocolFieldType): string | number | bigint | boolean | null => { switch (type) { - case 'bool': case 'boolean': return Math.random() > 0.5 case 'string': - return getStringValue() + return createStringValue() } - return getNumberValue(type) + return createNumberValue(type) } -export const generateSentenceFromModel = (model: StoredSentence): NMEALike => { - let sentence: string = `$${model.sentence}` - model.fields.forEach(field => { - const value = getValue(field.type) - sentence += `,${value.toString()}` +export const createPayload = (model: StoredSentence): string => { + let payload = '' + model.payload.forEach(field => { + const value = createValue(field.type) + payload += (value !== null) ? `${value.toString()},` : ',' }) - const cs = getChecksum(sentence.slice(1)) - const checksum = numberChecksumToString(cs) - sentence += `*${checksum}\r\n` - return sentence + return payload.slice(0, -1) } -export const getFakeSentence = (text: string, sentence: string): string => { - const frame = text.slice(START_FLAG_LENGTH, -END_FLAG_LENGTH).split(DELIMITER)[0] - const info = frame.split(SEPARATOR).slice(1) - const newFrame = [sentence, ...info].join(SEPARATOR) - const checksum = numberChecksumToString(getChecksum(newFrame)) - return `$${newFrame}${DELIMITER}${checksum}${END_FLAG}` +export const createFakeSentence = (model: StoredSentence, talker?: string): NMEALike => { + const id = (talker !== undefined) ? `${talker}${model.id}` : model.id + const payload = createPayload(model) + const info = `${id},${payload}` + const checksum = numberChecksumToString(calculateChecksum(info)) + return `${START_FLAG}${info}${DELIMITER}${checksum}${END_FLAG}` } diff --git a/packages/nmea-parser/src/types.ts b/packages/nmea-parser/src/types.ts index 4c4d332..c5d1202 100644 --- a/packages/nmea-parser/src/types.ts +++ b/packages/nmea-parser/src/types.ts @@ -1,65 +1,81 @@ import type { - DataSchema, - FieldParsedSchema, - FieldSchema, - FieldTypeSchema, - FieldUnknownSchema, + ChecksumSchema, + Float32Schema, + Float64Schema, + Int16Schema, + Int32Schema, + Int64Schema, + Int8Schema, + IntegerSchema, JSONSchemaInputSchema, - NMEAKnownSentenceSchema, + MapStoredSentencesSchema, NMEALikeSchema, - NMEAPreParsedSentenceSchema, + NMEAParsedFieldchema, + NMEAParsedPayloadSchema, NMEASentenceSchema, - NMEAUknownSentenceSchema, - NMEAUnparsedSentenceSchema, - OutputSentenceSchema, + ProtocolFieldSchema, + ProtocolFieldTypeSchema, ProtocolSchema, + ProtocolSentencePayloadSchema, ProtocolSentenceSchema, - ProtocolsFileSchema, + ProtocolsFileContentSchema, ProtocolsInputSchema, - StoredSentenceDataSchema, StoredSentenceSchema, - StoredSentencesSchema, TalkerSchema, + Uint16Schema, + Uint32Schema, + Uint64Schema, + Uint8Schema, + UnsignedIntegerSchema, + ValueSchema, VersionSchema } from './schemas' // COMMONS +export type Integer = ReturnType +export type Int8 = ReturnType +export type Int16 = ReturnType +export type Int32 = ReturnType +export type Int64 = ReturnType + +export type UnsignedInteger = ReturnType +export type Uint8 = ReturnType +export type Uint16 = ReturnType +export type Uint32 = ReturnType +export type Uint64 = ReturnType + +export type Float32 = ReturnType +export type Float64 = ReturnType // PROTOCOLS -export type FieldType = ReturnType -export type Field = ReturnType -export type FieldUnknown = ReturnType +export type ProtocolFieldType = ReturnType +export type ProtocolField = ReturnType export type ProtocolSentence = ReturnType export type Version = ReturnType export type Protocol = ReturnType -export type ProtocolsFile = ReturnType +export type ProtocolsFileContent = ReturnType export type ProtocolsInput = ReturnType export type StoredSentence = ReturnType -export type StoredSentences = ReturnType -export type ParserSentences = Record +export type StoredPayload = ReturnType +export type MapStoredSentences = ReturnType // JSON Schema export type JSONSchemaInput = ReturnType // SENTENCES -export type NMEALike = ReturnType +export type Checksum = ReturnType +export type Value = ReturnType export type Talker = ReturnType -export type NMEAUnparsedSentence = ReturnType -export type NMEAPreParsed = ReturnType -export type Data = ReturnType -export type FieldParsed = ReturnType -export type StoredSentenceData = ReturnType -export type NMEAUknownSentence = ReturnType -export type NMEAKnownSentence = ReturnType +export type Field = ReturnType +export type Payload = ReturnType export type NMEASentence = ReturnType -export type OutputSentence = ReturnType -export type Sentence = null | OutputSentence +export type Sentence = StoredSentence & { talker?: Talker } // PARSER -export interface ProtocolOutput { - protocol: string - version?: string - sentences: string[] -} +export type ProtocolOutput = Record +export type NMEALike = ReturnType export interface NMEAParser { + // Mandatory parseData: (data: string) => NMEASentence[] addProtocols: (protocols: ProtocolsInput) => void - getProtocols: () => ProtocolOutput[] - getSentence: (id: string) => Sentence + // Nice to have + getSentences: () => StoredSentence[] + getSentencesByProtocol: () => ProtocolOutput + getSentence: (id: string) => Sentence | null getFakeSentenceByID: (id: string) => NMEALike | null } diff --git a/packages/nmea-parser/src/utils.ts b/packages/nmea-parser/src/utils.ts index d5e5d1d..30af9be 100644 --- a/packages/nmea-parser/src/utils.ts +++ b/packages/nmea-parser/src/utils.ts @@ -1,5 +1,4 @@ -import { CODE, TALKERS, TALKERS_SPECIAL } from './constants' -import type { Talker } from './types' +import { CODE } from './constants' export const numberToHexString = (num: number): string => num.toString(16) @@ -11,19 +10,3 @@ export const isBoundedASCII = (char: string, min: number, max: number): boolean export const isLowerCharASCII = (char: string): boolean => isBoundedASCII(char, CODE.a, CODE.z) export const isUpperCharASCII = (char: string): boolean => isBoundedASCII(char, CODE.A, CODE.Z) export const isNumberCharASCII = (char: string): boolean => isBoundedASCII(char, CODE['0'], CODE['9']) - -export const getTalker = (id: string): Talker => { - // Known Talker - const description = TALKERS.get(id) - if (typeof description === 'string') { return { id, description } } - // Special Talker U# - if (id.startsWith('U') && id.length === 2 && !isNaN(Number(id[1]))) { - return { id, description: TALKERS_SPECIAL.U } - } - // Special Talker Pxxx -> Propietary - if (id.startsWith('P')) { - return { id, description: TALKERS_SPECIAL.P } - } - // Uknown talker - return { id, description: 'unknown' } -} diff --git a/packages/nmea-parser/tests/index.test.ts b/packages/nmea-parser/tests/index.test.ts index a7dff3d..82a2672 100644 --- a/packages/nmea-parser/tests/index.test.ts +++ b/packages/nmea-parser/tests/index.test.ts @@ -1,43 +1,34 @@ import fs from 'node:fs' import path from 'node:path' -import { describe, test, expect } from 'vitest' +import { describe, expect, test } from 'vitest' import { NMEAParser as Parser } from '../src' -import { generateSentenceFromModel, getFakeSentence } from '../src/sentences' -import { NMEAKnownSentenceSchema, NMEALikeSchema, NMEAUknownSentenceSchema } from '../src/schemas' -import { readProtocolsFile } from '../src/protocols' -import { Protocol } from '../src/types' +import { readProtocolsYAMLFile } from '../src/protocols' +import { NMEALikeSchema, } from '../src/schemas' +import { createFakeSentence } from '../src/sentences' +import type { Protocol } from '../src/types' const NORSUB_FILE = path.join(__dirname, '..', 'protocols', 'norsub.yaml') describe('Parser', () => { test('Default constructor', () => { const parser = new Parser() - const parserProtocols = parser.getProtocols() - expect(parserProtocols[0].protocol.includes('NMEA')).toBeTruthy() - + // Sentence const parserSentences = parser.getSentences() - const expectedSentences = ['AAM', 'GGA'] - expectedSentences.forEach(sentence => expect(Object.keys(parserSentences).includes(sentence)).toBeTruthy()) + expect( + ['AAM', 'GGA'].some(id => parserSentences.filter(sentence => sentence.id === id).length > 0) + ).toBeTruthy() + // Sentences by Protocol + const parserProtocols = parser.getSentencesByProtocol() + expect('NMEA' in parserProtocols).toBeTruthy() + }) test('Add protocols with file', () => { - const file = NORSUB_FILE const parser = new Parser() + // Add file + const file = NORSUB_FILE parser.addProtocols({ file }) - - const parserProtocols = parser.getProtocols() - - const expectedProtocols = [ - 'NMEA', - 'GYROCOMPAS1', 'Tokimek PTVG', 'RDI ADCP', 'SMCA', 'SMCC', - 'NORSUB', 'NORSUB2', 'NORSUB6', 'NORSUB7', 'NORSUB7b', 'NORSUB8', //'NORSUB PRDID', - ] - expectedProtocols.forEach(protocol => { - const result = parserProtocols.filter(p => p.protocol === protocol) - if (result.length === 0) { console.log(`Protocol ${protocol} is not included`) } - expect(result.length).toBeGreaterThan(0) - }) - + // Sentences const parserSentences = parser.getSentences() const expectedSentences = [ 'AAM', 'GGA', @@ -45,31 +36,23 @@ describe('Parser', () => { 'PNORSUB', 'PNORSUB2', 'PNORSUB6', 'PNORSUB7', 'PNORSUB7b', 'PNORSUB8', 'PRDID', 'PTVG', 'PRDID', 'PSMCA', 'PSMCC', ] - expectedSentences.forEach(sentence => { - const result = Object.keys(parserSentences).includes(sentence) - if (!result) { console.log(`Sentence ${sentence} is not included`) } - expect(result).toBeTruthy() - }) - }) - - test('Add protocols with content', () => { - const content = fs.readFileSync(NORSUB_FILE, 'utf-8') - const parser = new Parser() - parser.addProtocols({ content }) - - const parserProtocols = parser.getProtocols() - + expect(expectedSentences.every(id => parserSentences.filter(sentence => sentence.id === id).length > 0)).toBeTruthy() + // Check protocols + const parserProtocols = parser.getSentencesByProtocol() const expectedProtocols = [ 'NMEA', 'GYROCOMPAS1', 'Tokimek PTVG', 'RDI ADCP', 'SMCA', 'SMCC', 'NORSUB', 'NORSUB2', 'NORSUB6', 'NORSUB7', 'NORSUB7b', 'NORSUB8', //'NORSUB PRDID', ] - expectedProtocols.forEach(protocol => { - const result = parserProtocols.filter(p => p.protocol === protocol) - if (result.length === 0) { console.log(`Protocol ${protocol} is not included`) } - expect(result.length).toBeGreaterThan(0) - }) - + expectedProtocols.forEach(protocol => expect(protocol in parserProtocols).toBeTruthy()) + }) + + test('Add protocols with content', () => { + const parser = new Parser() + // Add file content + const content = fs.readFileSync(NORSUB_FILE, 'utf-8') + parser.addProtocols({ content }) + // Sentences const parserSentences = parser.getSentences() const expectedSentences = [ 'AAM', 'GGA', @@ -77,31 +60,23 @@ describe('Parser', () => { 'PNORSUB', 'PNORSUB2', 'PNORSUB6', 'PNORSUB7', 'PNORSUB7b', 'PNORSUB8', 'PRDID', 'PTVG', 'PRDID', 'PSMCA', 'PSMCC', ] - expectedSentences.forEach(sentence => { - const result = Object.keys(parserSentences).includes(sentence) - if (!result) { console.log(`Sentence ${sentence} is not included`) } - expect(result).toBeTruthy() - }) - }) - - test('Add protocols with protocols', () => { - const { protocols } = readProtocolsFile(NORSUB_FILE) - const parser = new Parser() - parser.addProtocols({ protocols }) - - const parserProtocols = parser.getProtocols() - + expect(expectedSentences.every(id => parserSentences.filter(sentence => sentence.id === id).length > 0)).toBeTruthy() + // Check protocols + const parserProtocols = parser.getSentencesByProtocol() const expectedProtocols = [ 'NMEA', 'GYROCOMPAS1', 'Tokimek PTVG', 'RDI ADCP', 'SMCA', 'SMCC', 'NORSUB', 'NORSUB2', 'NORSUB6', 'NORSUB7', 'NORSUB7b', 'NORSUB8', //'NORSUB PRDID', ] - expectedProtocols.forEach(protocol => { - const result = parserProtocols.filter(p => p.protocol === protocol) - if (result.length === 0) { console.log(`Protocol ${protocol} is not included`) } - expect(result.length).toBeGreaterThan(0) - }) - + expectedProtocols.forEach(protocol => expect(protocol in parserProtocols).toBeTruthy()) + }) + + test('Add protocols with protocols', () => { + const parser = new Parser() + // Add file object + const { protocols } = readProtocolsYAMLFile(NORSUB_FILE) + parser.addProtocols({ protocols }) + // Sentences const parserSentences = parser.getSentences() const expectedSentences = [ 'AAM', 'GGA', @@ -109,11 +84,15 @@ describe('Parser', () => { 'PNORSUB', 'PNORSUB2', 'PNORSUB6', 'PNORSUB7', 'PNORSUB7b', 'PNORSUB8', 'PRDID', 'PTVG', 'PRDID', 'PSMCA', 'PSMCC', ] - expectedSentences.forEach(sentence => { - const result = Object.keys(parserSentences).includes(sentence) - if (!result) { console.log(`Sentence ${sentence} is not included`) } - expect(result).toBeTruthy() - }) + expect(expectedSentences.every(id => parserSentences.filter(sentence => sentence.id === id).length > 0)).toBeTruthy() + // Check protocols + const parserProtocols = parser.getSentencesByProtocol() + const expectedProtocols = [ + 'NMEA', + 'GYROCOMPAS1', 'Tokimek PTVG', 'RDI ADCP', 'SMCA', 'SMCC', + 'NORSUB', 'NORSUB2', 'NORSUB6', 'NORSUB7', 'NORSUB7b', 'NORSUB8', //'NORSUB PRDID', + ] + expectedProtocols.forEach(protocol => expect(protocol in parserProtocols).toBeTruthy()) }) test('Add protocols error', () => { @@ -128,56 +107,51 @@ describe('Parser', () => { const parser = new Parser() parser.addProtocols({ file: NORSUB_FILE }) const storedSentences = parser.getSentences() - Object.values(storedSentences).forEach(storedSentence => { - const input = generateSentenceFromModel(storedSentence) - expect(input).toBeTypeOf('string') - const output = parser.parseData(input)[0] - const parsed = NMEAKnownSentenceSchema.safeParse(output) - if (!parsed.success) { - console.error((parsed.errors as string[])[0]) - } - expect(parsed.success).toBeTruthy() - }) + const fakeinput = storedSentences.reduce((acc, curr) => acc += createFakeSentence(curr), '') + const output = parser.parseData(fakeinput) + expect(output.length).toBe(storedSentences.length) }) test('Uncompleted frames WITHOUT memory', () => { const parser = new Parser() const storedSentences = parser.getSentences() - const input1 = generateSentenceFromModel(storedSentences['AAM']) + const input1 = createFakeSentence(storedSentences.filter(sentence => sentence.id === 'AAM')[0]) const halfInput1 = input1.slice(0, 10) const halfInput2 = input1.slice(10) - const input2 = generateSentenceFromModel(storedSentences['GGA']); - [ - halfInput1 + input2, - halfInput1 + halfInput1+ input2, - input2 + halfInput2, - input2 + halfInput2 + halfInput2, - 'asdfasfaf' + input2 + 'lakjs' - ].forEach(input => { - const output = parser.parseData(input) - if (output.length !== 1) { console.error(`Problem parsing frame -> ${input}`) } - expect(output).toHaveLength(1) - }) + const input2 = createFakeSentence(storedSentences.filter(sentence => sentence.id === 'GGA')[0]) + ;[ + halfInput1 + input2, + halfInput1 + halfInput1 + input2, + input2 + halfInput2, + input2 + halfInput2 + halfInput2, + 'asdfasfaf' + input2 + 'lakjs' + ].forEach(input => { + const output = parser.parseData(input) + if (output.length !== 1) { console.error(`Problem parsing frame -> ${input}`) } + expect(output).toHaveLength(1) + }) }) test('Uncompleted frames WITH memory', () => { const parser = new Parser() parser.memory = true const storedSentences = parser.getSentences() - const input1 = generateSentenceFromModel(storedSentences['AAM']) + const input1 = createFakeSentence(storedSentences.filter(sentence => sentence.id === 'AAM')[0]) const halfInput1 = input1.slice(0, 10) const halfInput2 = input1.slice(10) - const input2 = generateSentenceFromModel(storedSentences['GGA']); - [ - halfInput1 + input2, - halfInput1 + halfInput1+ input2, - input2 + halfInput2, - input2 + halfInput2 + halfInput2, - 'asdfasfaf' + input2 + 'lakjs' - ].forEach(input => { - const output = parser.parseData(input) - expect(output).toHaveLength(1) - }) + const input2 = createFakeSentence(storedSentences.filter(sentence => sentence.id === 'GGA')[0]) + // Not uncompleted Frames + ;[ + halfInput1 + input2, + halfInput1 + halfInput1 + input2, + input2 + halfInput2, + input2 + halfInput2 + halfInput2, + 'asdfasfaf' + input2 + 'lakjs' + ].forEach(input => { + const output = parser.parseData(input) + expect(output).toHaveLength(1) + }) + // Uncompleted frames parser.parseData(halfInput1) const mem = parser.parseData(halfInput2) expect(mem).toHaveLength(1) @@ -186,23 +160,17 @@ describe('Parser', () => { test('Unknown frames', () => { const parser = new Parser() const storedSentences = parser.getSentences() - const aam = storedSentences['AAM'] - const gga = storedSentences['GGA'] - const input1 = getFakeSentence(generateSentenceFromModel(aam), 'XXX') - const input2 = getFakeSentence(generateSentenceFromModel(gga), 'YYY'); - [input1, input2].forEach(input => { - const output = parser.parseData(input) - if (output.length !== 1) { - console.error(`Problem parsing frame -> ${input}`) - expect(output).toHaveLength(1) - } else { - const parsed = NMEAUknownSentenceSchema.safeParse(output[0]) - if (!parsed.success) { - console.error((parsed.errors as string[])[0]) + const aam = storedSentences.filter(sentence => sentence.id === 'AAM')[0] + const gga = storedSentences.filter(sentence => sentence.id === 'GGA')[0] + const input1 = createFakeSentence(aam, 'XXX') + const input2 = createFakeSentence(gga, 'YYY') + ;[input1, input2].forEach(input => { + const output = parser.parseData(input) + if (output.length !== 1) { + console.error(`Problem parsing frame -> ${input}`) } - expect(parsed.success).toBeTruthy() - } - }) + expect(output).toHaveLength(1) + }) }) test('Sentence info', () => { @@ -216,47 +184,46 @@ describe('Parser', () => { sentence = parser.getSentence('GPAAM') expect(sentence?.protocol.name).toBe('NMEA') expect(sentence?.protocol.standard).toBeTruthy() - expect(sentence?.talker?.id).toBe('GP') + expect(sentence?.talker?.value).toBe('GP') // Known special talker sentence = parser.getSentence('U8AAM') expect(sentence?.protocol.name).toBe('NMEA') expect(sentence?.protocol.standard).toBeTruthy() - expect(sentence?.talker?.id).toBe('U8') + expect(sentence?.talker?.value).toBe('U8') sentence = parser.getSentence('PdfgsdfAAM') - expect(sentence?.protocol.name).toBe('NMEA') - expect(sentence?.protocol.standard).toBeTruthy() - expect(sentence?.talker?.id).toBe('Pdfgsdf') + expect(sentence).toBeNull() + // expect(sentence?.protocol.name).toBe('unknown') + // expect(sentence?.protocol.standard).toBeTruthy() + // expect(sentence?.talker?.value).toBe('Pdfgsdf') // Unknown talker sentence = parser.getSentence('XXAAM') - expect(sentence?.protocol.name).toBe('NMEA') - expect(sentence?.protocol.standard).toBeTruthy() - expect(sentence?.talker?.id).toBe('XX') - expect(sentence?.talker?.description).toBe('unknown') + expect(sentence).toBeNull() + // expect(sentence?.protocol.name).toBe('NMEA') + // expect(sentence?.protocol.standard).toBeTruthy() + // expect(sentence?.talker?.value).toBe('XX') + // expect(sentence?.talker?.description).toBe('unknown') }) test('Generate fake sentences without talkers', () => { const parser = new Parser() - const sentences = parser.getSentences() - for (const key in sentences) { - const id = sentences[key].sentence - const fakeSentence = parser.getFakeSentenceByID(id) + parser.getSentences().forEach(sentence => { + const fakeSentence = parser.getFakeSentenceByID(sentence.id) expect(fakeSentence).not.toBeNull() expect(NMEALikeSchema.is(fakeSentence)).toBeTruthy() if (fakeSentence !== null) { const parsed = parser.parseData(fakeSentence) expect(parsed).toHaveLength(1) - expect(parsed[0].sentence).toBe(id) + expect(parsed[0].id).toBe(sentence.id) } - } + }) }) test('Generate fake sentences with talkers', () => { const parser = new Parser() - const sentences = parser.getSentences() - for (const key in sentences) { + parser.getSentences().forEach(sentence => { const talker = 'GP' - const sentence = sentences[key].sentence - const id = talker + sentence + const sentenceID = sentence.id + const id = talker + sentenceID const fakeSentence = parser.getFakeSentenceByID(id) expect(fakeSentence).not.toBeNull() expect(NMEALikeSchema.is(fakeSentence)).toBeTruthy() @@ -265,10 +232,10 @@ describe('Parser', () => { expect(starts).toBeTruthy() const parsed = parser.parseData(fakeSentence) expect(parsed).toHaveLength(1) - expect(parsed[0].sentence).toBe(sentence) - expect(parsed[0].talker?.id).toBe(talker) + expect(parsed[0].id).toBe(sentenceID) + expect(parsed[0].talker?.value).toBe(talker) } - } + }) }) test('Generate fake sentences unknown', () => { diff --git a/packages/nmea-parser/tests/norsub.ts b/packages/nmea-parser/tests/norsub.ts index 6cb4a00..4382244 100644 --- a/packages/nmea-parser/tests/norsub.ts +++ b/packages/nmea-parser/tests/norsub.ts @@ -1,865 +1,867 @@ -export const PROTOCOLS = { - "protocols": [ +import type { ProtocolsFileContent } from '../src' + +export const PROTOCOLS: ProtocolsFileContent = { + protocols: [ { - "protocol": "GYROCOMPAS1", - "standard": false, - "sentences": [ + protocol: 'GYROCOMPAS1', + standard: false, + sentences: [ { - "sentence": "HEHDT", - "fields": [ + id: 'HEHDT', + payload: [ { - "name": "heading", - "type": "float", - "units": "deg" + name: 'heading', + type: 'float32', + units: 'deg' }, { - "name": "symbol", - "type": "string" + name: 'symbol', + type: 'string' } ] }, { - "sentence": "PHTRO", - "fields": [ + id: 'PHTRO', + payload: [ { - "name": "pitch", - "type": "float", - "units": "deg" + name: 'pitch', + type: 'float32', + units: 'deg' }, { - "name": "pitch_direction", - "type": "string", - "note": "M bow up, P bow down" + name: 'pitch_direction', + type: 'string', + description: 'M bow up, P bow down' }, { - "name": "roll", - "type": "float", - "units": "deg" + name: 'roll', + type: 'float32', + units: 'deg' }, { - "name": "roll_direction", - "type": "string", - "note": "M bow up, P bow down" + name: 'roll_direction', + type: 'string', + description: 'M bow up, P bow down' } ] }, { - "sentence": "PHINF", - "fields": [ + id: 'PHINF', + payload: [ { - "name": "status", - "type": "string" + name: 'status', + type: 'string' } ] } ] }, { - "protocol": "NORSUB", - "standard": false, - "sentences": [ + protocol: 'NORSUB', + standard: false, + sentences: [ { - "sentence": "PNORSUB", - "fields": [ + id: 'PNORSUB', + payload: [ { - "name": "time", - "type": "uint32", - "units": "ms" + name: 'time', + type: 'uint32', + units: 'ms' }, { - "name": "delay", - "type": "uint32", - "units": "ms" + name: 'delay', + type: 'uint32', + units: 'ms' }, { - "name": "roll", - "type": "double", - "units": "deg" + name: 'roll', + type: 'float64', + units: 'deg' }, { - "name": "pitch", - "type": "double", - "units": "deg" + name: 'pitch', + type: 'float64', + units: 'deg' }, { - "name": "heading", - "type": "double", - "units": "deg", - "note": "0 - 360" + name: 'heading', + type: 'float64', + units: 'deg', + description: '0 - 360' }, { - "name": "heave", - "type": "double", - "units": "m", - "note": "z-down" + name: 'heave', + type: 'float64', + units: 'm', + description: 'z-down' }, { - "name": "status", - "type": "uint32", - "note": "0 - Error\n\n1 - No Error" + name: 'status', + type: 'uint32', + description: '0 - Error\n\n1 - No Error' } ] } ] }, { - "protocol": "NORSUB2", - "standard": false, - "sentences": [ + protocol: 'NORSUB2', + standard: false, + sentences: [ { - "sentence": "PNORSUB2", - "fields": [ + id: 'PNORSUB2', + payload: [ { - "name": "time", - "type": "uint32", - "units": "ms" + name: 'time', + type: 'uint32', + units: 'ms' }, { - "name": "delay", - "type": "uint32", - "units": "ms" + name: 'delay', + type: 'uint32', + units: 'ms' }, { - "name": "roll", - "type": "double", - "units": "deg" + name: 'roll', + type: 'float64', + units: 'deg' }, { - "name": "pitch", - "type": "double", - "units": "deg" + name: 'pitch', + type: 'float64', + units: 'deg' }, { - "name": "heading", - "type": "double", - "units": "deg", - "note": "0 - 360" + name: 'heading', + type: 'float64', + units: 'deg', + description: '0 - 360' }, { - "name": "heave", - "type": "double", - "units": "m", - "note": "z-down" + name: 'heave', + type: 'float64', + units: 'm', + description: 'z-down' }, { - "name": "heave_velocity", - "type": "double", - "units": "m/s", - "note": "z-down" + name: 'heave_velocity', + type: 'float64', + units: 'm/s', + description: 'z-down' }, { - "name": "status", - "type": "uint32", - "note": "0 - Error\n\n1 - No Error" + name: 'status', + type: 'uint32', + description: '0 - Error\n\n1 - No Error' } ] } ] }, { - "protocol": "NORSUB6", - "standard": false, - "sentences": [ + protocol: 'NORSUB6', + standard: false, + sentences: [ { - "sentence": "PNORSUB6", - "fields": [ + id: 'PNORSUB6', + payload: [ { - "name": "time", - "type": "uint32", - "units": "ms" + name: 'time', + type: 'uint32', + units: 'ms' }, { - "name": "delay", - "type": "uint32", - "units": "ms" + name: 'delay', + type: 'uint32', + units: 'ms' }, { - "name": "roll", - "type": "double", - "units": "deg" + name: 'roll', + type: 'float64', + units: 'deg' }, { - "name": "pitch", - "type": "double", - "units": "deg" + name: 'pitch', + type: 'float64', + units: 'deg' }, { - "name": "heading", - "type": "double", - "units": "deg", - "note": "0 - 360" + name: 'heading', + type: 'float64', + units: 'deg', + description: '0 - 360' }, { - "name": "surge", - "type": "double", - "units": "m" + name: 'surge', + type: 'float64', + units: 'm' }, { - "name": "sway", - "type": "double", - "units": "m" + name: 'sway', + type: 'float64', + units: 'm' }, { - "name": "heave", - "type": "double", - "units": "m", - "note": "z-down" + name: 'heave', + type: 'float64', + units: 'm', + description: 'z-down' }, { - "name": "roll_rate", - "type": "double", - "units": "deg/s" + name: 'roll_rate', + type: 'float64', + units: 'deg/s' }, { - "name": "pitch_rate", - "type": "double", - "units": "deg/s" + name: 'pitch_rate', + type: 'float64', + units: 'deg/s' }, { - "name": "yaw_rate", - "type": "double", - "units": "deg/s" + name: 'yaw_rate', + type: 'float64', + units: 'deg/s' }, { - "name": "surge_velocity", - "type": "double", - "units": "m/s" + name: 'surge_velocity', + type: 'float64', + units: 'm/s' }, { - "name": "sway_velocity", - "type": "double", - "units": "m/s" + name: 'sway_velocity', + type: 'float64', + units: 'm/s' }, { - "name": "heave_velocity", - "type": "double", - "units": "m/s", - "note": "z-down" + name: 'heave_velocity', + type: 'float64', + units: 'm/s', + description: 'z-down' }, { - "name": "acceleration_x", - "type": "double", - "units": "m/s2" + name: 'acceleration_x', + type: 'float64', + units: 'm/s2' }, { - "name": "acceleration_y", - "type": "double", - "units": "m/s2" + name: 'acceleration_y', + type: 'float64', + units: 'm/s2' }, { - "name": "acceleration_z", - "type": "double", - "units": "m/s2" + name: 'acceleration_z', + type: 'float64', + units: 'm/s2' }, { - "name": "status", - "type": "uint32", - "note": "0 - Error\n\n1 - No Error" + name: 'status', + type: 'uint32', + description: '0 - Error\n\n1 - No Error' } ] } ] }, { - "protocol": "NORSUB7", - "standard": false, - "sentences": [ + protocol: 'NORSUB7', + standard: false, + sentences: [ { - "sentence": "PNORSUB7", - "fields": [ + id: 'PNORSUB7', + payload: [ { - "name": "time", - "type": "uint32", - "units": "ms" + name: 'time', + type: 'uint32', + units: 'ms' }, { - "name": "delay", - "type": "uint32", - "units": "ms" + name: 'delay', + type: 'uint32', + units: 'ms' }, { - "name": "roll", - "type": "double", - "units": "deg" + name: 'roll', + type: 'float64', + units: 'deg' }, { - "name": "pitch", - "type": "double", - "units": "deg" + name: 'pitch', + type: 'float64', + units: 'deg' }, { - "name": "heading", - "type": "double", - "units": "deg", - "note": "0 - 360" + name: 'heading', + type: 'float64', + units: 'deg', + description: '0 - 360' }, { - "name": "surge", - "type": "double", - "units": "m" + name: 'surge', + type: 'float64', + units: 'm' }, { - "name": "sway", - "type": "double", - "units": "m" + name: 'sway', + type: 'float64', + units: 'm' }, { - "name": "heave", - "type": "double", - "units": "m", - "note": "z-down" + name: 'heave', + type: 'float64', + units: 'm', + description: 'z-down' }, { - "name": "roll_rate", - "type": "double", - "units": "deg/s" + name: 'roll_rate', + type: 'float64', + units: 'deg/s' }, { - "name": "pitch_rate", - "type": "double", - "units": "deg/s" + name: 'pitch_rate', + type: 'float64', + units: 'deg/s' }, { - "name": "yaw_rate", - "type": "double", - "units": "deg/s" + name: 'yaw_rate', + type: 'float64', + units: 'deg/s' }, { - "name": "surge_velocity", - "type": "double", - "units": "m/s" + name: 'surge_velocity', + type: 'float64', + units: 'm/s' }, { - "name": "sway_velocity", - "type": "double", - "units": "m/s" + name: 'sway_velocity', + type: 'float64', + units: 'm/s' }, { - "name": "heave_velocity", - "type": "double", - "units": "m/s", - "note": "z-down" + name: 'heave_velocity', + type: 'float64', + units: 'm/s', + description: 'z-down' }, { - "name": "acceleration_x", - "type": "double", - "units": "m/s2" + name: 'acceleration_x', + type: 'float64', + units: 'm/s2' }, { - "name": "acceleration_y", - "type": "double", - "units": "m/s2" + name: 'acceleration_y', + type: 'float64', + units: 'm/s2' }, { - "name": "acceleration_z", - "type": "double", - "units": "m/s2" + name: 'acceleration_z', + type: 'float64', + units: 'm/s2' }, { - "name": "period_x", - "type": "double", - "units": "s" + name: 'period_x', + type: 'float64', + units: 's' }, { - "name": "period_y", - "type": "double", - "units": "s" + name: 'period_y', + type: 'float64', + units: 's' }, { - "name": "period_z", - "type": "double", - "units": "s" + name: 'period_z', + type: 'float64', + units: 's' }, { - "name": "amplitude_x", - "type": "double", - "units": "m" + name: 'amplitude_x', + type: 'float64', + units: 'm' }, { - "name": "amplitude_y", - "type": "double", - "units": "m" + name: 'amplitude_y', + type: 'float64', + units: 'm' }, { - "name": "amplitude_z", - "type": "double", - "units": "m" + name: 'amplitude_z', + type: 'float64', + units: 'm' }, { - "name": "status", - "type": "uint32" + name: 'status', + type: 'uint32' } ] } ] }, { - "protocol": "NORSUB7b", - "standard": false, - "sentences": [ + protocol: 'NORSUB7b', + standard: false, + sentences: [ { - "sentence": "PNORSUB7b", - "fields": [ + id: 'PNORSUB7b', + payload: [ { - "name": "time", - "type": "uint32", - "units": "ms" + name: 'time', + type: 'uint32', + units: 'ms' }, { - "name": "delay", - "type": "uint32", - "units": "ms" + name: 'delay', + type: 'uint32', + units: 'ms' }, { - "name": "roll", - "type": "double", - "units": "deg" + name: 'roll', + type: 'float64', + units: 'deg' }, { - "name": "pitch", - "type": "double", - "units": "deg" + name: 'pitch', + type: 'float64', + units: 'deg' }, { - "name": "heading", - "type": "double", - "units": "deg", - "note": "0 - 360" + name: 'heading', + type: 'float64', + units: 'deg', + description: '0 - 360' }, { - "name": "surge", - "type": "double", - "units": "m" + name: 'surge', + type: 'float64', + units: 'm' }, { - "name": "sway", - "type": "double", - "units": "m" + name: 'sway', + type: 'float64', + units: 'm' }, { - "name": "heave", - "type": "double", - "units": "m", - "note": "z-down" + name: 'heave', + type: 'float64', + units: 'm', + description: 'z-down' }, { - "name": "roll_rate", - "type": "double", - "units": "deg/s" + name: 'roll_rate', + type: 'float64', + units: 'deg/s' }, { - "name": "pitch_rate", - "type": "double", - "units": "deg/s" + name: 'pitch_rate', + type: 'float64', + units: 'deg/s' }, { - "name": "yaw_rate", - "type": "double", - "units": "deg/s" + name: 'yaw_rate', + type: 'float64', + units: 'deg/s' }, { - "name": "surge_velocity", - "type": "double", - "units": "m/s" + name: 'surge_velocity', + type: 'float64', + units: 'm/s' }, { - "name": "sway_velocity", - "type": "double", - "units": "m/s" + name: 'sway_velocity', + type: 'float64', + units: 'm/s' }, { - "name": "heave_velocity", - "type": "double", - "units": "m/s", - "note": "z-down" + name: 'heave_velocity', + type: 'float64', + units: 'm/s', + description: 'z-down' }, { - "name": "acceleration_x", - "type": "double", - "units": "m/s2" + name: 'acceleration_x', + type: 'float64', + units: 'm/s2' }, { - "name": "acceleration_y", - "type": "double", - "units": "m/s2" + name: 'acceleration_y', + type: 'float64', + units: 'm/s2' }, { - "name": "acceleration_z", - "type": "double", - "units": "m/s2" + name: 'acceleration_z', + type: 'float64', + units: 'm/s2' }, { - "name": "period_x", - "type": "double", - "units": "s" + name: 'period_x', + type: 'float64', + units: 's' }, { - "name": "period_y", - "type": "double", - "units": "s" + name: 'period_y', + type: 'float64', + units: 's' }, { - "name": "period_z", - "type": "double", - "units": "s" + name: 'period_z', + type: 'float64', + units: 's' }, { - "name": "amplitude_x", - "type": "double", - "units": "m" + name: 'amplitude_x', + type: 'float64', + units: 'm' }, { - "name": "amplitude_y", - "type": "double", - "units": "m" + name: 'amplitude_y', + type: 'float64', + units: 'm' }, { - "name": "amplitude_z", - "type": "double", - "units": "m" + name: 'amplitude_z', + type: 'float64', + units: 'm' }, { - "name": "status_a", - "type": "uint16" + name: 'status_a', + type: 'uint16' }, { - "name": "status_b", - "type": "uint16" + name: 'status_b', + type: 'uint16' } ] } ] }, { - "protocol": "NORSUB8", - "standard": false, - "sentences": [ + protocol: 'NORSUB8', + standard: false, + sentences: [ { - "sentence": "PNORSUB8", - "description": "The whole regular attitude information from the MRU", - "fields": [ + id: 'PNORSUB8', + description: 'The whole regular attitude information from the MRU', + payload: [ { - "name": "time", - "type": "uint32", - "units": "us" + name: 'time', + type: 'uint32', + units: 'us' }, { - "name": "delay", - "type": "uint32", - "units": "us" + name: 'delay', + type: 'uint32', + units: 'us' }, { - "name": "roll", - "type": "double", - "units": "deg" + name: 'roll', + type: 'float64', + units: 'deg' }, { - "name": "pitch", - "type": "double", - "units": "deg" + name: 'pitch', + type: 'float64', + units: 'deg' }, { - "name": "heading", - "type": "double", - "units": "deg", - "note": "From 0 to 360" + name: 'heading', + type: 'float64', + units: 'deg', + description: 'From 0 to 360' }, { - "name": "surge", - "type": "double", - "units": "m" + name: 'surge', + type: 'float64', + units: 'm' }, { - "name": "sway", - "type": "double", - "units": "m" + name: 'sway', + type: 'float64', + units: 'm' }, { - "name": "heave", - "type": "double", - "units": "m", - "note": "z-down" + name: 'heave', + type: 'float64', + units: 'm', + description: 'z-down' }, { - "name": "roll_rate", - "type": "double", - "units": "deg/s" + name: 'roll_rate', + type: 'float64', + units: 'deg/s' }, { - "name": "pitch_rate", - "type": "double", - "units": "deg/s" + name: 'pitch_rate', + type: 'float64', + units: 'deg/s' }, { - "name": "yaw_rate", - "type": "double", - "units": "deg/s" + name: 'yaw_rate', + type: 'float64', + units: 'deg/s' }, { - "name": "surge_velocity", - "type": "double", - "units": "m/s" + name: 'surge_velocity', + type: 'float64', + units: 'm/s' }, { - "name": "sway_velocity", - "type": "double", - "units": "m/s" + name: 'sway_velocity', + type: 'float64', + units: 'm/s' }, { - "name": "heave_velocity", - "type": "double", - "units": "m/s", - "note": "z-down" + name: 'heave_velocity', + type: 'float64', + units: 'm/s', + description: 'z-down' }, { - "name": "acceleration_x", - "type": "double", - "units": "m/s2" + name: 'acceleration_x', + type: 'float64', + units: 'm/s2' }, { - "name": "acceleration_y", - "type": "double", - "units": "m/s2" + name: 'acceleration_y', + type: 'float64', + units: 'm/s2' }, { - "name": "acceleration_z", - "type": "double", - "units": "m/s2" + name: 'acceleration_z', + type: 'float64', + units: 'm/s2' }, { - "name": "period_x", - "type": "double", - "units": "s" + name: 'period_x', + type: 'float64', + units: 's' }, { - "name": "period_y", - "type": "double", - "units": "s" + name: 'period_y', + type: 'float64', + units: 's' }, { - "name": "period_z", - "type": "double", - "units": "s" + name: 'period_z', + type: 'float64', + units: 's' }, { - "name": "amplitude_x", - "type": "double", - "units": "m" + name: 'amplitude_x', + type: 'float64', + units: 'm' }, { - "name": "amplitude_y", - "type": "double", - "units": "m" + name: 'amplitude_y', + type: 'float64', + units: 'm' }, { - "name": "amplitude_z", - "type": "double", - "units": "m" + name: 'amplitude_z', + type: 'float64', + units: 'm' }, { - "name": "status", - "type": "uint32" + name: 'status', + type: 'uint32' } ] } ] }, { - "protocol": "NORSUB PRDID", - "standard": false, - "sentences": [ + protocol: 'NORSUB PRDID', + standard: false, + sentences: [ { - "sentence": "PRDID", - "fields": [ + id: 'PRDID', + payload: [ { - "name": "pitch", - "type": "double", - "units": "deg" + name: 'pitch', + type: 'float64', + units: 'deg' }, { - "name": "roll", - "type": "double", - "units": "deg" + name: 'roll', + type: 'float64', + units: 'deg' } ] } ] }, { - "protocol": "Tokimek PTVG", - "standard": false, - "sentences": [ + protocol: 'Tokimek PTVG', + standard: false, + sentences: [ { - "sentence": "PTVG", - "fields": [ + id: 'PTVG', + payload: [ { - "name": "pitch", - "type": "number", - "units": "deg", - "note": "Multiplied by 100, a [-] bow up / a [space] bow down" + name: 'pitch', + type: 'float64', + units: 'deg', + description: 'Multiplied by 100, a [-] bow up / a [space] bow down' }, { - "name": "roll", - "type": "number", - "units": "deg", - "note": "Multiplied by 100, a [-] bow up / a [space] bow down" + name: 'roll', + type: 'float64', + units: 'deg', + description: 'Multiplied by 100, a [-] bow up / a [space] bow down' }, { - "name": "heading", - "type": "number", - "units": "deg" + name: 'heading', + type: 'float64', + units: 'deg' } ] } ] }, { - "protocol": "RDI ADCP", - "standard": false, - "sentences": [ + protocol: 'RDI ADCP', + standard: false, + sentences: [ { - "sentence": "PRDID", - "fields": [ + id: 'PRDID', + payload: [ { - "name": "pitch", - "type": "double", - "units": "deg", - "note": "s if [+] is bow up / s is [-] if bow down, leading zeros" + name: 'pitch', + type: 'float64', + units: 'deg', + description: 's if [+] is bow up / s is [-] if bow down, leading zeros' }, { - "name": "roll", - "type": "double", - "units": "deg", - "note": "s if [+] is bow up / s is [-] if bow down, leading zeros" + name: 'roll', + type: 'float64', + units: 'deg', + description: 's if [+] is bow up / s is [-] if bow down, leading zeros' }, { - "name": "heading", - "type": "double", - "units": "deg" + name: 'heading', + type: 'float64', + units: 'deg' } ] } ] }, { - "protocol": "SMCA", - "standard": false, - "sentences": [ + protocol: 'SMCA', + standard: false, + sentences: [ { - "sentence": "PSMCA", - "fields": [ + id: 'PSMCA', + payload: [ { - "name": "pitch", - "type": "double", - "units": "deg", - "note": "±100 degs, resolution 0.001 degs" + name: 'pitch', + type: 'float64', + units: 'deg', + description: '±100 degs, resolution 0.001 degs' }, { - "name": "roll", - "type": "double", - "units": "deg", - "note": "±100 degs, resolution 0.001 degs" + name: 'roll', + type: 'float64', + units: 'deg', + description: '±100 degs, resolution 0.001 degs' }, { - "name": "heading", - "type": "double", - "units": "m", - "note": "±10 m, resolution 0.01 m" + name: 'heading', + type: 'float64', + units: 'm', + description: '±10 m, resolution 0.01 m' }, { - "name": "surge", - "type": "double", - "units": "m", - "note": "±10 m, resolution 0.01 m" + name: 'surge', + type: 'float64', + units: 'm', + description: '±10 m, resolution 0.01 m' }, { - "name": "sway", - "type": "double", - "units": "m", - "note": "±10 m, resolution 0.01 m" + name: 'sway', + type: 'float64', + units: 'm', + description: '±10 m, resolution 0.01 m' } ] } ] }, { - "protocol": "SMCC", - "standard": false, - "sentences": [ + protocol: 'SMCC', + standard: false, + sentences: [ { - "sentence": "PSMCC", - "fields": [ + id: 'PSMCC', + payload: [ { - "name": "pitch", - "type": "double", - "units": "deg", - "note": "±100 degs, resolution 0.001 degs" + name: 'pitch', + type: 'float64', + units: 'deg', + description: '±100 degs, resolution 0.001 degs' }, { - "name": "roll", - "type": "double", - "units": "deg", - "note": "±100 degs, resolution 0.001 degs" + name: 'roll', + type: 'float64', + units: 'deg', + description: '±100 degs, resolution 0.001 degs' }, { - "name": "yaw", - "type": "double", - "units": "deg", - "note": "0-359.9 degs, resolution 0.1 degs" + name: 'yaw', + type: 'float64', + units: 'deg', + description: '0-359.9 degs, resolution 0.1 degs' }, { - "name": "surge", - "type": "double", - "units": "m", - "note": "±10 m, resolution 0.01 m" + name: 'surge', + type: 'float64', + units: 'm', + description: '±10 m, resolution 0.01 m' }, { - "name": "sway", - "type": "double", - "units": "m", - "note": "±10 m, resolution 0.01 m" + name: 'sway', + type: 'float64', + units: 'm', + description: '±10 m, resolution 0.01 m' }, { - "name": "heave", - "type": "double", - "units": "m", - "note": "±10 m, resolution 0.01 m" + name: 'heave', + type: 'float64', + units: 'm', + description: '±10 m, resolution 0.01 m' }, { - "name": "surge_velocity", - "type": "double", - "units": "m/s", - "note": "±100 m/s, resolution 0.01 m/s" + name: 'surge_velocity', + type: 'float64', + units: 'm/s', + description: '±100 m/s, resolution 0.01 m/s' }, { - "name": "sway_velocity", - "type": "double", - "units": "m/s", - "note": "±100 m/s, resolution 0.01 m/s" + name: 'sway_velocity', + type: 'float64', + units: 'm/s', + description: '±100 m/s, resolution 0.01 m/s' }, { - "name": "heave_velocity", - "type": "double", - "units": "m/s", - "note": "±100 m/s, resolution 0.01 m/s" + name: 'heave_velocity', + type: 'float64', + units: 'm/s', + description: '±100 m/s, resolution 0.01 m/s' }, { - "name": "acceleration_x", - "type": "double", - "units": "m/s2", - "note": "±100 m/s2, resolution 0.01 m/s2" + name: 'acceleration_x', + type: 'float64', + units: 'm/s2', + description: '±100 m/s2, resolution 0.01 m/s2' }, { - "name": "acceleration_y", - "type": "double", - "units": "m/s2", - "note": "±100 m/s2, resolution 0.01 m/s2" + name: 'acceleration_y', + type: 'float64', + units: 'm/s2', + description: '±100 m/s2, resolution 0.01 m/s2' }, { - "name": "acceleration_z", - "type": "double", - "units": "m/s2", - "note": "±100 m/s2, resolution 0.01 m/s2" + name: 'acceleration_z', + type: 'float64', + units: 'm/s2', + description: '±100 m/s2, resolution 0.01 m/s2' } ] } diff --git a/packages/nmea-parser/tests/parser.test.ts b/packages/nmea-parser/tests/parser.test.ts deleted file mode 100644 index 88bc4f9..0000000 --- a/packages/nmea-parser/tests/parser.test.ts +++ /dev/null @@ -1,286 +0,0 @@ -import fs from 'node:fs' -import path from 'node:path' -import { describe, test, expect } from 'vitest' -import { Parser } from '../src/parser' -import { generateSentenceFromModel, getFakeSentence } from '../src/sentences' -import { NMEAKnownSentenceSchema, NMEASentenceSchema, NMEAUknownSentenceSchema } from '../src/schemas' -import { TALKERS, TALKERS_SPECIAL } from '../src/constants' -import { readProtocolsFile } from '../src/protocols' -import { NMEASentence, Protocol } from '../src/types' - -const NORSUB_FILE = path.join(__dirname, '..', 'protocols', 'norsub.yaml') - -describe('Parser', () => { - test('Default constructor', () => { - const parser = new Parser() - const parserProtocols = parser.getProtocols() - expect(parserProtocols[0].protocol.includes('NMEA')).toBeTruthy() - - const parserSentences = parser.getSentences() - const expectedSentences = ['AAM', 'GGA'] - expectedSentences.forEach(sentence => expect(Object.keys(parserSentences).includes(sentence)).toBeTruthy()) - }) - - test('Add protocols with file', () => { - const file = NORSUB_FILE - const parser = new Parser() - parser.addProtocols({ file }) - - const parserProtocols = parser.getProtocols() - - const expectedProtocols = [ - 'NMEA', - 'GYROCOMPAS1', 'Tokimek PTVG', 'RDI ADCP', 'SMCA', 'SMCC', - 'NORSUB', 'NORSUB2', 'NORSUB6', 'NORSUB7', 'NORSUB7b', 'NORSUB8', //'NORSUB PRDID', - ] - expectedProtocols.forEach(protocol => { - const result = parserProtocols.filter(p => p.protocol === protocol) - if (result.length === 0) { console.log(`Protocol ${protocol} is not included`) } - expect(result.length).toBeGreaterThan(0) - }) - - const parserSentences = parser.getSentences() - const expectedSentences = [ - 'AAM', 'GGA', - 'HEHDT', 'PHTRO', 'PHINF', - 'PNORSUB', 'PNORSUB2', 'PNORSUB6', 'PNORSUB7', 'PNORSUB7b', 'PNORSUB8', 'PRDID', - 'PTVG', 'PRDID', 'PSMCA', 'PSMCC', - ] - expectedSentences.forEach(sentence => { - const result = Object.keys(parserSentences).includes(sentence) - if (!result) { console.log(`Sentence ${sentence} is not included`) } - expect(result).toBeTruthy() - }) - }) - - test('Add protocols with content', () => { - const content = fs.readFileSync(NORSUB_FILE, 'utf-8') - const parser = new Parser() - parser.addProtocols({ content }) - - const parserProtocols = parser.getProtocols() - - const expectedProtocols = [ - 'NMEA', - 'GYROCOMPAS1', 'Tokimek PTVG', 'RDI ADCP', 'SMCA', 'SMCC', - 'NORSUB', 'NORSUB2', 'NORSUB6', 'NORSUB7', 'NORSUB7b', 'NORSUB8', //'NORSUB PRDID', - ] - expectedProtocols.forEach(protocol => { - const result = parserProtocols.filter(p => p.protocol === protocol) - if (result.length === 0) { console.log(`Protocol ${protocol} is not included`) } - expect(result.length).toBeGreaterThan(0) - }) - - const parserSentences = parser.getSentences() - const expectedSentences = [ - 'AAM', 'GGA', - 'HEHDT', 'PHTRO', 'PHINF', - 'PNORSUB', 'PNORSUB2', 'PNORSUB6', 'PNORSUB7', 'PNORSUB7b', 'PNORSUB8', 'PRDID', - 'PTVG', 'PRDID', 'PSMCA', 'PSMCC', - ] - expectedSentences.forEach(sentence => { - const result = Object.keys(parserSentences).includes(sentence) - if (!result) { console.log(`Sentence ${sentence} is not included`) } - expect(result).toBeTruthy() - }) - }) - - test('Add protocols with protocols', () => { - const { protocols } = readProtocolsFile(NORSUB_FILE) - const parser = new Parser() - parser.addProtocols({ protocols }) - - const parserProtocols = parser.getProtocols() - - const expectedProtocols = [ - 'NMEA', - 'GYROCOMPAS1', 'Tokimek PTVG', 'RDI ADCP', 'SMCA', 'SMCC', - 'NORSUB', 'NORSUB2', 'NORSUB6', 'NORSUB7', 'NORSUB7b', 'NORSUB8', //'NORSUB PRDID', - ] - expectedProtocols.forEach(protocol => { - const result = parserProtocols.filter(p => p.protocol === protocol) - if (result.length === 0) { console.log(`Protocol ${protocol} is not included`) } - expect(result.length).toBeGreaterThan(0) - }) - - const parserSentences = parser.getSentences() - const expectedSentences = [ - 'AAM', 'GGA', - 'HEHDT', 'PHTRO', 'PHINF', - 'PNORSUB', 'PNORSUB2', 'PNORSUB6', 'PNORSUB7', 'PNORSUB7b', 'PNORSUB8', 'PRDID', - 'PTVG', 'PRDID', 'PSMCA', 'PSMCC', - ] - expectedSentences.forEach(sentence => { - const result = Object.keys(parserSentences).includes(sentence) - if (!result) { console.log(`Sentence ${sentence} is not included`) } - expect(result).toBeTruthy() - }) - }) - - test('Add protocols error', () => { - const parser = new Parser() - expect(() => parser.addProtocols({})).toThrow() - expect(() => parser.addProtocols({ file: '' })).toThrow() - expect(() => parser.addProtocols({ content: '' })).toThrow() - expect(() => parser.addProtocols({ protocols: {} as Protocol[] })).toThrow() - }) - - test('Parsing NMEA + NorSub sentences', () => { - const parser = new Parser() - parser.addProtocols({ file: NORSUB_FILE }) - const storedSentences = parser.getSentences() - Object.values(storedSentences).forEach(storedSentence => { - const input = generateSentenceFromModel(storedSentence) - expect(input).toBeTypeOf('string') - const output = parser.parseData(input)[0] - const parsed = NMEAKnownSentenceSchema.safeParse(output) - if (!parsed.success) { - console.error((parsed.errors as string[])[0]) - } - expect(parsed.success).toBeTruthy() - }) - }) - - test('Parsing sentences with known regular talkers', () => { - const parser = new Parser() - const storedSentences = parser.getSentences() - const aam = storedSentences['AAM'] - const gga = storedSentences['GGA'] - const inputAAM = getFakeSentence(generateSentenceFromModel(aam), 'GPAAM') - const inputGGA = getFakeSentence(generateSentenceFromModel(gga), 'GAGGA'); - const input = inputAAM + inputGGA - const output = parser.parseData(input); - expect(output).toHaveLength(2) - if (output.length === 2) { - // Known talker - ['GP', 'GA'].forEach((id, index) => { - const parsed = NMEASentenceSchema.safeParse(output[index]) - if (!parsed.success) { console.error((parsed.errors as string[])[0] ) } - expect(parsed.success).toBeTruthy() - // @ts-ignore - const data = parsed.data as NMEASentence - expect(data.talker).toEqual({ id, description: TALKERS.get(id)}) - }) - } - }) - - test('Parsing sentences with known special talkers', () => { - const parser = new Parser() - const storedSentences = parser.getSentences() - const talkerU = 'U8' - const talkerP = 'PASDF' - const aam = storedSentences['AAM'] - const gga = storedSentences['GGA'] - const inputAAM = getFakeSentence(generateSentenceFromModel(aam), talkerU + 'AAM') - const inputGGA = getFakeSentence(generateSentenceFromModel(gga), talkerP + 'GGA'); - const input = inputAAM + inputGGA - const output = parser.parseData(input); - if (output.length !== 2) { - console.error(`Problem parsing input\n${input}}`) - console.error(`Output should have length 2 instead of ${output.length}`) - console.error(output) - expect(output).toHaveLength(2) - } else { - output.forEach(out => { - const parse = NMEASentenceSchema.safeParse(out) - if (!parse.success) { console.error((parse.errors as string[])[0] ) } - expect(parse.success).toBeTruthy() - }) - // Known special talker - const [outputU, outputP] = [output[0].talker, output[1].talker] - // @ts-ignore - expect(outputU).toEqual({ id: talkerU, description: TALKERS_SPECIAL['U']}) - expect(outputP).toEqual({ id: talkerP, description: TALKERS_SPECIAL['P']}) - } - }) - - test('Parsing sentences with unknown talkers', () => { - const parser = new Parser() - const storedSentences = parser.getSentences() - const talkerXX = 'XX' - const talkerUU = 'UU' - const aam = storedSentences['AAM'] - const gga = storedSentences['GGA'] - const inputAAM = getFakeSentence(generateSentenceFromModel(aam), talkerXX + 'AAM') - const inputGGA = getFakeSentence(generateSentenceFromModel(gga), talkerUU + 'GGA'); - const input = inputAAM + inputGGA - const output = parser.parseData(input); - expect(output).toHaveLength(2) - output.forEach(out => { - const parse = NMEASentenceSchema.safeParse(out) - if (!parse.success) { console.error((parse.errors as string[])[0] ) } - expect(parse.success).toBeTruthy() - }) - // Known special talker - const [outputU, outputP] = [output[0].talker, output[1].talker] - // @ts-ignore - expect(outputU).toEqual({ id: talkerXX, description: 'unknown'}) - expect(outputP).toEqual({ id: talkerUU, description: 'unknown'}) - }) - - test('Uncompleted frames WITHOUT memory', () => { - const parser = new Parser() - const storedSentences = parser.getSentences() - const input1 = generateSentenceFromModel(storedSentences['AAM']) - const halfInput1 = input1.slice(0, 10) - const halfInput2 = input1.slice(10) - const input2 = generateSentenceFromModel(storedSentences['GGA']); - [ - halfInput1 + input2, - halfInput1 + halfInput1+ input2, - input2 + halfInput2, - input2 + halfInput2 + halfInput2, - 'asdfasfaf' + input2 + 'lakjs' - ].forEach(input => { - const output = parser.parseData(input) - if (output.length !== 1) { console.error(`Problem parsing frame -> ${input}`) } - expect(output).toHaveLength(1) - }) - }) - - test('Uncompleted frames WITH memory', () => { - const parser = new Parser() - parser.memory = true - const storedSentences = parser.getSentences() - const input1 = generateSentenceFromModel(storedSentences['AAM']) - const halfInput1 = input1.slice(0, 10) - const halfInput2 = input1.slice(10) - const input2 = generateSentenceFromModel(storedSentences['GGA']); - [ - halfInput1 + input2, - halfInput1 + halfInput1+ input2, - input2 + halfInput2, - input2 + halfInput2 + halfInput2, - 'asdfasfaf' + input2 + 'lakjs' - ].forEach(input => { - const output = parser.parseData(input) - if (output.length !== 1) { console.error(`Problem parsing frame -> ${input}`) } - expect(output).toHaveLength(1) - }) - parser.parseData(halfInput1) - const mem = parser.parseData(halfInput2) - expect(mem).toHaveLength(1) - }) - - test('Unknown frames', () => { - const parser = new Parser() - const storedSentences = parser.getSentences() - const aam = storedSentences['AAM'] - const gga = storedSentences['GGA'] - const input1 = getFakeSentence(generateSentenceFromModel(aam), 'XXX') - const input2 = getFakeSentence(generateSentenceFromModel(gga), 'YYY'); - [input1, input2].forEach(input => { - const output = parser.parseData(input) - if (output.length !== 1) { - console.error(`Problem parsing frame -> ${input}`) - expect(output).toHaveLength(1) - } else { - const parsed = NMEAUknownSentenceSchema.safeParse(output[0]) - if (!parsed.success) { - console.error((parsed.errors as string[])[0]) - } - expect(parsed.success).toBeTruthy() - } - }) - }) -}) diff --git a/packages/nmea-parser/tests/protocols.test.ts b/packages/nmea-parser/tests/protocols.test.ts index 4806293..b4061f5 100644 --- a/packages/nmea-parser/tests/protocols.test.ts +++ b/packages/nmea-parser/tests/protocols.test.ts @@ -1,7 +1,7 @@ import path from 'node:path' import { describe, test, expect } from 'vitest' import { Protocol, StoredSentence } from '../src/types' -import { getStoreSentences, readProtocolsFile, readProtocolsString } from '../src/protocols' +import { getStoreSentences, readProtocolsYAMLFile, readProtocolsYAMLString } from '../src/protocols' import { ProtocolSchema } from '../src/schemas' import { PROTOCOLS } from './norsub' @@ -12,131 +12,33 @@ const EXPECTED_PROTOCOLS: Protocol[] = [ standard: false, sentences: [ { - sentence: 'PNORSUB8', + id: 'PNORSUB8', description: 'The whole regular attitude information from the MRU', - fields: [ - { - name: 'time', - type: 'uint32', - units: 'us' - }, - { - name: 'delay', - type: 'uint32', - units: 'us', - }, - { - name: 'roll', - type: 'double', - units: 'deg', - }, - { - name: 'pitch', - type: 'double', - units: 'deg', - }, - { - name: 'heading', - type: 'double', - units: 'deg', - note: 'From 0 to 360' - }, - { - name: 'surge', - type: 'double', - units: 'm', - }, - { - name: 'sway', - type: 'double', - units: 'm', - }, - { - name: 'heave', - type: 'double', - units: 'm', - note: 'z-down' - }, - { - name: 'roll_rate', - type: 'double', - units: 'deg/s', - }, - { - name: 'pitch_rate', - type: 'double', - units: 'deg/s', - }, - { - name: 'yaw_rate', - type: 'double', - units: 'deg/s', - }, - { - name: 'surge_velocity', - type: 'double', - units: 'm/s', - }, - { - name: 'sway_velocity', - type: 'double', - units: 'm/s', - }, - { - name: 'heave_velocity', - type: 'double', - units: 'm/s', - note: 'z-down' - }, - { - name: 'acceleration_x', - type: 'double', - units: 'm/s2', - }, - { - name: 'acceleration_y', - type: 'double', - units: 'm/s2', - }, - { - name: 'acceleration_z', - type: 'double', - units: 'm/s2', - }, - { - name: 'period_x', - type: 'double', - units: 's', - }, - { - name: 'period_y', - type: 'double', - units: 's', - }, - { - name: 'period_z', - type: 'double', - units: 's', - }, - { - name: 'amplitude_x', - type: 'double', - units: 'm', - }, - { - name: 'amplitude_y', - type: 'double', - units: 'm', - }, - { - name: 'amplitude_z', - type: 'double', - units: 'm', - }, - { - name: 'status', - type: 'uint32' - } + payload: [ + { name: 'time', type: 'uint32', units: 'us' }, + { name: 'delay', type: 'uint32', units: 'us' }, + { name: 'roll', type: 'float64', units: 'deg' }, + { name: 'pitch', type: 'float64', units: 'deg' }, + { name: 'heading', type: 'float64', units: 'deg', description: 'From 0 to 360' }, + { name: 'surge', type: 'float64', units: 'm' }, + { name: 'sway', type: 'float64', units: 'm' }, + { name: 'heave', type: 'float64', units: 'm', description: 'z-down' }, + { name: 'roll_rate', type: 'float64', units: 'deg/s' }, + { name: 'pitch_rate', type: 'float64', units: 'deg/s' }, + { name: 'yaw_rate', type: 'float64', units: 'deg/s' }, + { name: 'surge_velocity', type: 'float64', units: 'm/s', }, + { name: 'sway_velocity', type: 'float64', units: 'm/s', }, + { name: 'heave_velocity', type: 'float64', units: 'm/s', description: 'z-down' }, + { name: 'acceleration_x', type: 'float64', units: 'm/s2' }, + { name: 'acceleration_y', type: 'float64', units: 'm/s2' }, + { name: 'acceleration_z', type: 'float64', units: 'm/s2' }, + { name: 'period_x', type: 'float64', units: 's' }, + { name: 'period_y', type: 'float64', units: 's' }, + { name: 'period_z', type: 'float64', units: 's' }, + { name: 'amplitude_x', type: 'float64', units: 'm' }, + { name: 'amplitude_y', type: 'float64', units: 'm' }, + { name: 'amplitude_z', type: 'float64', units: 'm' }, + { name: 'status', type: 'uint32' } ], } ] @@ -145,67 +47,72 @@ const EXPECTED_PROTOCOLS: Protocol[] = [ protocol: 'GYROCOMPAS1', standard: false, sentences: [ - { sentence: 'HEHDT', fields: [ - { name: 'heading', type: 'float', units: 'deg' }, - { name: 'symbol', type: 'string' }, - ] }, - { sentence: 'PHTRO', fields: [ - { name: 'pitch', type: 'float', units: 'deg' }, - { name: 'pitch_direction', type: 'string', note: 'M bow up, P bow down' }, - { name: 'roll', type: 'float', units: 'deg' }, - { name: 'roll_direction', type: 'string', note: 'M bow up, P bow down' }, - ] }, - { sentence: 'PHINF', fields: [ { name: 'status', type: 'string' } ] }, + { + id: 'HEHDT', + payload: [ + { name: 'heading', type: 'float32', units: 'deg' }, + { name: 'symbol', type: 'string' }, + ] + }, + { + id: 'PHTRO', + payload: [ + { name: 'pitch', type: 'float32', units: 'deg' }, + { name: 'pitch_direction', type: 'string', description: 'M bow up, P bow down' }, + { name: 'roll', type: 'float32', units: 'deg' }, + { name: 'roll_direction', type: 'string', description: 'M bow up, P bow down' }, + ] + }, + { id: 'PHINF', payload: [ { name: 'status', type: 'string' } ] }, ] } ] const EXPECTED_STORED_SENTECES: Record = { 'PNORSUB8': { - sentence: EXPECTED_PROTOCOLS[0].sentences[0].sentence, + id: EXPECTED_PROTOCOLS[0].sentences[0].id, protocol: { name: EXPECTED_PROTOCOLS[0].protocol, standard: EXPECTED_PROTOCOLS[0].standard, version: EXPECTED_PROTOCOLS[0]?.version, }, - fields: EXPECTED_PROTOCOLS[0].sentences[0].fields, + payload: EXPECTED_PROTOCOLS[0].sentences[0].payload, description: EXPECTED_PROTOCOLS[0].sentences[0]?.description }, 'HEHDT': { - sentence: EXPECTED_PROTOCOLS[1].sentences[0].sentence, + id: EXPECTED_PROTOCOLS[1].sentences[0].id, protocol: { name: EXPECTED_PROTOCOLS[1].protocol, standard: EXPECTED_PROTOCOLS[1].standard, version: EXPECTED_PROTOCOLS[1]?.version, }, - fields: EXPECTED_PROTOCOLS[1].sentences[0].fields, + payload: EXPECTED_PROTOCOLS[1].sentences[0].payload, description: EXPECTED_PROTOCOLS[1].sentences[0]?.description }, 'PHTRO': { - sentence: EXPECTED_PROTOCOLS[1].sentences[1].sentence, + id: EXPECTED_PROTOCOLS[1].sentences[1].id, protocol: { name: EXPECTED_PROTOCOLS[1].protocol, standard: EXPECTED_PROTOCOLS[1].standard, version: EXPECTED_PROTOCOLS[1]?.version, }, - fields: EXPECTED_PROTOCOLS[1].sentences[1].fields, + payload: EXPECTED_PROTOCOLS[1].sentences[1].payload, description: EXPECTED_PROTOCOLS[1].sentences[1]?.description }, 'PHINF': { - sentence: EXPECTED_PROTOCOLS[1].sentences[2].sentence, + id: EXPECTED_PROTOCOLS[1].sentences[2].id, protocol: { name: EXPECTED_PROTOCOLS[1].protocol, standard: EXPECTED_PROTOCOLS[1].standard, version: EXPECTED_PROTOCOLS[1]?.version, }, - fields: EXPECTED_PROTOCOLS[1].sentences[2].fields, + payload: EXPECTED_PROTOCOLS[1].sentences[2].payload, description: EXPECTED_PROTOCOLS[1].sentences[2]?.description }, } describe('Protocols Files', () => { - test('Right protocols file', () => { - const { protocols } = readProtocolsFile(PROTOCOLS_FILE) + const { protocols } = readProtocolsYAMLFile(PROTOCOLS_FILE) protocols.forEach(protocol => { const parsed = ProtocolSchema.safeParse(protocol) if (!parsed.success) { console.error((parsed.errors as string[])[0]) } @@ -217,7 +124,7 @@ describe('Protocols Files', () => { describe('Protocols File to StoredSentences', () => { test('Happy path', () => { - const { protocols } = readProtocolsFile(PROTOCOLS_FILE) + const { protocols } = readProtocolsYAMLFile(PROTOCOLS_FILE) const sentences = getStoreSentences({ protocols }) Object.keys(EXPECTED_STORED_SENTECES).forEach(key => { // sentences.forEach((value, key) => { @@ -232,7 +139,7 @@ describe('Protocols content to StoredSentences', () => { test('Happy path', () => { // const content = fs.readFileSync(PROTOCOLS_FILE, 'utf-8') const content = JSON.stringify(PROTOCOLS) - const { protocols } = readProtocolsString(content) + const { protocols } = readProtocolsYAMLString(content) const sentences = getStoreSentences({ protocols }) Object.keys(EXPECTED_STORED_SENTECES).forEach(key => { // sentences.forEach((value, key) => { diff --git a/packages/nmea-parser/tests/sentences.test.ts b/packages/nmea-parser/tests/sentences.test.ts index 9448b75..bfb1457 100644 --- a/packages/nmea-parser/tests/sentences.test.ts +++ b/packages/nmea-parser/tests/sentences.test.ts @@ -1,230 +1,418 @@ import { describe, expect, test } from 'vitest' -import { generateSentenceFromModel, getNMEAUnparsedSentence, getNumberValue, getUnknownSentence, getValue } from '../src/sentences' -import { FieldType, NMEAPreParsed, NMEAUnparsedSentence, StoredSentence } from '../src/types' -import { Int16Schema, Int32Schema, Int8Schema, IntegerSchema, NMEALikeSchema, NMEAPreParsedSentenceSchema, Uint16Schema, Uint32Schema, Uint8Schema, UnsignedIntegerSchema } from '../src/schemas' -import { CHECKSUM_LENGTH, DELIMITER_LENGTH, END_FLAG_LENGTH, SEPARATOR } from '../src/constants' +import { DELIMITER, END_FLAG, SEPARATOR, START_FLAG, TALKERS, TALKERS_SPECIAL } from '../src/constants' +import { Float32Schema, Float64Schema, Int16Schema, Int32Schema, Int64Schema, Int8Schema, NMEALikeSchema, StoredSentenceSchema, StringSchema, Uint16Schema, Uint32Schema, Uint64Schema, Uint8Schema } from '../src/schemas' +import { createFakeSentence, createPayload, createValue, getIdPayloadAndChecksum, getKnownNMEASentence, getTalker, getUnknowNMEASentence, getUnparsedNMEAFrames, hasSameNumberOfFields, parseValue } from '../src/sentences' +import { Checksum, NMEASentence, ProtocolField, StoredSentence, Talker } from '../src/types' +import { stringChecksumToNumber } from '../src/checksum' -describe('getNumberValue', () => { +const TEST_STORED_SENTENCE: StoredSentence = { + id: 'TEST', + protocol: { + name: 'TESTING PROTOCOL', + standard: false, + version: '1.2.3' + }, + payload: [ + { name: 'latitude', type: 'float64', units: 'deg' }, + { name: 'longitude', type: 'float32', units: 'deg' }, + { name: '2', type: 'int8' }, + { name: '3', type: 'int16' }, + { name: '4', type: 'int32' }, + { name: '5', type: 'int64' }, + { name: '6', type: 'uint8' }, + { name: '7', type: 'uint16' }, + { name: '8', type: 'uint32' }, + { name: '9', type: 'uint64' }, + { name: '10', type: 'boolean' }, + { name: '11', type: 'string' } + ], + description: 'This is just an invented sentence for testing' +} - test('invalid number type', () => { - expect(() => getNumberValue('a' as FieldType)).toThrowError() +const HDT_SENTENCE: StoredSentence = { + id: 'HDT', + protocol: { + name: 'NMEA', + standard: false, + version: '3.1' + }, + description: 'Heading - True', + payload: [ + { + name: 'heading', + type: 'float32', + description: 'Heading, degrees True' + }, + { + name: 'true', + type: 'string', + description: 'T = True' + } + ] +} + +describe('getUnparsedNMEAFrames', () => { + const sample1 = '$TEST,a,b,c*5A\r\n' + const sample2 = '$TEST,-1,3,4,T*59\r\n' + + test('Happy path', () => { + const sample = `$$as;dfj;aklsfj${sample1}\r\n**aslkjh${sample2}` + const result = getUnparsedNMEAFrames(sample) + expect(result).toHaveLength(2) + expect(result).toEqual([sample1, sample2]) + }) + + test('none if not contains "\\r\\n"', () => { + const sample = sample1.replace(END_FLAG, '') + const result = getUnparsedNMEAFrames(sample) + expect(result).toHaveLength(0) + }) + + test('none if not contains minmal length', () => { + const sample = '$TEST,a,b,c,*de\r\n' + const result = getUnparsedNMEAFrames(sample) + expect(result).toHaveLength(0) + }) + + test('none if not contains "$"', () => { + const sample = sample1.replace(START_FLAG, '') + const result = getUnparsedNMEAFrames(sample) + expect(result).toHaveLength(0) + }) + + test('none if not contains "*"', () => { + const sample = sample1.replace(DELIMITER, '') + const result = getUnparsedNMEAFrames(sample) + expect(result).toHaveLength(0) + }) + + test('none if not contains ","', () => { + const sample = sample1.replace(SEPARATOR, '') + const result = getUnparsedNMEAFrames(sample) + expect(result).toHaveLength(0) + }) + + test('none if not contains valid checksum', () => { + const sample = sample1.replace('5A', '5B') + const result = getUnparsedNMEAFrames(sample) + expect(result).toHaveLength(0) + }) + + test('none if info contains invalid characters', () => { + const sample = sample1.replace('a', '-1\r') + const result = getUnparsedNMEAFrames(sample) + expect(result).toHaveLength(0) + }) +}) + +test('getIdPayloadAndChecksum', () => { + const id = 'TEST' + const payload = '1,2.3,a,,' + const checksum = 'ad' + const sentence = `${START_FLAG}${id},${payload}${DELIMITER}${checksum}${END_FLAG}` + expect(getIdPayloadAndChecksum(sentence)).toEqual({ id, payload, checksum }) +}) + +test('hasSameFields', () => { + const validPayload = '1,2,3,4,5,6,7,8,9,10,11,12' + expect(hasSameNumberOfFields(validPayload, TEST_STORED_SENTENCE)).toBeTruthy() + const invalidPayload = '1,2,3,4,5,6,7,8,9,10,11' + expect(hasSameNumberOfFields(invalidPayload, TEST_STORED_SENTENCE)).toBeFalsy() +}) + +describe('parseValue', () => { + test('string', () => { + expect(parseValue('8', 'string')).toBe('8') + }) + + test('boolean', () => { + // Boolean + [ + ['false', false], + ['0', false], + ['True', true], + ['1', true] + ].forEach(([input, expected]) => expect(parseValue(input as string, 'boolean')).toBe(expected)); + // Null + [ + ['falsee', null], + ['00', null], + ['Trrue', null], + ['1.2', null] + ].forEach(([input, expected]) => expect(parseValue(input as string, 'boolean')).toBe(expected)); }) test('uint8', () => { - ['uint8', 'char'].forEach(type => { - const value = getNumberValue(type as FieldType) - const expected = Uint8Schema.parse(value) - expect(value).toBe(expected) - }) + // Bad + ['-1', '1.2', '1a', Math.pow(2, 8).toString()].forEach(num => expect(parseValue(num, 'uint8')).toBeNull()); + // Good + ['1', '-0', (Math.pow(2, 8) - 1).toString()].forEach(num => expect(parseValue(num, 'uint8')).toBe(Number(num))) }) test('uint16', () => { - ['uint16', 'unsigned short'].forEach(type => { - const value = getNumberValue(type as FieldType) - const expected = Uint16Schema.parse(value) - expect(value).toBe(expected) - }) + // Bad + ['-1', '1.2', '1a', Math.pow(2, 16).toString()].forEach(num => expect(parseValue(num, 'uint16')).toBeNull()); + // Good + ['1', '-0', (Math.pow(2, 16) - 1).toString()].forEach(num => expect(parseValue(num, 'uint16')).toBe(Number(num))) }) test('uint32', () => { - ['uint32', 'unsigned int'].forEach(type => { - const value = getNumberValue(type as FieldType) - const expected = Uint32Schema.parse(value) - expect(value).toBe(expected) - }) + // Bad + ['-1', '1.2', '1a', Math.pow(2, 32).toString()].forEach(num => expect(parseValue(num, 'uint32')).toBeNull()); + // Good + ['1', '-0', (Math.pow(2, 32) - 1).toString()].forEach(num => expect(parseValue(num, 'uint32')).toBe(Number(num))) }) - // test('uint64', () => { - // ['uint64', 'unsigned long'].forEach(type => { - // const value = getNumberValue(type as FieldType) - // const expected = NaturalSchema.parse(value) - // expect(value).toBe(expected) - // }) - // }) + test('uint64', () => { + // Bad + ['-1', '1.2', '1a'].forEach(num => expect(parseValue(num, 'uint64')).toBeNull()); + // Good + [(Math.pow(2, 32)).toString()].forEach(num => expect(parseValue(num, 'uint64')).toBe(BigInt(num))) + }) test('int8', () => { - ['int8', 'signed char'].forEach(type => { - const value = getNumberValue(type as FieldType) - const expected = Int8Schema.parse(value) - expect(value).toBe(expected) - }) + // Bad + ['1.2', '1a', Math.pow(2, 8).toString()].forEach(num => expect(parseValue(num, 'int8')).toBeNull()); + // Good + ['1', '-0', (Math.pow(2, 7) - 1).toString()].forEach(num => expect(parseValue(num, 'int8')).toBe(Number(num))) }) test('int16', () => { - ['int16', 'short'].forEach(type => { - const value = getNumberValue(type as FieldType) - const expected = Int16Schema.parse(value) - expect(value).toBe(expected) - }) + // Bad + ['1.2', '1a', Math.pow(2, 16).toString()].forEach(num => expect(parseValue(num, 'int16')).toBeNull()); + // Good + ['1', '-0', (Math.pow(2, 16) - 1).toString()].forEach(num => expect(parseValue(num, 'int16')).toBe(Number(num))) }) test('int32', () => { - ['int32', 'int'].forEach(type => { - const value = getNumberValue(type as FieldType) - const expected = Int32Schema.parse(value) - expect(value).toBe(expected) - }) + // Bad + ['1.2', '1a', Math.pow(2, 32).toString()].forEach(num => expect(parseValue(num, 'int32')).toBeNull()); + // Good + ['1', '-0', (Math.pow(2, 32) - 1).toString()].forEach(num => expect(parseValue(num, 'int32')).toBe(Number(num))) }) - // test('int64', () => { - // ['int64', 'long'].forEach(type => { - // const value = getNumberValue(type as FieldType) - // const expected = IntegerSchema.parse(value) - // expect(value).toBe(expected) - // }) - // }) + test('int64', () => { + // Bad + ['1.2', '1a'].forEach(num => expect(parseValue(num, 'int64')).toBeNull()); + // Good + [(Math.pow(2, 32)).toString()].forEach(num => expect(parseValue(num, 'int64')).toBe(BigInt(num))) + }) test('float32', () => { - ['float32', 'float'].forEach(type => { - const value = getNumberValue(type as FieldType) - const expected = IntegerSchema.safeParse(value) - if (expected.success) { console.log(`Value = ${value} | Parsed = ${expected.data}`)} - expect(expected.success).toBeFalsy() - }) + // Bad + ['1a'].forEach(num => expect(parseValue(num, 'float32')).toBeNull()); + // Good + ['1.2'].forEach(num => expect(parseValue(num, 'float32')).toBe(Number(num))) }) test('float64', () => { - ['float64', 'double'].forEach(type => { - const value = getNumberValue(type as FieldType) - const expected = IntegerSchema.safeParse(value) - if (expected.success) { console.log(`Value = ${value} | Parsed = ${expected.data}`)} - expect(expected.success).toBeFalsy() - }) + // Bad + ['1a'].forEach(num => expect(parseValue(num, 'float64')).toBeNull()); + // Good + ['1.2'].forEach(num => expect(parseValue(num, 'float64')).toBe(Number(num))) + }) + + test('unknown', () => { + ['integer', 'float', 'char', 'bool', 'double'].forEach(type => expect(parseValue('a', type)).toBeNull()) }) }) -describe('getValue', () => { - test('boolean', () => { - ['bool', 'boolean'].forEach(type => { - const value = getValue(type as FieldType) - expect(typeof value === 'boolean').toBeTruthy() - }) +describe('getKnownNMEASentence', () => { + test('Happy path', () => { + const received = Date.now() + const sample = '$HDT,123.456,T*25\r\n' + const sentenceID = 'HDT' + const sentencePayload = '123.456,T' + const checksum: Checksum = { sample: '25', value: stringChecksumToNumber('25') } + const expected: NMEASentence = { + received, + sample, + id: sentenceID, + checksum, + protocol: HDT_SENTENCE.protocol, + payload: [ + { ...HDT_SENTENCE.payload[0], value: 123.456, sample: '123.456', units: 'unknown' }, + { ...HDT_SENTENCE.payload[1], value: 'T' , sample: 'T', units: 'unknown' } + ] + } + const result = getKnownNMEASentence({ received, sample, sentenceID, sentencePayload, checksum, model: HDT_SENTENCE }) + expect(result).toEqual(expected) }) +}) - test('string', () => { - const value = getValue('string') - expect(typeof value === 'string').toBeTruthy() +describe('getTalker', () => { + test('Regular Talker', () => { + const sentenceID = 'GPHDT' + const talker = TALKERS.filter(talker => talker[0] === 'GP')[0] + const expected: Talker = { value: 'GP', description: talker[1] } + const result = getTalker(sentenceID) + expect(result).toEqual(expected) }) - test('numbers -> unsigned integers', () => { - ['uint8', 'uint16', 'uint32'].forEach(type => { - const value = getNumberValue(type as FieldType) - const expected = UnsignedIntegerSchema.parse(value) - expect(value).toBe(expected) - }); - }) - test('numbers -> integers', () => { - ['int8', 'int16', 'int32'].forEach(type => { - const value = getNumberValue(type as FieldType) - const expected = IntegerSchema.parse(value) - expect(value).toBe(expected) - }); - }) - test('numbers -> floats', () => { - ['float32', 'float64'].forEach(type => { - const value = getNumberValue(type as FieldType) - const expected = IntegerSchema.safeParse(value) - if (expected.success) { console.log(`Value = ${value} | Parsed = ${expected.data}`)} - expect(expected.success).toBeFalsy() - }) + test('Propietary Talker', () => { + const sentenceID = 'PNORSUB8' + const expected: Talker = { value: sentenceID, description: TALKERS_SPECIAL.P } + const result = getTalker(sentenceID) + expect(result).toEqual(expected) }) - test('Invalid type', () => { - expect(() => getValue('sstring' as FieldType)).toThrowError() + test('User Configured', () => { + // Good + const sentenceID = 'U8TEST' + const expected: Talker = { value: 'U8', description: TALKERS_SPECIAL.U } + const result = getTalker(sentenceID) + expect(result).toEqual(expected) + // Bad + expect(getTalker('UXTEST')).toBeNull() + }) + + test('Unknown talker', () => { + ['UXTEXT', 'pNorsub', 'GGGA'].forEach(id => expect(getTalker(id)).toBeNull()) }) }) -describe('generateSentenceFromModel', () => { - const testSentence: StoredSentence = { - sentence: 'TEST', - protocol: { - name: 'TESTING PROTOCOL', - standard: false, - version: '1.2.3' - }, - fields: [ - { name: 'latitude', type: 'number', units: 'deg' }, - { name: 'longitude', type: 'float32', units: 'deg' }, - { name: 'altitude', type: 'float64', units: 'm' }, - { name: 'a', type: 'int8'}, - { name: 'b', type: 'int16'}, - { name: 'c', type: 'int32'}, - { name: 'd', type: 'uint8'}, - { name: 'e', type: 'uint16'}, - { name: 'f', type: 'uint32'}, - { name: 'g', type: 'boolean'}, - { name: 'h', type: 'string'}, +test('getUnknownSentence', () => { + const received = Date.now() + const sample = '$TEST,1,,2,T*89\r\n' + const sentenceID = 'TEST' + const sentencePayload = '1,,2,T' + const checksum: Checksum = { sample: '89', value: 137 } + const expected: NMEASentence = { + received, sample, id: sentenceID, checksum, + payload: [ + { name: 'unknown', sample: '1', value: '1', type: 'string', units: 'unknown' }, + { name: 'unknown', sample: '', value: '', type: 'string', units: 'unknown' }, + { name: 'unknown', sample: '2', value: '2', type: 'string', units: 'unknown' }, + { name: 'unknown', sample: 'T', value: 'T', type: 'string', units: 'unknown' }, ], - description: 'This is just an invented sentence for testing' + description: 'unknown nmea sentence', + protocol: { name: 'unknown', standard: false } } - test('Happy path', () => { - const expected = generateSentenceFromModel(testSentence) - const parsed = NMEALikeSchema.parse(expected) - expect(parsed).toBe(expected) - - const info = parsed.slice(1, - (DELIMITER_LENGTH + CHECKSUM_LENGTH + END_FLAG_LENGTH)).split(SEPARATOR) - const field0 = info[0] - const field1 = Number(info[1]) - const field2 = Number(info[2]) - const field3 = Number(info[3]) - const field4 = Number(info[4]) - const field5 = Number(info[5]) - const field6 = Number(info[6]) - const field7 = Number(info[7]) - const field8 = Number(info[8]) - const field9 = Number(info[9]) - const field10 = Boolean(info[10]) - const field11 = info[11] - - expect(field0).toBe('TEST') - const rest1 = Float64Array.from([field1])[0] - field1; - const rest2 = Float32Array.from([field2])[0] - field2; - const rest3 = Float64Array.from([field3])[0] - field3; - [rest1, rest2, rest3].forEach(rest => { - expect(rest).toBeLessThan(0.1) + const result = getUnknowNMEASentence({ received, sample, sentenceID, sentencePayload, checksum }) + expect(result).toEqual(expected) +}) + +describe('createValue', () => { + test('boolean', () => { + const value = createValue('boolean') + expect(typeof value === 'boolean').toBeTruthy() + }) + + test('string', () => { + const value = createValue('string') + expect(typeof value === 'string').toBeTruthy() + }) + + test('unsigned integers', () => { + // Uint8 + let value = createValue('uint8') + let expected = Uint8Schema.parse(value) + expect(value).toBe(expected) + // Uint16 + value = createValue('uint16') + expected = Uint16Schema.parse(value) + expect(value).toBe(expected) + // Uint32 + value = createValue('uint32') + expected = Uint32Schema.parse(value) + expect(value).toBe(expected) + // Uint64 + value = createValue('uint64') + const bigexpected = Uint64Schema.parse(value) + expect(value).toBe(bigexpected) + }) + + test('integers', () => { + // Int8 + let value = createValue('int8') + let expected = Int8Schema.parse(value) + expect(value).toBe(expected) + // Int16 + value = createValue('int16') + expected = Int16Schema.parse(value) + expect(value).toBe(expected) + // Int32 + value = createValue('int32') + expected = Int32Schema.parse(value) + expect(value).toBe(expected) + // Int64 + value = createValue('int64') + const bigexpected = Int64Schema.parse(value) + expect(value).toBe(bigexpected) + }) + + test('floats', () => { + // Float32 + let value = createValue('float32') + let expected = Float32Schema.parse(value) + expect(value).toBe(expected) + // Float64 + value = createValue('float64') + expected = Float64Schema.parse(value) + expect(value).toBe(expected) + }) + + test('invalid', () => { + ['bool', 'number', 'float', 'String'].forEach(type => { + // @ts-expect-error + const value = createValue(type as ProtocolField) + expect(value).toBeNull() }) - expect(Int8Array.from([field4])[0]).toBe(field4) - expect(Int16Array.from([field5])[0]).toBe(field5) - expect(Int32Array.from([field6])[0]).toBe(field6) - expect(Uint8Array.from([field7])[0]).toBe(field7) - expect(Uint16Array.from([field8])[0]).toBe(field8) - expect(Uint32Array.from([field9])[0]).toBe(field9) - expect(field10).toBeTypeOf('boolean') - expect(field11).toBeTypeOf('string') }) }) -describe('unknown sentence', () => { +test('createPayload', () => { const testSentence: StoredSentence = { - sentence: 'TEST', + id: 'TEST', protocol: { name: 'TESTING PROTOCOL', standard: false, version: '1.2.3' }, - fields: [ - { name: 'latitude', type: 'number', units: 'deg' }, + payload: [ + { name: 'latitude', type: 'float64', units: 'deg' }, { name: 'longitude', type: 'float32', units: 'deg' }, - { name: 'altitude', type: 'float64', units: 'm' }, - { name: 'a', type: 'int8'}, - { name: 'b', type: 'int16'}, - { name: 'c', type: 'int32'}, - { name: 'd', type: 'uint8'}, - { name: 'e', type: 'uint16'}, - { name: 'f', type: 'uint32'}, - { name: 'g', type: 'boolean'}, - { name: 'h', type: 'string'}, + { name: '2', type: 'int8' }, + { name: '3', type: 'int16' }, + { name: '4', type: 'int32' }, + { name: '5', type: 'int64' }, + { name: '6', type: 'uint8' }, + { name: '7', type: 'uint16' }, + { name: '8', type: 'uint32' }, + { name: '9', type: 'uint64' }, + { name: '10', type: 'boolean' }, + { name: '11', type: 'string' } ], description: 'This is just an invented sentence for testing' } - test('getUnknownSentence', () => { - const text = generateSentenceFromModel(testSentence) - const unparsedSentence: NMEAUnparsedSentence = getNMEAUnparsedSentence(text) as NMEAUnparsedSentence - const preparsedFrame = { timestamp: Date.now(), ...unparsedSentence } - const input: NMEAPreParsed = NMEAPreParsedSentenceSchema.parse(preparsedFrame) - const result = getUnknownSentence(input) - expect(result.protocol.name).toBe('UNKNOWN') - expect(result.raw).toBe(text) + expect(StoredSentenceSchema.parse(testSentence)).toEqual(testSentence) + const payload = createPayload(testSentence) + const fields = payload.split(SEPARATOR) + expect(Float64Schema.is(Number(fields[0]))).toBeTruthy() + expect(Float32Schema.is(Number(fields[1]))).toBeTruthy() + expect(Int8Schema.is(Number(fields[2]))).toBeTruthy() + expect(Int16Schema.is(Number(fields[3]))).toBeTruthy() + expect(Int32Schema.is(Number(fields[4]))).toBeTruthy() + expect(Int64Schema.is(BigInt(fields[5]))).toBeTruthy() + expect(Uint8Schema.is(Number(fields[6]))).toBeTruthy() + expect(Uint16Schema.is(Number(fields[7]))).toBeTruthy() + expect(Uint32Schema.is(Number(fields[8]))).toBeTruthy() + expect(Uint64Schema.is(BigInt(fields[9]))).toBeTruthy() + expect(fields[10] === 'true' || fields[10] === 'false').toBeTruthy() + expect(StringSchema.is(fields[11])).toBeTruthy() +}) - }) -}) \ No newline at end of file +test('createFakeSentence', () => { + const sample = createFakeSentence(TEST_STORED_SENTENCE) + expect(NMEALikeSchema.is(sample)).toBeTruthy() + // Check that fake sentence can be parsed + const received = Date.now() + const aux = sample.slice(START_FLAG.length, -END_FLAG.length) + const [info, cs] = aux.split(DELIMITER) + const [sentenceID, ...rest] = info.split(SEPARATOR) + const sentencePayload = rest.join(SEPARATOR) + const checksum: Checksum = { value: stringChecksumToNumber(cs), sample: cs} + const parsed = getUnknowNMEASentence({ received, sample, sentenceID, sentencePayload, checksum }) + expect(parsed.sample).toBe(sample) + expect(parsed.checksum).toEqual(checksum) +}) From dc42adb0898c8b241df5667b448bbce0bcf0a65c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Crist=C3=B3bal=20Contreras=20Rubio?= Date: Mon, 22 Jul 2024 17:24:16 +0200 Subject: [PATCH 5/6] NMEA-PARSER: v2.0.0 --- packages/nmea-parser/README.md | 156 ++++++++++++++------------ packages/nmea-parser/package.json | 9 +- packages/nmea-parser/tsconfig.json | 9 +- packages/nmea-parser/vitest.config.ts | 4 +- 4 files changed, 102 insertions(+), 76 deletions(-) diff --git a/packages/nmea-parser/README.md b/packages/nmea-parser/README.md index 3f67917..d70ee05 100644 --- a/packages/nmea-parser/README.md +++ b/packages/nmea-parser/README.md @@ -1,8 +1,6 @@ # NMEA parser -![npm (scoped)](https://img.shields.io/npm/v/%40coremarine/nmea-parser) -[![publish](https://github.com/core-marine-dev/devices/actions/workflows/nmea-parser.yml/badge.svg)](https://github.com/core-marine-dev/devices/actions/workflows/nmea-parser.yml) -![npm](https://img.shields.io/npm/dy/%40coremarine/nmea-parser) +![npm (scoped)](https://img.shields.io/npm/v/%40coremarine/nmea-parser) [![publish](https://github.com/core-marine-dev/devices/actions/workflows/nmea-parser.yml/badge.svg)](https://github.com/core-marine-dev/devices/actions/workflows/nmea-parser.yml) ![npm](https://img.shields.io/npm/dy/%40coremarine/nmea-parser) **NMEA Parser** is a library to parse NMEA 0183 sentences. @@ -14,7 +12,7 @@ This library parse **ALL** NMEA-like sentences (sentences that follow these rule - Start with `$` - Fields separated by `,` - An `*` character splitting info and checksum -- Two digits of checksum +- Two hexadecimal digits of checksum - End with `\r\n` If the parser knows the NMEA sentences it gives more metadata. Known frames are: @@ -54,15 +52,15 @@ The output it is an array of parsed sentences which have this type ```typescript type NMEASentence = { // Sentence ID - sentence: string, - // Array just with the data of each field (easier to just read data and not fields metadata) - data: Array, + id: string, // Array with ordered fields and their metadata - fields: Array<{ + payload: Array<{ name: string, - data: string | number | boolean | null, + value: string | number | bigint | boolean | null, + type: 'string' | 'boolean' | 'uint8' | 'uint16' | 'uint32' | 'uint64' | 'int8' | 'int16' | 'int32' | 'int64' | 'float32' | 'float64' | 'unknown', units?: string, - note?: string, + description?: string, + metadata?: any }>, // Protocol information protocol: { @@ -71,21 +69,26 @@ type NMEASentence = { version: string, }, // UTC timestamp when the sentence was parsed - timestamp: number, + received: number, // Whole ASCII string sentence - raw: string, + sample: string, // Sentence checksum - checksum: number + checksum: { + sample: string, + value: number + } // Sentence talker - talker?: null | { id: string, description: string } + talker?: { + value: string, + description: string + } } ``` If the sentence is unknown for the parser you have: -- `protocol` is equal to `{ name: 'UNKNOWN' }` -- `data` is an `Array` -- In `fields` each element is equal to `{ name: 'unknown', data: string }` +- `protocol` is equal to `{ name: 'unknown' }` +- In `payload` each element is equal to `{ name: 'unknown', value: string, type: string }` ### Feed the parser @@ -111,7 +114,7 @@ To get that is with method `getFakeSentenceByID()`. If the sentence it is not su ```typescript type Sentence = { // ID of the sentence - sentence: string, + id: string, // Protocol info protocol: { name: string, @@ -119,14 +122,17 @@ type Sentence = { version?: string }, // Ordered fields with its info - fields: Array<{ + payload: Array<{ name: string, - type: 'string' | 'boolean' | 'uint8' | 'uint16' | 'uint32' | 'int8' | 'int16' | 'int32' | 'float32' | 'float64', + type: 'string' | 'boolean' | 'uint8' | 'uint16' | 'uint32' | 'uint64' | 'int8' | 'int16' | 'int32' | 'int64' | 'float32' | 'float64', units?: string, - note?: string + description?: string }>, // Optional talker - talker?: null | { id: string, description: string } + talker?: { + value: string, + description: string + } // Optional description description?: string } @@ -183,15 +189,15 @@ protocols: # Array of sentences sentences: # Each sentence has this structure - - sentence: + - id: description: # Array of fields - fields: + payload: # Each field has this structure - name: type: units: - note: + description: ``` It will be parsed into an `Protocol` object then (see next section). @@ -205,13 +211,13 @@ protocols: version: '3.1' standard: true sentences: - - sentence: AAM + - id: AAM description: Waypoint Arrival Alarm - fields: + payload: # 1 - name: status type: string - note: "BOOLEAN\n + description: "BOOLEAN\n A = arrival circle entered\n @@ -219,7 +225,7 @@ protocols: # 2 - name: status type: string - note: "BOOLEAN\n + description: "BOOLEAN\n A = perpendicular passed at waypoint\n @@ -234,9 +240,9 @@ protocols: # 5 - name: waypoint_id type: string - - sentence: GGA + - id: GGA description: Global Positioning System Fix Data - fields: + payload: # 1 - name: utc_position type: string @@ -248,7 +254,7 @@ protocols: # 3 - name: latitude_direction type: string - note: "N: North\n + description: "N: North\n S: South" # 4 - name: longitude @@ -257,12 +263,12 @@ protocols: # 5 - name: longitude_direction type: string - note: "E - East\n + description: "E - East\n W - West" # 6 - name: gps_quality type: int8 - note: "0: Fix not valid\n + description: "0: Fix not valid\n 1: GPS fix\n 2: Differential GPS fix (DGNSS), SBAS, OmniSTAR VBS, Beacon, RTX in GVBS mode\n 3: Not applicable\n @@ -281,7 +287,7 @@ protocols: - name: altitude type: float64 units: m - note: "Orthometric height Mean-Sea-Level (MSL reference)" + description: "Orthometric height Mean-Sea-Level (MSL reference)" # 10 - name: altitude_units type: string @@ -290,7 +296,7 @@ protocols: - name: geoid_separation type: float64 units: m - note: "Geoidal Separation: the difference between the WGS-84 earth ellipsoid surface and mean-sea-level (geoid) surface, \"-\" = mean-sea-level surface below WGS-84 ellipsoid surface." + description: "Geoidal Separation: the difference between the WGS-84 earth ellipsoid surface and mean-sea-level (geoid) surface, \"-\" = mean-sea-level surface below WGS-84 ellipsoid surface." # 12 - name: geoid_separation_units type: string @@ -299,11 +305,11 @@ protocols: - name: age_of_differential_gps_data type: float64 units: sec - note: "Time in seconds since last SC104 Type 1 or 9 update, null field when DGPS is not used300" + description: "Time in seconds since last SC104 Type 1 or 9 update, null field when DGPS is not used300" # 14 - name: reference_station_id type: uint16 - note: "Reference station ID, range 0000 to 4095. A null field when any reference station ID is selected and no corrections are received. See table below for a description of the field values.\n + description: "Reference station ID, range 0000 to 4095. A null field when any reference station ID is selected and no corrections are received. See table below for a description of the field values.\n 0002 CenterPoint or ViewPoint RTX\n @@ -332,73 +338,73 @@ protocols: 1020 HP/G2 (GPS)\n 1021 HP/G2 (GPS/GLONASS)" - - sentence: HDT + - id: HDT description: Heading - True - fields: + payload: # 1 - name: heading type: float32 - note: "Heading, degrees True" + description: "Heading, degrees True" # 2 - name: "true" type: string - note: "T = True" - - sentence: ZDA + description: "T = True" + - id: ZDA description: Time & Date - UTC, day, month, year and local time zone - fields: + payload: # 1 - name: utc_time type: string - note: "UTC time (hours, minutes, seconds, may have fractional subseconds)" + description: "UTC time (hours, minutes, seconds, may have fractional subseconds)" # 2 - name: day type: int8 - note: "Day, 01 to 31" + description: "Day, 01 to 31" # 3 - name: month type: int8 - note: "Month, 01 to 12" + description: "Month, 01 to 12" # 4 - name: year type: int16 - note: "Year (4 digits)" + description: "Year (4 digits)" # 5 - name: local_zone_hours type: int8 - note: "Local zone description, 00 to +- 13 hours" + description: "Local zone description, 00 to +- 13 hours" # 6 - name: local_zone_minutes type: int8 - note: "Local zone minutes description, 00 to 59, apply same sign as local hours" + description: "Local zone minutes description, 00 to 59, apply same sign as local hours" # Propietary GYROCOMPAS1 - protocol: GYROCOMPAS1 standard: false sentences: - - sentence: HEHDT - fields: + - id: HEHDT + payload: - name: heading type: float units: deg - name: symbol type: string - - sentence: PHTRO - fields: + - id: PHTRO + payload: - name: pitch type: float units: deg - name: pitch_direction type: string - note: M bow up, P bow down + description: M bow up, P bow down - name: roll type: float units: deg - name: roll_direction type: string - note: M bow up, P bow down - - sentence: PHINF - fields: + description: M bow up, P bow down + - id: PHINF + payload: - name: status type: string ``` @@ -411,16 +417,16 @@ It is an object which has the next type. type Protocol = { // Protocol name protocol: string, - // Semantic version - version?: string, // If the protocol is NMEA stantard or propietary (false by default) standard?: boolean = false, + // Semantic version + version?: string, // Array of sentences sentences: Array<{ // Sentence ID - sentence: string, + id: string, // Each field metadata - fields: Array<{ + payload: Array<{ name: string, type: 'string' | 'boolean' | 'uint8' | 'uint16' | 'uint32' | 'int8' | 'int16' | 'int32' | 'float32' | 'float64', units?: string, @@ -460,14 +466,26 @@ type Protocol = { - Get known protocols with their sentences ```typescript - type ProtocolOutput = { - protocol: string, - version?: string, - sentences: string[] + type StoredSentence = { + id: string, + protocol: { + name: string, + standard: boolean, + version?: string + }, + payload: Array<{ + name: string, + type: 'string' | 'boolean' | 'uint8' | 'uint16' | 'uint32' | 'int8' | 'int16' | 'int32' | 'float32' | 'float64', + units?: string, + description?: string + }>, + description?: string } + type ProtocolOutput = Record + // GET protocols - const knownProtocols: ProtocolOutput[] = parser.getProtocols() + const knownProtocols: ProtocolOutput = parser.getSentencesByProtocol() ``` - Get sentence info by id @@ -475,7 +493,7 @@ type Protocol = { ```typescript type Sentence = { // ID of the sentence - sentence: string, + id: string, // Protocol info protocol: { name: string, @@ -483,7 +501,7 @@ type Protocol = { version?: string }, // Ordered fields with its info - fields: Array<{ + payload: Array<{ name: string, type: 'string' | 'boolean' | 'uint8' | 'uint16' | 'uint32' | 'int8' | 'int16' | 'int32' | 'float32' | 'float64', units?: string, diff --git a/packages/nmea-parser/package.json b/packages/nmea-parser/package.json index c2eee98..ebdf9cd 100644 --- a/packages/nmea-parser/package.json +++ b/packages/nmea-parser/package.json @@ -1,6 +1,6 @@ { "name": "@coremarine/nmea-parser", - "version": "1.7.0", + "version": "2.0.0", "description": "Library to parse NMEA 0183 sentences", "author": "CoreMarine", "license": "MIT", @@ -43,10 +43,10 @@ }, "scripts": { "protocols": "node yaml-to-json.js ./protocols/nmea.yaml ./src/nmea.ts && node yaml-to-json.js ./protocols/norsub.yaml ./tests/norsub.ts", - "build": "npm run protocols && npm run format && tsup", + "build": "npm run format && tsup", "lint": "ts-standard", "format": "ts-standard --fix", - "test": "npm run protocols && vitest", + "test": "vitest", "test:coverage": "vitest run --coverage" }, "dependencies": { @@ -66,7 +66,8 @@ "tests", "vitest.config.ts", "tsup.config.ts", - "yaml-to-json.js" + "yaml-to-json.js", + "legacy" ] }, "eslintConfig": { diff --git a/packages/nmea-parser/tsconfig.json b/packages/nmea-parser/tsconfig.json index 9335929..168b9e5 100644 --- a/packages/nmea-parser/tsconfig.json +++ b/packages/nmea-parser/tsconfig.json @@ -3,5 +3,10 @@ "compilerOptions": { "outDir": "./dist", }, - "include": ["src/**/*"], -} + "include": [ + "src/**/*" + ], + "exclude": [ + "legacy/*" + ] +} \ No newline at end of file diff --git a/packages/nmea-parser/vitest.config.ts b/packages/nmea-parser/vitest.config.ts index df8f570..11eb7d8 100644 --- a/packages/nmea-parser/vitest.config.ts +++ b/packages/nmea-parser/vitest.config.ts @@ -8,7 +8,9 @@ export default defineConfig({ '**/*/constants.ts', '**/*/types.ts', 'dist/*', - 'yaml-to-json.js' + 'yaml-to-json.js', + 'legacy/*', + '*.config.ts' ] } } From fec12dd7468ddfae15f65ea7e5f944b5d512dc0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Crist=C3=B3bal=20Contreras=20Rubio?= Date: Mon, 22 Jul 2024 17:31:23 +0200 Subject: [PATCH 6/6] NMEA-PARSER: added random integers 64 bit from node and not web --- packages/nmea-parser/src/sentences.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nmea-parser/src/sentences.ts b/packages/nmea-parser/src/sentences.ts index c35a2b5..06ad26c 100644 --- a/packages/nmea-parser/src/sentences.ts +++ b/packages/nmea-parser/src/sentences.ts @@ -1,3 +1,4 @@ +import crypto from 'node:crypto' import { calculateChecksum, numberChecksumToString, stringChecksumToNumber } from './checksum' import { CHECKSUM_LENGTH, DELIMITER, END_FLAG, END_FLAG_LENGTH, MINIMAL_LENGTH, NMEA_ID_LENGTH, SEPARATOR, START_FLAG, TALKERS, TALKERS_SPECIAL } from './constants' import { Float32Schema, Float64Schema, Int16Schema, Int32Schema, Int64Schema, Int8Schema, NMEASentenceSchema, Uint16Schema, Uint32Schema, Uint64Schema, Uint8Schema } from './schemas'