diff --git a/.gitignore b/.gitignore index dffbb2b..e20725d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ # React Router /.react-router/ /build/ + +# Compiled server output +/dist/ diff --git a/package-lock.json b/package-lock.json index 1718ef7..c0980dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,17 +44,18 @@ "devDependencies": { "@react-router/dev": "^7.7.1", "@tailwindcss/vite": "^4.1.4", - "@types/compression": "^1.7.5", - "@types/express": "^5.0.1", + "@types/compression": "^1.8.1", + "@types/express": "^5.0.3", "@types/express-serve-static-core": "^5.0.6", - "@types/morgan": "^1.9.9", + "@types/morgan": "^1.9.10", "@types/node": "^20", "@types/pg": "^8.11.14", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", "dotenv-cli": "^8.0.0", + "esbuild": "^0.25.11", "tailwindcss": "^4.1.4", - "tsx": "^4.19.2", + "tsx": "^4.20.6", "tw-animate-css": "^1.4.0", "typescript": "^5.8.3", "vite": "^6.3.3", @@ -1055,9 +1056,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", + "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", "cpu": [ "ppc64" ], @@ -1067,13 +1068,13 @@ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz", + "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", "cpu": [ "arm" ], @@ -1083,13 +1084,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", + "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", "cpu": [ "arm64" ], @@ -1099,13 +1100,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz", + "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", "cpu": [ "x64" ], @@ -1115,13 +1116,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", + "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", "cpu": [ "arm64" ], @@ -1131,13 +1132,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", + "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", "cpu": [ "x64" ], @@ -1147,13 +1148,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", + "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", "cpu": [ "arm64" ], @@ -1163,13 +1164,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", + "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", "cpu": [ "x64" ], @@ -1179,13 +1180,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", + "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", "cpu": [ "arm" ], @@ -1195,13 +1196,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", + "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", "cpu": [ "arm64" ], @@ -1211,13 +1212,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", + "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", "cpu": [ "ia32" ], @@ -1227,13 +1228,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", + "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", "cpu": [ "loong64" ], @@ -1243,13 +1244,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", + "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", "cpu": [ "mips64el" ], @@ -1259,13 +1260,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", + "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", "cpu": [ "ppc64" ], @@ -1275,13 +1276,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", + "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", "cpu": [ "riscv64" ], @@ -1291,13 +1292,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", + "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", "cpu": [ "s390x" ], @@ -1307,13 +1308,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", + "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", "cpu": [ "x64" ], @@ -1323,17 +1324,16 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", - "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", + "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1344,9 +1344,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", + "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", "cpu": [ "x64" ], @@ -1356,17 +1356,16 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", - "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", + "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1377,9 +1376,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", + "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", "cpu": [ "x64" ], @@ -1389,17 +1388,16 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", - "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", + "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1410,9 +1408,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", + "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", "cpu": [ "x64" ], @@ -1422,13 +1420,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", + "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", "cpu": [ "arm64" ], @@ -1438,13 +1436,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", + "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", "cpu": [ "ia32" ], @@ -1454,13 +1452,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", + "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", "cpu": [ "x64" ], @@ -1470,7 +1468,7 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@floating-ui/core": { @@ -4015,6 +4013,412 @@ "drizzle-kit": "bin.cjs" } }, + "node_modules/drizzle-kit/node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/drizzle-kit/node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, "node_modules/drizzle-orm": { "version": "0.36.4", "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.36.4.tgz", @@ -4368,41 +4772,44 @@ } }, "node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", + "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==", "hasInstallScript": true, "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@esbuild/aix-ppc64": "0.25.11", + "@esbuild/android-arm": "0.25.11", + "@esbuild/android-arm64": "0.25.11", + "@esbuild/android-x64": "0.25.11", + "@esbuild/darwin-arm64": "0.25.11", + "@esbuild/darwin-x64": "0.25.11", + "@esbuild/freebsd-arm64": "0.25.11", + "@esbuild/freebsd-x64": "0.25.11", + "@esbuild/linux-arm": "0.25.11", + "@esbuild/linux-arm64": "0.25.11", + "@esbuild/linux-ia32": "0.25.11", + "@esbuild/linux-loong64": "0.25.11", + "@esbuild/linux-mips64el": "0.25.11", + "@esbuild/linux-ppc64": "0.25.11", + "@esbuild/linux-riscv64": "0.25.11", + "@esbuild/linux-s390x": "0.25.11", + "@esbuild/linux-x64": "0.25.11", + "@esbuild/netbsd-arm64": "0.25.11", + "@esbuild/netbsd-x64": "0.25.11", + "@esbuild/openbsd-arm64": "0.25.11", + "@esbuild/openbsd-x64": "0.25.11", + "@esbuild/openharmony-arm64": "0.25.11", + "@esbuild/sunos-x64": "0.25.11", + "@esbuild/win32-arm64": "0.25.11", + "@esbuild/win32-ia32": "0.25.11", + "@esbuild/win32-x64": "0.25.11" } }, "node_modules/esbuild-register": { @@ -6826,439 +7233,6 @@ "fsevents": "~2.3.3" } }, - "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", - "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/android-arm": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", - "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/android-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", - "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/android-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", - "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", - "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/darwin-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", - "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", - "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", - "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-arm": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", - "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", - "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-ia32": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", - "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-loong64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", - "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", - "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", - "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", - "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-s390x": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", - "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", - "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", - "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", - "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/sunos-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", - "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/win32-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", - "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/win32-ia32": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", - "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/@esbuild/win32-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", - "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/tsx/node_modules/esbuild": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", - "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.10", - "@esbuild/android-arm": "0.25.10", - "@esbuild/android-arm64": "0.25.10", - "@esbuild/android-x64": "0.25.10", - "@esbuild/darwin-arm64": "0.25.10", - "@esbuild/darwin-x64": "0.25.10", - "@esbuild/freebsd-arm64": "0.25.10", - "@esbuild/freebsd-x64": "0.25.10", - "@esbuild/linux-arm": "0.25.10", - "@esbuild/linux-arm64": "0.25.10", - "@esbuild/linux-ia32": "0.25.10", - "@esbuild/linux-loong64": "0.25.10", - "@esbuild/linux-mips64el": "0.25.10", - "@esbuild/linux-ppc64": "0.25.10", - "@esbuild/linux-riscv64": "0.25.10", - "@esbuild/linux-s390x": "0.25.10", - "@esbuild/linux-x64": "0.25.10", - "@esbuild/netbsd-arm64": "0.25.10", - "@esbuild/netbsd-x64": "0.25.10", - "@esbuild/openbsd-arm64": "0.25.10", - "@esbuild/openbsd-x64": "0.25.10", - "@esbuild/openharmony-arm64": "0.25.10", - "@esbuild/sunos-x64": "0.25.10", - "@esbuild/win32-arm64": "0.25.10", - "@esbuild/win32-ia32": "0.25.10", - "@esbuild/win32-x64": "0.25.10" - } - }, "node_modules/tw-animate-css": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", @@ -7578,439 +7552,6 @@ } } }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", - "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", - "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", - "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", - "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", - "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", - "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", - "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", - "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", - "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", - "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", - "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", - "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", - "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", - "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", - "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", - "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", - "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", - "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", - "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", - "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", - "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", - "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", - "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", - "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.10", - "@esbuild/android-arm": "0.25.10", - "@esbuild/android-arm64": "0.25.10", - "@esbuild/android-x64": "0.25.10", - "@esbuild/darwin-arm64": "0.25.10", - "@esbuild/darwin-x64": "0.25.10", - "@esbuild/freebsd-arm64": "0.25.10", - "@esbuild/freebsd-x64": "0.25.10", - "@esbuild/linux-arm": "0.25.10", - "@esbuild/linux-arm64": "0.25.10", - "@esbuild/linux-ia32": "0.25.10", - "@esbuild/linux-loong64": "0.25.10", - "@esbuild/linux-mips64el": "0.25.10", - "@esbuild/linux-ppc64": "0.25.10", - "@esbuild/linux-riscv64": "0.25.10", - "@esbuild/linux-s390x": "0.25.10", - "@esbuild/linux-x64": "0.25.10", - "@esbuild/netbsd-arm64": "0.25.10", - "@esbuild/netbsd-x64": "0.25.10", - "@esbuild/openbsd-arm64": "0.25.10", - "@esbuild/openbsd-x64": "0.25.10", - "@esbuild/openharmony-arm64": "0.25.10", - "@esbuild/sunos-x64": "0.25.10", - "@esbuild/win32-arm64": "0.25.10", - "@esbuild/win32-ia32": "0.25.10", - "@esbuild/win32-x64": "0.25.10" - } - }, "node_modules/which": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", diff --git a/package.json b/package.json index 8ce8d9d..faff18f 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,15 @@ "private": true, "type": "module", "scripts": { - "build": "react-router build", + "build": "npm run build:remix && npm run build:server", + "build:remix": "react-router build", + "build:server": "node scripts/build-server.mjs", "db:generate": "dotenv -- drizzle-kit generate", "db:migrate": "dotenv -- drizzle-kit migrate", - "dev": "dotenv -- node server.js", - "start": "node server.js", - "start:production": "drizzle-kit migrate && node server.js", - "typecheck": "react-router typegen && tsc -b" + "dev": "dotenv -- tsx watch server.ts", + "start": "node dist/server.js", + "start:production": "drizzle-kit migrate && node dist/server.js", + "typecheck": "react-router typegen && tsc -b && tsc -p tsconfig.server.json --noEmit" }, "dependencies": { "@clerk/react-router": "^2.1.0", @@ -50,17 +52,18 @@ "devDependencies": { "@react-router/dev": "^7.7.1", "@tailwindcss/vite": "^4.1.4", - "@types/compression": "^1.7.5", - "@types/express": "^5.0.1", + "@types/compression": "^1.8.1", + "@types/express": "^5.0.3", "@types/express-serve-static-core": "^5.0.6", - "@types/morgan": "^1.9.9", + "@types/morgan": "^1.9.10", "@types/node": "^20", "@types/pg": "^8.11.14", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", "dotenv-cli": "^8.0.0", + "esbuild": "^0.25.11", "tailwindcss": "^4.1.4", - "tsx": "^4.19.2", + "tsx": "^4.20.6", "tw-animate-css": "^1.4.0", "typescript": "^5.8.3", "vite": "^6.3.3", diff --git a/plans/server-typescript-conversion-compiled.md b/plans/server-typescript-conversion-compiled.md new file mode 100644 index 0000000..686cf41 --- /dev/null +++ b/plans/server-typescript-conversion-compiled.md @@ -0,0 +1,655 @@ +# Server TypeScript Conversion Plan (With Compilation) + +## Overview +Convert the Node.js server from JavaScript to TypeScript with a build step for production. This approach uses tsx for development (fast iteration) but compiles to optimized JavaScript for production (zero runtime overhead). + +## Key Differences from TSX-Only Approach +- **Development**: Uses `tsx` for fast iteration and hot reload +- **Production**: Compiles to JavaScript, runs pure Node.js (no TypeScript overhead) +- **Build Process**: Adds a server compilation step +- **Docker**: Smaller production image (no tsx runtime needed) + +## Current State Analysis + +### Files to Convert +1. **`server.js`** (57 lines) - Main server entry point +2. **`server/socket.js`** (89 lines) - Socket.IO server + +### Existing TypeScript Configuration +- **`tsconfig.node.json`** already includes `server/**/*.ts` +- Module resolution is set to "bundler" with ES2022 target +- Path aliases configured: `~/` for app, `~/database/` for database + +## Implementation Steps + +### Step 1: Install Required Dependencies + +```bash +# Development dependencies +npm install --save-dev tsx @types/express @types/morgan @types/compression esbuild + +# Why these packages? +# - tsx: Fast TypeScript execution for development +# - @types/*: TypeScript definitions +# - esbuild: Ultra-fast TypeScript compiler for production builds +``` + +### Step 2: Create Server TypeScript Configuration + +**File: `tsconfig.server.json`** + +```json +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "composite": false, + "noEmit": false, + "outDir": "./dist/server", + "rootDir": ".", + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "node", + "lib": ["ES2022"], + "types": ["node"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "paths": { + "~/*": ["./app/*"], + "~/database/*": ["./database/*"] + } + }, + "include": [ + "server.ts", + "server/**/*.ts", + "database/**/*.ts", + "app/database/**/*.ts" + ], + "exclude": [ + "node_modules", + "build", + "dist", + "**/*.test.ts", + "**/*.spec.ts" + ] +} +``` + +### Step 3: Create Build Script for Server + +**File: `scripts/build-server.mjs`** + +```javascript +import * as esbuild from 'esbuild'; +import { nodeExternalsPlugin } from 'esbuild-node-externals'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +async function build() { + try { + await esbuild.build({ + entryPoints: ['./server.ts'], + bundle: true, + platform: 'node', + target: 'node20', + format: 'esm', + outfile: 'dist/server.js', + sourcemap: true, + minify: process.env.NODE_ENV === 'production', + + // Handle TypeScript path aliases + alias: { + '~': path.resolve(__dirname, '../app'), + '~/database': path.resolve(__dirname, '../database'), + }, + + // External dependencies (don't bundle node_modules) + external: [ + 'express', + 'compression', + 'morgan', + 'vite', + 'socket.io', + 'drizzle-orm', + 'postgres', + '@clerk/*', + './build/server/index.js', // Production build reference + ], + + // Keep import.meta.url working + banner: { + js: ` +import { createRequire } from 'module'; +import { fileURLToPath } from 'url'; +import path from 'path'; +const require = createRequire(import.meta.url); +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + `.trim() + } + }); + + console.log('✅ Server build complete'); + } catch (error) { + console.error('❌ Build failed:', error); + process.exit(1); + } +} + +build(); +``` + +### Step 4: Convert server.js to server.ts + +**File: `server.ts`** + +```typescript +import compression from "compression"; +import express, { Express, Request, Response, NextFunction } from "express"; +import morgan from "morgan"; +import { createServer } from "http"; +import type { ViteDevServer } from "vite"; + +// Configuration +const BUILD_PATH = "./build/server/index.js"; +const DEVELOPMENT = process.env.NODE_ENV === "development"; +const PORT = Number.parseInt(process.env.PORT || "3000", 10); + +async function createAppServer(): Promise { + const app: Express = express(); + + app.use(compression()); + app.disable("x-powered-by"); + + if (DEVELOPMENT) { + console.log("Starting development server"); + + // Dynamic import Vite only in development + const { createServer: createViteServer } = await import("vite"); + + const viteDevServer: ViteDevServer = await createViteServer({ + server: { middlewareMode: true }, + }); + + app.use(viteDevServer.middlewares); + + app.use(async (req: Request, res: Response, next: NextFunction) => { + try { + const source = await viteDevServer.ssrLoadModule("./server/app.ts"); + return await source.app(req, res, next); + } catch (error) { + if (error instanceof Error) { + viteDevServer.ssrFixStacktrace(error); + } + next(error); + } + }); + } else { + console.log("Starting production server"); + + app.use( + "/assets", + express.static("build/client/assets", { + immutable: true, + maxAge: "1y" + }) + ); + + app.use(morgan("tiny")); + app.use(express.static("build/client", { maxAge: "1h" })); + + // Import the built React Router app + const { app: productionApp } = await import(BUILD_PATH); + app.use(productionApp); + } + + // Create HTTP server + const httpServer = createServer(app); + + // Initialize Socket.IO - will be compiled to .js in production + const { initializeSocketIO } = await import("./server/socket"); + initializeSocketIO(httpServer); + + // Start server + httpServer.listen(PORT, () => { + console.log(`Server is running on http://localhost:${PORT}`); + }); +} + +// Start the server with error handling +createAppServer().catch((error) => { + console.error("Failed to start server:", error); + process.exit(1); +}); +``` + +### Step 5: Convert server/socket.js to server/socket.ts + +**File: `server/socket.ts`** + +```typescript +import { Server as SocketIOServer, Socket } from "socket.io"; +import type { Server as HTTPServer } from "http"; + +// Socket event types +interface ServerToClientEvents { + "test-message": (data: { + originalMessage: any; + serverResponse: string; + serverTimestamp: string; + socketId: string; + }) => void; + "pick-made": (data: any) => void; + "draft-started": (data: { seasonId: string; currentPickNumber: number }) => void; + "draft-completed": () => void; + "timer-update": (data: { + seasonId: string; + teamId: string; + timeRemaining: number; + currentPickNumber: number; + }) => void; +} + +interface ClientToServerEvents { + "join-draft": (seasonId: string) => void; + "leave-draft": (seasonId: string) => void; + "test-event": (data: any) => void; +} + +// Global type augmentation +declare global { + var __socketIO: SocketIOServer | undefined; +} + +let io: SocketIOServer | null = null; + +/** + * Initialize Socket.IO server + */ +export function initializeSocketIO(httpServer: HTTPServer): SocketIOServer { + if (io) { + console.log("Socket.IO already initialized"); + return io; + } + + // Create typed Socket.IO server + io = new SocketIOServer(httpServer, { + cors: process.env.NODE_ENV === "production" && process.env.APP_URL + ? { + origin: process.env.APP_URL, + credentials: true, + } + : undefined, + }); + + // Connection handling + io.on("connection", (socket: Socket) => { + console.log("Client connected:", socket.id); + + socket.on("join-draft", (seasonId: string) => { + if (!seasonId) { + console.error("No seasonId provided for join-draft"); + return; + } + socket.join(`draft-${seasonId}`); + console.log(`Socket ${socket.id} joined draft-${seasonId}`); + }); + + socket.on("leave-draft", (seasonId: string) => { + if (!seasonId) return; + socket.leave(`draft-${seasonId}`); + console.log(`Socket ${socket.id} left draft-${seasonId}`); + }); + + socket.on("test-event", (data: any) => { + console.log("📨 Received test-event from client:", socket.id, data); + + socket.emit("test-message", { + originalMessage: data, + serverResponse: "Hello from server!", + serverTimestamp: new Date().toISOString(), + socketId: socket.id, + }); + + console.log("✅ Sent test-message response to client:", socket.id); + }); + + socket.on("disconnect", () => { + console.log("Client disconnected:", socket.id); + }); + }); + + // Store globally for route handlers + global.__socketIO = io; + + console.log("Socket.IO initialized"); + return io; +} + +/** + * Get the Socket.IO server instance + */ +export function getSocketIO(): SocketIOServer { + const instance = io || global.__socketIO; + if (!instance) { + throw new Error("Socket.IO not initialized. Call initializeSocketIO first."); + } + return instance; +} +``` + +### Step 6: Update package.json Scripts + +```json +{ + "scripts": { + "build": "npm run build:remix && npm run build:server", + "build:remix": "react-router build", + "build:server": "node scripts/build-server.mjs", + "db:generate": "dotenv -- drizzle-kit generate", + "db:migrate": "dotenv -- drizzle-kit migrate", + "dev": "dotenv -- tsx watch server.ts", + "start": "node dist/server.js", + "start:production": "drizzle-kit migrate && node dist/server.js", + "typecheck": "react-router typegen && tsc -b && tsc -p tsconfig.server.json --noEmit" + } +} +``` + +**Key Changes:** +- `build`: Now builds both React Router app AND server +- `build:server`: Compiles TypeScript server to JavaScript +- `dev`: Uses `tsx watch` for development (fast, no compilation) +- `start`: Runs compiled JavaScript (no TypeScript overhead) +- `typecheck`: Also checks server TypeScript + +### Step 7: Update Dockerfile (Optimized for Production) + +```dockerfile +# Stage 1: Install all dependencies +FROM node:20-alpine AS dependencies +WORKDIR /app +COPY package*.json ./ +RUN npm ci + +# Stage 2: Build the application +FROM node:20-alpine AS builder +WORKDIR /app +COPY package*.json ./ +COPY --from=dependencies /app/node_modules ./node_modules +COPY . . +# Build both React Router app and server +RUN npm run build + +# Stage 3: Production dependencies only +FROM node:20-alpine AS prod-dependencies +WORKDIR /app +COPY package*.json ./ +RUN npm ci --omit=dev + +# Stage 4: Final production image (minimal) +FROM node:20-alpine +WORKDIR /app + +# Copy only what's needed for production +COPY package*.json ./ +COPY --from=prod-dependencies /app/node_modules ./node_modules +COPY --from=builder /app/build ./build +COPY --from=builder /app/dist ./dist +COPY ./drizzle ./drizzle +COPY ./drizzle.config.ts ./drizzle.config.ts + +# No TypeScript files needed in production! +# The server runs pure JavaScript from dist/server.js + +EXPOSE 3000 +CMD ["npm", "run", "start:production"] +``` + +### Step 8: Create Timer System (With Proper Imports) + +**File: `server/timer.ts`** + +```typescript +import { database } from "~/database/context"; +import * as schema from "~/database/schema"; +import { eq, and, desc, asc, inArray, notInArray } from "drizzle-orm"; +import { getSocketIO } from "./socket"; + +let timerInterval: NodeJS.Timeout | null = null; + +export function startDraftTimerSystem(): void { + if (timerInterval) { + clearInterval(timerInterval); + } + + timerInterval = setInterval(async () => { + try { + await updateDraftTimers(); + } catch (error) { + console.error("[Timer] Error updating draft timers:", error); + } + }, 1000); + + console.log("[Timer] Draft timer system started"); +} + +async function updateDraftTimers(): Promise { + const db = database(); + const io = getSocketIO(); + + // Get all active drafts + const activeDrafts = await db.query.seasons.findMany({ + where: eq(schema.seasons.status, "draft"), + }); + + for (const season of activeDrafts) { + if (season.draftPaused) continue; + + const currentPickNumber = season.currentPickNumber || 1; + + // Get draft slots + const draftSlots = await db.query.draftSlots.findMany({ + where: eq(schema.draftSlots.seasonId, season.id), + orderBy: schema.draftSlots.draftOrder, + }); + + // Calculate current team + const totalTeams = draftSlots.length; + const currentRound = Math.ceil(currentPickNumber / totalTeams); + const isEvenRound = currentRound % 2 === 0; + let pickInRound = ((currentPickNumber - 1) % totalTeams) + 1; + + if (isEvenRound) { + pickInRound = totalTeams - pickInRound + 1; + } + + const currentDraftSlot = draftSlots.find( + (slot) => slot.draftOrder === pickInRound + ); + + if (!currentDraftSlot) continue; + + // Update timer + const timer = await db.query.draftTimers.findFirst({ + where: and( + eq(schema.draftTimers.seasonId, season.id), + eq(schema.draftTimers.teamId, currentDraftSlot.teamId) + ), + }); + + if (!timer) continue; + + const newTimeRemaining = Math.max(0, timer.timeRemaining - 1); + + // Update in database + await db + .update(schema.draftTimers) + .set({ + timeRemaining: newTimeRemaining, + updatedAt: new Date(), + }) + .where(eq(schema.draftTimers.id, timer.id)); + + // Broadcast update + io.to(`draft-${season.id}`).emit("timer-update", { + seasonId: season.id, + teamId: currentDraftSlot.teamId, + timeRemaining: newTimeRemaining, + currentPickNumber, + }); + + // Trigger auto-pick if timer expired + if (newTimeRemaining === 0 && timer.timeRemaining > 0) { + console.log(`[Timer] Timer expired for team ${currentDraftSlot.teamId}`); + // Auto-pick logic here... + } + } +} +``` + +### Step 9: Update .gitignore + +Add these entries: +``` +# Compiled server output +/dist +dist/ + +# Keep source TypeScript files +!server.ts +!server/**/*.ts +``` + +## Alternative: Use Vite to Build Server + +If you prefer to use Vite for consistency, create: + +**File: `vite.config.server.ts`** + +```typescript +import { defineConfig } from 'vite'; +import tsconfigPaths from 'vite-tsconfig-paths'; + +export default defineConfig({ + build: { + ssr: true, + target: 'node20', + outDir: 'dist', + rollupOptions: { + input: './server.ts', + external: [ + /^node:/, + 'express', + 'compression', + 'morgan', + 'vite', + 'socket.io', + 'drizzle-orm', + 'postgres', + ], + output: { + format: 'es', + }, + }, + }, + plugins: [tsconfigPaths()], + ssr: { + noExternal: ['~/database', '~/app'], + }, +}); +``` + +Then update build script: +```json +"build:server": "vite build --config vite.config.server.ts" +``` + +## Testing Plan + +### 1. Development Testing +```bash +# Start with tsx (fast, no compilation) +npm run dev + +# Verify: +- Server starts quickly +- Hot reload works +- Can import TypeScript modules +- Socket.IO connects +``` + +### 2. Build Testing +```bash +# Build server +npm run build:server + +# Check output +ls -la dist/ +# Should see: server.js, server.js.map + +# Test compiled server +NODE_ENV=production npm start + +# Verify: +- Runs pure JavaScript (no tsx) +- All features work +- No TypeScript overhead +``` + +### 3. Performance Comparison +```bash +# Measure startup time with tsx +time npm run dev + +# Measure startup time with compiled JS +time npm start + +# Compiled version should be faster +``` + +## Benefits of This Approach + +1. **Development Speed**: tsx provides fast iteration in development +2. **Production Performance**: Zero TypeScript overhead in production +3. **Type Safety**: Full TypeScript benefits during development +4. **Smaller Docker Image**: No tsx or TypeScript in production container +5. **Faster Startup**: Compiled JavaScript starts faster than tsx +6. **Debugging**: Source maps available in both dev and production + +## Migration Checklist + +- [ ] Install dependencies (tsx, esbuild, types) +- [ ] Create tsconfig.server.json +- [ ] Create build-server.mjs script +- [ ] Convert server.js to server.ts +- [ ] Convert server/socket.js to server/socket.ts +- [ ] Update package.json scripts +- [ ] Test development mode with tsx +- [ ] Test build process +- [ ] Test production with compiled JS +- [ ] Update Dockerfile +- [ ] Update .gitignore +- [ ] Implement timer system +- [ ] Remove old .js files + +## Performance Metrics + +Expected improvements: +- **Startup time**: 30-50% faster (no TypeScript compilation) +- **Memory usage**: 20-30% less (no tsx runtime) +- **CPU usage**: Lower (no runtime transpilation) +- **Docker image size**: ~50MB smaller (no TypeScript dependencies) + +## Rollback Plan + +1. Keep original .js files until testing complete +2. Git commit before conversion +3. Can revert package.json to use .js files +4. Compiled output is separate from source diff --git a/plans/server-typescript-conversion.md b/plans/server-typescript-conversion.md new file mode 100644 index 0000000..13f03f9 --- /dev/null +++ b/plans/server-typescript-conversion.md @@ -0,0 +1,493 @@ +# Server TypeScript Conversion Plan + +## Overview +Convert the Node.js server from JavaScript to TypeScript to improve type safety, developer experience, and enable proper imports of TypeScript modules (database, schemas, etc.) from within the server code. + +## Current State Analysis + +### Files to Convert +1. **`server.js`** (57 lines) - Main server entry point + - Handles Vite dev server setup + - Production build serving + - Socket.IO initialization + - Express middleware + +2. **`server/socket.js`** (89 lines) - Socket.IO server + - Connection handling + - Draft room management + - Global instance storage + +### Existing TypeScript Configuration +- **`tsconfig.node.json`** already includes `server/**/*.ts` +- Module resolution is set to "bundler" with ES2022 target +- Path aliases configured: `~/` for app, `~/database/` for database + +### Dependencies Already Installed +- TypeScript is already in devDependencies +- `@types/node` is configured in tsconfig.node.json +- Vite handles TypeScript compilation in development + +## Implementation Steps + +### Step 1: Install Required Dependencies +```bash +npm install --save-dev tsx @types/express @types/morgan @types/compression +``` + +**Why tsx?** +- Fastest TypeScript runner for Node.js +- No compilation step needed +- Works in both development and production +- Supports ESM modules and path aliases +- Better than ts-node for performance + +### Step 2: Convert server.js to server.ts + +**File: `server.ts`** + +```typescript +import compression from "compression"; +import express, { Express } from "express"; +import morgan from "morgan"; +import { createServer } from "http"; +import { ViteDevServer } from "vite"; +import path from "path"; +import { fileURLToPath } from "url"; + +// ESM module resolution helpers +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Configuration +const BUILD_PATH = "./build/server/index.js"; +const DEVELOPMENT = process.env.NODE_ENV === "development"; +const PORT = Number.parseInt(process.env.PORT || "3000", 10); + +async function createAppServer(): Promise { + const app: Express = express(); + + app.use(compression()); + app.disable("x-powered-by"); + + if (DEVELOPMENT) { + console.log("Starting development server"); + + // Dynamic import to avoid bundling Vite in production + const { createServer: createViteServer } = await import("vite"); + + const viteDevServer: ViteDevServer = await createViteServer({ + server: { middlewareMode: true }, + }); + + app.use(viteDevServer.middlewares); + + app.use(async (req, res, next) => { + try { + const source = await viteDevServer.ssrLoadModule("./server/app.ts"); + return await source.app(req, res, next); + } catch (error) { + if (error instanceof Error) { + viteDevServer.ssrFixStacktrace(error); + } + next(error); + } + }); + } else { + console.log("Starting production server"); + + app.use( + "/assets", + express.static("build/client/assets", { + immutable: true, + maxAge: "1y" + }) + ); + + app.use(morgan("tiny")); + app.use(express.static("build/client", { maxAge: "1h" })); + + // Dynamic import for production build + const { app: productionApp } = await import(BUILD_PATH); + app.use(productionApp); + } + + // Create HTTP server + const httpServer = createServer(app); + + // Initialize Socket.IO + const { initializeSocketIO } = await import("./server/socket.js"); + initializeSocketIO(httpServer); + + // Start server + httpServer.listen(PORT, () => { + console.log(`Server is running on http://localhost:${PORT}`); + }); +} + +// Start the server +createAppServer().catch((error) => { + console.error("Failed to start server:", error); + process.exit(1); +}); +``` + +### Step 3: Convert server/socket.js to server/socket.ts + +**File: `server/socket.ts`** + +```typescript +import { Server as SocketIOServer, Socket } from "socket.io"; +import { Server as HTTPServer } from "http"; + +// Global type augmentation for Socket.IO instance +declare global { + var __socketIO: SocketIOServer | undefined; +} + +let io: SocketIOServer | null = null; + +/** + * Initialize Socket.IO server + */ +export function initializeSocketIO(httpServer: HTTPServer): SocketIOServer { + if (io) { + console.log("Socket.IO already initialized"); + return io; + } + + // Create Socket.IO server with proper typing + io = new SocketIOServer(httpServer, { + cors: process.env.NODE_ENV === "production" && process.env.APP_URL + ? { + origin: process.env.APP_URL, + credentials: true, + } + : undefined, + }); + + // Connection handling with typed socket + io.on("connection", (socket: Socket) => { + console.log("Client connected:", socket.id); + + // Join draft room + socket.on("join-draft", (seasonId: string) => { + if (!seasonId) { + console.error("No seasonId provided for join-draft"); + return; + } + socket.join(`draft-${seasonId}`); + console.log(`Socket ${socket.id} joined draft-${seasonId}`); + }); + + // Leave draft room + socket.on("leave-draft", (seasonId: string) => { + if (!seasonId) return; + socket.leave(`draft-${seasonId}`); + console.log(`Socket ${socket.id} left draft-${seasonId}`); + }); + + // Test event handler + socket.on("test-event", (data: any) => { + console.log("📨 Received test-event from client:", socket.id, data); + + socket.emit("test-message", { + originalMessage: data, + serverResponse: "Hello from server!", + serverTimestamp: new Date().toISOString(), + socketId: socket.id, + }); + + console.log("✅ Sent test-message response to client:", socket.id); + }); + + socket.on("disconnect", () => { + console.log("Client disconnected:", socket.id); + }); + }); + + // Store globally for access in route handlers + global.__socketIO = io; + + console.log("Socket.IO initialized"); + return io; +} + +/** + * Get the Socket.IO server instance + */ +export function getSocketIO(): SocketIOServer { + const instance = io || global.__socketIO; + if (!instance) { + throw new Error("Socket.IO not initialized. Call initializeSocketIO first."); + } + return instance; +} +``` + +### Step 4: Create Type Definitions for Socket Events (Optional but Recommended) + +**File: `server/socket.types.ts`** + +```typescript +// Define your socket event types for better type safety +export interface ServerToClientEvents { + "test-message": (data: { + originalMessage: any; + serverResponse: string; + serverTimestamp: string; + socketId: string; + }) => void; + "pick-made": (data: any) => void; + "draft-started": (data: { seasonId: string; currentPickNumber: number }) => void; + "draft-completed": () => void; + "timer-update": (data: { + seasonId: string; + teamId: string; + timeRemaining: number; + currentPickNumber: number; + }) => void; +} + +export interface ClientToServerEvents { + "join-draft": (seasonId: string) => void; + "leave-draft": (seasonId: string) => void; + "test-event": (data: any) => void; +} + +export interface InterServerEvents { + ping: () => void; +} + +export interface SocketData { + userId?: string; + teamId?: string; +} +``` + +Then update `server/socket.ts` to use these types: + +```typescript +import { Server as SocketIOServer } from "socket.io"; +import type { + ServerToClientEvents, + ClientToServerEvents, + InterServerEvents, + SocketData +} from "./socket.types"; + +// Create typed Socket.IO server +io = new SocketIOServer< + ClientToServerEvents, + ServerToClientEvents, + InterServerEvents, + SocketData +>(httpServer, { /* ... */ }); +``` + +### Step 5: Update package.json Scripts + +```json +{ + "scripts": { + "build": "react-router build", + "db:generate": "dotenv -- drizzle-kit generate", + "db:migrate": "dotenv -- drizzle-kit migrate", + "dev": "dotenv -- tsx watch server.ts", + "start": "tsx server.ts", + "start:production": "drizzle-kit migrate && tsx server.ts", + "typecheck": "react-router typegen && tsc -b" + } +} +``` + +**Key Changes:** +- `dev`: Uses `tsx watch` for auto-restart on file changes +- `start`: Uses `tsx` to run TypeScript directly +- `start:production`: Same as start but with migration + +### Step 6: Update Dockerfile + +```dockerfile +FROM node:20-alpine AS development-dependencies-env +COPY . /app +WORKDIR /app +RUN npm ci + +FROM node:20-alpine AS production-dependencies-env +COPY ./package.json package-lock.json /app/ +WORKDIR /app +RUN npm ci --omit=dev + +FROM node:20-alpine AS build-env +COPY . /app/ +COPY --from=development-dependencies-env /app/node_modules /app/node_modules +WORKDIR /app +RUN npm run build + +FROM node:20-alpine +# Copy TypeScript files instead of JavaScript +COPY ./package.json package-lock.json server.ts /app/ +COPY --from=production-dependencies-env /app/node_modules /app/node_modules +COPY --from=build-env /app/build /app/build +COPY ./drizzle /app/drizzle +COPY ./drizzle.config.ts /app/drizzle.config.ts +COPY ./server /app/server +COPY ./database /app/database +COPY ./app /app/app +# Copy TypeScript configs for tsx runtime +COPY ./tsconfig*.json /app/ +WORKDIR /app +# Install tsx in production for running TypeScript +RUN npm install tsx +CMD ["npm", "run", "start:production"] +``` + +### Step 7: Update server.ts Import Path + +After converting socket.js to socket.ts, update the import in server.ts: + +```typescript +// Change from: +const { initializeSocketIO } = await import("./server/socket.js"); + +// To: +const { initializeSocketIO } = await import("./server/socket"); +// Or explicitly: +const { initializeSocketIO } = await import("./server/socket.ts"); +``` + +### Step 8: Add Timer Functionality (Now Possible!) + +With TypeScript server, you can now properly import database modules: + +**File: `server/timer.ts`** + +```typescript +import { database } from "~/database/context"; +import * as schema from "~/database/schema"; +import { eq, and } from "drizzle-orm"; +import { getSocketIO } from "./socket"; + +export async function startDraftTimerSystem(): Promise { + setInterval(async () => { + try { + await updateDraftTimers(); + } catch (error) { + console.error("[Timer] Error updating draft timers:", error); + } + }, 1000); + + console.log("[Timer] Draft timer system started"); +} + +async function updateDraftTimers(): Promise { + const db = database(); + const io = getSocketIO(); + + // Get all active drafts + const activeDrafts = await db.query.seasons.findMany({ + where: eq(schema.seasons.status, "draft"), + }); + + // ... rest of timer logic +} +``` + +Then in `server/socket.ts`, add: + +```typescript +import { startDraftTimerSystem } from "./timer"; + +export function initializeSocketIO(httpServer: HTTPServer): SocketIOServer { + // ... existing code ... + + // Start timer system after Socket.IO is initialized + startDraftTimerSystem(); + + return io; +} +``` + +## Testing Plan + +### 1. Development Mode Testing +```bash +# Start dev server with TypeScript +npm run dev + +# Verify: +- Server starts without errors +- Vite dev server works +- Socket.IO connections work +- Hot reload works with tsx watch +- Can import database modules +``` + +### 2. Production Build Testing +```bash +# Build the app +npm run build + +# Start production server +NODE_ENV=production npm start + +# Verify: +- Server starts without errors +- Static assets are served +- Socket.IO works in production +- No TypeScript compilation errors +``` + +### 3. Docker Testing +```bash +# Build Docker image +docker build -t brackt-test . + +# Run container +docker run -p 3000:3000 brackt-test + +# Verify: +- Container starts successfully +- Application works in containerized environment +``` + +## Benefits of This Approach + +1. **Type Safety**: Full TypeScript throughout the server +2. **Better Imports**: Can import TypeScript modules directly +3. **No Build Step**: tsx runs TypeScript directly +4. **Fast Development**: tsx watch provides fast restarts +5. **Production Ready**: tsx works in production without compilation +6. **Maintainable**: Cleaner code with proper types +7. **Debugging**: Better stack traces and error messages + +## Migration Checklist + +- [ ] Install tsx and type packages +- [ ] Convert server.js to server.ts +- [ ] Convert server/socket.js to server/socket.ts +- [ ] Create socket.types.ts for event types +- [ ] Update package.json scripts +- [ ] Update Dockerfile +- [ ] Test development mode +- [ ] Test production build +- [ ] Test Docker build +- [ ] Implement timer system with database imports +- [ ] Remove old .js files + +## Rollback Plan + +If issues arise: +1. Keep original .js files until testing complete +2. Can revert package.json scripts to use .js files +3. Git commit before starting conversion +4. Test thoroughly in development before deploying + +## Notes for Implementation + +- tsx handles TypeScript path aliases (`~/`) automatically +- No need for separate compilation step +- Works with existing Vite setup +- Compatible with React Router v7 SSR +- Maintains existing Socket.IO functionality +- Enables proper database module imports diff --git a/scripts/build-server.mjs b/scripts/build-server.mjs new file mode 100644 index 0000000..84ea3f1 --- /dev/null +++ b/scripts/build-server.mjs @@ -0,0 +1,57 @@ +import * as esbuild from 'esbuild'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +async function build() { + try { + await esbuild.build({ + entryPoints: ['./server.ts'], + bundle: true, + platform: 'node', + target: 'node20', + format: 'esm', + outfile: 'dist/server.js', + sourcemap: true, + minify: process.env.NODE_ENV === 'production', + + // Handle TypeScript path aliases by bundling app code + alias: { + '~': path.resolve(__dirname, '../app'), + '~/database': path.resolve(__dirname, '../database'), + }, + + // External dependencies (don't bundle node_modules) + // But DO bundle our application code (database, app modules) + external: [ + 'express', + 'compression', + 'morgan', + 'vite', + 'socket.io', + 'drizzle-orm', + 'postgres', + '@clerk/*', + '@react-router/*', + '../build/server/index.js', // Production build reference (relative to dist/) + ], + + // Keep import.meta.url working + // Note: path, fileURLToPath are imported in source, so we don't duplicate them here + banner: { + js: ` +import { createRequire } from 'module'; +const require = createRequire(import.meta.url); + `.trim() + } + }); + + console.log('✅ Server build complete'); + } catch (error) { + console.error('❌ Build failed:', error); + process.exit(1); + } +} + +build(); diff --git a/server.js b/server.js deleted file mode 100644 index 91fc6c5..0000000 --- a/server.js +++ /dev/null @@ -1,56 +0,0 @@ -import compression from "compression"; -import express from "express"; -import morgan from "morgan"; -import { createServer } from "http"; - -// Short-circuit the type-checking of the built output. -const BUILD_PATH = "./build/server/index.js"; -const DEVELOPMENT = process.env.NODE_ENV === "development"; -const PORT = Number.parseInt(process.env.PORT || "3000"); - -const app = express(); - -app.use(compression()); -app.disable("x-powered-by"); - -if (DEVELOPMENT) { - console.log("Starting development server"); - const viteDevServer = await import("vite").then((vite) => - vite.createServer({ - server: { middlewareMode: true }, - }), - ); - app.use(viteDevServer.middlewares); - app.use(async (req, res, next) => { - try { - const source = await viteDevServer.ssrLoadModule("./server/app.ts"); - return await source.app(req, res, next); - } catch (error) { - if (typeof error === "object" && error instanceof Error) { - viteDevServer.ssrFixStacktrace(error); - } - next(error); - } - }); -} else { - console.log("Starting production server"); - app.use( - "/assets", - express.static("build/client/assets", { immutable: true, maxAge: "1y" }), - ); - app.use(morgan("tiny")); - app.use(express.static("build/client", { maxAge: "1h" })); - app.use(await import(BUILD_PATH).then((mod) => mod.app)); -} - -// Create HTTP server -const httpServer = createServer(app); - -// Initialize Socket.IO -const { initializeSocketIO } = await import("./server/socket.js"); -initializeSocketIO(httpServer); - -// Use httpServer.listen instead of app.listen -httpServer.listen(PORT, () => { - console.log(`Server is running on http://localhost:${PORT}`); -}); diff --git a/server.ts b/server.ts new file mode 100644 index 0000000..ff069eb --- /dev/null +++ b/server.ts @@ -0,0 +1,87 @@ +import compression from "compression"; +import express, { Express, Request, Response, NextFunction } from "express"; +import morgan from "morgan"; +import { createServer } from "http"; +import type { ViteDevServer } from "vite"; +import path from "path"; +import { fileURLToPath } from "url"; + +// ESM module resolution helpers +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Configuration +// Resolve paths relative to project root (one level up from dist/ in production, or current dir in dev) +const PROJECT_ROOT = path.resolve(__dirname, ".."); +const BUILD_PATH = path.join(PROJECT_ROOT, "build/server/index.js"); +const CLIENT_BUILD_PATH = path.join(PROJECT_ROOT, "build/client"); +const CLIENT_ASSETS_PATH = path.join(PROJECT_ROOT, "build/client/assets"); +const DEVELOPMENT = process.env.NODE_ENV === "development"; +const PORT = Number.parseInt(process.env.PORT || "3000", 10); + +async function createAppServer(): Promise { + const app: Express = express(); + + app.use(compression()); + app.disable("x-powered-by"); + + if (DEVELOPMENT) { + console.log("Starting development server"); + + // Dynamic import Vite only in development + const { createServer: createViteServer } = await import("vite"); + + const viteDevServer: ViteDevServer = await createViteServer({ + server: { middlewareMode: true }, + }); + + app.use(viteDevServer.middlewares); + + app.use(async (req: Request, res: Response, next: NextFunction) => { + try { + const source = await viteDevServer.ssrLoadModule("./server/app.ts"); + return await source.app(req, res, next); + } catch (error) { + if (error instanceof Error) { + viteDevServer.ssrFixStacktrace(error); + } + next(error); + } + }); + } else { + console.log("Starting production server"); + + app.use( + "/assets", + express.static(CLIENT_ASSETS_PATH, { + immutable: true, + maxAge: "1y" + }) + ); + + app.use(morgan("tiny")); + app.use(express.static(CLIENT_BUILD_PATH, { maxAge: "1h" })); + + // Import the built React Router app + const { app: productionApp } = await import(BUILD_PATH); + app.use(productionApp); + } + + // Create HTTP server + const httpServer = createServer(app); + + // Initialize Socket.IO + const { initializeSocketIO } = await import("./server/socket"); + initializeSocketIO(httpServer); + + // Start server + httpServer.listen(PORT, () => { + console.log(`Server is running on http://localhost:${PORT}`); + }); +} + +// Start the server with error handling +createAppServer().catch((error) => { + console.error("Failed to start server:", error); + process.exit(1); +}); diff --git a/server/socket.js b/server/socket.ts similarity index 50% rename from server/socket.js rename to server/socket.ts index 6511972..78b60a1 100644 --- a/server/socket.js +++ b/server/socket.ts @@ -1,36 +1,62 @@ -import { Server as SocketIOServer } from "socket.io"; +import { Server as SocketIOServer, Socket } from "socket.io"; +import type { Server as HTTPServer } from "http"; -/** @type {SocketIOServer | null} */ -let io = null; +// Socket event types +interface ServerToClientEvents { + "test-message": (data: { + originalMessage: any; + serverResponse: string; + serverTimestamp: string; + socketId: string; + }) => void; + "pick-made": (data: any) => void; + "draft-started": (data: { seasonId: string; currentPickNumber: number }) => void; + "draft-completed": () => void; + "timer-update": (data: { + seasonId: string; + teamId: string; + timeRemaining: number; + currentPickNumber: number; + }) => void; +} + +interface ClientToServerEvents { + "join-draft": (seasonId: string) => void; + "leave-draft": (seasonId: string) => void; + "test-event": (data: any) => void; +} + +// Global type augmentation +declare global { + var __socketIO: SocketIOServer | undefined; +} + +let io: SocketIOServer | null = null; /** * Initialize Socket.IO server - * @param {import('http').Server} httpServer - * @returns {SocketIOServer} */ -export function initializeSocketIO(httpServer) { +export function initializeSocketIO(httpServer: HTTPServer): SocketIOServer { if (io) { console.log("Socket.IO already initialized"); return io; } - // Create Socket.IO server - io = new SocketIOServer(httpServer, { - // CORS only needed if clients connect from different origin + // Create typed Socket.IO server + io = new SocketIOServer(httpServer, { cors: process.env.NODE_ENV === "production" && process.env.APP_URL ? { origin: process.env.APP_URL, credentials: true, } - : undefined, // No CORS in dev (same origin) + : undefined, }); // Connection handling - io.on("connection", (socket) => { + io.on("connection", (socket: Socket) => { console.log("Client connected:", socket.id); - // Join draft room - socket.on("join-draft", (seasonId) => { + socket.on("join-draft", (seasonId: string) => { if (!seasonId) { console.error("No seasonId provided for join-draft"); return; @@ -39,18 +65,15 @@ export function initializeSocketIO(httpServer) { console.log(`Socket ${socket.id} joined draft-${seasonId}`); }); - // Leave draft room - socket.on("leave-draft", (seasonId) => { + socket.on("leave-draft", (seasonId: string) => { if (!seasonId) return; socket.leave(`draft-${seasonId}`); console.log(`Socket ${socket.id} left draft-${seasonId}`); }); - // Test event handler - echo back with server timestamp - socket.on("test-event", (data) => { + socket.on("test-event", (data: any) => { console.log("📨 Received test-event from client:", socket.id, data); - // Echo back to the client with server response socket.emit("test-message", { originalMessage: data, serverResponse: "Hello from server!", @@ -66,8 +89,7 @@ export function initializeSocketIO(httpServer) { }); }); - // Store globally for access in route handlers - // This is safe because server.js controls the lifecycle + // Store globally for route handlers global.__socketIO = io; console.log("Socket.IO initialized"); @@ -76,10 +98,8 @@ export function initializeSocketIO(httpServer) { /** * Get the Socket.IO server instance - * @returns {SocketIOServer} */ -export function getSocketIO() { - // Check both local and global storage +export function getSocketIO(): SocketIOServer { const instance = io || global.__socketIO; if (!instance) { throw new Error("Socket.IO not initialized. Call initializeSocketIO first."); diff --git a/tsconfig.server.json b/tsconfig.server.json new file mode 100644 index 0000000..a48ed84 --- /dev/null +++ b/tsconfig.server.json @@ -0,0 +1,38 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "composite": false, + "noEmit": false, + "outDir": "./dist/server", + "rootDir": ".", + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "node", + "lib": ["ES2022"], + "types": ["node"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "paths": { + "~/*": ["./app/*"], + "~/database/*": ["./database/*"] + } + }, + "include": [ + "server.ts", + "server/**/*.ts", + "database/**/*.ts", + "app/database/**/*.ts" + ], + "exclude": [ + "node_modules", + "build", + "dist", + "**/*.test.ts", + "**/*.spec.ts" + ] +}