diff --git a/README.md b/README.md index 2ee653b..854146a 100644 --- a/README.md +++ b/README.md @@ -65,11 +65,6 @@ git clone https://github.com/liamcottle/meshtastic-map cd meshtastic-map ``` -Install Meshtastic protobufs definitions -``` -git clone https://github.com/meshtastic/protobufs src/protobufs -``` - Install NodeJS dependencies ``` diff --git a/package-lock.json b/package-lock.json index 1fb2318..2cbfa9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ }, "devDependencies": { "jest": "^30.1.3", - "prisma": "^6.16.2" + "prisma": "^7.4.1" } }, "node_modules/@ampproject/remapping": { @@ -69,7 +69,6 @@ "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -604,6 +603,73 @@ "dev": true, "license": "MIT" }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", + "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", + "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/types": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", + "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", + "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@electric-sql/pglite": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.15.tgz", + "integrity": "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@electric-sql/pglite-socket": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite-socket/-/pglite-socket-0.0.20.tgz", + "integrity": "sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "pglite-server": "dist/scripts/server.js" + }, + "peerDependencies": { + "@electric-sql/pglite": "0.3.15" + } + }, + "node_modules/@electric-sql/pglite-tools": { + "version": "0.2.20", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite-tools/-/pglite-tools-0.2.20.tgz", + "integrity": "sha512-BK50ZnYa3IG7ztXhtgYf0Q7zijV32Iw1cYS8C+ThdQlwx12V5VZ9KRJ42y82Hyb4PkTxZQklVQA9JHyUlex33A==", + "devOptional": true, + "license": "Apache-2.0", + "peerDependencies": { + "@electric-sql/pglite": "0.3.15" + } + }, "node_modules/@emnapi/core": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz", @@ -638,6 +704,19 @@ "tslib": "^2.4.0" } }, + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1064,6 +1143,20 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mrleebo/prisma-ast": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@mrleebo/prisma-ast/-/prisma-ast-0.13.1.tgz", + "integrity": "sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "chevrotain": "^10.5.0", + "lilconfig": "^2.1.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -1124,66 +1217,138 @@ } }, "node_modules/@prisma/config": { - "version": "6.16.2", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.16.2.tgz", - "integrity": "sha512-mKXSUrcqXj0LXWPmJsK2s3p9PN+aoAbyMx7m5E1v1FufofR1ZpPoIArjjzOIm+bJRLLvYftoNYLx1tbHgF9/yg==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-7.4.1.tgz", + "integrity": "sha512-vteSXm8N46bo3FW9MhPGVHAj+KRgrR6TWtlSk6GqToCKjTnOexXdPZyiDyEsfVW38YhqEmVl6w/6iHN8uYVJcw==", "devOptional": true, "license": "Apache-2.0", "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", - "effect": "3.16.12", + "effect": "3.18.4", "empathic": "2.0.0" } }, "node_modules/@prisma/debug": { - "version": "6.16.2", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.16.2.tgz", - "integrity": "sha512-bo4/gA/HVV6u8YK2uY6glhNsJ7r+k/i5iQ9ny/3q5bt9ijCj7WMPUwfTKPvtEgLP+/r26Z686ly11hhcLiQ8zA==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.4.1.tgz", + "integrity": "sha512-qEtzO8oLouRv18JDQUC3G3Gnv+fGVscHZm/x1DBB/WT+kOvPDQLM2woX6IGgWnSMYYlrxjuALshT7G/blvY0bQ==", "devOptional": true, "license": "Apache-2.0" }, + "node_modules/@prisma/dev": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@prisma/dev/-/dev-0.20.0.tgz", + "integrity": "sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "@electric-sql/pglite": "0.3.15", + "@electric-sql/pglite-socket": "0.0.20", + "@electric-sql/pglite-tools": "0.2.20", + "@hono/node-server": "1.19.9", + "@mrleebo/prisma-ast": "0.13.1", + "@prisma/get-platform": "7.2.0", + "@prisma/query-plan-executor": "7.2.0", + "foreground-child": "3.3.1", + "get-port-please": "3.2.0", + "hono": "4.11.4", + "http-status-codes": "2.3.0", + "pathe": "2.0.3", + "proper-lockfile": "4.1.2", + "remeda": "2.33.4", + "std-env": "3.10.0", + "valibot": "1.2.0", + "zeptomatch": "2.1.0" + } + }, "node_modules/@prisma/engines": { - "version": "6.16.2", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.16.2.tgz", - "integrity": "sha512-7yf3AjfPUgsg/l7JSu1iEhsmZZ/YE00yURPjTikqm2z4btM0bCl2coFtTGfeSOWbQMmq45Jab+53yGUIAT1sjA==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.4.1.tgz", + "integrity": "sha512-BZEBdHvNJx5PzIG37EI/Zi5UUI5hGWjkYsQmKa7OIK6evAvebOTwutjS/VRI6cA6grmA52eLZR+oekGRMqkKxQ==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.16.2", - "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", - "@prisma/fetch-engine": "6.16.2", - "@prisma/get-platform": "6.16.2" + "@prisma/debug": "7.4.1", + "@prisma/engines-version": "7.5.0-4.55ae170b1ced7fc6ed07a15f110549408c501bb3", + "@prisma/fetch-engine": "7.4.1", + "@prisma/get-platform": "7.4.1" } }, "node_modules/@prisma/engines-version": { - "version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43.tgz", - "integrity": "sha512-ThvlDaKIVrnrv97ujNFDYiQbeMQpLa0O86HFA2mNoip4mtFqM7U5GSz2ie1i2xByZtvPztJlNRgPsXGeM/kqAA==", + "version": "7.5.0-4.55ae170b1ced7fc6ed07a15f110549408c501bb3", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.5.0-4.55ae170b1ced7fc6ed07a15f110549408c501bb3.tgz", + "integrity": "sha512-fUxVd1TjOW8K4XsZ8dAm88sDW5Ry7AxWDfsYEWwScS6Fjo3caKC6hgNumUfsmsy0Il9LjDn5X0PpVXNt3iwayw==", "devOptional": true, "license": "Apache-2.0" }, - "node_modules/@prisma/fetch-engine": { - "version": "6.16.2", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.16.2.tgz", - "integrity": "sha512-wPnZ8DMRqpgzye758ZvfAMiNJRuYpz+rhgEBZi60ZqDIgOU2694oJxiuu3GKFeYeR/hXxso4/2oBC243t/whxQ==", + "node_modules/@prisma/engines/node_modules/@prisma/get-platform": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.4.1.tgz", + "integrity": "sha512-kN4tmkQzlgm/KtE+jTNSYjsDxxe/5i6GApPI32BN9T0tlgsgSBtDJbjGBICttkAIjsh73dXf8raPKxO/2n2UUg==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.16.2", - "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", - "@prisma/get-platform": "6.16.2" + "@prisma/debug": "7.4.1" + } + }, + "node_modules/@prisma/fetch-engine": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-7.4.1.tgz", + "integrity": "sha512-Z9kbuxX2bvEsyeS3LZEiEnxG0lVtZbpYgaAnPj69N+A9f2De8Lta0EoFtld9zhfERVPIQWhSWUc8himky3qYdA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.4.1", + "@prisma/engines-version": "7.5.0-4.55ae170b1ced7fc6ed07a15f110549408c501bb3", + "@prisma/get-platform": "7.4.1" + } + }, + "node_modules/@prisma/fetch-engine/node_modules/@prisma/get-platform": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.4.1.tgz", + "integrity": "sha512-kN4tmkQzlgm/KtE+jTNSYjsDxxe/5i6GApPI32BN9T0tlgsgSBtDJbjGBICttkAIjsh73dXf8raPKxO/2n2UUg==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.4.1" } }, "node_modules/@prisma/get-platform": { - "version": "6.16.2", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.16.2.tgz", - "integrity": "sha512-U/P36Uke5wS7r1+omtAgJpEB94tlT4SdlgaeTc6HVTTT93pXj7zZ+B/cZnmnvjcNPfWddgoDx8RLjmQwqGDYyA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.2.0.tgz", + "integrity": "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.16.2" + "@prisma/debug": "7.2.0" + } + }, + "node_modules/@prisma/get-platform/node_modules/@prisma/debug": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.2.0.tgz", + "integrity": "sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/query-plan-executor": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/query-plan-executor/-/query-plan-executor-7.2.0.tgz", + "integrity": "sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/studio-core": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@prisma/studio-core/-/studio-core-0.13.1.tgz", + "integrity": "sha512-agdqaPEePRHcQ7CexEfkX1RvSH9uWDb6pXrZnhCRykhDFAV0/0P3d07WtfiY8hZWb7oRU4v+NkT4cGFHkQJIPg==", + "devOptional": true, + "license": "Apache-2.0", + "peerDependencies": { + "@types/react": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" } }, "node_modules/@protobufjs/aspromise": { @@ -1268,9 +1433,9 @@ } }, "node_modules/@standard-schema/spec": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", - "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "devOptional": true, "license": "MIT" }, @@ -1365,6 +1530,17 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, "node_modules/@types/readable-stream": { "version": "4.0.21", "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.21.tgz", @@ -1771,6 +1947,16 @@ "node": ">=12.17" } }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/babel-jest": { "version": "30.1.2", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.1.2.tgz", @@ -2010,7 +2196,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001737", "electron-to-chromium": "^1.5.211", @@ -2210,6 +2395,21 @@ "node": ">=10" } }, + "node_modules/chevrotain": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", + "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "10.5.0", + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "@chevrotain/utils": "10.5.0", + "lodash": "4.17.21", + "regexp-to-ast": "0.5.0" + } + }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -2481,9 +2681,9 @@ } }, "node_modules/confbox": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", - "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", "devOptional": true, "license": "MIT" }, @@ -2556,7 +2756,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -2567,6 +2767,14 @@ "node": ">= 8" } }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT", + "peer": true + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2617,6 +2825,16 @@ "devOptional": true, "license": "MIT" }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -2682,9 +2900,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/effect": { - "version": "3.16.12", - "resolved": "https://registry.npmjs.org/effect/-/effect-3.16.12.tgz", - "integrity": "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", + "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -2986,9 +3204,9 @@ } }, "node_modules/exsolve": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", - "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", "devOptional": true, "license": "MIT" }, @@ -3147,7 +3365,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -3207,6 +3425,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3261,6 +3489,13 @@ "node": ">=8.0.0" } }, + "node_modules/get-port-please": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz", + "integrity": "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==", + "devOptional": true, + "license": "MIT" + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -3342,9 +3577,23 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, + "devOptional": true, "license": "ISC" }, + "node_modules/grammex": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/grammex/-/grammex-3.1.12.tgz", + "integrity": "sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/graphmatch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/graphmatch/-/graphmatch-1.1.1.tgz", + "integrity": "sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3383,6 +3632,16 @@ "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", "license": "MIT" }, + "node_modules/hono": { + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.4.tgz", + "integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -3410,6 +3669,13 @@ "url": "https://opencollective.com/express" } }, + "node_modules/http-status-codes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", + "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", + "devOptional": true, + "license": "MIT" + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -3572,6 +3838,13 @@ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "devOptional": true, + "license": "MIT" + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -3589,7 +3862,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/istanbul-lib-coverage": { @@ -4324,9 +4597,9 @@ } }, "node_modules/jiti": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", - "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "devOptional": true, "license": "MIT", "bin": { @@ -4413,6 +4686,16 @@ "node": ">=6" } }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -4433,6 +4716,13 @@ "node": ">=8" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -4449,6 +4739,22 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/lru.min": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz", + "integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==", + "devOptional": true, + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -4702,6 +5008,40 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/mysql2": { + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz", + "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.0", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "lru.min": "^1.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/napi-postinstall": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz", @@ -4812,25 +5152,30 @@ "license": "MIT" }, "node_modules/nypm": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", - "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", + "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", "devOptional": true, "license": "MIT", "dependencies": { - "citty": "^0.1.6", - "consola": "^3.4.2", + "citty": "^0.2.0", "pathe": "^2.0.3", - "pkg-types": "^2.3.0", - "tinyexec": "^1.0.1" + "tinyexec": "^1.0.2" }, "bin": { "nypm": "dist/cli.mjs" }, "engines": { - "node": "^14.16.0 || >=16.10.0" + "node": ">=18" } }, + "node_modules/nypm/node_modules/citty": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz", + "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5015,7 +5360,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -5115,6 +5460,20 @@ "pathe": "^2.0.3" } }, + "node_modules/postgres": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.7.tgz", + "integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==", + "devOptional": true, + "license": "Unlicense", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/porsager" + } + }, "node_modules/pretty-format": { "version": "30.0.5", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", @@ -5144,27 +5503,34 @@ } }, "node_modules/prisma": { - "version": "6.16.2", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.16.2.tgz", - "integrity": "sha512-aRvldGE5UUJTtVmFiH3WfNFNiqFlAtePUxcI0UEGlnXCX7DqhiMT5TRYwncHFeA/Reca5W6ToXXyCMTeFPdSXA==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-7.4.1.tgz", + "integrity": "sha512-gDKOXwnPiMdB+uYMhMeN8jj4K7Cu3Q2wB/wUsITOoOk446HtVb8T9BZxFJ1Zop6alc89k6PMNdR2FZCpbXp/jw==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@prisma/config": "6.16.2", - "@prisma/engines": "6.16.2" + "@prisma/config": "7.4.1", + "@prisma/dev": "0.20.0", + "@prisma/engines": "7.4.1", + "@prisma/studio-core": "0.13.1", + "mysql2": "3.15.3", + "postgres": "3.4.7" }, "bin": { "prisma": "build/index.js" }, "engines": { - "node": ">=18.18" + "node": "^20.19 || ^22.12 || >=24.0" }, "peerDependencies": { - "typescript": ">=5.1.0" + "better-sqlite3": ">=9.0.0", + "typescript": ">=5.4.0" }, "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, "typescript": { "optional": true } @@ -5185,6 +5551,25 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "devOptional": true, + "license": "ISC" + }, "node_modules/protobufjs": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", @@ -5287,6 +5672,31 @@ "destr": "^2.0.3" } }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "devOptional": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -5324,6 +5734,23 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/regexp-to-ast": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/remeda": { + "version": "2.33.4", + "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.33.4.tgz", + "integrity": "sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/remeda" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -5357,6 +5784,16 @@ "node": ">=8" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", @@ -5425,6 +5862,14 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "devOptional": true, + "license": "MIT", + "peer": true + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -5477,6 +5922,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==", + "devOptional": true + }, "node_modules/serve-static": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", @@ -5500,7 +5951,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -5513,7 +5964,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -5595,7 +6046,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, + "devOptional": true, "license": "ISC", "engines": { "node": ">=14" @@ -5675,6 +6126,16 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -5696,6 +6157,13 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -5981,11 +6449,14 @@ } }, "node_modules/tinyexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", - "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", "devOptional": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/tmpl": { "version": "1.0.5", @@ -6174,6 +6645,21 @@ "node": ">=10.12.0" } }, + "node_modules/valibot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", + "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -6196,7 +6682,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -6502,6 +6988,17 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zeptomatch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/zeptomatch/-/zeptomatch-2.1.0.tgz", + "integrity": "sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "grammex": "^3.1.11", + "graphmatch": "^1.1.0" + } } } } diff --git a/package.json b/package.json index 989093f..b4285ae 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,6 @@ }, "devDependencies": { "jest": "^30.1.3", - "prisma": "^6.16.2" + "prisma": "^7.4.1" } } diff --git a/screenshot.png b/screenshot.png index 25ee627..13d17b2 100644 Binary files a/screenshot.png and b/screenshot.png differ diff --git a/src/public/assets/css/styles.css b/src/public/assets/css/styles.css deleted file mode 100644 index 8811cad..0000000 --- a/src/public/assets/css/styles.css +++ /dev/null @@ -1,115 +0,0 @@ -/* used to prevent ui flicker before vuejs loads */ -[v-cloak] { - display: none; -} - -.icon-longfast { - background-color: #009016; - border-radius: 25px; - border: 1px solid #2C2D3C; -} - -.icon-mediumfast { - background-color: #326be7; - border-radius: 25px; - border: 1px solid #2C2D3C; -} - -.icon-shortslow { - background-color: #0077e6; - border-radius: 25px; - border: 1px solid #2C2D3C; -} - -.icon-mqtt-connected { - background-color: #2563eb; /* Change to use same color as disconnected // #16a34a; */ - border-radius: 25px; - border: 1px solid #2C2D3C; -} - -.icon-mqtt-disconnected { - background-color: #2563eb; - border-radius: 25px; - border: 1px solid #2C2D3C; -} - -.icon-offline { - background-color: #e2286c; - border-radius: 25px; - border: 1px solid #2C2D3C; -} - -.icon-position-history { - background-color: #a855f7; - border-radius: 25px; - border: 1px solid #2C2D3C; -} - -.icon-traceroute-start { - background-color: #16a34a; /* green */ - border-radius: 25px; - border: 1px solid #2C2D3C; -} - -.icon-traceroute-end { - background-color: #dc2626; /* red */ - border-radius: 25px; - border: 1px solid #2C2D3C; -} - -.waypoint-label { - font-size: 26px; - background-color: transparent; -} - -.link { - color: #2563eb; -} - -.link:hover { - text-decoration: underline; -} - -.tooltip { - position: relative; - display: inline-block; -} - -.tooltip .tooltip-text { - visibility: hidden; - width: 80px; - background-color: black; - color: #fff; - text-align: center; - padding: 4px 0; - border-radius: 6px; - position: absolute; - z-index: 10000; - top: 100%; - left: 50%; - margin-top: 8px; - margin-left: -40px; /* Use half of the width (120/2 = 60), to center the tooltip */ -} - -.tooltip .tooltip-text::after { - content: " "; - position: absolute; - bottom: 100%; /* At the top of the tooltip */ - left: 50%; - margin-left: -5px; - border-width: 5px; - border-style: solid; - border-color: transparent transparent black transparent; -} - -.tooltip:hover .tooltip-text { - visibility: visible; -} - -.z-search { - z-index: 1001; -} - -.z-sidebar { - z-index: 1002; -} \ No newline at end of file diff --git a/src/public/assets/js/app.js b/src/public/assets/js/app.js deleted file mode 100644 index 86b4542..0000000 --- a/src/public/assets/js/app.js +++ /dev/null @@ -1,956 +0,0 @@ -Vue.createApp({ - data() { - return { - - isShowingAnnouncement: this.shouldShowAnnouncement(), - - configNodesMaxAgeInSeconds: window.getConfigNodesMaxAgeInSeconds(), - configNodesOfflineAgeInSeconds: window.getConfigNodesOfflineAgeInSeconds(), - configWaypointsMaxAgeInSeconds: window.getConfigWaypointsMaxAgeInSeconds(), - configConnectionsMaxDistanceInMeters: window.getConfigConnectionsMaxDistanceInMeters(), - configZoomLevelGoToNode: window.getConfigZoomLevelGoToNode(), - configAutoUpdatePositionInUrl: window.getConfigAutoUpdatePositionInUrl(), - configEnableMapAnimations: window.getConfigEnableMapAnimations(), - configTemperatureFormat: window.getConfigTemperatureFormat(), - configConnectionsTimePeriodInSeconds: window.getConfigConnectionsTimePeriodInSeconds(), - configConnectionsColoredLines: window.getConfigConnectionsColoredLines(), - configConnectionsBidirectionalOnly: window.getConfigConnectionsBidirectionalOnly(), - configConnectionsMinSnrDb: window.getConfigConnectionsMinSnrDb(), - configConnectionsBidirectionalMinSnr: window.getConfigConnectionsBidirectionalMinSnr(), - - isShowingHardwareModels: false, - hardwareModelStats: null, - - isShowingInfoModal: this.shouldShowInfoModal(), - isShowingMobileSearch: false, - isShowingSettings: false, - - nodes: [], - searchText: "", - - selectedNode: null, - selectedNodeDeviceMetrics: [], - selectedNodeEnvironmentMetrics: [], - selectedNodePowerMetrics: [], - selectedNodeMqttMetrics: [], - selectedNodeTraceroutes: [], - - deviceMetricsTimeRange: "7d", - environmentMetricsTimeRange: "7d", - powerMetricsTimeRange: "7d", - - isPositionHistoryModalExpanded: true, - positionHistoryDateTimeFrom: null, - positionHistoryDateTimeTo: null, - selectedNodePositionHistory: [], - selectedNodeToShowPositionHistory: null, - selectedNodePositionHistoryMarkers: [], - selectedNodePositionHistoryPolyLines: [], - - selectedTraceRoute: null, - tracerouteEdges: [], - - selectedNodeToShowConnections: null, - - moment: window.moment, - - }; - }, - mounted: function() { - - // load data - this.loadHardwareModelStats(); - - // handle map click callback from outside of vue - window._onMapClick = () => { - this.searchText = ""; - this.isShowingMobileSearch = false; - }; - - // handle node callback from outside of vue - window._onNodeClick = (node) => { - this.selectedNode = node; - this.loadNodeDeviceMetrics(node.node_id); - this.loadNodeEnvironmentMetrics(node.node_id); - this.loadNodePowerMetrics(node.node_id); - this.loadNodeMqttMetrics(node.node_id); - this.loadNodeTraceroutes(node.node_id); - //this.loadNodePositionHistory(node.node_id); - }; - - // handle node callback from outside of vue - window._onShowNodeConnectionsClick = (node) => { - this.selectedNodeToShowConnections = node; - }; - - // handle nodes updated callback from outside of vue - window._onNodesUpdated = (nodes) => { - this.nodes = nodes; - }; - - }, - methods: { - getAnnouncementId: function() { - // change this when making a new announcement - return "1"; - }, - shouldShowAnnouncement: function() { - const lastSeenAnnouncementId = window.localStorage.getItem("last-seen-announcement-id"); - return lastSeenAnnouncementId?.toString() !== this.getAnnouncementId(); - }, - dismissAnnouncement: function() { - window.localStorage.setItem("last-seen-announcement-id", this.getAnnouncementId()); - this.isShowingAnnouncement = false; - }, - shouldShowInfoModal: function() { - return !window.getConfigHasSeenInfoModal() - && !window.isMobile(); - }, - loadHardwareModelStats: function() { - window.axios.get('/api/v1/stats/hardware-models').then((response) => { - this.hardwareModelStats = response.data.hardware_model_stats; - }).catch((error) => { - // do nothing - }); - }, - loadNodeDeviceMetrics: function(nodeId) { - - // calculate unix timestamps in milliseconds for supported time ranges - const oneDayAgoInMilliseconds = new Date().getTime() - (86400 * 1000); - const threeDaysAgoInMilliseconds = new Date().getTime() - (259200 * 1000); - const sevenDaysAgoInMilliseconds = new Date().getTime() - (604800 * 1000); - const thirtyDaysAgoInMilliseconds = new Date().getTime() - (259200 * 1000 * 10); - - // determine how long back to load device metrics from - var timeFrom = threeDaysAgoInMilliseconds; - switch(this.deviceMetricsTimeRange){ - case "1d": { - timeFrom = oneDayAgoInMilliseconds; - break; - } - case "3d": { - timeFrom = threeDaysAgoInMilliseconds; - break; - } - case "7d": { - timeFrom = sevenDaysAgoInMilliseconds; - break; - } - case "30d": { - timeFrom = thirtyDaysAgoInMilliseconds; - break; - } - } - - window.axios.get(`/api/v1/nodes/${nodeId}/device-metrics`, { - params: { - time_from: timeFrom, - }, - }).then((response) => { - // reverse response, as it's newest to oldest, but we want oldest to newest - this.selectedNodeDeviceMetrics = response.data.device_metrics.reverse(); - this.renderDeviceMetricCharts(); - }).catch(() => { - this.selectedNodeDeviceMetrics = []; - this.renderDeviceMetricCharts(); - }); - }, - loadNodeEnvironmentMetrics: function(nodeId) { - - // calculate unix timestamps in milliseconds for supported time ranges - const oneDayAgoInMilliseconds = new Date().getTime() - (86400 * 1000); - const threeDaysAgoInMilliseconds = new Date().getTime() - (259200 * 1000); - const sevenDaysAgoInMilliseconds = new Date().getTime() - (604800 * 1000); - const thirtyDaysAgoInMilliseconds = new Date().getTime() - (259200 * 1000 * 10); - - // determine how long back to load environment metrics from - var timeFrom = threeDaysAgoInMilliseconds; - switch(this.environmentMetricsTimeRange){ - case "1d": { - timeFrom = oneDayAgoInMilliseconds; - break; - } - case "3d": { - timeFrom = threeDaysAgoInMilliseconds; - break; - } - case "7d": { - timeFrom = sevenDaysAgoInMilliseconds; - break; - } - case "30d": { - timeFrom = thirtyDaysAgoInMilliseconds; - break; - } - } - - window.axios.get(`/api/v1/nodes/${nodeId}/environment-metrics`, { - params: { - time_from: timeFrom, - }, - }).then((response) => { - // reverse response, as it's newest to oldest, but we want oldest to newest - this.selectedNodeEnvironmentMetrics = response.data.environment_metrics.reverse(); - this.renderEnvironmentMetricCharts(); - }).catch(() => { - this.selectedNodeEnvironmentMetrics = []; - this.renderEnvironmentMetricCharts(); - }); - }, - loadNodePowerMetrics: function(nodeId) { - - // calculate unix timestamps in milliseconds for supported time ranges - const oneDayAgoInMilliseconds = new Date().getTime() - (86400 * 1000); - const threeDaysAgoInMilliseconds = new Date().getTime() - (259200 * 1000); - const sevenDaysAgoInMilliseconds = new Date().getTime() - (604800 * 1000); - const thirtyDaysAgoInMilliseconds = new Date().getTime() - (259200 * 1000 * 10); - - // determine how long back to load power metrics from - var timeFrom = threeDaysAgoInMilliseconds; - switch(this.powerMetricsTimeRange){ - case "1d": { - timeFrom = oneDayAgoInMilliseconds; - break; - } - case "3d": { - timeFrom = threeDaysAgoInMilliseconds; - break; - } - case "7d": { - timeFrom = sevenDaysAgoInMilliseconds; - break; - } - case "30d": { - timeFrom = thirtyDaysAgoInMilliseconds; - break; - } - } - - window.axios.get(`/api/v1/nodes/${nodeId}/power-metrics`, { - params: { - time_from: timeFrom, - }, - }).then((response) => { - // reverse response, as it's newest to oldest, but we want oldest to newest - this.selectedNodePowerMetrics = response.data.power_metrics.reverse(); - this.renderPowerMetricCharts(); - }).catch(() => { - this.selectedNodePowerMetrics = []; - this.renderPowerMetricCharts(); - }); - }, - loadNodeMqttMetrics: function(nodeId) { - this.selectedNodeMqttMetrics = []; - window.axios.get(`/api/v1/nodes/${nodeId}/mqtt-metrics`).then((response) => { - this.selectedNodeMqttMetrics = response.data.mqtt_metrics; - }).catch(() => { - // do nothing - }); - }, - loadNodeTraceroutes: function(nodeId) { - this.selectedNodeTraceroutes = []; - window.axios.get(`/api/v1/nodes/${nodeId}/traceroutes`, { - params: { - count: 5, - }, - }).then((response) => { - this.selectedNodeTraceroutes = response.data.traceroutes; - }).catch(() => { - // do nothing - }); - }, - loadNodePositionHistory: function(nodeId) { - this.selectedNodePositionHistory = []; - window.axios.get(`/api/v1/nodes/${nodeId}/position-history`, { - params: { - // parse from datetime-local format, and send as unix timestamp in milliseconds - time_from: moment(this.positionHistoryDateTimeFrom, "YYYY-MM-DDTHH:mm").format("x"), - time_to: moment(this.positionHistoryDateTimeTo, "YYYY-MM-DDTHH:mm").format("x"), - }, - }).then((response) => { - this.selectedNodePositionHistory = response.data.position_history; - if(this.selectedNodeToShowPositionHistory != null){ - clearAllPositionHistory(); - onPositionHistoryUpdated(response.data.position_history); - } - - }).catch(() => { - // do nothing - }); - }, - renderDeviceMetricCharts: function() { - try { - this.updateDeviceMetricsChart(); - } catch(e) { - console.log(e); - } - }, - updateDeviceMetricsChart: function() { - - // destroy existing chart - const chartElementId = "deviceMetricsChart"; - const existingChart = window.Chart.getChart(chartElementId); - if(existingChart != null){ - existingChart.destroy(); - } - - // get chart element - const chartElement = window.document.getElementById(chartElementId); - if(!chartElement){ - return; - } - - // create chart data - const labels = []; - const batteryMetrics = []; - const channelUtilizationMetrics = []; - const airUtilTxMetrics = []; - for(const deviceMetric of this.selectedNodeDeviceMetrics){ - labels.push(moment(deviceMetric.created_at)); - batteryMetrics.push(deviceMetric.battery_level); - channelUtilizationMetrics.push(deviceMetric.channel_utilization); - airUtilTxMetrics.push(deviceMetric.air_util_tx); - } - - // create chart - new window.Chart(chartElement, { - type: 'line', - data: { - labels: labels, - datasets: [ - { - label: 'Battery Level', - borderColor: '#3b82f6', - backgroundColor: '#3b82f6', - pointStyle: false, // no points - fill: false, - data: batteryMetrics, - }, - { - label: 'Channel Util', - borderColor: '#22c55e', - backgroundColor: '#22c55e', - showLine: false, // no lines between points - fill: false, - data: channelUtilizationMetrics, - }, - { - label: 'Air Util TX', - borderColor: '#f97316', - backgroundColor: '#f97316', - showLine: false, // no lines between points - fill: false, - data: airUtilTxMetrics, - - }, - ], - }, - options: { - responsive: true, - borderWidth: 2, - elements: { - point: { - radius: 2, - }, - }, - scales: { - x: { - position: 'top', - type: 'time', - time: { - unit: 'day', - displayFormats: { - day: 'MMM DD', // Jan 01 - }, - }, - }, - y: { - min: 0, - max: 101, // 101 is "Plugged In", need to include for tooltip to work - ticks: { - callback: (label) => `${label}%`, - }, - }, - }, - plugins: { - legend: { - display: false, - }, - tooltip: { - mode: "index", - intersect: false, - callbacks: { - label: (item) => { - return `${item.dataset.label}: ${item.formattedValue}%`; - }, - }, - }, - }, - } - }); - - }, - renderEnvironmentMetricCharts: function() { - try { - this.updateEnvironmentMetricsChart(); - } catch(e) { - console.log(e); - } - }, - updateEnvironmentMetricsChart: function() { - - // destroy existing chart - const chartElementId = "environmentMetricsChart"; - const existingChart = window.Chart.getChart(chartElementId); - if(existingChart != null){ - existingChart.destroy(); - } - - // get chart element - const chartElement = window.document.getElementById(chartElementId); - if(!chartElement){ - return; - } - - // create chart data - const labels = []; - const temperatureMetrics = []; - const relativeHumidityMetrics = []; - const barometricPressureMetrics = []; - const iaqMetrics = []; - for(const deviceMetric of this.selectedNodeEnvironmentMetrics){ - labels.push(moment(deviceMetric.created_at)); - temperatureMetrics.push(deviceMetric.temperature); - relativeHumidityMetrics.push(deviceMetric.relative_humidity); - barometricPressureMetrics.push(deviceMetric.barometric_pressure); - iaqMetrics.push(deviceMetric.iaq); - } - - // create chart - new window.Chart(chartElement, { - type: 'line', - data: { - labels: labels, - datasets: [ - { - label: 'Temperature', - suffix: '°C', - borderColor: '#3b82f6', - backgroundColor: '#3b82f6', - pointStyle: false, // no points - fill: false, - data: temperatureMetrics, - yAxisID: 'y', - }, - { - label: 'Humidity', - suffix: '%', - borderColor: '#22c55e', - backgroundColor: '#22c55e', - pointStyle: false, // no points - fill: false, - data: relativeHumidityMetrics, - yAxisID: 'y', - }, - { - label: 'Pressure', - suffix: 'hPa', - borderColor: '#f97316', - backgroundColor: '#f97316', - pointStyle: false, // no points - fill: false, - data: barometricPressureMetrics, - yAxisID: 'y1', - - }, - { - label: 'IAQ', - suffix: 'IAQ', - borderColor: '#f472b6', - backgroundColor: '#f472b6', - pointStyle: false, // no points - fill: false, - data: iaqMetrics, - yAxisID: 'yIAQ', - - }, - ], - }, - options: { - responsive: true, - borderWidth: 2, - spanGaps: 1000 * 60 * 60 * 24, // only show lines between metrics with a 24 hour or less gap - elements: { - point: { - radius: 2, - }, - }, - scales: { - x: { - position: 'top', - type: 'time', - time: { - unit: 'day', - displayFormats: { - day: 'MMM DD', // Jan 01 - }, - }, - }, - y: { - min: -20, - max: 100, - }, - y1: { - min: 800, - max: 1100, - ticks: { - stepSize: 10, - callback: (label) => `${label} hPa`, - }, - position: 'right', - grid: { - drawOnChartArea: false, // only want the grid lines for one axis to show up - }, - }, - yIAQ: { - type: 'linear', - display: false, - }, - }, - plugins: { - legend: { - display: false, - }, - tooltip: { - mode: "index", - intersect: false, - callbacks: { - label: (item) => { - return `${item.dataset.label}: ${item.formattedValue}${item.dataset.suffix}`; - }, - }, - }, - }, - } - }); - - }, - renderPowerMetricCharts: function() { - try { - this.updatePowerMetricsChart(); - } catch(e) { - console.log(e); - } - }, - updatePowerMetricsChart: function() { - - // destroy existing chart - const chartElementId = "powerMetricsChart"; - const existingChart = window.Chart.getChart(chartElementId); - if(existingChart != null){ - existingChart.destroy(); - } - - // get chart element - const chartElement = window.document.getElementById(chartElementId); - if(!chartElement){ - return; - } - - // create chart data - const labels = []; - const channel1VoltageReadings = []; - const channel2VoltageReadings = []; - const channel3VoltageReadings = []; - const channel1CurrentReadings = []; - const channel2CurrentReadings = []; - const channel3CurrentReadings = []; - for(const powerMetric of this.selectedNodePowerMetrics){ - labels.push(moment(powerMetric.created_at)); - channel1VoltageReadings.push(powerMetric.ch1_voltage); - channel2VoltageReadings.push(powerMetric.ch2_voltage); - channel3VoltageReadings.push(powerMetric.ch3_voltage); - channel1CurrentReadings.push(powerMetric.ch1_current); - channel2CurrentReadings.push(powerMetric.ch2_current); - channel3CurrentReadings.push(powerMetric.ch3_current); - } - - // create chart - new window.Chart(chartElement, { - type: 'line', - data: { - labels: labels, - datasets: [ - { - label: 'Ch1 Voltage', - suffix: "V", - borderColor: '#3b82f6', - backgroundColor: '#3b82f6', - pointStyle: false, // no points - fill: false, - data: channel1VoltageReadings, - yAxisID: 'y', - }, - { - label: 'Ch2 Voltage', - suffix: "V", - borderColor: '#22c55e', - backgroundColor: '#22c55e', - pointStyle: false, // no points - fill: false, - data: channel2VoltageReadings, - yAxisID: 'y', - }, - { - label: 'Ch3 Voltage', - suffix: "V", - borderColor: '#f97316', - backgroundColor: '#f97316', - pointStyle: false, // no points - fill: false, - data: channel3VoltageReadings, - yAxisID: 'y', - }, - { - label: 'Ch1 Current', - suffix: "mA", - borderColor: '#93c5fd', - backgroundColor: '#93c5fd', - pointStyle: false, // no points - fill: false, - data: channel1CurrentReadings, - yAxisID: 'y1', - }, - { - label: 'Ch2 Current', - suffix: "mA", - borderColor: '#86efac', - backgroundColor: '#86efac', - pointStyle: false, // no points - fill: false, - data: channel2CurrentReadings, - yAxisID: 'y1', - }, - { - label: 'Ch3 Current', - suffix: "mA", - borderColor: '#fdba74', - backgroundColor: '#fdba74', - pointStyle: false, // no points - fill: false, - data: channel3CurrentReadings, - yAxisID: 'y1', - }, - ], - }, - options: { - responsive: true, - borderWidth: 2, - spanGaps: 1000 * 60 * 60 * 3, // only show lines between metrics with a 3 hour or less gap - elements: { - point: { - radius: 2, - }, - }, - scales: { - x: { - position: 'top', - type: 'time', - time: { - unit: 'day', - displayFormats: { - day: 'MMM DD', // Jan 01 - }, - }, - }, - y: { - min: 0, - suggestedMax: 6, - ticks: { - callback: (label) => `${label}V`, - }, - }, - y1: { - suggestedMin: -50, - suggestedMax: 50, - ticks: { - stepSize: 50, - callback: (label) => `${label}mA`, - }, - position: 'right', - grid: { - drawOnChartArea: false, // only want the grid lines for one axis to show up - }, - }, - }, - plugins: { - legend: { - display: false, - }, - tooltip: { - mode: "index", - intersect: false, - callbacks: { - label: (item) => { - return `${item.dataset.label}: ${item.formattedValue}${item.dataset.suffix}`; - }, - }, - }, - }, - } - }); - - }, - showTraceRoute: function(traceroute) { - this.selectedTraceRoute = traceroute; - }, - findNodeById: function(id) { - return window.findNodeById(id); - }, - findNodeMarkerById: function(id) { - return window.findNodeMarkerById(id); - }, - onSearchResultNodeClick: function(node) { - - // clear search - this.searchText = ""; - - // hide search - this.isShowingMobileSearch = false; - - // go to node - if(window.goToNode(node.node_id)){ - return; - } - - // fallback to showing node details since we can't go to the node - window.showNodeDetails(node.node_id); - - }, - dismissInfoModal: function() { - this.isShowingInfoModal = false; - window.setConfigHasSeenInfoModal(true); - }, - getRegionFrequencyRange: function(regionName) { - return window.getRegionFrequencyRange(regionName); - }, - showNodePositionHistory: function(nodeId) { - - // find node - const node = findNodeById(nodeId); - if(!node){ - return; - } - - // update ui - this.selectedNode = null; - this.selectedNodeToShowPositionHistory = node; - this.isPositionHistoryModalExpanded = true; - - // close node info tooltip as position history shows under it - window.closeAllTooltips(); - - // reset default time range when opening position history ui - // YYYY-MM-DDTHH:mm is the format expected by the datetime-local input type - this.positionHistoryDateTimeFrom = moment().subtract(1, "hours").format('YYYY-MM-DDTHH:mm'); - this.positionHistoryDateTimeTo = moment().format('YYYY-MM-DDTHH:mm'); - - // load position history - this.loadNodePositionHistory(nodeId); - - }, - onPositionHistoryQuickRangeClick: function(range) { - - // update position history time range - switch(range){ - case "1h": { - this.positionHistoryDateTimeFrom = moment().subtract(1, "hours").format('YYYY-MM-DDTHH:mm'); - this.positionHistoryDateTimeTo = moment().format('YYYY-MM-DDTHH:mm'); - break; - } - case "24h": { - this.positionHistoryDateTimeFrom = moment().subtract(24, "hours").format('YYYY-MM-DDTHH:mm'); - this.positionHistoryDateTimeTo = moment().format('YYYY-MM-DDTHH:mm'); - break; - } - case "7d": { - this.positionHistoryDateTimeFrom = moment().subtract(7, "days").format('YYYY-MM-DDTHH:mm'); - this.positionHistoryDateTimeTo = moment().format('YYYY-MM-DDTHH:mm'); - break; - } - } - - // reload position history - const node = this.selectedNodeToShowPositionHistory; - if(node){ - this.loadNodePositionHistory(node.node_id); - } - - }, - getShareLinkForNode: function(nodeId) { - return window.location.origin + `/?node_id=${nodeId}`; - }, - copyShareLinkForNode: function(nodeId) { - - // make sure copy to clipboard is supported - if(!navigator.clipboard || !navigator.clipboard.writeText){ - alert("Clipboard not supported. Site must be served via https on iOS."); - return; - } - - // copy share link to clipboard - const url = this.getShareLinkForNode(nodeId); - navigator.clipboard.writeText(url); - - // tell user we copied it - alert("Link copied to clipboard!"); - - }, - dismissShowingNodeConnections: function() { - window._onHideNodeConnectionsClick(); - this.selectedNodeToShowConnections = null; - }, - dismissShowingNodePositionHistory: function() { - this.selectedNodePositionHistory = []; - this.selectedNodeToShowPositionHistory = null; - this.selectedNodePositionHistoryMarkers = []; - this.selectedNodePositionHistoryPolyLines = []; - cleanUpPositionHistory(); - }, - formatUptimeSeconds: function(secondsToFormat) { - secondsToFormat = Number(secondsToFormat); - var days = Math.floor(secondsToFormat / (3600 * 24)); - var hours = Math.floor((secondsToFormat % (3600 * 24)) / 3600); - var minutes = Math.floor((secondsToFormat % 3600) / 60); - var seconds = Math.floor(secondsToFormat % 60); - var daysPlural = days === 1 ? 'day' : 'days'; - return `${days} ${daysPlural} ${hours}h ${minutes}m ${seconds}s`; - }, - formatTemperature: function(celsius) { - switch(this.configTemperatureFormat){ - case "celsius": { - return `${Number(celsius).toFixed(0)}°C`; - } - case "fahrenheit": { - const fahrenheit = this.celsiusToFahrenheit(celsius); - return `${fahrenheit.toFixed(0)}°F`; - } - } - }, - convertTemperature: function(celsius) { - switch(this.configTemperatureFormat){ - case "celsius": { - return celsius; - } - case "fahrenheit": { - return this.celsiusToFahrenheit(celsius); - } - } - }, - getTemperatureUnit: function() { - switch(this.configTemperatureFormat){ - case "celsius": return "°C"; - case "fahrenheit": return "°F"; - } - }, - celsiusToFahrenheit: function(celsius) { - return (celsius * 9/5) + 32; - }, - getNodeColour(nodeId) { - // convert node id to a hex colour - return "#" + (nodeId & 0x00FFFFFF).toString(16).padStart(6, '0'); - }, - getNodeTextColour(nodeId) { - - // extract rgb components - const r = (nodeId & 0xFF0000) >> 16; - const g = (nodeId & 0x00FF00) >> 8; - const b = nodeId & 0x0000FF; - - // calculate brightness - const brightness = ((r * 0.299) + (g * 0.587) + (b * 0.114)) / 255; - - // determine text color based on brightness - return brightness > 0.5 ? "#000000" : "#FFFFFF"; - - }, - }, - computed: { - searchedNodes() { - - // search nodes - const nodes = this.nodes.filter((node) => { - const matchesId = node.node_id?.toLowerCase()?.includes(this.searchText.toLowerCase()); - const matchesHexId = node.node_id_hex?.toLowerCase()?.includes(this.searchText.toLowerCase()); - const matchesLongName = node.long_name?.toLowerCase()?.includes(this.searchText.toLowerCase()); - const matchesShortName = node.short_name?.toLowerCase()?.includes(this.searchText.toLowerCase()); - return matchesId || matchesHexId || matchesLongName || matchesShortName; - }); - - // order alphabetically by long name - nodes.sort((nodeA, nodeB) => { - const nodeALongName = nodeA.long_name || ""; - const nodeBLongName = nodeB.long_name || ""; - return nodeALongName.localeCompare(nodeBLongName); - }); - - // only return the first 500 results to avoid ui lag... - return nodes.slice(0, 500); - - }, - selectedNodeLatestPowerMetric() { - const [ latestPowerMetric ] = this.selectedNodePowerMetrics.slice(-1); - return latestPowerMetric; - }, - }, - watch: { - configNodesMaxAgeInSeconds() { - window.setConfigNodesMaxAgeInSeconds(this.configNodesMaxAgeInSeconds); - }, - configNodesOfflineAgeInSeconds() { - window.setConfigNodesOfflineAgeInSeconds(this.configNodesOfflineAgeInSeconds); - }, - configWaypointsMaxAgeInSeconds() { - window.setConfigWaypointsMaxAgeInSeconds(this.configWaypointsMaxAgeInSeconds); - }, - configConnectionsMaxDistanceInMeters() { - window.setConfigConnectionsMaxDistanceInMeters(this.configConnectionsMaxDistanceInMeters); - }, - configZoomLevelGoToNode() { - window.setConfigZoomLevelGoToNode(this.configZoomLevelGoToNode); - }, - configAutoUpdatePositionInUrl() { - window.setConfigAutoUpdatePositionInUrl(this.configAutoUpdatePositionInUrl); - }, - configEnableMapAnimations() { - window.setConfigEnableMapAnimations(this.configEnableMapAnimations); - }, - configTemperatureFormat() { - window.setConfigTemperatureFormat(this.configTemperatureFormat); - }, - configConnectionsTimePeriodInSeconds() { - window.setConfigConnectionsTimePeriodInSeconds(this.configConnectionsTimePeriodInSeconds); - }, - configConnectionsColoredLines() { - window.setConfigConnectionsColoredLines(this.configConnectionsColoredLines); - }, - configConnectionsBidirectionalOnly() { - window.setConfigConnectionsBidirectionalOnly(this.configConnectionsBidirectionalOnly); - }, - configConnectionsMinSnrDb() { - window.setConfigConnectionsMinSnrDb(this.configConnectionsMinSnrDb); - }, - configConnectionsBidirectionalMinSnr() { - window.setConfigConnectionsBidirectionalMinSnr(this.configConnectionsBidirectionalMinSnr); - }, - deviceMetricsTimeRange() { - this.loadNodeDeviceMetrics(this.selectedNode.node_id); - }, - environmentMetricsTimeRange() { - this.loadNodeEnvironmentMetrics(this.selectedNode.node_id); - }, - powerMetricsTimeRange() { - this.loadNodePowerMetrics(this.selectedNode.node_id); - }, - }, -}).mount('#app'); \ No newline at end of file diff --git a/src/public/assets/js/config.js b/src/public/assets/js/config.js deleted file mode 100644 index 5866a4c..0000000 --- a/src/public/assets/js/config.js +++ /dev/null @@ -1,199 +0,0 @@ -function getConfigHasSeenInfoModal() { - return localStorage.getItem("config_has_seen_info_modal") === "true"; -} - -function setConfigHasSeenInfoModal(value) { - return localStorage.setItem("config_has_seen_info_modal", value); -} - -function getConfigAutoUpdatePositionInUrl() { - // use user preference, or enable by default - const value = localStorage.getItem("config_auto_update_position_in_url"); - return value === "true" || value == null; -} - -function setConfigAutoUpdatePositionInUrl(value) { - return localStorage.setItem("config_auto_update_position_in_url", value); -} - -function getConfigEnableMapAnimations() { - - const value = localStorage.getItem("config_enable_map_animations"); - - // enable animations by default - if(value === null){ - return true; - } - - return value === "true"; - -} - -function setConfigEnableMapAnimations(value) { - return localStorage.setItem("config_enable_map_animations", value); -} - -function getConfigTemperatureFormat() { - return localStorage.getItem("config_temperature_format") || "celsius"; -} - -function setConfigTemperatureFormat(format) { - return localStorage.setItem("config_temperature_format", format); -} - -function getConfigMapSelectedTileLayer() { - return localStorage.getItem("config_map_selected_tile_layer") || "Thunderforest Neighbourhood"; -} - -function setConfigMapSelectedTileLayer(layer) { - return localStorage.setItem("config_map_selected_tile_layer", layer); -} - -function getConfigMapEnabledOverlayLayers() { - - try { - const value = localStorage.getItem("config_map_enabled_overlay_layers"); - if(value){ - return JSON.parse(value); - } - } catch(e) {} - - // overlays enabled by default - return ["Legend", "Position History", "Traceroutes"]; - -} - -function setConfigMapEnabledOverlayLayers(layers) { - return localStorage.setItem("config_map_enabled_overlay_layers", JSON.stringify(layers)); -} - -function getConfigNodesMaxAgeInSeconds() { - const value = localStorage.getItem("config_nodes_max_age_in_seconds"); - return value != null ? parseInt(value) : null; -} - -function setConfigNodesMaxAgeInSeconds(value) { - if(value != null){ - return localStorage.setItem("config_nodes_max_age_in_seconds", value); - } else { - return localStorage.removeItem("config_nodes_max_age_in_seconds"); - } -} - -function getConfigNodesOfflineAgeInSeconds() { - const value = localStorage.getItem("config_nodes_offline_age_in_seconds"); - return value != null ? parseInt(value) : 10800; -} - -function setConfigNodesOfflineAgeInSeconds(value) { - if(value != null){ - return localStorage.setItem("config_nodes_offline_age_in_seconds", value); - } else { - return localStorage.removeItem("config_nodes_offline_age_in_seconds"); - } -} - -function getConfigWaypointsMaxAgeInSeconds() { - const value = localStorage.getItem("config_waypoints_max_age_in_seconds"); - return value != null ? parseInt(value) : null; -} - -function setConfigWaypointsMaxAgeInSeconds(value) { - if(value != null){ - return localStorage.setItem("config_waypoints_max_age_in_seconds", value); - } else { - return localStorage.removeItem("config_waypoints_max_age_in_seconds"); - } -} - -function getConfigConnectionsMaxDistanceInMeters() { - const value = localStorage.getItem("config_connections_max_distance_in_meters"); - // default to 70km (70,000 meters) - return value != null ? parseInt(value) : 70000; -} - -function setConfigConnectionsMaxDistanceInMeters(value) { - return localStorage.setItem("config_connections_max_distance_in_meters", value); -} - -function getConfigZoomLevelGoToNode() { - const value = localStorage.getItem("config_zoom_level_go_to_node"); - const parsedValue = value != null ? parseInt(value) : null; - return parsedValue || 15; -} - -function setConfigZoomLevelGoToNode(value) { - return localStorage.setItem("config_zoom_level_go_to_node", value); -} - -function getConfigConnectionsTimePeriodInSeconds() { - const value = localStorage.getItem("config_connections_time_period_in_seconds"); - // default to 7 days if unset - return value != null ? parseInt(value) : 604800; -} - -function setConfigConnectionsTimePeriodInSeconds(value) { - return localStorage.setItem("config_connections_time_period_in_seconds", value); -} - -function getConfigConnectionsColoredLines() { - const value = localStorage.getItem("config_connections_colored_lines"); - // disable colored lines by default - if(value === null){ - return false; - } - return value === "true"; -} - -function setConfigConnectionsColoredLines(value) { - return localStorage.setItem("config_connections_colored_lines", value); -} - -function getConfigConnectionsBidirectionalOnly() { - const value = localStorage.getItem("config_connections_bidirectional_only"); - // disable bidirectional filter by default - if(value === null){ - return false; - } - return value === "true"; -} - -function setConfigConnectionsBidirectionalOnly(value) { - return localStorage.setItem("config_connections_bidirectional_only", value); -} - -function getConfigConnectionsMinSnrDb() { - const value = localStorage.getItem("config_connections_min_snr_db"); - // default to null (unset) - if(value === null || value === ""){ - return null; - } - const parsed = parseFloat(value); - return isNaN(parsed) ? null : parsed; -} - -function setConfigConnectionsMinSnrDb(value) { - if(value === null || value === "" || value === undefined){ - return localStorage.removeItem("config_connections_min_snr_db"); - } - // Convert to string for localStorage (handles both number and string inputs) - const stringValue = typeof value === "number" ? value.toString() : String(value); - return localStorage.setItem("config_connections_min_snr_db", stringValue); -} - -function getConfigConnectionsBidirectionalMinSnr() { - const value = localStorage.getItem("config_connections_bidirectional_min_snr"); - // disable bidirectional minimum SNR by default - if(value === null){ - return false; - } - return value === "true"; -} - -function setConfigConnectionsBidirectionalMinSnr(value) { - return localStorage.setItem("config_connections_bidirectional_min_snr", value); -} - -function isMobile() { - return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); -} \ No newline at end of file diff --git a/src/public/assets/js/map.js b/src/public/assets/js/map.js deleted file mode 100644 index e25c067..0000000 --- a/src/public/assets/js/map.js +++ /dev/null @@ -1,1692 +0,0 @@ -// global state -var nodes = []; -var nodeMarkers = {}; -var selectedNodeOutlineCircle = null; -var waypoints = []; - -// set map bounds to be a little more than full size to prevent panning off screen -var bounds = [ - [-100, 70], // top left - [100, 500], // bottom right -]; - -// create map positioned over NRW -if(!isMobile()){ - var map = L.map('map', { - maxBounds: bounds, - }).setView([ - 51.1, - 366.82, - ], 9); -} else { - var map = L.map('map', { - maxBounds: bounds, - }).setView([ - 51.1, - 366.82, - ], 8); -} - -// remove leaflet link -map.attributionControl.setPrefix(''); - -var openThunderforestLandscapeMapTileLayer = L.tileLayer('https://tiles.nixware.dev/landscape/{z}/{x}/{y}.png', { - maxZoom: 22, - attribution: 'Tiles © Gravitystorm Limited | Data from Meshtastic', -}); - -var openThunderforestAtlasMapTileLayer = L.tileLayer('https://tiles.nixware.dev/atlas/{z}/{x}/{y}.png', { - maxZoom: 22, - attribution: 'Tiles © Gravitystorm Limited | Data from Meshtastic', -}); - -var openThunderforestNeighbourhoodMapTileLayer = L.tileLayer('https://tiles.nixware.dev/neighbourhood/{z}/{x}/{y}.png', { - maxZoom: 22, - attribution: 'Tiles © Gravitystorm Limited | Data from Meshtastic', -}); - -var openStreetMapTileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { - maxZoom: 22, // increase from 18 to 22 - attribution: 'Tiles © OpenStreetMap | Data from Meshtastic', -}); - -var openTopoMapTileLayer = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', { - maxZoom: 17, // open topo map doesn't have tiles closer than this - attribution: 'Tiles © OpenStreetMap | Data from Meshtastic', -}); - -var esriWorldImageryTileLayer = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { - maxZoom: 21, // esri doesn't have tiles closer than this - attribution: 'Tiles © Esri | Data from Meshtastic' -}); - -var googleSatelliteTileLayer = L.tileLayer('https://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', { - maxZoom: 21, - subdomains: ['mt0', 'mt1', 'mt2', 'mt3'], - attribution: 'Tiles © Google | Data from Meshtastic' -}); - -var googleHybridTileLayer = L.tileLayer('https://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', { - maxZoom: 21, - subdomains: ['mt0', 'mt1', 'mt2', 'mt3'], - attribution: 'Tiles © Google | Data from Meshtastic' -}); - -var tileLayers = { - "Thunderforest Neighbourhood": openThunderforestNeighbourhoodMapTileLayer, - "Thunderforest Landscape": openThunderforestLandscapeMapTileLayer, - "Thunderforest Atlas": openThunderforestAtlasMapTileLayer, - "OpenStreetMap": openStreetMapTileLayer, - "OpenTopoMap": openTopoMapTileLayer, - "Esri Satellite": esriWorldImageryTileLayer, - "Google Satellite": googleSatelliteTileLayer, - "Google Hybrid": googleHybridTileLayer, -}; - -// use tile layer based on config -const selectedTileLayerName = getConfigMapSelectedTileLayer(); -const selectedTileLayer = tileLayers[selectedTileLayerName] || openThunderforestNeighbourhoodMapTileLayer; -selectedTileLayer.addTo(map); - -// create layer groups -var nodesLayerGroup = new L.LayerGroup(); -var backboneConnectionsLayerGroup = new L.LayerGroup(); -var nodeConnectionsLayerGroup = new L.LayerGroup(); -var nodesClusteredLayerGroup = L.markerClusterGroup({ - showCoverageOnHover: false, - disableClusteringAtZoom: 10, // zoom level where node clustering is disabled -}); -var nodesRouterLayerGroup = L.markerClusterGroup({ - showCoverageOnHover: false, - disableClusteringAtZoom: 10, // zoom level where node clustering is disabled -}); -var nodesBackboneLayerGroup = new L.LayerGroup(); -//var nodesMediumFastLayerGroup = new L.LayerGroup(); -var nodesShortSlowLayerGroup = new L.LayerGroup(); -var nodesLongFastLayerGroup = new L.LayerGroup(); -var waypointsLayerGroup = new L.LayerGroup(); -var nodePositionHistoryLayerGroup = new L.LayerGroup(); -var traceroutesLayerGroup = new L.LayerGroup(); -var connectionsLayerGroup = new L.LayerGroup(); - -// create icons -var iconMqttConnected = L.divIcon({ - className: 'icon-mqtt-connected', - iconSize: [16, 16], // increase from 12px to 16px to make hover easier -}); - -var iconLongFast = L.divIcon({ - className: 'icon-longfast', - iconSize: [16, 16], // increase from 12px to 16px to make hover easier -}); - -/*var iconMediumFast = L.divIcon({ - className: 'icon-mediumfast', - iconSize: [16, 16], // increase from 12px to 16px to make hover easier -});*/ - -var iconShortSlow = L.divIcon({ - className: 'icon-shortslow', - iconSize: [16, 16], -}); - -var iconMqttDisconnected = L.divIcon({ - className: 'icon-mqtt-disconnected', - iconSize: [16, 16], // increase from 12px to 16px to make hover easier -}); - -var iconOffline = L.divIcon({ - className: 'icon-offline', - iconSize: [16, 16], // increase from 12px to 16px to make hover easier -}); - -var iconPositionHistory = L.divIcon({ - className: 'icon-position-history', - iconSize: [16, 16], // increase from 12px to 16px to make hover easier -}); - -var iconTracerouteStart = L.divIcon({ - className: 'icon-traceroute-start', - iconSize: [16, 16], -}); - -var iconTracerouteEnd = L.divIcon({ - className: 'icon-traceroute-end', - iconSize: [16, 16], -}); - -// create legend -var legendLayerGroup = new L.LayerGroup(); -var legend = L.control({position: 'bottomleft'}); -legend.onAdd = function (map) { - var div = L.DomUtil.create('div', 'leaflet-control-layers'); - div.style.backgroundColor = 'white'; - div.style.padding = '12px'; - div.innerHTML = `
` +
- `${escapeString(node.long_name)}` +
- `