diff --git a/deno.json b/deno.json index 3c63d00..c6baf8d 100644 --- a/deno.json +++ b/deno.json @@ -6,6 +6,7 @@ "@felix/bcrypt": "jsr:@felix/bcrypt@^1.0.5", "@hono/hono": "jsr:@hono/hono@^4.8.5", "@hono/zod-validator": "jsr:@hono/zod-validator@^0.7.2", + "@oneday/http-status": "jsr:@oneday/http-status@^0.2.0", "@proxnet/undead-logging": "jsr:@proxnet/undead-logging@^1.5.0", "@std/assert": "jsr:@std/assert@1", "sharp": "npm:sharp@^0.34.3", diff --git a/deno.lock b/deno.lock index 1b436ef..6673845 100644 --- a/deno.lock +++ b/deno.lock @@ -3,23 +3,23 @@ "specifiers": { "jsr:@denosaurs/plug@^1.0.6": "1.1.0", "jsr:@felix/bcrypt@^1.0.5": "1.0.5", - "jsr:@hono/hono@^4.8.3": "4.8.5", - "jsr:@hono/hono@^4.8.5": "4.8.5", + "jsr:@hono/hono@^4.8.3": "4.9.4", + "jsr:@hono/hono@^4.8.5": "4.9.4", "jsr:@hono/zod-validator@~0.7.2": "0.7.2", - "jsr:@proxnet/undead-logging@^1.5.0": "1.5.0", - "jsr:@std/assert@1": "1.0.13", + "jsr:@oneday/http-status@0.2": "0.2.0", + "jsr:@proxnet/undead-logging@^1.5.0": "1.5.1", + "jsr:@std/assert@1": "1.0.14", "jsr:@std/encoding@1": "1.0.10", "jsr:@std/fmt@1": "1.0.8", "jsr:@std/fs@1": "1.0.19", - "jsr:@std/internal@^1.0.6": "1.0.9", - "jsr:@std/internal@^1.0.9": "1.0.9", - "jsr:@std/path@1": "1.1.1", - "jsr:@std/path@^1.1.1": "1.1.1", - "npm:@types/node@*": "22.15.15", - "npm:chalk@^5.3.0": "5.4.1", - "npm:madge@*": "8.0.0", + "jsr:@std/internal@^1.0.10": "1.0.10", + "jsr:@std/internal@^1.0.9": "1.0.10", + "jsr:@std/path@1": "1.1.2", + "jsr:@std/path@^1.1.1": "1.1.2", + "npm:@types/node@*": "24.2.0", + "npm:chalk@^5.3.0": "5.6.0", "npm:sharp@~0.34.3": "0.34.3", - "npm:zod@^4.0.5": "4.0.5" + "npm:zod@^4.0.5": "4.1.5" }, "jsr": { "@denosaurs/plug@1.1.0": { @@ -37,8 +37,8 @@ "jsr:@denosaurs/plug" ] }, - "@hono/hono@4.8.5": { - "integrity": "78f72e532f378e379915a7e1ae7bd8a171b02324bd37b70877fd35375e8c2d6b" + "@hono/hono@4.9.4": { + "integrity": "de006e32f3bab18e5a5bed4c75c5e0aaa46958d3653e6646b534a874d690dccc" }, "@hono/zod-validator@0.7.2": { "integrity": "2c055aabbd9e349b32bd0761bed1efd31b5b47f2cb42cd6eef91186b6d5cf4e1", @@ -47,16 +47,19 @@ "npm:zod" ] }, - "@proxnet/undead-logging@1.5.0": { - "integrity": "b358b3caf2dc17d91a840dfbdb85d3cf253909928810df501ac131dfbcb33c45", + "@oneday/http-status@0.2.0": { + "integrity": "3d0cc33d237ec8c8b355dbbd77e5c45c8335eec7aaffcca4b33220d829e13f42" + }, + "@proxnet/undead-logging@1.5.1": { + "integrity": "f858b6357d52c4bc1bbab279200dae86ed573ea45d945a8dfaf2f2cb23c4b649", "dependencies": [ "npm:chalk" ] }, - "@std/assert@1.0.13": { - "integrity": "ae0d31e41919b12c656c742b22522c32fb26ed0cba32975cb0de2a273cb68b29", + "@std/assert@1.0.14": { + "integrity": "68d0d4a43b365abc927f45a9b85c639ea18a9fab96ad92281e493e4ed84abaa4", "dependencies": [ - "jsr:@std/internal@^1.0.6" + "jsr:@std/internal@^1.0.10" ] }, "@std/encoding@1.0.10": { @@ -72,46 +75,19 @@ "jsr:@std/path@^1.1.1" ] }, - "@std/internal@1.0.9": { - "integrity": "bdfb97f83e4db7a13e8faab26fb1958d1b80cc64366501af78a0aee151696eb8" + "@std/internal@1.0.10": { + "integrity": "e3be62ce42cab0e177c27698e5d9800122f67b766a0bea6ca4867886cbde8cf7" }, - "@std/path@1.1.1": { - "integrity": "fe00026bd3a7e6a27f73709b83c607798be40e20c81dde655ce34052fd82ec76", + "@std/path@1.1.2": { + "integrity": "c0b13b97dfe06546d5e16bf3966b1cadf92e1cc83e56ba5476ad8b498d9e3038", "dependencies": [ - "jsr:@std/internal@^1.0.9" + "jsr:@std/internal@^1.0.10" ] } }, "npm": { - "@babel/helper-string-parser@7.27.1": { - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==" - }, - "@babel/helper-validator-identifier@7.27.1": { - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==" - }, - "@babel/parser@7.28.0": { - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", - "dependencies": [ - "@babel/types" - ], - "bin": true - }, - "@babel/types@7.28.0": { - "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==", - "dependencies": [ - "@babel/helper-string-parser", - "@babel/helper-validator-identifier" - ] - }, - "@dependents/detective-less@5.0.1": { - "integrity": "sha512-Y6+WUMsTFWE5jb20IFP4YGa5IrGY/+a/FbOSjDF/wz9gepU2hwCYSXRHP/vPwBvwcY3SVMASt4yXxbXNXigmZQ==", - "dependencies": [ - "gonzales-pe", - "node-source-walk" - ] - }, - "@emnapi/runtime@1.4.5": { - "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", + "@emnapi/runtime@1.5.0": { + "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", "dependencies": [ "tslib" ] @@ -255,198 +231,14 @@ "os": ["win32"], "cpu": ["x64"] }, - "@jridgewell/sourcemap-codec@1.5.4": { - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==" - }, - "@nodelib/fs.scandir@2.1.5": { - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": [ - "@nodelib/fs.stat", - "run-parallel" - ] - }, - "@nodelib/fs.stat@2.0.5": { - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" - }, - "@nodelib/fs.walk@1.2.8": { - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": [ - "@nodelib/fs.scandir", - "fastq" - ] - }, - "@ts-graphviz/adapter@2.0.6": { - "integrity": "sha512-kJ10lIMSWMJkLkkCG5gt927SnGZcBuG0s0HHswGzcHTgvtUe7yk5/3zTEr0bafzsodsOq5Gi6FhQeV775nC35Q==", - "dependencies": [ - "@ts-graphviz/common" - ] - }, - "@ts-graphviz/ast@2.0.7": { - "integrity": "sha512-e6+2qtNV99UT6DJSoLbHfkzfyqY84aIuoV8Xlb9+hZAjgpum8iVHprGeAMQ4rF6sKUAxrmY8rfF/vgAwoPc3gw==", - "dependencies": [ - "@ts-graphviz/common" - ] - }, - "@ts-graphviz/common@2.1.5": { - "integrity": "sha512-S6/9+T6x8j6cr/gNhp+U2olwo1n0jKj/682QVqsh7yXWV6ednHYqxFw0ZsY3LyzT0N8jaZ6jQY9YD99le3cmvg==" - }, - "@ts-graphviz/core@2.0.7": { - "integrity": "sha512-w071DSzP94YfN6XiWhOxnLpYT3uqtxJBDYdh6Jdjzt+Ce6DNspJsPQgpC7rbts/B8tEkq0LHoYuIF/O5Jh5rPg==", - "dependencies": [ - "@ts-graphviz/ast", - "@ts-graphviz/common" - ] - }, - "@types/node@22.15.15": { - "integrity": "sha512-R5muMcZob3/Jjchn5LcO8jdKwSCbzqmPB6ruBxMcf9kbxtniZHP327s6C37iOfuw8mbKK3cAQa7sEl7afLrQ8A==", + "@types/node@24.2.0": { + "integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==", "dependencies": [ "undici-types" ] }, - "@typescript-eslint/types@8.32.1": { - "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==" - }, - "@typescript-eslint/typescript-estree@8.32.1_typescript@5.8.3": { - "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", - "dependencies": [ - "@typescript-eslint/types", - "@typescript-eslint/visitor-keys", - "debug", - "fast-glob", - "is-glob", - "minimatch@9.0.5", - "semver", - "ts-api-utils", - "typescript" - ] - }, - "@typescript-eslint/visitor-keys@8.32.1": { - "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", - "dependencies": [ - "@typescript-eslint/types", - "eslint-visitor-keys" - ] - }, - "@vue/compiler-core@3.5.17": { - "integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==", - "dependencies": [ - "@babel/parser", - "@vue/shared", - "entities", - "estree-walker", - "source-map-js" - ] - }, - "@vue/compiler-dom@3.5.17": { - "integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==", - "dependencies": [ - "@vue/compiler-core", - "@vue/shared" - ] - }, - "@vue/compiler-sfc@3.5.17": { - "integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==", - "dependencies": [ - "@babel/parser", - "@vue/compiler-core", - "@vue/compiler-dom", - "@vue/compiler-ssr", - "@vue/shared", - "estree-walker", - "magic-string", - "postcss", - "source-map-js" - ] - }, - "@vue/compiler-ssr@3.5.17": { - "integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==", - "dependencies": [ - "@vue/compiler-dom", - "@vue/shared" - ] - }, - "@vue/shared@3.5.17": { - "integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==" - }, - "ansi-regex@5.0.1": { - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles@4.3.0": { - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": [ - "color-convert" - ] - }, - "any-promise@1.3.0": { - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" - }, - "app-module-path@2.2.0": { - "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==" - }, - "ast-module-types@6.0.1": { - "integrity": "sha512-WHw67kLXYbZuHTmcdbIrVArCq5wxo6NEuj3hiYAWr8mwJeC+C2mMCIBIWCiDoCye/OF/xelc+teJ1ERoWmnEIA==" - }, - "balanced-match@1.0.2": { - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base64-js@1.5.1": { - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "bl@4.1.0": { - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": [ - "buffer", - "inherits", - "readable-stream" - ] - }, - "brace-expansion@1.1.12": { - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dependencies": [ - "balanced-match", - "concat-map" - ] - }, - "brace-expansion@2.0.2": { - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dependencies": [ - "balanced-match" - ] - }, - "braces@3.0.3": { - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dependencies": [ - "fill-range" - ] - }, - "buffer@5.7.1": { - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dependencies": [ - "base64-js", - "ieee754" - ] - }, - "chalk@4.1.2": { - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": [ - "ansi-styles", - "supports-color" - ] - }, - "chalk@5.4.1": { - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==" - }, - "cli-cursor@3.1.0": { - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": [ - "restore-cursor" - ] - }, - "cli-spinners@2.9.2": { - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==" - }, - "clone@1.0.4": { - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" + "chalk@5.6.0": { + "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==" }, "color-convert@2.0.1": { "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", @@ -471,558 +263,12 @@ "color-string" ] }, - "commander@12.1.0": { - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==" - }, - "commander@7.2.0": { - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" - }, - "commondir@1.0.1": { - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" - }, - "concat-map@0.0.1": { - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "debug@4.4.1": { - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dependencies": [ - "ms" - ] - }, - "deep-extend@0.6.0": { - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "defaults@1.0.4": { - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dependencies": [ - "clone" - ] - }, - "dependency-tree@11.1.1": { - "integrity": "sha512-pnkCd8VGOq70EVaEQxDC9mZCjCwYj4yG4j8h+PEJswuWp+rdE6p8zbtVvWk+yPwaVimOjlhNi782U9K5KOU9MQ==", - "dependencies": [ - "commander@12.1.0", - "filing-cabinet", - "precinct", - "typescript" - ], - "bin": true - }, "detect-libc@2.0.4": { "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==" }, - "detective-amd@6.0.1": { - "integrity": "sha512-TtyZ3OhwUoEEIhTFoc1C9IyJIud3y+xYkSRjmvCt65+ycQuc3VcBrPRTMWoO/AnuCyOB8T5gky+xf7Igxtjd3g==", - "dependencies": [ - "ast-module-types", - "escodegen", - "get-amd-module-type", - "node-source-walk" - ], - "bin": true - }, - "detective-cjs@6.0.1": { - "integrity": "sha512-tLTQsWvd2WMcmn/60T2inEJNhJoi7a//PQ7DwRKEj1yEeiQs4mrONgsUtEJKnZmrGWBBmE0kJ1vqOG/NAxwaJw==", - "dependencies": [ - "ast-module-types", - "node-source-walk" - ] - }, - "detective-es6@5.0.1": { - "integrity": "sha512-XusTPuewnSUdoxRSx8OOI6xIA/uld/wMQwYsouvFN2LAg7HgP06NF1lHRV3x6BZxyL2Kkoih4ewcq8hcbGtwew==", - "dependencies": [ - "node-source-walk" - ] - }, - "detective-postcss@7.0.1_postcss@8.5.6": { - "integrity": "sha512-bEOVpHU9picRZux5XnwGsmCN4+8oZo7vSW0O0/Enq/TO5R2pIAP2279NsszpJR7ocnQt4WXU0+nnh/0JuK4KHQ==", - "dependencies": [ - "is-url", - "postcss", - "postcss-values-parser" - ] - }, - "detective-sass@6.0.1": { - "integrity": "sha512-jSGPO8QDy7K7pztUmGC6aiHkexBQT4GIH+mBAL9ZyBmnUIOFbkfZnO8wPRRJFP/QP83irObgsZHCoDHZ173tRw==", - "dependencies": [ - "gonzales-pe", - "node-source-walk" - ] - }, - "detective-scss@5.0.1": { - "integrity": "sha512-MAyPYRgS6DCiS6n6AoSBJXLGVOydsr9huwXORUlJ37K3YLyiN0vYHpzs3AdJOgHobBfispokoqrEon9rbmKacg==", - "dependencies": [ - "gonzales-pe", - "node-source-walk" - ] - }, - "detective-stylus@5.0.1": { - "integrity": "sha512-Dgn0bUqdGbE3oZJ+WCKf8Dmu7VWLcmRJGc6RCzBgG31DLIyai9WAoEhYRgIHpt/BCRMrnXLbGWGPQuBUrnF0TA==" - }, - "detective-typescript@14.0.0_typescript@5.8.3": { - "integrity": "sha512-pgN43/80MmWVSEi5LUuiVvO/0a9ss5V7fwVfrJ4QzAQRd3cwqU1SfWGXJFcNKUqoD5cS+uIovhw5t/0rSeC5Mw==", - "dependencies": [ - "@typescript-eslint/typescript-estree", - "ast-module-types", - "node-source-walk", - "typescript" - ] - }, - "detective-vue2@2.2.0_typescript@5.8.3": { - "integrity": "sha512-sVg/t6O2z1zna8a/UIV6xL5KUa2cMTQbdTIIvqNM0NIPswp52fe43Nwmbahzj3ww4D844u/vC2PYfiGLvD3zFA==", - "dependencies": [ - "@dependents/detective-less", - "@vue/compiler-sfc", - "detective-es6", - "detective-sass", - "detective-scss", - "detective-stylus", - "detective-typescript", - "typescript" - ] - }, - "enhanced-resolve@5.18.1": { - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", - "dependencies": [ - "graceful-fs", - "tapable" - ] - }, - "entities@4.5.0": { - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" - }, - "escodegen@2.1.0": { - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dependencies": [ - "esprima", - "estraverse", - "esutils" - ], - "optionalDependencies": [ - "source-map" - ], - "bin": true - }, - "eslint-visitor-keys@4.2.0": { - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==" - }, - "esprima@4.0.1": { - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": true - }, - "estraverse@5.3.0": { - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - }, - "estree-walker@2.0.2": { - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" - }, - "esutils@2.0.3": { - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, - "fast-glob@3.3.3": { - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dependencies": [ - "@nodelib/fs.stat", - "@nodelib/fs.walk", - "glob-parent", - "merge2", - "micromatch" - ] - }, - "fastq@1.19.1": { - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dependencies": [ - "reusify" - ] - }, - "filing-cabinet@5.0.3": { - "integrity": "sha512-PlPcMwVWg60NQkhvfoxZs4wEHjhlOO/y7OAm4sKM60o1Z9nttRY4mcdQxp/iZ+kg/Vv6Hw1OAaTbYVM9DA9pYg==", - "dependencies": [ - "app-module-path", - "commander@12.1.0", - "enhanced-resolve", - "module-definition", - "module-lookup-amd", - "resolve", - "resolve-dependency-path", - "sass-lookup", - "stylus-lookup", - "tsconfig-paths", - "typescript" - ], - "bin": true - }, - "fill-range@7.1.1": { - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dependencies": [ - "to-regex-range" - ] - }, - "fs.realpath@1.0.0": { - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "function-bind@1.1.2": { - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" - }, - "get-amd-module-type@6.0.1": { - "integrity": "sha512-MtjsmYiCXcYDDrGqtNbeIYdAl85n+5mSv2r3FbzER/YV3ZILw4HNNIw34HuV5pyl0jzs6GFYU1VHVEefhgcNHQ==", - "dependencies": [ - "ast-module-types", - "node-source-walk" - ] - }, - "get-own-enumerable-property-symbols@3.0.2": { - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" - }, - "glob-parent@5.1.2": { - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": [ - "is-glob" - ] - }, - "glob@7.2.3": { - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": [ - "fs.realpath", - "inflight", - "inherits", - "minimatch@3.1.2", - "once", - "path-is-absolute" - ], - "deprecated": true - }, - "gonzales-pe@4.3.0": { - "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==", - "dependencies": [ - "minimist" - ], - "bin": true - }, - "graceful-fs@4.2.11": { - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "has-flag@4.0.0": { - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "hasown@2.0.2": { - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": [ - "function-bind" - ] - }, - "ieee754@1.2.1": { - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "inflight@1.0.6": { - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": [ - "once", - "wrappy" - ], - "deprecated": true - }, - "inherits@2.0.4": { - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini@1.3.8": { - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, "is-arrayish@0.3.2": { "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, - "is-core-module@2.16.1": { - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dependencies": [ - "hasown" - ] - }, - "is-extglob@2.1.1": { - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" - }, - "is-glob@4.0.3": { - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": [ - "is-extglob" - ] - }, - "is-interactive@1.0.0": { - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==" - }, - "is-number@7.0.0": { - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-obj@1.0.1": { - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==" - }, - "is-regexp@1.0.0": { - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==" - }, - "is-unicode-supported@0.1.0": { - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" - }, - "is-url-superb@4.0.0": { - "integrity": "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==" - }, - "is-url@1.2.4": { - "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" - }, - "json5@2.2.3": { - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "bin": true - }, - "log-symbols@4.1.0": { - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dependencies": [ - "chalk@4.1.2", - "is-unicode-supported" - ] - }, - "madge@8.0.0": { - "integrity": "sha512-9sSsi3TBPhmkTCIpVQF0SPiChj1L7Rq9kU2KDG1o6v2XH9cCw086MopjVCD+vuoL5v8S77DTbVopTO8OUiQpIw==", - "dependencies": [ - "chalk@4.1.2", - "commander@7.2.0", - "commondir", - "debug", - "dependency-tree", - "ora", - "pluralize", - "pretty-ms", - "rc", - "stream-to-array", - "ts-graphviz", - "walkdir" - ], - "bin": true - }, - "magic-string@0.30.17": { - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dependencies": [ - "@jridgewell/sourcemap-codec" - ] - }, - "merge2@1.4.1": { - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" - }, - "micromatch@4.0.8": { - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dependencies": [ - "braces", - "picomatch" - ] - }, - "mimic-fn@2.1.0": { - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "minimatch@3.1.2": { - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": [ - "brace-expansion@1.1.12" - ] - }, - "minimatch@9.0.5": { - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dependencies": [ - "brace-expansion@2.0.2" - ] - }, - "minimist@1.2.8": { - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" - }, - "module-definition@6.0.1": { - "integrity": "sha512-FeVc50FTfVVQnolk/WQT8MX+2WVcDnTGiq6Wo+/+lJ2ET1bRVi3HG3YlJUfqagNMc/kUlFSoR96AJkxGpKz13g==", - "dependencies": [ - "ast-module-types", - "node-source-walk" - ], - "bin": true - }, - "module-lookup-amd@9.0.4": { - "integrity": "sha512-DWJEuLVvjxh5b8wrvJC5wr2a7qo7pOWXIgdCBNazU416kcIyzO4drxvlqKhsHzYwxcC4cWuhoK+MiWCKCGnv7A==", - "dependencies": [ - "commander@12.1.0", - "glob", - "requirejs", - "requirejs-config-file" - ], - "bin": true - }, - "ms@2.1.3": { - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "nanoid@3.3.11": { - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "bin": true - }, - "node-source-walk@7.0.1": { - "integrity": "sha512-3VW/8JpPqPvnJvseXowjZcirPisssnBuDikk6JIZ8jQzF7KJQX52iPFX4RYYxLycYH7IbMRSPUOga/esVjy5Yg==", - "dependencies": [ - "@babel/parser" - ] - }, - "once@1.4.0": { - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": [ - "wrappy" - ] - }, - "onetime@5.1.2": { - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": [ - "mimic-fn" - ] - }, - "ora@5.4.1": { - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dependencies": [ - "bl", - "chalk@4.1.2", - "cli-cursor", - "cli-spinners", - "is-interactive", - "is-unicode-supported", - "log-symbols", - "strip-ansi", - "wcwidth" - ] - }, - "parse-ms@2.1.0": { - "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==" - }, - "path-is-absolute@1.0.1": { - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" - }, - "path-parse@1.0.7": { - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "picocolors@1.1.1": { - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - }, - "picomatch@2.3.1": { - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" - }, - "pluralize@8.0.0": { - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==" - }, - "postcss-values-parser@6.0.2_postcss@8.5.6": { - "integrity": "sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==", - "dependencies": [ - "color-name", - "is-url-superb", - "postcss", - "quote-unquote" - ] - }, - "postcss@8.5.6": { - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dependencies": [ - "nanoid", - "picocolors", - "source-map-js" - ] - }, - "precinct@12.2.0_postcss@8.5.6_typescript@5.8.3": { - "integrity": "sha512-NFBMuwIfaJ4SocE9YXPU/n4AcNSoFMVFjP72nvl3cx69j/ke61/hPOWFREVxLkFhhEGnA8ZuVfTqJBa+PK3b5w==", - "dependencies": [ - "@dependents/detective-less", - "commander@12.1.0", - "detective-amd", - "detective-cjs", - "detective-es6", - "detective-postcss", - "detective-sass", - "detective-scss", - "detective-stylus", - "detective-typescript", - "detective-vue2", - "module-definition", - "node-source-walk", - "postcss", - "typescript" - ], - "bin": true - }, - "pretty-ms@7.0.1": { - "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", - "dependencies": [ - "parse-ms" - ] - }, - "queue-microtask@1.2.3": { - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" - }, - "quote-unquote@1.0.0": { - "integrity": "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==" - }, - "rc@1.2.8": { - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": [ - "deep-extend", - "ini", - "minimist", - "strip-json-comments" - ], - "bin": true - }, - "readable-stream@3.6.2": { - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": [ - "inherits", - "string_decoder", - "util-deprecate" - ] - }, - "requirejs-config-file@4.0.0": { - "integrity": "sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==", - "dependencies": [ - "esprima", - "stringify-object" - ] - }, - "requirejs@2.3.7": { - "integrity": "sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw==", - "bin": true - }, - "resolve-dependency-path@4.0.1": { - "integrity": "sha512-YQftIIC4vzO9UMhO/sCgXukNyiwVRCVaxiWskCBy7Zpqkplm8kTAISZ8O1MoKW1ca6xzgLUBjZTcDgypXvXxiQ==" - }, - "resolve@1.22.10": { - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dependencies": [ - "is-core-module", - "path-parse", - "supports-preserve-symlinks-flag" - ], - "bin": true - }, - "restore-cursor@3.1.0": { - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": [ - "onetime", - "signal-exit" - ] - }, - "reusify@1.1.0": { - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==" - }, - "run-parallel@1.2.0": { - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dependencies": [ - "queue-microtask" - ] - }, - "safe-buffer@5.2.1": { - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "sass-lookup@6.1.0": { - "integrity": "sha512-Zx+lVyoWqXZxHuYWlTA17Z5sczJ6braNT2C7rmClw+c4E7r/n911Zwss3h1uHI9reR5AgHZyNHF7c2+VIp5AUA==", - "dependencies": [ - "commander@12.1.0", - "enhanced-resolve" - ], - "bin": true - }, "semver@7.7.2": { "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "bin": true @@ -1060,128 +306,20 @@ ], "scripts": true }, - "signal-exit@3.0.7": { - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, "simple-swizzle@0.2.2": { "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "dependencies": [ "is-arrayish" ] }, - "source-map-js@1.2.1": { - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" - }, - "source-map@0.6.1": { - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "stream-to-array@2.3.0": { - "integrity": "sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==", - "dependencies": [ - "any-promise" - ] - }, - "string_decoder@1.3.0": { - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": [ - "safe-buffer" - ] - }, - "stringify-object@3.3.0": { - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dependencies": [ - "get-own-enumerable-property-symbols", - "is-obj", - "is-regexp" - ] - }, - "strip-ansi@6.0.1": { - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": [ - "ansi-regex" - ] - }, - "strip-bom@3.0.0": { - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" - }, - "strip-json-comments@2.0.1": { - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" - }, - "stylus-lookup@6.1.0": { - "integrity": "sha512-5QSwgxAzXPMN+yugy61C60PhoANdItfdjSEZR8siFwz7yL9jTmV0UBKDCfn3K8GkGB4g0Y9py7vTCX8rFu4/pQ==", - "dependencies": [ - "commander@12.1.0" - ], - "bin": true - }, - "supports-color@7.2.0": { - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": [ - "has-flag" - ] - }, - "supports-preserve-symlinks-flag@1.0.0": { - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "tapable@2.2.2": { - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==" - }, - "to-regex-range@5.0.1": { - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": [ - "is-number" - ] - }, - "ts-api-utils@2.1.0_typescript@5.8.3": { - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dependencies": [ - "typescript" - ] - }, - "ts-graphviz@2.1.6": { - "integrity": "sha512-XyLVuhBVvdJTJr2FJJV2L1pc4MwSjMhcunRVgDE9k4wbb2ee7ORYnPewxMWUav12vxyfUM686MSGsqnVRIInuw==", - "dependencies": [ - "@ts-graphviz/adapter", - "@ts-graphviz/ast", - "@ts-graphviz/common", - "@ts-graphviz/core" - ] - }, - "tsconfig-paths@4.2.0": { - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dependencies": [ - "json5", - "minimist", - "strip-bom" - ] - }, "tslib@2.8.1": { "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, - "typescript@5.8.3": { - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "bin": true + "undici-types@7.10.0": { + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==" }, - "undici-types@6.21.0": { - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" - }, - "util-deprecate@1.0.2": { - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "walkdir@0.4.1": { - "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==" - }, - "wcwidth@1.0.1": { - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dependencies": [ - "defaults" - ] - }, - "wrappy@1.0.2": { - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "zod@4.0.5": { - "integrity": "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==" + "zod@4.1.5": { + "integrity": "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==" } }, "workspace": { @@ -1189,6 +327,7 @@ "jsr:@felix/bcrypt@^1.0.5", "jsr:@hono/hono@^4.8.5", "jsr:@hono/zod-validator@~0.7.2", + "jsr:@oneday/http-status@0.2", "jsr:@proxnet/undead-logging@^1.5.0", "jsr:@std/assert@1", "npm:sharp@~0.34.3", diff --git a/res/rooms.json b/res/rooms.json index 3b4821c..d61a6dd 100644 --- a/res/rooms.json +++ b/res/rooms.json @@ -1272,7 +1272,7 @@ "Name": "RecCenter", "ReplicationId": "02ed2947-2db9-62c4-49b0-76d70fd432bb", "Description": "A social hub to meet and mingle with friends new and old.", - "Accessibility": 2, + "Accessibility": 1, "SupportsLevelVoting": false, "CloningAllowed": false, "SupportsScreens": true, @@ -1303,7 +1303,7 @@ "Description": "Take turns drawing, acting, and guessing funny phrases with your friends!", "Accessibility": 1, "SupportsLevelVoting": false, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": false, "SupportsWalkVR": true, "SupportsTeleportVR": true, @@ -1332,7 +1332,7 @@ "Description": "A leisurely stroll through the grass. Throw your disc into the goal. Sounds easy, right?", "Accessibility": 1, "SupportsLevelVoting": false, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": true, "SupportsWalkVR": true, "SupportsTeleportVR": true, @@ -1361,7 +1361,7 @@ "Description": "Throw your disc through hazards and around wind machines on this challenging course!", "Accessibility": 1, "SupportsLevelVoting": false, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": true, "SupportsWalkVR": true, "SupportsTeleportVR": true, @@ -1390,7 +1390,7 @@ "Description": "Throw dodgeballs to knock out your friends in this gym classic!", "Accessibility": 1, "SupportsLevelVoting": false, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": true, "SupportsWalkVR": true, "SupportsTeleportVR": true, @@ -1419,7 +1419,7 @@ "Description": "A simple rally game between two players in a plexiglass tube with a zero-g ball.", "Accessibility": 1, "SupportsLevelVoting": false, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": false, "SupportsWalkVR": true, "SupportsTeleportVR": true, @@ -1448,7 +1448,7 @@ "Description": "Red and Blue teams splat each other in capture the flag and team battle.", "Accessibility": 1, "SupportsLevelVoting": true, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": true, "SupportsWalkVR": true, "SupportsTeleportVR": true, @@ -1542,7 +1542,7 @@ "Description": "Red and Blue teams splat each other in capture the flag and team battle.", "Accessibility": 1, "SupportsLevelVoting": true, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": false, "SupportsWalkVR": false, "SupportsTeleportVR": true, @@ -1623,7 +1623,7 @@ "Description": "The goblin king stole Coach's Golden Trophy. Team up and embark on an epic quest to recover it!", "Accessibility": 1, "SupportsLevelVoting": false, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": true, "SupportsWalkVR": true, "SupportsTeleportVR": true, @@ -1652,7 +1652,7 @@ "Description": "Robot invaders threaten the galaxy! Team up with your friends and bring the laser heat!", "Accessibility": 1, "SupportsLevelVoting": false, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": true, "SupportsWalkVR": true, "SupportsTeleportVR": true, @@ -1681,7 +1681,7 @@ "Description": "Can your band of adventurers brave the enchanted wilds, and lift the curse of the crimson cauldron?", "Accessibility": 1, "SupportsLevelVoting": false, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": true, "SupportsWalkVR": true, "SupportsTeleportVR": true, @@ -1710,7 +1710,7 @@ "Description": "Can your pirate crew get to the Isle, defeat its fearsome guardian, and escape with the gold?", "Accessibility": 1, "SupportsLevelVoting": false, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": true, "SupportsWalkVR": true, "SupportsTeleportVR": true, @@ -1739,7 +1739,7 @@ "Description": "Teams of three run around slamming themselves into an over-sized soccer ball. Goal!", "Accessibility": 1, "SupportsLevelVoting": false, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": true, "SupportsWalkVR": true, "SupportsTeleportVR": true, @@ -1768,7 +1768,7 @@ "Description": "Teams battle each other and waves of robots.", "Accessibility": 1, "SupportsLevelVoting": true, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": true, "SupportsWalkVR": true, "SupportsTeleportVR": true, @@ -1924,7 +1924,7 @@ "Name": "PerformanceHall", "ReplicationId": "22ab0d3c-3d7d-70e4-eb5c-c8c47cca1906", "Description": "A theater for plays, music, comedy and other performances.", - "Accessibility": 2, + "Accessibility": 1, "SupportsLevelVoting": false, "CloningAllowed": true, "SupportsScreens": true, @@ -2361,7 +2361,7 @@ "Description": "Throw dodgeballs to knock out your friends in this gym classic!", "Accessibility": 1, "SupportsLevelVoting": false, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": false, "SupportsWalkVR": false, "SupportsTeleportVR": true, @@ -2390,7 +2390,7 @@ "Description": "Gather your vampire hunting crew, conquer a legendary castle, and restore peace to Rec Room!", "Accessibility": 1, "SupportsLevelVoting": false, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": true, "SupportsWalkVR": true, "SupportsTeleportVR": true, @@ -2419,7 +2419,7 @@ "Description": "Hang out with friends, bowl a few games, eat snacks!", "Accessibility": 1, "SupportsLevelVoting": false, - "CloningAllowed": true, + "CloningAllowed": false, "SupportsScreens": true, "SupportsWalkVR": true, "SupportsTeleportVR": true, diff --git a/src/main.ts b/src/main.ts index c15d453..c686aae 100644 --- a/src/main.ts +++ b/src/main.ts @@ -12,7 +12,9 @@ import { PushNotificationId } from "./server/socket/signalr/types.ts"; import { genericResponse } from "./util/api.ts"; import { getNetConfig } from "./net.ts"; import { TokenFormat, TokenType } from "./server/platforms/types.ts"; -import { Context } from "node:vm"; +import { HonoEnv } from "./util/types.ts"; +import { Context } from "@hono/hono"; +import { compress } from "@hono/hono/compress"; LoggingConfiguration.resetTimeFormat = TimeFormat.Unix; LoggingConfiguration.resetLogTiming = LogTiming.Microtask; @@ -48,8 +50,10 @@ await routeImporter(AppRoot.app, 'src/', [ 'routes/img' ]); +AppRoot.app.use(compress()); + // deno-lint-ignore require-await -AppRoot.app.use('*', async (c: Context) => { +AppRoot.app.use('*', async (c: Context) => { return c.json(genericResponse(false, "Resource Not Found"), 404); }); diff --git a/src/routes/api/routes/rooms.ts b/src/routes/api/routes/rooms.ts new file mode 100644 index 0000000..69b4e9e --- /dev/null +++ b/src/routes/api/routes/rooms.ts @@ -0,0 +1,26 @@ +import z from "zod"; +import { createHonoRoute } from "../../../util/import.ts"; +import { typedZValidator } from "../../../util/validators.ts"; +import Server from "../../../server/server.ts"; + +export const route = createHonoRoute("/rooms"); + +const getRoomDetailsParamSchema = z.object({ + id: z.coerce.number().max(Math.pow(2, 31)) +}); +route.app.get('/v4/details/:id', typedZValidator('param', getRoomDetailsParamSchema), async (c, nxt) => { + const room = await Server.Rooms.get(parseInt(c.req.param('id'))); + if (room === null) return await nxt(); + else return c.json(await room.export()); +}); +const getRoomByNameParamSchema = z.object({ + name: z.string() +}); +route.app.get('/v2/name/:name', typedZValidator('param', getRoomByNameParamSchema), async (c, nxt) => { + const id = await Server.Rooms.getIdFromName(c.req.param('name')); + if (id == null) return await nxt(); + + const room = await Server.Rooms.get(id); + if (room == null) return await nxt(); + else return c.json((await room.export()).Room); +}); \ No newline at end of file diff --git a/src/routes/api/routes/settings.ts b/src/routes/api/routes/settings.ts index fe93467..9dd31b9 100644 --- a/src/routes/api/routes/settings.ts +++ b/src/routes/api/routes/settings.ts @@ -1,5 +1,5 @@ import z from "zod"; -import { authenticate, genericResponse } from "../../../util/api.ts"; +import { authenticate } from "../../../util/api.ts"; import { createHonoRoute } from "../../../util/import.ts"; import { typedZValidator } from "../../../util/validators.ts"; import { HonoEnv } from "../../../util/types.ts"; @@ -23,7 +23,6 @@ const settingsSetSchema = z.object({ }); route.app.post('/v2/set', typedZValidator('json', settingsSetSchema), async c => { const { Key, Value } = c.req.valid('json'); - if (!Key) return c.json(genericResponse(false, "Internal Server Error"), 500); await c.get('profile').Settings.setSetting(Key, Value); return c.status(200); diff --git a/src/routes/img/base.ts b/src/routes/img/base.ts index 571fd11..2428776 100644 --- a/src/routes/img/base.ts +++ b/src/routes/img/base.ts @@ -2,39 +2,50 @@ import z from "zod"; import Server from "../../server/server.ts"; import { createHonoRoute } from "../../util/import.ts"; import { typedZValidator } from "../../util/validators.ts"; -import { FileType, File } from "../../server/content/base.ts"; import sharp from "sharp"; import path from "node:path"; import { RootPath } from "../../util/path.ts"; import Logging from "@proxnet/undead-logging"; +import { statusResponse } from "../../util/api.ts"; +import { HTTPStatus } from "@oneday/http-status"; +import { Buffer } from "node:buffer"; export const route = createHonoRoute('/img'); -async function convertImage(query: {cropSquare?: boolean | undefined;width?: number | undefined;height?: number | undefined;}, data: Uint8Array): Promise> { - const image = sharp(data); - const rootMetadata = await image.metadata(); - const squareSize = Math.min(rootMetadata.width, rootMetadata.height); - if (query.cropSquare) image.resize(squareSize, squareSize); +const log = new Logging("ImageRoute"); - const newImage = sharp(await image.png().toBuffer()); - if (query.width && query.height) - newImage.resize(query.width, query.height); - else if (query.width) - newImage.resize(query.width); - else if (query.height) - newImage.resize(undefined, query.height); +async function convertImage(query: ImageQuery, data: Uint8Array): Promise | null> { + try { + const image = sharp(data); + const rootMetadata = await image.metadata(); + const squareSize = Math.min(rootMetadata.width, rootMetadata.height); + if (query.cropSquare) image.resize(squareSize, squareSize); - return await newImage.png().toBuffer(); + const newImage = sharp(await image.png().toBuffer()); + if (query.width && query.height) + newImage.resize(query.width, query.height); + else if (query.width) + newImage.resize(query.width); + else if (query.height) + newImage.resize(undefined, query.height); + + return await newImage.png().toBuffer(); + } catch (err) { + log.w(`Image transformation failed: ${(err as Error).stack}`); + return null; + } } const imgNameParamSchema = z.object({ - imgName: z.string().min(4).includes('.') + imgName: z.string().min(5).includes('.') }); const imgQuerySchema = z.object({ cropSquare: z.coerce.boolean().optional(), width: z.coerce.number().min(64).max(3840).optional(), height: z.coerce.number().min(64).max(2160).optional(), }); +type ImageQuery = z.infer; + route.app.get('/:imgName', typedZValidator('param', imgNameParamSchema), typedZValidator('query', imgQuerySchema), @@ -43,29 +54,29 @@ route.app.get('/:imgName', const { imgName } = c.req.valid('param'); const query = c.req.valid('query'); - const file: File | null = await Server.Content.getFile(`img/${imgName}`); - let raw: Uint8Array | null = null; - if (!file) { - try { - raw = await Deno.readFile(path.join(RootPath, "res/baseimg/", imgName)); - } catch { - raw = null; - } - } - - if (!raw && file && file.Meta.Type !== FileType.Image) return c.status(404); - - try { - let result: Uint8Array | null = null; - if (file) result = await convertImage(query, file.Data); - else if (raw) result = await convertImage(query, raw); - - if (result) return c.body(result, 200, { "Cache-Control": "public, no-transform, max-age=1800", "Content-Type": "image/png" }); - else return c.status(404); - - } catch (err) { - new Logging("ImageRoute").w(`Sharp error: ${err}`); - return c.status(500); + const datas: Uint8Array[] = (await Promise.all | null>([ + new Promise(resolve => { + Deno.readFile(path.join(RootPath, "res/baseimg/", imgName)).then(img => { + resolve(img); + }).catch(() => { + resolve(null); + }); + }), + new Promise(resolve => { + Server.Content.getFile(`img/${imgName}`).then(file => { + if (file) resolve(file.Data); + else resolve(null); + }).catch(() => { + resolve(null); + }); + }) + ])).filter(val => val !== null); + + if (datas.length == 0) return statusResponse(c, HTTPStatus.NotFound); + else { + const result = await convertImage(query, datas[0]); + if (result == null) return statusResponse(c, HTTPStatus.InternalServerError, "Image transformation failed. Contact an administrator."); + return c.body(Buffer.from(result), 200, { "Cache-Control": "public, no-transform, max-age=1800", "Content-Type": "image/png" }); } } diff --git a/src/server/instances/Instance.ts b/src/server/instances/Instance.ts index 8de42c8..11a940e 100644 --- a/src/server/instances/Instance.ts +++ b/src/server/instances/Instance.ts @@ -45,8 +45,8 @@ export class Instance { getPlayers() { return this.#players.values().toArray(); } - playerIsHere(profile: Profile) { - return Boolean(this.getPlayers().find(prof => prof.same(profile))); + hasPlayer(profile: Profile) { + return this.#players.values().some(prof => prof.getId() === profile.getId()); } removePlayer(profile: Profile) { this.#players.delete(profile); diff --git a/src/server/rooms/base.ts b/src/server/rooms/base.ts index 15b7a87..7e88731 100644 --- a/src/server/rooms/base.ts +++ b/src/server/rooms/base.ts @@ -3,19 +3,146 @@ import { ServerContentBase } from "../ContentBase.ts"; import KV from "../persistence/kv.ts"; import type Profile from "../profiles/profile.ts"; import { RoomFactory } from "./internal/RoomFactory.ts"; -import { FactoryMode } from "./internal/RoomDataTypes.ts"; +import { FactoryMode, HardwareSupports, RoomDataTypes, WriteMode } from "./internal/RoomDataTypes.ts"; +import { AGRoom, AGRoomLocation, AGRoomRuntimeConfig } from "./internal/ClientRoomTypes.ts"; +import Command from "../commands/command.ts"; +import z from "zod"; +import { RoomLocation } from "../instances/base.ts"; export class ServerRoomsBase extends ServerContentBase { #subroomKv = new KV('subrooms', true); #log = new Logging("Rooms"); + static agRoomIdsKey = "agrooms"; + static baseRoomIdsKey = "baserooms"; + static roomNamesKey = "room_names"; static playerDormsKey = "dorms"; - protected override async start() { + #agrooms: Set = new Set(); + #baserooms: Set = new Set(); + + override async start() { await this.#subroomKv.init(); this.#log.i('[sub]rooms database initialized'); + + const agrooms = await this.kv.getKv().get>([ServerRoomsBase.agRoomIdsKey]); + if (agrooms.value !== null) this.#agrooms = agrooms.value; + this.#log.i(`${this.#agrooms.size} AG rooms exist`); + + const baserooms = await this.kv.getKv().get>([ServerRoomsBase.baseRoomIdsKey]); + if (baserooms.value !== null) this.#baserooms = baserooms.value; + + this.server.Commands.addRootCommand(new Command({ + key: ["rooms", "r", "room"], + subcommands: [ + new Command({ + key: ["initag", "initagrooms", "initagroom", "iag"], + zod: z.tuple([]).rest(z.string()), + exec: async (...arrayPath: string[]) => { + const path = arrayPath.join(' '); + try { + const config = JSON.parse((await Deno.readTextFile(path)).toString()) as AGRoomRuntimeConfig; + this.#log.d('Starting AG room initialization'); + this.initBuiltinRooms(config.Rooms, config.Locations); + } catch (err) { + return err as Error; + } + }, + help: "Initialize AG rooms with AGRoomRuntimeConfig from provided file path" + }), + new Command({ + key: ["getplayerdorm", "playerdorm", "pd", "dorm"], + zod: z.tuple([z.coerce.number().min(1).max(Math.pow(2, 31))]), + exec: async (playerId: number) => { + const factory = await this.getPlayerDorm(this.server.Profiles.get(playerId)) + }, + help: "Get the domroom information for a certain profile/player" + }) + ], + })) + } + async #writeAgRooms() { + await this.kv.getKv().set([ServerRoomsBase.agRoomIdsKey], this.#agrooms); + } + async #writeBaseRooms() { + await this.kv.getKv().set([ServerRoomsBase.baseRoomIdsKey], this.#baserooms); + } + + async initBuiltinRooms(rooms: AGRoom[], locations: AGRoomLocation[]) { + await Promise.all(rooms.map(async room => { + if (room.Accessibility == RoomDataTypes.RoomAccessibility.Private) return; + if ([ + "ArtTesting", + "AnimationRecordingStudio", + "Calibration", + "ARRoom", + "Registration", + "DormRoom" + ].includes(room.Name)) return; + + const roomFactory = await this.write(); + if (roomFactory == null) { + this.#log.w(`No factory given while writing builtin room "${room.Name}"!`); + return; + } + + const supportsVRLow = room.Scenes.map(scene => locations.find(loc => loc.ReplicationId == scene.RoomSceneLocationId)) + .filter(val => typeof val !== 'undefined').some(loc => loc.SupportsVRLow) ?? false; + const supportsMobile = room.Scenes.map(scene => locations.find(loc => loc.ReplicationId == scene.RoomSceneLocationId)) + .filter(val => typeof val !== 'undefined').some(loc => loc.SupportsMobile) ?? false; + + roomFactory.Name = room.Name; + roomFactory.Description = room.Description; + roomFactory.Accessibility = room.Accessibility; + roomFactory.State = RoomDataTypes.RoomState.Active; + roomFactory.Description = room.Description; + roomFactory.IsAGRoom = true; + roomFactory.CloningAllowed = room.CloningAllowed; + roomFactory.ImageName = `${room.Name}.png` + + const supportPromises: Promise[] = []; + roomFactory.removeAllHardwareSupport(); + if (room.SupportsScreens) supportPromises.push(roomFactory.addHardwareSupport("screens")); + if (room.SupportsWalkVR) supportPromises.push(roomFactory.addHardwareSupport("walk_vr")); + if (room.SupportsTeleportVR) supportPromises.push(roomFactory.addHardwareSupport("teleport_vr")); + if (supportsMobile) supportPromises.push(roomFactory.addHardwareSupport("mobile")); + if (supportsVRLow) supportPromises.push(roomFactory.addHardwareSupport("low_vr")); + + await Promise.all(room.Scenes.map(async scene => { + const subroomFactory = await roomFactory.newSubroom(); + subroomFactory.addSave(""); + + subroomFactory.RoomId = roomFactory.getRoomId(); + subroomFactory.Name = scene.Name; + subroomFactory.RoomSceneLocationId = scene.RoomSceneLocationId; + subroomFactory.IsSandbox = scene.IsSandbox; + subroomFactory.CanMatchmakeInto = scene.CanMatchmakeInto; + subroomFactory.MaxPlayers = scene.MaxPlayers; + + await subroomFactory.write(); + + roomFactory.addSubroom(subroomFactory.RoomSceneId); + })); + + await Promise.all(supportPromises); + + this.#agrooms.add(roomFactory.getRoomId()); + await roomFactory.write(); + + if (room.CloningAllowed) this.#baserooms.add(roomFactory.getRoomId()); + })); + + await this.#writeAgRooms(); + await this.#writeBaseRooms(); + this.#log.i(`${this.#agrooms.size} AG rooms added: [${this.#agrooms.values().toArray().join(',')}]`); + } + + async getAvailableRoomId() { + let id = Math.round(Math.random() * Math.pow(2, 31)); + while ((await this.kv.getKv().get([RoomFactory.roomsKey, id, 'meta'])).value !== null) id = await this.getAvailableRoomId(); + return id; } getKv() { @@ -30,11 +157,48 @@ export class ServerRoomsBase extends ServerContentBase { async getPlayerDorm(profile: Profile) { const id = await this.kv.getKv().get([ServerRoomsBase.playerDormsKey, profile.getId()]); - if (id.value == null) return null; + if (id.value == null) { + const roomFactory = await new RoomFactory(this.server, this.kv).init({ mode: FactoryMode.Write, writeMode: WriteMode.WriteIfFree, id: await this.getAvailableRoomId() }); + if (!roomFactory) return null; + roomFactory.setRoomProperties({ + Name: `DormRoom`, + Description: "Your private dorm.", + CreatorPlayerId: profile.getId(), + ImageName: "", + State: RoomDataTypes.RoomState.Active, + Accessibility: RoomDataTypes.RoomAccessibility.Private, + SupportsLevelVoting: false, + IsAGRoom: false, + IsDormRoom: true, + CloningAllowed: false, + AllowsJuniors: true, + RoomWarningMask: 0, + CustomRoomWarning: "", + DisableMicAutoMute: null + }); + + await roomFactory.addHardwareSupport(...HardwareSupports); + roomFactory.setTags(new Set(["dormroom"])); + + const subroomFactory = await roomFactory.newSubroom(); + subroomFactory.CanMatchmakeInto = true; + subroomFactory.IsSandbox = true; + subroomFactory.MaxPlayers = 4; + subroomFactory.Name = "Home"; + subroomFactory.RoomId = roomFactory.getRoomId(); + subroomFactory.RoomSceneLocationId = RoomLocation.DormRoom; + subroomFactory.addSave(""); + roomFactory.addSubroom(subroomFactory.RoomSceneId); + + await subroomFactory.write(); + await roomFactory.write(); + + return roomFactory; + } else return await new RoomFactory(this.server, this.kv).init({ mode: FactoryMode.Fetch, id: id.value }); } - async getFromName(name: string) { + async getByName(name: string) { const id = await this.getIdFromName(name); if (id == null) return null; return await this.get(id); @@ -44,4 +208,8 @@ export class ServerRoomsBase extends ServerContentBase { return await new RoomFactory(this.server, this.kv).init({ mode: FactoryMode.Fetch, id: id }); } + async write(mode?: WriteMode) { + return await new RoomFactory(this.server, this.kv).init({ mode: FactoryMode.Write, writeMode: mode ? mode : WriteMode.WriteIfFree, id: await this.getAvailableRoomId() }); + } + } \ No newline at end of file diff --git a/src/server/rooms/internal/ClientRoomTypes.ts b/src/server/rooms/internal/ClientRoomTypes.ts index 510c3d4..5ec7265 100644 --- a/src/server/rooms/internal/ClientRoomTypes.ts +++ b/src/server/rooms/internal/ClientRoomTypes.ts @@ -55,4 +55,9 @@ export interface AGRoomLocation { } }[] } +} + +export interface AGRoomRuntimeConfig { + Locations: AGRoomLocation[], + Rooms: AGRoom[] } \ No newline at end of file diff --git a/src/server/rooms/internal/RoomDataTypes.ts b/src/server/rooms/internal/RoomDataTypes.ts index 7285bbc..1c9987c 100644 --- a/src/server/rooms/internal/RoomDataTypes.ts +++ b/src/server/rooms/internal/RoomDataTypes.ts @@ -8,6 +8,9 @@ export enum FactoryMode { Write = 'write' } +/** + * All clients RecRoom can run on. + */ export const HardwareSupports = ["screens", "walk_vr", "teleport_vr", "low_vr", "mobile"] as const; export type HardwareSupport = typeof HardwareSupports[number]; @@ -138,8 +141,7 @@ export interface DatabaseRoomContent { } export interface DatabaseRoom { Hardware: Set, - Subrooms: number[], - Tags: Set, + Tags: Set, CoOwners: Set, InvitedCoOwners: Set, Hosts: Set, @@ -154,14 +156,13 @@ export interface DatabaseSubroom { IsSandbox: boolean, MaxPlayers: number, CanMatchmakeInto: boolean, - LatestSaveId: number, - Saves: RoomSave[] + LatestSaveId: number | null, } +export type RoomSaveMap = Map export interface RoomSave { - SaveId: number, DataBlobName: string, - SavedAt: string + SavedAt: Date } export * as RoomDataTypes from "./RoomDataTypes.ts"; \ No newline at end of file diff --git a/src/server/rooms/internal/RoomFactory.ts b/src/server/rooms/internal/RoomFactory.ts index 582828c..2ab1f38 100644 --- a/src/server/rooms/internal/RoomFactory.ts +++ b/src/server/rooms/internal/RoomFactory.ts @@ -1,17 +1,17 @@ import Logging from "@proxnet/undead-logging"; import type KV from "../../persistence/kv.ts"; import { type ServerBase } from "../../server.ts"; -import { DatabaseRoom, FactoryMode, GalvanicTagDTO, RoomDataTypes, TagDTO, TagType, WriteMode } from "./RoomDataTypes.ts"; +import { DatabaseRoom, FactoryMode, GalvanicTagDTO, HardwareSupports, RoomAccessibility, RoomDataTypes, RoomState, TagDTO, TagType, WriteMode } from "./RoomDataTypes.ts"; import { SubroomFactory } from "./SubroomFactory.ts"; import { ServerRoomsBase } from "../base.ts"; const log = new Logging("RoomFactory"); -const roomDebug = true; +const roomDebug = false; interface FactoryOptionsBase { mode: FactoryMode, - id?: number, + id: number, name?: string } export interface WriteFactoryOptions extends FactoryOptionsBase { @@ -36,6 +36,7 @@ export class RoomFactory { writeMode: WriteMode = WriteMode.WriteIfFree; #obj: DatabaseRoom | null = null; + #subrooms: Set | null = null; #hardwareSupport: Set | null = null; #tags: Set | null = null; @@ -49,6 +50,22 @@ export class RoomFactory { } + /** + * Initialize the factory. Retrieves the room from the database and populates factory values. + * + * Does not fetch subroom content, only available subroom IDs. + * + * When using write mode, values are initialized to defaults. + * + * Defaults: + * - All hardware is supported + * - Cloning is not allowed + * - Room is not AG or dorm + * - State is Moderation_Closed + * - Accessibility is Unlisted + * - CreatorPlayerId is 1 (Coach) + * - Name and Description are empty strings + */ async init(options: FactoryOptions) { if (typeof options.id == 'undefined' && typeof options.name == 'undefined') throw new Error("Must specify a room ID or a room name"); @@ -63,11 +80,38 @@ export class RoomFactory { } const obj = await this.#kv.getKv().get([RoomFactory.roomsKey, this.#roomId, 'meta']); - if (obj.value == null) return null; - else this.#obj = obj.value; + if (options.mode == FactoryMode.Fetch && obj.value == null) return null; + else this.#obj = options.mode == FactoryMode.Fetch ? obj.value : { + Hardware: new Set(HardwareSupports), + Tags: new Set(), + CoOwners: new Set(), + InvitedCoOwners: new Set(), + Hosts: new Set(), + InvitedHosts: new Set(), + Room: { + Name: "", + Description: "", + CreatorPlayerId: 1, + ImageName: "", + State: RoomState.Moderation_Closed, + Accessibility: RoomAccessibility.Unlisted, + SupportsLevelVoting: false, + IsAGRoom: false, + IsDormRoom: false, + CloningAllowed: false, + AllowsJuniors: true, + RoomWarningMask: 0, + CustomRoomWarning: "", + DisableMicAutoMute: null + } + }; + + const subrooms = await this.#kv.getKv().get>([RoomFactory.roomsKey, this.#roomId, "subrooms"]); + if (options.mode == FactoryMode.Write || subrooms.value == null) this.#subrooms = new Set(); + else this.#subrooms = subrooms.value; const hardwareSupport = await this.#kv.getKv().get>([RoomFactory.roomsKey, this.#roomId, 'hardware']); - if (hardwareSupport.value == null) return null; + if (hardwareSupport.value == null) this.#hardwareSupport = new Set(HardwareSupports); else this.#hardwareSupport = hardwareSupport.value; const tags = await this.#kv.getKv().get>([RoomFactory.roomsKey, this.#roomId, 'tags']); @@ -102,7 +146,7 @@ export class RoomFactory { const key = [RoomFactory.roomsKey, this.#roomId, 'meta']; const val = await this.#kv.getKv().get(key); - if (val == null) await w(); + if (val.value == null) await w(); else { if (this.writeMode == WriteMode.Overwrite) await w(); else throw new Error("Room already exists"); @@ -117,7 +161,7 @@ export class RoomFactory { const galvTags = this.getGalvanicTags(); const subroomExports = (await Promise.all( - this.getSubrooms().map(subroom => this.getSubroom(subroom)) + this.getSubrooms().values().map(subroom => this.getSubroom(subroom)) )).map(factory => factory.export()); return { @@ -154,14 +198,36 @@ export class RoomFactory { VisitCount: await this.getVisitCount() } } + setRoomProperties(props: RoomDataTypes.DatabaseRoomContent) { + Object.assign(this, props); + } + + getRoomId() { + if (!this.#roomId) throw this.#cannotAccessBeforeInitError; + return this.#roomId; + } getSubrooms() { - if (!this.#obj) throw this.#cannotAccessBeforeInitError; - return this.#obj.Subrooms; + if (!this.#subrooms) throw this.#cannotAccessBeforeInitError; + return this.#subrooms; } async getSubroom(id: number) { + if (!this.#subrooms) throw this.#cannotAccessBeforeInitError; + if (!this.#subrooms.has(id)) throw new Error("Subroom not available to this room"); return await new SubroomFactory(this.#server, this.#kv).init({ mode: FactoryMode.Fetch, writeMode: WriteMode.WriteIfFree, id }); } + getAvailableSubroomId() { + let id = Math.round(Math.random() * Math.pow(2, 31)); + if (this.getSubrooms().has(id)) id = this.getAvailableSubroomId(); + return id; + } + async newSubroom(mode?: WriteMode) { + return await new SubroomFactory(this.#server, this.#kv).init({ mode: FactoryMode.Write, writeMode: mode ? mode : WriteMode.WriteIfFree, id: this.getAvailableSubroomId() }); + } + addSubroom(id: number) { + if (!this.#subrooms) throw this.#cannotAccessBeforeInitError; + this.#subrooms.add(id); + } get Name() { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else return this.#obj.Room.Name } set Name(data) { if (!this.#obj) throw this.#cannotAccessBeforeInitError; this.#obj.Room.Name = data } @@ -178,8 +244,8 @@ export class RoomFactory { get State() { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else return this.#obj.Room.State } set State(data) { if (!this.#obj) throw this.#cannotAccessBeforeInitError; this.#obj.Room.State = data } - get RoomAccessibility() { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else return this.#obj.Room.Accessibility } - set RoomAccessibility(data) { if (!this.#obj) throw this.#cannotAccessBeforeInitError; this.#obj.Room.Accessibility = data } + get Accessibility() { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else return this.#obj.Room.Accessibility } + set Accessibility(data) { if (!this.#obj) throw this.#cannotAccessBeforeInitError; this.#obj.Room.Accessibility = data } get SupportsLevelVoting() { if (!this.#obj) throw this.#cannotAccessBeforeInitError; else return this.#obj.Room.SupportsLevelVoting } set SupportsLevelVoting(data) { if (!this.#obj) throw this.#cannotAccessBeforeInitError; this.#obj.Room.SupportsLevelVoting = data } @@ -229,6 +295,20 @@ export class RoomFactory { this.#hardwareSupport.delete(hardware); await this.#saveHardwareSupport(); } + /** + * Removes support for every hardware type in the room. When saving immediately after this, no client can legally join the room. + * + * In production, ensure at least one hardware type is supported. + */ + removeAllHardwareSupport() { + this.#hardwareSupport = new Set(); + } + /** + * Adds support for every hardware type in the room. + */ + addAllHardwareSupport() { + this.#hardwareSupport = new Set(HardwareSupports); + } /** * Visits are fetched during every access, not during init @@ -252,8 +332,8 @@ export class RoomFactory { const tags: GalvanicTagDTO[] = []; if (this.IsAGRoom) tags.push({ Tag: "recroomoriginal", Type: TagType.AGOnly }); - if (this.IsDormRoom) tags.push({ Tag: "dormroom", Type: TagType.Auto }); - if (this.Name === "Paintball" || this.Name === "PaintballVR") tags.push({ Tag: "paintball", Type: TagType.Auto }); + if (this.IsDormRoom) tags.push({ Tag: "dormroom", Type: TagType.AGOnly }); + if (this.Name === "Paintball" || this.Name === "PaintballVR") tags.push({ Tag: "paintball", Type: TagType.AGOnly }); const hardwareSupport = this.getHardwareSupport(); if (hardwareSupport.has('screens')) tags.push({ Tag: "screen", Type: TagType.Auto }); if (hardwareSupport.has('walk_vr')) tags.push({ Tag: "walkvr", Type: TagType.Auto }); diff --git a/src/server/rooms/internal/SubroomFactory.ts b/src/server/rooms/internal/SubroomFactory.ts index 582bf2d..8cec311 100644 --- a/src/server/rooms/internal/SubroomFactory.ts +++ b/src/server/rooms/internal/SubroomFactory.ts @@ -1,6 +1,7 @@ +import Logging from "@proxnet/undead-logging"; import type KV from "../../persistence/kv.ts"; import { type ServerBase } from "../../server.ts"; -import { DatabaseSubroom, FactoryMode, RoomDataTypes, RoomSave, WriteMode } from "./RoomDataTypes.ts"; +import { DatabaseSubroom, FactoryMode, RoomDataTypes, RoomSave, RoomSaveMap, WriteMode } from "./RoomDataTypes.ts"; export interface SubroomFactoryOptions { mode: FactoryMode, @@ -8,6 +9,8 @@ export interface SubroomFactoryOptions { id: number } +const log = new Logging("SubroomFactoryBase"); + export class SubroomFactory { #server: ServerBase; @@ -19,7 +22,7 @@ export class SubroomFactory { writeMode: WriteMode = WriteMode.WriteIfFree; #obj: DatabaseSubroom | null = null; - #saves: RoomSave[] | null = null; + #saves: RoomSaveMap | null = null; #cannotAccessBeforeInitError = new Error("Cannot access properties before initialization"); #cannotWriteBeforeInitError = new Error("Cannot write before initialization"); @@ -32,13 +35,22 @@ export class SubroomFactory { } async init(options: SubroomFactoryOptions) { + const data = await this.#kv.getKv().get([options.id, 'meta']); - if (data == null && this.factoryMode == FactoryMode.Fetch) throw new Error("No such subroom"); - - const saves = await this.#kv.getKv().get([options.id, 'saves']); - this.#saves = saves.value; - - this.#obj = data.value; + if (options.mode == FactoryMode.Fetch && data == null) throw new Error("No such subroom"); + + const saves = await this.#kv.getKv().get([options.id, 'saves']); + this.#saves = saves.value ?? new Map(); + + this.#obj = options.mode == FactoryMode.Fetch ? data.value : { + RoomId: 0, + RoomSceneLocationId: "", + Name: "Subroom data init failed, contact an admin!", + IsSandbox: false, + MaxPlayers: 8, + CanMatchmakeInto: true, + LatestSaveId: null + }; // use template object when writing this.#subroomId = options.id; return this; @@ -66,7 +78,7 @@ export class SubroomFactory { IsSandbox: this.IsSandbox, MaxPlayers: this.MaxPlayers, CanMatchmakeInto: this.CanMatchmakeInto, - DataModifiedAt: save.SavedAt, + DataModifiedAt: save.SavedAt.toISOString(), DataBlobName: save.DataBlobName } } @@ -98,24 +110,41 @@ export class SubroomFactory { if (!this.#saves) throw this.#cannotAccessBeforeInitError; let newId = Math.round(Math.random() * Math.pow(2, 31)); - while (this.#saves.some(save => save.SaveId == newId)) newId = this.#getAvailableSaveId(); + while (this.#saves.has(newId)) newId = this.#getAvailableSaveId(); return newId; } - addSave(dataBlobName: string) { + /** + * Add a save (history) to the scene. Automatically resets the LatestSaveId. + * + * Use empty string for no datablob + * + * @param dataBlobName Filename of datablob on CDN + */ + addSave(dataBlobName?: string) { if (!this.#saves) throw this.#cannotAccessBeforeInitError; - this.#saves.push({ - SaveId: this.#getAvailableSaveId(), - DataBlobName: dataBlobName, - SavedAt: new Date().toISOString() + + const newId = this.#getAvailableSaveId(); + this.#saves.set(newId, { + DataBlobName: dataBlobName ?? "", + SavedAt: new Date() }); + + this.LatestSaveId = newId; } getSaves() { if (!this.#saves) throw this.#cannotAccessBeforeInitError; return this.#saves; } getLatestSave() { - if (!this.#saves) throw this.#cannotAccessBeforeInitError; - return this.#saves.find(save => save.SaveId == this.LatestSaveId); + if (!this.#saves || !this.#obj) throw this.#cannotAccessBeforeInitError; + else if (!this.#obj.LatestSaveId) throw new Error(`No save is marked as the latest save`); + else { + if (this.#saves.size === 0) { + log.w(`No save could be found when fetching the latest save for subroomid ${this.#subroomId}!`); + return null; + } else if (this.#saves.size === 1) return this.#saves.values().toArray()[0]; + else return this.#saves.get(this.#obj.LatestSaveId); + } } } \ No newline at end of file diff --git a/src/util/api.ts b/src/util/api.ts index 68efe61..380c702 100644 --- a/src/util/api.ts +++ b/src/util/api.ts @@ -5,9 +5,15 @@ import z from "zod"; import { verify } from "@hono/hono/jwt"; import Server from "../server/server.ts"; import { TokenFormat } from "../server/platforms/types.ts"; +import { HTTPStatus, httpStatusText } from "@oneday/http-status"; +import { ContentfulStatusCode } from "@hono/hono/utils/http-status"; const log = new Logging("APIUtils"); +export function statusResponse(c: Context, code: HTTPStatus, msg?: string) { + return c.json(genericResponse(code < 400, msg ?? httpStatusText(code)), code as ContentfulStatusCode); +} + export function genericResponse(success: boolean, msg?: string, data?: null) { return { success, msg, data } }; diff --git a/src/util/import.ts b/src/util/import.ts index d83c471..09d72bf 100644 --- a/src/util/import.ts +++ b/src/util/import.ts @@ -44,10 +44,10 @@ export async function importer(importKey: string, prefix: string, paths: stri return items; } -export function createHonoRoute(path: string): RouteImport { +export function createHonoRoute(path: string) { const route: RouteImport = { path, app: new Hono() } - return route + return route; } \ No newline at end of file