From eb8b6de724afcb11323895f78f573d9c0628b936 Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Thu, 5 Mar 2026 07:11:54 -0600 Subject: [PATCH 01/16] Basic initial work. --- .gitignore | 3 + Containerfile | 9 + drizzle.config.js | 8 + eslint.config.mjs | 21 + package-lock.json | 3516 +++++++++++++++++++++++++++++++ package.json | 38 + podman-run.sh | 6 + public/css/colors.css | 0 public/css/colors.scss | 5 + public/css/generic.css | 51 + public/css/generic.scss | 63 + public/css/index.css | 151 ++ public/css/index.scss | 178 ++ public/css/profile.css | 23 + public/css/profile.scss | 33 + public/css/profileSettings.css | 23 + public/css/profileSettings.scss | 32 + public/img/1280x720_dummy.webp | Bin 0 -> 6394 bytes public/img/logoSmall.webp | Bin 0 -> 26444 bytes public/img/people.svg | 1 + server.js | 8 + src/app.js | 35 + src/controllers/api.js | 6 + src/controllers/index.js | 9 + src/db/db.js | 6 + src/db/index.js | 9 + src/db/schema.js | 15 + src/middleware/errorHandler.js | 10 + src/middleware/formToJson.js | 0 src/middleware/isAuth.js | 17 + src/middleware/requireAuth.js | 22 + src/routes/api.js | 10 + src/routes/index.js | 10 + src/services/account.js | 0 src/services/auth.js | 8 + src/services/crypto.js | 93 + src/services/logger.js | 26 + src/services/setup.js | 43 + src/services/time.js | 0 src/views/index.ejs | 101 + src/views/partials/head.ejs | 0 src/views/partials/navbar.ejs | 13 + 42 files changed, 4602 insertions(+) create mode 100644 .gitignore create mode 100644 Containerfile create mode 100644 drizzle.config.js create mode 100644 eslint.config.mjs create mode 100644 package-lock.json create mode 100644 package.json create mode 100755 podman-run.sh create mode 100644 public/css/colors.css create mode 100644 public/css/colors.scss create mode 100644 public/css/generic.css create mode 100644 public/css/generic.scss create mode 100644 public/css/index.css create mode 100644 public/css/index.scss create mode 100644 public/css/profile.css create mode 100644 public/css/profile.scss create mode 100644 public/css/profileSettings.css create mode 100644 public/css/profileSettings.scss create mode 100644 public/img/1280x720_dummy.webp create mode 100644 public/img/logoSmall.webp create mode 100644 public/img/people.svg create mode 100644 server.js create mode 100644 src/app.js create mode 100644 src/controllers/api.js create mode 100644 src/controllers/index.js create mode 100644 src/db/db.js create mode 100644 src/db/index.js create mode 100644 src/db/schema.js create mode 100644 src/middleware/errorHandler.js create mode 100644 src/middleware/formToJson.js create mode 100644 src/middleware/isAuth.js create mode 100644 src/middleware/requireAuth.js create mode 100644 src/routes/api.js create mode 100644 src/routes/index.js create mode 100644 src/services/account.js create mode 100644 src/services/auth.js create mode 100644 src/services/crypto.js create mode 100644 src/services/logger.js create mode 100644 src/services/setup.js create mode 100644 src/services/time.js create mode 100644 src/views/index.ejs create mode 100644 src/views/partials/head.ejs create mode 100644 src/views/partials/navbar.ejs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a3e0a6a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/node_modules +.env +data/** diff --git a/Containerfile b/Containerfile new file mode 100644 index 0000000..e34ea12 --- /dev/null +++ b/Containerfile @@ -0,0 +1,9 @@ +FROM node:24-slim +WORKDIR /app +COPY package*.json ./ +RUN npm ci --omit=dev +COPY . . +EXPOSE 40500 +CMD sh -c 'npx drizzle-kit generate \ + && npx drizzle-kit migrate \ + && node server.js' diff --git a/drizzle.config.js b/drizzle.config.js new file mode 100644 index 0000000..066c4c5 --- /dev/null +++ b/drizzle.config.js @@ -0,0 +1,8 @@ +const { defineConfig } = require("drizzle-kit"); + +module.exports = defineConfig({ + dialect: "sqlite", + schema: "./src/db/schema.js", + out: "./data/drizzle", + dbCredentials: { url: "./data/openminerva-session-server.db" }, +}); diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..f08d91e --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,21 @@ +import js from "@eslint/js"; +import globals from "globals"; +import { defineConfig } from "eslint/config"; + +export default defineConfig([ + { + files: ["**/*.{js,mjs,cjs}"], + plugins: { js }, + extends: ["js/recommended"], + languageOptions: { globals: globals.browser }, + rules: { + "no-unused-vars": "error", + "semi-style": ["error", "last"], + indent: ["error", "tab"], + }, + }, + { + files: ["**/*.js"], + languageOptions: { sourceType: "commonjs" }, + }, +]); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..3108868 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3516 @@ +{ + "name": "session-server", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "session-server", + "version": "1.0.0", + "license": "UNLICENSED", + "dependencies": { + "bcrypt": "^6.0.0", + "better-sqlite3": "^12.5.0", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "dotenv": "^17.2.3", + "drizzle-kit": "^0.31.7", + "drizzle-orm": "^0.44.7", + "ejs": "^3.1.10", + "express": "^5.1.0", + "jsonwebtoken": "^9.0.2", + "method-override": "^3.0.0", + "multer": "^2.0.2", + "sharp": "^0.34.5", + "winston": "^3.18.3" + }, + "devDependencies": {} + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "license": "MIT", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@drizzle-team/brocli": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz", + "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==", + "license": "Apache-2.0" + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild-kit/core-utils": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", + "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==", + "deprecated": "Merged into tsx: https://tsx.is", + "license": "MIT", + "dependencies": { + "esbuild": "~0.18.20", + "source-map-support": "^0.5.21" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/@esbuild-kit/esm-loader": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz", + "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==", + "deprecated": "Merged into tsx: https://tsx.is", + "license": "MIT", + "dependencies": { + "@esbuild-kit/core-utils": "^3.3.2", + "get-tsconfig": "^4.7.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/better-sqlite3": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.6.2.tgz", + "integrity": "sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x || 25.x" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/color": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/drizzle-kit": { + "version": "0.31.8", + "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.8.tgz", + "integrity": "sha512-O9EC/miwdnRDY10qRxM8P3Pg8hXe3LyU4ZipReKOgTwn4OqANmftj8XJz1UPUAS6NMHf0E2htjsbQujUTkncCg==", + "license": "MIT", + "dependencies": { + "@drizzle-team/brocli": "^0.10.2", + "@esbuild-kit/esm-loader": "^2.5.5", + "esbuild": "^0.25.4", + "esbuild-register": "^3.5.0" + }, + "bin": { + "drizzle-kit": "bin.cjs" + } + }, + "node_modules/drizzle-orm": { + "version": "0.44.7", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.44.7.tgz", + "integrity": "sha512-quIpnYznjU9lHshEOAYLoZ9s3jweleHlZIAWR/jX9gAWNg/JhQ1wj0KGRf7/Zm+obRrYd9GjPVJg790QY9N5AQ==", + "license": "Apache-2.0", + "peerDependencies": { + "@aws-sdk/client-rds-data": ">=3", + "@cloudflare/workers-types": ">=4", + "@electric-sql/pglite": ">=0.2.0", + "@libsql/client": ">=0.10.0", + "@libsql/client-wasm": ">=0.10.0", + "@neondatabase/serverless": ">=0.10.0", + "@op-engineering/op-sqlite": ">=2", + "@opentelemetry/api": "^1.4.1", + "@planetscale/database": ">=1.13", + "@prisma/client": "*", + "@tidbcloud/serverless": "*", + "@types/better-sqlite3": "*", + "@types/pg": "*", + "@types/sql.js": "*", + "@upstash/redis": ">=1.34.7", + "@vercel/postgres": ">=0.8.0", + "@xata.io/client": "*", + "better-sqlite3": ">=7", + "bun-types": "*", + "expo-sqlite": ">=14.0.0", + "gel": ">=2", + "knex": "*", + "kysely": "*", + "mysql2": ">=2", + "pg": ">=8", + "postgres": ">=3", + "sql.js": ">=1", + "sqlite3": ">=5" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-rds-data": { + "optional": true + }, + "@cloudflare/workers-types": { + "optional": true + }, + "@electric-sql/pglite": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "@libsql/client-wasm": { + "optional": true + }, + "@neondatabase/serverless": { + "optional": true + }, + "@op-engineering/op-sqlite": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@prisma/client": { + "optional": true + }, + "@tidbcloud/serverless": { + "optional": true + }, + "@types/better-sqlite3": { + "optional": true + }, + "@types/pg": { + "optional": true + }, + "@types/sql.js": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/postgres": { + "optional": true + }, + "@xata.io/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "bun-types": { + "optional": true + }, + "expo-sqlite": { + "optional": true + }, + "gel": { + "optional": true + }, + "knex": { + "optional": true + }, + "kysely": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "postgres": { + "optional": true + }, + "prisma": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/esbuild-register": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", + "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/method-override": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", + "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", + "license": "MIT", + "dependencies": { + "debug": "3.1.0", + "methods": "~1.1.2", + "parseurl": "~1.3.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/method-override/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/method-override/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.87.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", + "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/winston": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d6cb91a --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "session-server", + "version": "1.0.0", + "description": "The server for listing and advertising active sessions", + "homepage": "https://github.com/OpenMinerva/session-server#readme", + "bugs": { + "url": "https://github.com/OpenMinerva/session-server/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/OpenMinerva/session-server.git" + }, + "license": "UNLICENSED", + "author": "", + "type": "commonjs", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "dependencies": { + "bcrypt": "^6.0.0", + "better-sqlite3": "^12.5.0", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "dotenv": "^17.2.3", + "drizzle-kit": "^0.31.7", + "drizzle-orm": "^0.44.7", + "ejs": "^3.1.10", + "express": "^5.1.0", + "jsonwebtoken": "^9.0.2", + "method-override": "^3.0.0", + "multer": "^2.0.2", + "sharp": "^0.34.5", + "winston": "^3.18.3" + }, + "devDependencies": {} +} diff --git a/podman-run.sh b/podman-run.sh new file mode 100755 index 0000000..713269d --- /dev/null +++ b/podman-run.sh @@ -0,0 +1,6 @@ +IMAGE="openminerva-session-server" +sass public/css --no-source-map + +# podman stop "$IMAGE" +podman build -t "$IMAGE" . +podman run -p 40500:40500 -d --replace --name "$IMAGE" "$IMAGE" diff --git a/public/css/colors.css b/public/css/colors.css new file mode 100644 index 0000000..e69de29 diff --git a/public/css/colors.scss b/public/css/colors.scss new file mode 100644 index 0000000..47883aa --- /dev/null +++ b/public/css/colors.scss @@ -0,0 +1,5 @@ +$bg-1: #2b2b2b; +$bg-1-hover: #363636; + +$bg-g1-1: #2557a3; +$bg-g1-2: #0a2247; \ No newline at end of file diff --git a/public/css/generic.css b/public/css/generic.css new file mode 100644 index 0000000..08f07c5 --- /dev/null +++ b/public/css/generic.css @@ -0,0 +1,51 @@ +body { + color: white; + font-family: Verdana, Geneva, Tahoma, sans-serif; + margin: 0; +} +body .navbar { + background-color: #2b2b2b; + display: flex; + flex-direction: row; + padding: 0rem 1rem; + height: 60px; +} +body .navbar .logo { + font-size: 2rem; + font-weight: 700; + margin: auto 0; +} +body .navbar .navigation { + height: 100%; + display: flex; + flex-direction: row; + margin-left: auto; +} +body .navbar .navigation .nav-button { + display: flex; + flex-direction: column; + width: 7rem; + color: white; + text-decoration: none; + height: 100%; + transition: background-color ease-in-out 0.05s; +} +body .navbar .navigation .nav-button span { + margin: auto; +} +body .navbar .navigation .nav-button:hover { + background-color: #363636; +} +body .text-entry { + border: 1px solid gray; + border-radius: 10px; + color: black; + padding: 0.25rem 1rem; +} +body .text-entry:focus { + outline: none; +} + +.hidden { + display: none !important; +} \ No newline at end of file diff --git a/public/css/generic.scss b/public/css/generic.scss new file mode 100644 index 0000000..7dafbd1 --- /dev/null +++ b/public/css/generic.scss @@ -0,0 +1,63 @@ +@use "colors"; + + +body { + color: white; + font-family: Verdana, Geneva, Tahoma, sans-serif; + margin: 0; + + .navbar { + background-color: colors.$bg-1; + display: flex; + flex-direction: row; + padding: 0rem 1rem; + height: 60px; + + .logo { + font-size: 2rem; + font-weight: 700; + margin: auto 0; + } + + .navigation { + height: 100%; + display: flex; + flex-direction: row; + margin-left: auto; + + .nav-button { + display: flex; + flex-direction: column; + width: 7rem; + color: white; + text-decoration: none; + height: 100%; + + transition: background-color ease-in-out 0.05s; + + span { + margin: auto; + } + } + + .nav-button:hover { + background-color: colors.$bg-1-hover; + } + } + } + + .text-entry { + border: 1px solid gray; + border-radius: 10px; + color: black; + padding: 0.25rem 1rem; + } + + .text-entry:focus { + outline: none; + } +} + +.hidden { + display: none !important; +} \ No newline at end of file diff --git a/public/css/index.css b/public/css/index.css new file mode 100644 index 0000000..08fcd0d --- /dev/null +++ b/public/css/index.css @@ -0,0 +1,151 @@ +.show-board { + width: 100%; + height: 500px; + display: flex; + background: linear-gradient(-90deg, #2557a3, #0a2247); +} +.show-board .center-container { + margin: auto; + display: flex; + flex-direction: column; +} +.show-board .center-container .show-board-title { + font-size: 4rem; + font-weight: 700; + margin-bottom: 2rem; +} +.show-board .center-container .show-board-stats { + margin: auto; + display: flex; + flex-direction: row; + width: 100%; +} +.show-board .center-container .show-board-stats .entry { + margin: auto; +} +.show-board .center-container .show-board-stats .entry .number { + font-size: 2rem; + text-align: center; + font-weight: 500; +} +.show-board .center-container .show-board-stats .entry .title { + font-size: 1.5rem; +} + +.search-container { + width: 1200px; + margin: 2rem auto 0 auto; + display: flex; + flex-direction: row; +} +.search-container .text-entry { + width: 100%; +} +.search-container .search-confirm { + margin-left: 0.5rem; +} +.search-container select, +.search-container .dropdown-selection { + min-width: 200px; + text-align: center; + border-radius: 10px; + border: 1px solid gray; + transition: background-color ease-in-out 0.05s; + background-color: #0064cc; + cursor: pointer; + color: white; + font-weight: 500; + margin-left: 0.5rem; +} +.search-container select:hover { + background-color: #49a3fc; +} +.search-container .dropdown-menu { + display: none; + border: 1px solid #ccc; + background-color: white; + padding: 5px; + position: absolute; + color: black; + width: 150px; + border-radius: 10px; + z-index: 2; +} +.search-container .dropdown-menu label { + width: 100%; + height: 2rem; + display: flex; + margin-right: auto; + text-align: center; + transition: background-color ease-in-out 0.05s; +} +.search-container .dropdown-menu label span { + margin: auto; +} +.search-container .dropdown-menu label input { + height: 30px; +} +.search-container .dropdown-menu label:hover { + background-color: #49a3fc; +} +.search-container .dropdown-selection:focus-within .dropdown-menu { + display: flex; + flex-direction: column; +} + +.session-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-gap: 1rem; + width: 1200px; + margin: 2rem auto 0 auto; +} +.session-grid .session-entry { + width: 100%; + height: 100%; + min-height: 20px; + background-color: #2b2b2b; + border-radius: 10px; + color: white; +} +.session-grid .session-entry .preview-image { + width: 100%; + display: flex; + position: relative; +} +.session-grid .session-entry .preview-image img { + height: 100%; + width: 100%; + border-radius: 10px 10px 0 0; +} +.session-grid .session-entry .preview-image .user-count { + position: absolute; + bottom: 0; + padding: 0.25rem 1.5rem 0.25rem 1rem; + display: flex; + flex-direction: row; + background-color: #2b2b2b; + clip-path: polygon(0 0, 80% 0%, 100% 100%, 0% 100%); +} +.session-grid .session-entry .preview-image .user-count div { + height: 20px; + aspect-ratio: 1/1; + display: flex; + flex-direction: row; +} +.session-grid .session-entry .preview-image .user-count div img { + height: 100%; + width: auto; +} +.session-grid .session-entry .session-title { + font-size: 1.1rem; + padding: 0.5rem 1rem; + box-sizing: border-box; + height: 60px; + display: -webkit-inline-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; +} \ No newline at end of file diff --git a/public/css/index.scss b/public/css/index.scss new file mode 100644 index 0000000..1e151ae --- /dev/null +++ b/public/css/index.scss @@ -0,0 +1,178 @@ +@use "colors"; + +.show-board { + width: 100%; + height: 500px; + display: flex; + background: linear-gradient(-90deg, colors.$bg-g1-1, colors.$bg-g1-2); + + .center-container { + margin: auto; + display: flex; + flex-direction: column; + + .show-board-title { + font-size: 4rem; + font-weight: 700; + margin-bottom: 2rem; + } + + .show-board-stats { + margin: auto; + display: flex; + flex-direction: row; + width: 100%; + + .entry { + margin: auto; + + .number { + font-size: 2rem; + text-align: center; + font-weight: 500; + } + + .title { + font-size: 1.5rem; + } + } + } + } +} + +.search-container { + width: 1200px; + margin: 2rem auto 0 auto; + display: flex; + flex-direction: row; + + .text-entry { + width: 100%; + } + + .search-confirm { + margin-left: 0.5rem; + } + + select, + .dropdown-selection { + min-width: 200px; + text-align: center; + border-radius: 10px; + border: 1px solid gray; + transition: background-color ease-in-out 0.05s; + background-color: #0064cc; + cursor: pointer; + color: white; + font-weight: 500; + margin-left: 0.5rem; + } + + select:hover { + background-color: #49a3fc; + } + + .dropdown-menu { + display: none; + border: 1px solid #ccc; + background-color: white; + padding: 5px; + position: absolute; + color: black; + width: 150px; + border-radius: 10px; + z-index: 2; + + label { + width: 100%; + height: 2rem; + display: flex; + margin-right: auto; + text-align: center; + transition: background-color ease-in-out 0.05s; + + span { + margin: auto; + } + + input { + height: 30px; + } + } + + label:hover { + background-color: #49a3fc; + } + } + + .dropdown-selection:focus-within .dropdown-menu { + display: flex; + flex-direction: column; + } +} + +.session-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + // grid-template-columns: repeat(2, 1fr); + grid-gap: 1rem; + width: 1200px; + margin: 2rem auto 0 auto; + + .session-entry { + width: 100%; + height: 100%; + min-height: 20px; + background-color: colors.$bg-1; + border-radius: 10px; + color: white; + + .preview-image { + width: 100%; + display: flex; + position: relative; + + img { + height: 100%; + width: 100%; + border-radius: 10px 10px 0 0; + } + + .user-count { + position: absolute; + bottom: 0; + padding: 0.25rem 1.5rem 0.25rem 1rem; + display: flex; + flex-direction: row; + background-color: colors.$bg-1; + clip-path: polygon(0 0, 80% 0%, 100% 100%, 0% 100%); + + div { + height: 20px; + aspect-ratio: 1/1; + display: flex; + flex-direction: row; + + img { + height: 100%; + width: auto; + } + } + } + } + + .session-title { + font-size: 1.1rem; + padding: 0.5rem 1rem; + box-sizing: border-box; + height: 60px; + + display: -webkit-inline-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + } + } +} \ No newline at end of file diff --git a/public/css/profile.css b/public/css/profile.css new file mode 100644 index 0000000..0696745 --- /dev/null +++ b/public/css/profile.css @@ -0,0 +1,23 @@ +.page-content .profile-head { + width: 100%; + height: 200px; + display: flex; + flex-direction: row; + margin-bottom: 2rem; +} +.page-content .profile-head .profile-picture-container { + width: 200px; + height: 200px; + background-color: black; + margin-right: 1rem; +} +.page-content .profile-head .profile-badge { + display: flex; + flex-direction: column; +} +.page-content .profile-head .profile-badge .profile-username { + font-size: 2rem; +} +.page-content .profile-body .profile-biography { + font-size: 1.15rem; +} \ No newline at end of file diff --git a/public/css/profile.scss b/public/css/profile.scss new file mode 100644 index 0000000..901e0d3 --- /dev/null +++ b/public/css/profile.scss @@ -0,0 +1,33 @@ +.page-content { + .profile-head { + width: 100%; + height: 200px; + display: flex; + flex-direction: row; + margin-bottom: 2rem; + + .profile-picture-container { + width: 200px; + height: 200px; + background-color: black; + margin-right: 1rem; + } + + .profile-badge { + display: flex; + flex-direction: column; + + .profile-username { + font-size: 2rem; + } + } + + .profile-actions {} + } + + .profile-body { + .profile-biography { + font-size: 1.15rem; + } + } +} \ No newline at end of file diff --git a/public/css/profileSettings.css b/public/css/profileSettings.css new file mode 100644 index 0000000..89c6626 --- /dev/null +++ b/public/css/profileSettings.css @@ -0,0 +1,23 @@ +.devices-tables { + width: 100%; +} +.devices-tables .min-width { + width: -moz-fit-content; + width: fit-content; +} +.devices-tables .fit-width { + width: -moz-max-content; + width: max-content; +} +.devices-tables .fill-width { + width: 100%; +} +.devices-tables tbody tr td div { + display: flex; + flex-direction: row; + gap: 1rem; +} +.devices-tables tbody tr td div button { + padding: 0.25rem 0.5rem; + box-sizing: border-box; +} \ No newline at end of file diff --git a/public/css/profileSettings.scss b/public/css/profileSettings.scss new file mode 100644 index 0000000..14e36d4 --- /dev/null +++ b/public/css/profileSettings.scss @@ -0,0 +1,32 @@ +.devices-tables { + width: 100%; + + .min-width { + width: fit-content; + } + + .fit-width { + width: max-content; + } + + .fill-width { + width: 100%; + } + + tbody { + tr { + td { + div { + display: flex; + flex-direction: row; + gap: 1rem; + + button { + padding: 0.25rem 0.5rem; + box-sizing: border-box; + } + } + } + } + } +} \ No newline at end of file diff --git a/public/img/1280x720_dummy.webp b/public/img/1280x720_dummy.webp new file mode 100644 index 0000000000000000000000000000000000000000..b50f87f5c8eb7b98d3d34a7179e8687d7905dc3b GIT binary patch literal 6394 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

xG4QN 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 \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 0000000..b4efa83 --- /dev/null +++ b/server.js @@ -0,0 +1,8 @@ +require("dotenv").config(); +const app = require("./src/app"); + +const PORT = process.env.PORT || 40500; + +app.listen(PORT, () => { + console.log(`Server running at http://localhost:${PORT}`); +}); diff --git a/src/app.js b/src/app.js new file mode 100644 index 0000000..bf45d4f --- /dev/null +++ b/src/app.js @@ -0,0 +1,35 @@ +const express = require("express"); +const cors = require("cors"); +const path = require("path"); +const cookieParser = require("cookie-parser"); +const methodOverride = require("method-override"); + +// Routes +const indexRoutes = require("./routes/index"); +const apiRoutes = require("./routes/api"); + +// Middleware +const errorHandler = require("./middleware/errorHandler"); +// const ensureSetup = require("./services/setup"); + +const app = express(); + +app.set("view engine", "ejs"); +app.set("views", path.join(__dirname, "views")); +app.use(express.static(path.join(__dirname, "..", "public"))); +app.use(methodOverride("_method")); + +// Global middleware +app.use(cors()); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); +app.use(cookieParser()); + +// Routes +app.use("/api", apiRoutes); // API is required to make an account. If this is after ensuring the setup we would be caught in a loop! +// app.use(ensureSetup.middleware); +app.use("/", indexRoutes); + +app.use(errorHandler); + +module.exports = app; \ No newline at end of file diff --git a/src/controllers/api.js b/src/controllers/api.js new file mode 100644 index 0000000..997f6ba --- /dev/null +++ b/src/controllers/api.js @@ -0,0 +1,6 @@ + +const getSessions = async (req, res, next) => { + return {} +} + +module.exports = { getSessions } \ No newline at end of file diff --git a/src/controllers/index.js b/src/controllers/index.js new file mode 100644 index 0000000..f1886cc --- /dev/null +++ b/src/controllers/index.js @@ -0,0 +1,9 @@ +const getIndex = async (req, res, next) => { + try { + res.render("index", { requestingUser: req.requestingUser }); + } catch (err) { + next(err); + } +}; + +module.exports = { getIndex }; diff --git a/src/db/db.js b/src/db/db.js new file mode 100644 index 0000000..0a8221d --- /dev/null +++ b/src/db/db.js @@ -0,0 +1,6 @@ +const { drizzle } = require("drizzle-orm/better-sqlite3"); +const Database = require("better-sqlite3"); +const schema = require("./schema"); + +const sqlite = new Database("./data/openminerva-session-server.db"); +module.exports = { db: drizzle(sqlite, { schema }) }; diff --git a/src/db/index.js b/src/db/index.js new file mode 100644 index 0000000..8a8bedc --- /dev/null +++ b/src/db/index.js @@ -0,0 +1,9 @@ +const { db } = require("./db"); +const { accounts, devices } = require("./schema"); +const { eq, and } = require("drizzle-orm"); +const crypto = require("../services/crypto"); +const logger = require("../services/logger"); +const ensureSetup = require("../services/setup"); +const { randomUUID } = require('crypto'); + +module.exports = {} \ No newline at end of file diff --git a/src/db/schema.js b/src/db/schema.js new file mode 100644 index 0000000..3d8424b --- /dev/null +++ b/src/db/schema.js @@ -0,0 +1,15 @@ +const { sqliteTable, text, integer } = require("drizzle-orm/sqlite-core"); +const { sql, relations } = require("drizzle-orm"); +const { randomUUID } = require("crypto"); + +const accounts = sqliteTable("accounts", { + id: text("id").primaryKey().unique().$defaultFn(() => randomUUID()), + username: text("username").notNull().unique(), + password: text("password").notNull(), + role: text("role", { enum: ["user", "moderator", "admin"] }).default("user").notNull(), + dateCreated: integer("dateCreated", { mode: "timestamp_ms" }).default(sql`(unixepoch() * 1000)`).notNull(), + dateDeleted: integer("dateDeleted", { mode: "timestamp_ms" }), + deleteRequested: integer("deleteRequested", { mode: "boolean" }).default(false), +}); + +module.exports = { accounts }; diff --git a/src/middleware/errorHandler.js b/src/middleware/errorHandler.js new file mode 100644 index 0000000..55e1346 --- /dev/null +++ b/src/middleware/errorHandler.js @@ -0,0 +1,10 @@ +const errorHandler = (err, req, res, next) => { + console.error(err.stack || err.message); + + const statusCode = err.statusCode || err.status || 500; + const message = err.message || "Internal Server Error"; + + res.status(statusCode).json({ error: message }); +}; + +module.exports = errorHandler; diff --git a/src/middleware/formToJson.js b/src/middleware/formToJson.js new file mode 100644 index 0000000..e69de29 diff --git a/src/middleware/isAuth.js b/src/middleware/isAuth.js new file mode 100644 index 0000000..8afc30b --- /dev/null +++ b/src/middleware/isAuth.js @@ -0,0 +1,17 @@ +const crypto = require("../services/crypto.js"); + +module.exports = async function auth(req, res, next) { + const token = req.cookies?.token || req.headers["authorization"]?.split(" ")[1]; + if (!token) return next(); + + try { + const decoded = await crypto.verifyJWT(token); + + if (decoded.ok == true) { + req.requestingUser = decoded.response; + next(); + } + } catch { + next(); + } +}; diff --git a/src/middleware/requireAuth.js b/src/middleware/requireAuth.js new file mode 100644 index 0000000..6211bb8 --- /dev/null +++ b/src/middleware/requireAuth.js @@ -0,0 +1,22 @@ +const jwt = require("jsonwebtoken"); +const authService = require("../services/auth.js"); +const crypto = require("../services/crypto.js"); + +module.exports = async function auth(req, res, next) { + const token = req.cookies?.token || req.headers["authorization"]?.split(" ")[1]; + if (!token) return res.redirect("/"); + + try { + const decoded = await crypto.verifyJWT(token); + + if (decoded.ok == true) { + req.requestingUser = decoded.response; + next(); + return; + } + + res.status(401).redirect("/"); + } catch { + res.status(401).redirect("/"); + } +}; diff --git a/src/routes/api.js b/src/routes/api.js new file mode 100644 index 0000000..4c97d61 --- /dev/null +++ b/src/routes/api.js @@ -0,0 +1,10 @@ +const express = require("express"); +const router = express.Router(); +const isAuth = require("../middleware/isAuth"); +const requireAuth = require("../middleware/requireAuth"); + +const apiController = require('../controllers/api') + +router.get("/v1/getSessions", isAuth, apiController.getSessions); + +module.exports = router \ No newline at end of file diff --git a/src/routes/index.js b/src/routes/index.js new file mode 100644 index 0000000..b2ae338 --- /dev/null +++ b/src/routes/index.js @@ -0,0 +1,10 @@ +const express = require("express"); +const router = express.Router(); +const isAuth = require("../middleware/isAuth"); +const requireAuth = require("../middleware/requireAuth"); + +const indexController = require("../controllers/index"); + +router.get("/", isAuth, indexController.getIndex); + +module.exports = router \ No newline at end of file diff --git a/src/services/account.js b/src/services/account.js new file mode 100644 index 0000000..e69de29 diff --git a/src/services/auth.js b/src/services/auth.js new file mode 100644 index 0000000..b2702bb --- /dev/null +++ b/src/services/auth.js @@ -0,0 +1,8 @@ +const cryptoLib = require("../services/crypto"); +const accountService = require('../services/account'); + +async function authLogin({ username, password }) { + return true; +} + +module.exports = { authLogin } \ No newline at end of file diff --git a/src/services/crypto.js b/src/services/crypto.js new file mode 100644 index 0000000..38bab52 --- /dev/null +++ b/src/services/crypto.js @@ -0,0 +1,93 @@ +const bcrypt = require("bcrypt"); +const crypto = require("crypto"); +const fs = require("fs") +const logger = require('../services/logger'); +const jwt = require("jsonwebtoken"); + +let privateKey = null; +let publicKey = null; + +async function createJWT(accountData, scope = "private_account_server_jwt", deviceId = null) { + if (!accountData.id) { + return { ok: false, message: "No ID specified." } + } + if (!accountData.username) { + return { ok: false, message: "No username specified." } + } + if (!accountData.role) { + return { ok: false, message: "No role specified." } + } + + const info = { + id: accountData.id, + username: accountData.username, + role: accountData.role, + scope: scope, + issuer: "http://localhost:40500", + deviceId + } + + // TODO: Remember me, extend expiresIn + const token = jwt.sign(info, privateKey, { expiresIn: "1d", algorithm: 'RS256' }); + + return { ok: true, response: token } +} + +async function verifyJWT(token) { + try { + const verification = await jwt.verify(token, privateKey); + + if (verification.scope != "private_account_server_jwt") { + return { ok: false, message: "Invalid scope." } + } + + return { ok: true, response: verification } + } catch (e) { + return { ok: false, message: "Unknown error." } + } +} + +/** + * Generates a public and private keypair. + * Will then store the keypair to disk + */ +async function generateKeyPairs({ saveToDisk = true } = {}) { + const keys = crypto.generateKeyPairSync("rsa", { + modulusLength: 2048, + publicKeyEncoding: { type: 'spki', format: 'pem' }, + privateKeyEncoding: { type: 'pkcs8', format: 'pem' } + }); + + // Save to the variables. + privateKey = keys.privateKey + publicKey = keys.publicKey + + // Save to disk for persistence. + await fs.mkdirSync("./data/keys/", { recursive: true }); + + fs.writeFileSync("./data/keys/public.pem", keys.publicKey); + fs.writeFileSync("./data/keys/private.pem", keys.privateKey); +} + +/** + * Returns the current private key of the instance. + * + * @returns {string} The current private key. + */ +function getPrivKey() { + return privateKey; +} + +/** + * Returns the current public key of the instance. + * + * @returns {string} The current public key. + */ +function getPubKey() { + return publicKey; +} +// FIXME: If keys are not available, will error. +publicKey = fs.readFileSync("./data/keys/public.pem") || null +privateKey = fs.readFileSync("./data/keys/private.pem") || null + +module.exports = { hashPassword, comparePassword, createJWT, verifyJWT, getPrivKey, getPubKey } \ No newline at end of file diff --git a/src/services/logger.js b/src/services/logger.js new file mode 100644 index 0000000..487aa06 --- /dev/null +++ b/src/services/logger.js @@ -0,0 +1,26 @@ +const winston = require("winston"); + +const logger = winston.createLogger({ + level: "debug", + transports: [ + new winston.transports.File({ + filename: "data/logs/error.log", + level: "error", + format: winston.format.combine(winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), winston.format.errors({ stack: true }), winston.format.json()), + }), + new winston.transports.File({ + filename: "data/logs/combined.log", + format: winston.format.combine(winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), winston.format.json()), + }), + ], +}); + +if (process.env.NODE_ENV !== "production") { + logger.add( + new winston.transports.Console({ + format: winston.format.combine(winston.format.colorize({ all: true }), winston.format.timestamp(), winston.format.simple()), + }), + ); +} + +module.exports = logger; diff --git a/src/services/setup.js b/src/services/setup.js new file mode 100644 index 0000000..9ff16d0 --- /dev/null +++ b/src/services/setup.js @@ -0,0 +1,43 @@ +const logger = require("../services/logger"); + +let isSetup; + +async function check() { + if (isSetup !== undefined) return isSetup; + + const accounts = await require('../services/account').getAccount({ role: "admin" }); + if (accounts.length > 0) { + isSetup = true + return true + } + + isSetup = false + return false +} + +async function middleware(req, res, next) { + if (req.path === "/signup") { + return next(); + } + + if (await check() == false) { + logger.info("Server is not set up, redirecting to the setup page."); + return res.redirect("/signup"); + } + + next(); +} +function setState(state = true) { + const newValue = Boolean(state); + if (newValue == isSetup) return + + logger.debug(`Changing setup state to ${newValue}`); + + if (newValue == true) { + logger.info(`Server is now set up!`); + } + + isSetup = newValue; +} + +module.exports = { check, middleware, setState }; diff --git a/src/services/time.js b/src/services/time.js new file mode 100644 index 0000000..e69de29 diff --git a/src/views/index.ejs b/src/views/index.ejs new file mode 100644 index 0000000..3bb3b6a --- /dev/null +++ b/src/views/index.ejs @@ -0,0 +1,101 @@ + + + + + + OpenMinerva | Session Server + <%- include('partials/head') %> + + + + + + <%- include('partials/navbar') %> + +

+ + + \ No newline at end of file diff --git a/src/views/partials/head.ejs b/src/views/partials/head.ejs new file mode 100644 index 0000000..e69de29 diff --git a/src/views/partials/navbar.ejs b/src/views/partials/navbar.ejs new file mode 100644 index 0000000..03f49c0 --- /dev/null +++ b/src/views/partials/navbar.ejs @@ -0,0 +1,13 @@ + \ No newline at end of file From b891cd3f97f9f705f8fd28fc00db07f6a9affd91 Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Thu, 5 Mar 2026 07:16:28 -0600 Subject: [PATCH 02/16] Removed unused files. --- public/css/profile.css | 23 ----------------------- public/css/profile.scss | 33 --------------------------------- public/css/profileSettings.css | 23 ----------------------- public/css/profileSettings.scss | 32 -------------------------------- src/services/crypto.js | 2 +- 5 files changed, 1 insertion(+), 112 deletions(-) delete mode 100644 public/css/profile.css delete mode 100644 public/css/profile.scss delete mode 100644 public/css/profileSettings.css delete mode 100644 public/css/profileSettings.scss diff --git a/public/css/profile.css b/public/css/profile.css deleted file mode 100644 index 0696745..0000000 --- a/public/css/profile.css +++ /dev/null @@ -1,23 +0,0 @@ -.page-content .profile-head { - width: 100%; - height: 200px; - display: flex; - flex-direction: row; - margin-bottom: 2rem; -} -.page-content .profile-head .profile-picture-container { - width: 200px; - height: 200px; - background-color: black; - margin-right: 1rem; -} -.page-content .profile-head .profile-badge { - display: flex; - flex-direction: column; -} -.page-content .profile-head .profile-badge .profile-username { - font-size: 2rem; -} -.page-content .profile-body .profile-biography { - font-size: 1.15rem; -} \ No newline at end of file diff --git a/public/css/profile.scss b/public/css/profile.scss deleted file mode 100644 index 901e0d3..0000000 --- a/public/css/profile.scss +++ /dev/null @@ -1,33 +0,0 @@ -.page-content { - .profile-head { - width: 100%; - height: 200px; - display: flex; - flex-direction: row; - margin-bottom: 2rem; - - .profile-picture-container { - width: 200px; - height: 200px; - background-color: black; - margin-right: 1rem; - } - - .profile-badge { - display: flex; - flex-direction: column; - - .profile-username { - font-size: 2rem; - } - } - - .profile-actions {} - } - - .profile-body { - .profile-biography { - font-size: 1.15rem; - } - } -} \ No newline at end of file diff --git a/public/css/profileSettings.css b/public/css/profileSettings.css deleted file mode 100644 index 89c6626..0000000 --- a/public/css/profileSettings.css +++ /dev/null @@ -1,23 +0,0 @@ -.devices-tables { - width: 100%; -} -.devices-tables .min-width { - width: -moz-fit-content; - width: fit-content; -} -.devices-tables .fit-width { - width: -moz-max-content; - width: max-content; -} -.devices-tables .fill-width { - width: 100%; -} -.devices-tables tbody tr td div { - display: flex; - flex-direction: row; - gap: 1rem; -} -.devices-tables tbody tr td div button { - padding: 0.25rem 0.5rem; - box-sizing: border-box; -} \ No newline at end of file diff --git a/public/css/profileSettings.scss b/public/css/profileSettings.scss deleted file mode 100644 index 14e36d4..0000000 --- a/public/css/profileSettings.scss +++ /dev/null @@ -1,32 +0,0 @@ -.devices-tables { - width: 100%; - - .min-width { - width: fit-content; - } - - .fit-width { - width: max-content; - } - - .fill-width { - width: 100%; - } - - tbody { - tr { - td { - div { - display: flex; - flex-direction: row; - gap: 1rem; - - button { - padding: 0.25rem 0.5rem; - box-sizing: border-box; - } - } - } - } - } -} \ No newline at end of file diff --git a/src/services/crypto.js b/src/services/crypto.js index 38bab52..e952642 100644 --- a/src/services/crypto.js +++ b/src/services/crypto.js @@ -90,4 +90,4 @@ function getPubKey() { publicKey = fs.readFileSync("./data/keys/public.pem") || null privateKey = fs.readFileSync("./data/keys/private.pem") || null -module.exports = { hashPassword, comparePassword, createJWT, verifyJWT, getPrivKey, getPubKey } \ No newline at end of file +module.exports = { createJWT, verifyJWT, getPrivKey, getPubKey } \ No newline at end of file From 48a851cbfbc2317df2ae04edb28870d572a3b23a Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Thu, 5 Mar 2026 07:28:01 -0600 Subject: [PATCH 03/16] Scale preview images on hover. --- public/css/index.css | 18 ++++++++++++++++++ public/css/index.scss | 25 ++++++++++++++++++++++++- src/views/index.ejs | 37 ++----------------------------------- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/public/css/index.css b/public/css/index.css index 08fcd0d..6f2f18d 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -112,11 +112,18 @@ width: 100%; display: flex; position: relative; + border-radius: 10px 10px 0 0; + overflow: hidden; } .session-grid .session-entry .preview-image img { height: 100%; width: 100%; border-radius: 10px 10px 0 0; + overflow: hidden; + scale: 1; +} +.session-grid .session-entry .preview-image .session-preview-image { + transition: scale ease-in-out 0.05s; } .session-grid .session-entry .preview-image .user-count { position: absolute; @@ -132,10 +139,12 @@ aspect-ratio: 1/1; display: flex; flex-direction: row; + width: 40px; } .session-grid .session-entry .preview-image .user-count div img { height: 100%; width: auto; + margin-right: 0.15rem; } .session-grid .session-entry .session-title { font-size: 1.1rem; @@ -148,4 +157,13 @@ line-clamp: 2; overflow: hidden; text-overflow: ellipsis; +} +.session-grid .session-entry:hover { + background-color: rgb(30.25, 30.25, 30.25); +} +.session-grid .session-entry:hover .preview-image .session-preview-image { + scale: 1.25; +} +.session-grid .session-entry:hover .preview-image .user-count { + background-color: rgb(30.25, 30.25, 30.25); } \ No newline at end of file diff --git a/public/css/index.scss b/public/css/index.scss index 1e151ae..aa03b3c 100644 --- a/public/css/index.scss +++ b/public/css/index.scss @@ -114,7 +114,6 @@ .session-grid { display: grid; grid-template-columns: repeat(4, 1fr); - // grid-template-columns: repeat(2, 1fr); grid-gap: 1rem; width: 1200px; margin: 2rem auto 0 auto; @@ -131,11 +130,19 @@ width: 100%; display: flex; position: relative; + border-radius: 10px 10px 0 0; + overflow: hidden; img { height: 100%; width: 100%; border-radius: 10px 10px 0 0; + overflow: hidden; + scale: 1; + } + + .session-preview-image { + transition: scale ease-in-out 0.05s; } .user-count { @@ -152,10 +159,12 @@ aspect-ratio: 1/1; display: flex; flex-direction: row; + width: 40px; img { height: 100%; width: auto; + margin-right: 0.15rem; } } } @@ -175,4 +184,18 @@ text-overflow: ellipsis; } } + + .session-entry:hover { + background-color: darken($color: colors.$bg-1, $amount: 5); + + .preview-image { + .session-preview-image { + scale: 1.25; + } + + .user-count { + background-color: darken($color: colors.$bg-1, $amount: 5); + } + } + } } \ No newline at end of file diff --git a/src/views/index.ejs b/src/views/index.ejs index 3bb3b6a..2c91c9b 100644 --- a/src/views/index.ejs +++ b/src/views/index.ejs @@ -46,47 +46,14 @@ +
- -
-
25 / 25 -
-
-
My awesome session! I hope that my cool and amazing session will show up - in the session list or something baka
-
- - - -
- -
-
25 / 25 -
-
-
My awesome session! I hope that my cool and amazing session will show up - in the session list or something baka
-
- - -
- -
-
25 / 25 -
-
-
My awesome session! I hope that my cool and amazing session will show up - in the session list or something baka
-
- - -
- +
25 / 25
From f4e39ddb458f038362f7d66f4de2ca4ee0a8519b Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Thu, 5 Mar 2026 09:50:43 -0600 Subject: [PATCH 04/16] Placeholder sessions page for session details. --- public/css/generic.css | 41 ++++++++- public/css/generic.scss | 45 +++++++++- public/css/index.css | 1 + public/css/index.scss | 1 + public/css/sessions.css | 132 +++++++++++++++++++++++++++++ public/css/sessions.scss | 155 ++++++++++++++++++++++++++++++++++ src/app.js | 2 + src/controllers/sessions.js | 9 ++ src/routes/sessions.js | 10 +++ src/views/index.ejs | 31 ++++--- src/views/partials/navbar.ejs | 4 +- src/views/session.ejs | 91 ++++++++++++++++++++ 12 files changed, 502 insertions(+), 20 deletions(-) create mode 100644 public/css/sessions.css create mode 100644 public/css/sessions.scss create mode 100644 src/controllers/sessions.js create mode 100644 src/routes/sessions.js create mode 100644 src/views/session.ejs diff --git a/public/css/generic.css b/public/css/generic.css index 08f07c5..2278a38 100644 --- a/public/css/generic.css +++ b/public/css/generic.css @@ -1,5 +1,4 @@ body { - color: white; font-family: Verdana, Geneva, Tahoma, sans-serif; margin: 0; } @@ -9,11 +8,14 @@ body .navbar { flex-direction: row; padding: 0rem 1rem; height: 60px; + color: white; } body .navbar .logo { font-size: 2rem; font-weight: 700; margin: auto 0; + text-decoration: none; + color: white; } body .navbar .navigation { height: 100%; @@ -45,6 +47,43 @@ body .text-entry { body .text-entry:focus { outline: none; } +body .page-content { + width: 1400px; + margin: 0 auto; +} + +.btn-blue { + background-color: #2466c8; + color: white; + text-shadow: 1px 1px 1px black; + transition: background-color ease-in-out 0.05s; +} + +.btn-blue:hover { + background-color: #253c5e; +} + +.btn-red { + background-color: #aa0803; + color: white; + text-shadow: 1px 1px 1px black; + transition: background-color ease-in-out 0.05s; +} + +.btn-red:hover { + background-color: #661815; +} + +.btn-gray { + background-color: #3b3b3b; + color: white; + text-shadow: 1px 1px 1px black; + transition: background-color ease-in-out 0.05s; +} + +.btn-gray:hover { + background-color: #202020; +} .hidden { display: none !important; diff --git a/public/css/generic.scss b/public/css/generic.scss index 7dafbd1..e57ab93 100644 --- a/public/css/generic.scss +++ b/public/css/generic.scss @@ -2,7 +2,6 @@ body { - color: white; font-family: Verdana, Geneva, Tahoma, sans-serif; margin: 0; @@ -12,11 +11,15 @@ body { flex-direction: row; padding: 0rem 1rem; height: 60px; + color: white; + .logo { font-size: 2rem; font-weight: 700; margin: auto 0; + text-decoration: none; + color: white; } .navigation { @@ -56,8 +59,48 @@ body { .text-entry:focus { outline: none; } + + .page-content { + width: 1400px; + margin: 0 auto; + } } +.btn-blue { + background-color: #2466c8; + color: white; + text-shadow: 1px 1px 1px black; + transition: background-color ease-in-out 0.05s; +} + +.btn-blue:hover { + background-color: #253c5e; +} + +.btn-red { + background-color: #aa0803; + color: white; + text-shadow: 1px 1px 1px black; + transition: background-color ease-in-out 0.05s; +} + +.btn-red:hover { + background-color: #661815; +} + +.btn-gray { + background-color: #3b3b3b; + color: white; + text-shadow: 1px 1px 1px black; + transition: background-color ease-in-out 0.05s; +} + +.btn-gray:hover { + background-color: #202020; +} + + + .hidden { display: none !important; } \ No newline at end of file diff --git a/public/css/index.css b/public/css/index.css index 6f2f18d..e1a374c 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -3,6 +3,7 @@ height: 500px; display: flex; background: linear-gradient(-90deg, #2557a3, #0a2247); + color: white; } .show-board .center-container { margin: auto; diff --git a/public/css/index.scss b/public/css/index.scss index aa03b3c..e1fa378 100644 --- a/public/css/index.scss +++ b/public/css/index.scss @@ -5,6 +5,7 @@ height: 500px; display: flex; background: linear-gradient(-90deg, colors.$bg-g1-1, colors.$bg-g1-2); + color: white; .center-container { margin: auto; diff --git a/public/css/sessions.css b/public/css/sessions.css new file mode 100644 index 0000000..1866860 --- /dev/null +++ b/public/css/sessions.css @@ -0,0 +1,132 @@ +.page-content { + display: flex; + flex-direction: row; + margin-top: 2rem !important; +} +.page-content .session-about { + width: 40%; + display: flex; + flex-direction: column; +} +.page-content .session-about .session-preview-image { + width: 100%; + border-radius: 20px; + overflow: hidden; +} +.page-content .session-about .session-preview-image img { + border-radius: 20px; + overflow: hidden; + width: 100%; +} +.page-content .session-about .session-quick { + width: 100%; + display: grid; + grid-template-columns: repeat(3, 1fr); +} +.page-content .session-about .session-quick .entry { + display: flex; + flex-direction: column; + margin: 0 auto 0 auto; +} +.page-content .session-about .session-quick .entry .icon { + display: flex; +} +.page-content .session-about .session-quick .entry .icon img { + margin: auto; + filter: brightness(0); +} +.page-content .session-about .session-quick .entry .count { + display: -webkit-inline-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; +} +.page-content .session-about .session-title { + font-size: 1.3rem; + display: -webkit-inline-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 4; + line-clamp: 4; + overflow: hidden; + text-overflow: ellipsis; + border-bottom: 1px solid #2b2b2b; + margin-top: 1rem; +} +.page-content .session-about .session-actions { + height: -moz-fit-content; + height: fit-content; + box-sizing: border-box; + width: 100%; + display: flex; + gap: 1rem; + margin-top: 1rem; +} +.page-content .session-about .session-actions a { + color: white; + padding: 0.5rem 2rem; + box-sizing: border-box; + border-radius: 20px; + text-decoration: none; + width: 100%; + display: flex; + transition: background-color ease-in-out 0.05s; +} +.page-content .session-about .session-actions a span { + margin: auto; +} +.page-content .session-about .session-bio { + color: rgb(33, 33, 33); + margin-top: 1rem; +} +.page-content .session-playerlist { + display: flex; + flex-direction: column; + width: 60%; +} +.page-content .session-playerlist .entry { + padding: 1rem; + display: flex; + flex-direction: row; +} +.page-content .session-playerlist .entry .username { + width: 100%; + display: flex; + background-color: #2b2b2b; + border-radius: 10px; +} +.page-content .session-playerlist .entry .username span { + margin: auto auto auto 1rem; + color: white; + padding: 0.25rem; + box-sizing: border-box; + display: -webkit-inline-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + line-clamp: 1; + overflow: hidden; + text-overflow: ellipsis; + border-bottom: 1px solid #2b2b2b; +} +.page-content .session-playerlist .entry .action-button { + width: 200px; + height: 40px; + border-radius: 10px; + display: flex; + text-decoration: none; + margin-left: 0.25rem; +} +.page-content .session-playerlist .entry .action-button .icon { + display: flex; + margin-left: 0.25rem; +} +.page-content .session-playerlist .entry .action-button .icon img { + width: 30px; + height: 30px; + margin: auto; +} +.page-content .session-playerlist .entry .action-button span { + margin: auto auto auto 0.25rem; + color: white; +} \ No newline at end of file diff --git a/public/css/sessions.scss b/public/css/sessions.scss new file mode 100644 index 0000000..9257ceb --- /dev/null +++ b/public/css/sessions.scss @@ -0,0 +1,155 @@ +@use "colors"; + +.page-content { + display: flex; + flex-direction: row; + margin-top: 2rem !important; + + .session-about { + width: 40%; + display: flex; + flex-direction: column; + + .session-preview-image { + width: 100%; + border-radius: 20px; + overflow: hidden; + + img { + border-radius: 20px; + overflow: hidden; + width: 100%; + } + } + + .session-quick { + width: 100%; + display: grid; + grid-template-columns: repeat(3, 1fr); + + .entry { + display: flex; + flex-direction: column; + margin: 0 auto 0 auto; + + .icon { + display: flex; + + img { + margin: auto; + filter: brightness(0); + } + } + + .count { + display: -webkit-inline-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + } + } + } + + .session-title { + font-size: 1.3rem; + display: -webkit-inline-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 4; + line-clamp: 4; + overflow: hidden; + text-overflow: ellipsis; + border-bottom: 1px solid colors.$bg-1; + margin-top: 1rem; + } + + .session-actions { + height: fit-content; + box-sizing: border-box; + width: 100%; + display: flex; + gap: 1rem; + margin-top: 1rem; + + a { + color: white; + padding: 0.5rem 2rem; + box-sizing: border-box; + border-radius: 20px; + text-decoration: none; + width: 100%; + display: flex; + transition: background-color ease-in-out 0.05s; + + span { + margin: auto; + } + } + } + + .session-bio { + color: rgb(33, 33, 33); + margin-top: 1rem; + } + } + + .session-playerlist { + display: flex; + flex-direction: column; + width: 60%; + + .entry { + padding: 1rem; + display: flex; + flex-direction: row; + + .username { + width: 100%; + display: flex; + background-color: colors.$bg-1; + border-radius: 10px; + + span { + margin: auto auto auto 1rem; + color: white; + padding: 0.25rem; + box-sizing: border-box; + + display: -webkit-inline-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + line-clamp: 1; + overflow: hidden; + text-overflow: ellipsis; + border-bottom: 1px solid colors.$bg-1; + } + } + + .action-button { + width: 200px; + height: 40px; + border-radius: 10px; + display: flex; + text-decoration: none; + margin-left: 0.25rem; + + .icon { + display: flex; + margin-left: 0.25rem; + + img { + width: 30px; + height: 30px; + margin: auto; + } + } + + span { + margin: auto auto auto 0.25rem; + color: white; + } + } + } + } +} \ No newline at end of file diff --git a/src/app.js b/src/app.js index bf45d4f..5bceff3 100644 --- a/src/app.js +++ b/src/app.js @@ -6,6 +6,7 @@ const methodOverride = require("method-override"); // Routes const indexRoutes = require("./routes/index"); +const sessionsRoutes = require("./routes/sessions"); const apiRoutes = require("./routes/api"); // Middleware @@ -28,6 +29,7 @@ app.use(cookieParser()); // Routes app.use("/api", apiRoutes); // API is required to make an account. If this is after ensuring the setup we would be caught in a loop! // app.use(ensureSetup.middleware); +app.use("/sessions", sessionsRoutes); app.use("/", indexRoutes); app.use(errorHandler); diff --git a/src/controllers/sessions.js b/src/controllers/sessions.js new file mode 100644 index 0000000..1899a4d --- /dev/null +++ b/src/controllers/sessions.js @@ -0,0 +1,9 @@ +const getIndex = async (req, res, next) => { + try { + res.render("session", { requestingUser: req.requestingUser }); + } catch (err) { + next(err); + } +}; + +module.exports = { getIndex }; diff --git a/src/routes/sessions.js b/src/routes/sessions.js new file mode 100644 index 0000000..074a564 --- /dev/null +++ b/src/routes/sessions.js @@ -0,0 +1,10 @@ +const express = require("express"); +const router = express.Router(); +const isAuth = require("../middleware/isAuth"); +const requireAuth = require("../middleware/requireAuth"); + +const sessionsController = require("../controllers/sessions"); + +router.get("/:session_id", isAuth, sessionsController.getIndex); + +module.exports = router \ No newline at end of file diff --git a/src/views/index.ejs b/src/views/index.ejs index 2c91c9b..13a263a 100644 --- a/src/views/index.ejs +++ b/src/views/index.ejs @@ -11,25 +11,24 @@ <%- include('partials/navbar') %> - -
- -
-
-
OpenMinerva
-
-
-
25
-
Users
-
-
-
3
-
Sessions
-
+ +
+
+
OpenMinerva
+
+
+
25
+
Users
+
+
+
3
+
Sessions
+
+
@@ -51,7 +50,7 @@
- +
diff --git a/src/views/partials/navbar.ejs b/src/views/partials/navbar.ejs index 03f49c0..638f4a8 100644 --- a/src/views/partials/navbar.ejs +++ b/src/views/partials/navbar.ejs @@ -1,7 +1,7 @@