commit 7fadab8cd65e36459a49adacbd7582761c1b9cc5 Author: Novacell Date: Tue May 26 20:18:12 2026 +0200 BaseFrontendBackend diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d1af73 --- /dev/null +++ b/.gitignore @@ -0,0 +1,57 @@ +# ============================================================================== +# Void-Genesis - Globale .gitignore +# ============================================================================== + +# ------------------------------------------------------------------------------ +# 1. Lokale Umgebungsvariablen & Secrets (SEHR WICHTIG!) +# ------------------------------------------------------------------------------ +# .env Dateien enthalten Passwörter, API-Keys und Datenbank-Zugänge (wie unsere +# POSTGRES_PASSWORD_LOBBY). Diese dürfen niemals ins Git-Repository (Gitea) +# gepusht werden, da sonst die Infrastruktur kompromittiert ist! +.env +.env.local +.env.*.local + +# ------------------------------------------------------------------------------ +# 2. Datenbank & Persistente Daten +# ------------------------------------------------------------------------------ +# Wir ignorieren alle Ordner, die "data" heißen (z.B. dev/data/postgres). +# Hier liegen die physischen Datenbank-Dateien von PostgreSQL. Diese ändern sich +# ständig und würden das Repository extrem aufblähen und unbrauchbar machen. +**/data/ + +# ------------------------------------------------------------------------------ +# 3. Node.js & Paketmanager-Logs +# ------------------------------------------------------------------------------ +# Der 'node_modules' Ordner enthält zehntausende Dateien fremder Pakete. +# Diese sind oft betriebssystemabhängig und werden stattdessen immer frisch über +# die package.json (via npm install) bzw. im Docker-Build installiert. +node_modules/ +# Log-Dateien, falls npm oder yarn bei einem lokalen Test abstürzen. +npm-debug.log* +yarn-debug.log* +yarn-error.log* +*.log + +# ------------------------------------------------------------------------------ +# 4. Build-Outputs (Frontend Vite / Backend) +# ------------------------------------------------------------------------------ +# Die fertig kompilierten Dateien (z.B. minimiertes HTML/JS/CSS von Vite). +# Diese generieren wir im Produktivbetrieb später on-the-fly, sie gehören +# nicht in die Versionskontrolle des Quellcodes. +dist/ +dist-ssr/ +build/ +out/ + +# ------------------------------------------------------------------------------ +# 5. Betriebssystem-spezifische Dateien & Editor-Configs +# ------------------------------------------------------------------------------ +# Automatisch generierte Metadaten von Windows oder macOS, die anderen +# Entwicklern oder dem Server nur im Weg sind. +.DS_Store +Thumbs.db +# (Optional) Lokale VS Code Einstellungen, die nicht für das Team gelten +.vscode/ + +gem_*.txt \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/dev/docker-compose.yml b/dev/docker-compose.yml new file mode 100644 index 0000000..7ac75b0 --- /dev/null +++ b/dev/docker-compose.yml @@ -0,0 +1,116 @@ +# ============================================================================== +# Void-Genesis - Lobby Dev-Umgebung +# ============================================================================== + +services: + # ---------------------------------------------------------------------------- + # 1. Lobby-Datenbank (PostgreSQL) + # ---------------------------------------------------------------------------- + lobby-db: + image: postgres:16-alpine + container_name: void_lobby_db + restart: unless-stopped + # Hier übergeben wir die Zugangsdaten aus der .env Datei an den Container + environment: + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_USER: ${POSTGRES_USER} + # Da der Container standardmäßig nach POSTGRES_PASSWORD sucht, leiten wir + # unser spezifisches User-Passwort für die Initialisierung hier weiter. + PGPASSWORD: ${POSTGRES_PASSWORD_LOBBY} + POSTGRES_DB: ${POSTGRES_DB} + # Wir binden den Port nur an Localhost. So kann man per SSH-Tunnel + # (z.B. mit DBeaver/PgAdmin) sicher darauf zugreifen. + ports: + - "${PORT_DB}:5432" + # Wir mounten den lokalen Ordner in den Container. Wenn der Container + # gelöscht wird, bleiben unsere Tabellen und Nutzerdaten erhalten. + volumes: + - ./data/postgres:/var/lib/postgresql/data + # Verbindet die Datenbank mit unserem internen Projekt-Netzwerk + networks: + - void_dev_network + # Ein Healthcheck stellt sicher, dass die DB wirklich Anfragen annehmen kann, + # bevor das Backend startet. + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] + interval: 5s + timeout: 5s + retries: 5 + + # ---------------------------------------------------------------------------- + # 2. Lobby-Backend (Node.js) + # ---------------------------------------------------------------------------- + lobby-backend: + # Wir bauen das Image dynamisch aus unserem Unterordner + build: + context: ./lobby-backend + dockerfile: Dockerfile.dev + container_name: void_lobby_backend + restart: unless-stopped + # Das Backend darf erst starten, wenn der Healthcheck der Datenbank 'healthy' ist. + depends_on: + lobby-db: + condition: service_healthy + # Wir mounten den lokalen Ordner direkt in das Arbeitsverzeichnis des Containers. + # Dadurch greifen Code-Änderungen sofort (Hot-Reload via Nodemon). + volumes: + - ./lobby-backend:/app + # Wir schließen node_modules vom lokalen Mount aus, damit der Container + # seine eigenen, nativen Linux-Binaries behält. + - /app/node_modules + ports: + - "${PORT_BACKEND}:3000" + # Übergabe der DB-Daten an Node.js, um die Connection-String zu bauen + environment: + - DB_HOST=lobby-db + - DB_PORT=5432 + - DB_USER=${POSTGRES_USER} + - DB_PASS=${POSTGRES_PASSWORD} + - DB_NAME=${POSTGRES_DB} + - NODE_ENV=development + + - JWT_ACCESS_SECRET=${JWT_ACCESS_SECRET} + - JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET} + + - BREVO_API_KEY=${BREVO_API_KEY} + - BREVO_SENDER_EMAIL=${BREVO_SENDER_EMAIL} + - BREVO_SENDER_NAME=${BREVO_SENDER_NAME} + - MAIL_MODE=${MAIL_MODE} + networks: + - void_dev_network + - npm_network + # ---------------------------------------------------------------------------- + # 3. Dev-Frontend (Vite + PixiJS) + # ---------------------------------------------------------------------------- + lobby-frontend: + build: + context: ./lobby-frontend + dockerfile: Dockerfile.dev + container_name: void_lobby_frontend + restart: unless-stopped + # Auch hier mounten wir den Code für Vite's HMR (Hot Module Replacement) + volumes: + - ./lobby-frontend:/app + - /app/node_modules + # Wir exposen den Vite-Standardport. Nginx Proxy Manager (NPM) kann dann + # dev.void-genesis.de auf diesen lokalen Port routen. + ports: + - "${PORT_FRONTEND}:5173" + environment: + - NODE_ENV=development + # Vite benötigt diese Einstellung oft in Docker, um File-Changes zu bemerken + - CHOKIDAR_USEPOLLING=true + networks: + - void_dev_network + - npm_network + +# ------------------------------------------------------------------------------ +# Netzwerke +# ------------------------------------------------------------------------------ +networks: + void_dev_network: + driver: bridge + # Wir binden das externe NPM-Netzwerk ein + npm_network: + external: true + name: nginx-proxy-manager_default \ No newline at end of file diff --git a/dev/lobby-backend/Dockerfile.dev b/dev/lobby-backend/Dockerfile.dev new file mode 100644 index 0000000..6994210 --- /dev/null +++ b/dev/lobby-backend/Dockerfile.dev @@ -0,0 +1,32 @@ +# ============================================================================== +# Void-Genesis - Lobby Backend (Dev Environment) +# ============================================================================== + +# Wir nutzen eine leichtgewichtige Alpine-Version der aktuellen Node.js LTS (z.B. 22) +# Alpine-Images sind extrem klein und reduzieren die Angriffsfläche. +FROM node:22-alpine + +# Setze das Arbeitsverzeichnis innerhalb des Containers auf /app. +# Alle folgenden Befehle (RUN, CMD, COPY) werden in diesem Ordner ausgeführt. +WORKDIR /app + +# Wir kopieren zuerst NUR die package.json (und package-lock.json, falls vorhanden). +# Das ist ein Best-Practice für Docker: Docker speichert diesen Schritt im Cache. +# 'npm install' wird beim erneuten Bauen nur dann ausgeführt, wenn sich an den +# Abhängigkeiten in der package.json etwas geändert hat. Das spart enorm Zeit! +COPY package*.json ./ + +# Installiere alle Abhängigkeiten (inklusive Dev-Dependencies wie nodemon) +RUN npm install + +# HINWEIS: Den restlichen Quellcode (src/) kopieren wir hier im Dev-Dockerfile +# absichtlich nicht mit COPY. Der Code wird stattdessen über die docker-compose.yml +# als Volume gemountet. So greifen deine Änderungen am Root-Server sofort im Container. + +# Wir dokumentieren hier, dass der Container auf Port 3000 lauscht. +# Das Backend wird diesen Port später nutzen. +EXPOSE 3000 + +# Der Standardbefehl, wenn der Container startet. Wir rufen das Script "dev" auf, +# welches wir gleich im nächsten Schritt in der package.json definieren werden. +CMD ["npm", "run", "dev"] \ No newline at end of file diff --git a/dev/lobby-backend/package-lock.json b/dev/lobby-backend/package-lock.json new file mode 100644 index 0000000..6ca319b --- /dev/null +++ b/dev/lobby-backend/package-lock.json @@ -0,0 +1,1618 @@ +{ + "name": "void-genesis-lobby-backend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "void-genesis-lobby-backend", + "version": "1.0.0", + "dependencies": { + "argon2": "^0.44.0", + "cors": "^2.8.5", + "express": "^4.19.2", + "jsonwebtoken": "^9.0.3", + "pg": "^8.11.5" + }, + "devDependencies": { + "nodemon": "^3.1.0" + } + }, + "node_modules/@epic-web/invariant": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", + "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", + "license": "MIT" + }, + "node_modules/@phc/format": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", + "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argon2": { + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.44.0.tgz", + "integrity": "sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@phc/format": "^1.0.0", + "cross-env": "^10.0.0", + "node-addon-api": "^8.5.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/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/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-env": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", + "license": "MIT", + "dependencies": { + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" + }, + "bin": { + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.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/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "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/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/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.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "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/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "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.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "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/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-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "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/jsonwebtoken/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/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/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/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": "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/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "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/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/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.8.0.tgz", + "integrity": "sha512-c5Ko1fZJIJmzhFIkhRN76WTq+fC6tWnGy9CXA0fA+XygsWZmEwG8vmbkNqxMyoaa0Tin4djul49NzdVcJJcjeA==", + "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/nodemon": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz", + "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^10.2.1", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/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-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/pg": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.21.0.tgz", + "integrity": "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.13.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.14.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.4.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.13.0.tgz", + "integrity": "sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.14.0.tgz", + "integrity": "sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "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": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "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/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.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/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/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "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-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "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/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "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/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "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/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "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/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "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/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "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/dev/lobby-backend/package.json b/dev/lobby-backend/package.json new file mode 100644 index 0000000..3da91d7 --- /dev/null +++ b/dev/lobby-backend/package.json @@ -0,0 +1,21 @@ +{ + "name": "void-genesis-lobby-backend", + "version": "1.0.0", + "description": "Backend für die Master-Account-Lobby", + "main": "src/server.js", + "type": "module", + "scripts": { + "start": "node src/server.js", + "dev": "nodemon src/server.js" + }, + "dependencies": { + "argon2": "^0.44.0", + "cors": "^2.8.5", + "express": "^4.19.2", + "jsonwebtoken": "^9.0.3", + "pg": "^8.11.5" + }, + "devDependencies": { + "nodemon": "^3.1.0" + } +} diff --git a/dev/lobby-backend/src/controllers/auth.controller.js b/dev/lobby-backend/src/controllers/auth.controller.js new file mode 100644 index 0000000..cf808d2 --- /dev/null +++ b/dev/lobby-backend/src/controllers/auth.controller.js @@ -0,0 +1,201 @@ +import * as authService from '../services/auth.service.js'; + +// Ein Standard-Regulärer-Ausdruck (Regex) zur Prüfung eines gültigen E-Mail-Formats +const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + +/** + * Controller-Methode für die Registierung-Route. + */ +export const register = async (req, res) => { + try { + // Daten aus dem Body extrahieren + const { username, email, password } = req.body; + + // Basis-Validierung (leere Felder, Passwortlänge) + if (!username || !email || !password) { + return res.status(400).json({ + // ERR_MISSING_FIELDS: Bitte alle Felder ausfüllen. + error: 'ERR_MISSING_FIELDS' + }); + } + if (password.length < 8) { + return res.status(400).json({ + // ERR_PASSWORD_TOO_SHORT: Das Passwort muss mindestens 8 Zeichen lang sein. + error: 'ERR_PASSWORD_TOO_SHORT' + }); + } + if (username.length < 3) { + return res.status(400).json({ + // ERR_USERNAME_TOO_SHORT: Der Benutzername muss mindestens 3 Zeichen lang sein. + error: 'ERR_USERNAME_TOO_SHORT' + }); + } + if (!EMAIL_REGEX.test(email)) { + return res.status(400).json({ + /// ERR_INVALID_EMAIL: Das E-Mail-Format ist ungültig. + error: 'ERR_INVALID_EMAIL' + }); + } + + // Den Auth-Service aufrufen (die eigenliche Arbeit) + const result = await authService.registerUser(username, email, password); + + // Erfolgreiche Antwort an der Frontend senden + return res.status(201).json({ + success: true, + // MSG_REGISTER_SUCCESS: Registrierung erfolgreich. Bitte prüfe deine E-Mails. + message: 'MSG_REGISTER_SUCCESS', + accountId: result.accountId + }); + + } catch (error) { + // Fehlerbehandlung: Wenn es unser eigener Conflict-Fehler ist (zB Email existiert) + if (error.code === 'ERR_USERNAME_TAKEN') { + return res.status(409).json({ + // ERR_USERNAME_TAKEN: Dieser Benutzername ist bereits vergeben. + error: 'ERR_USERNAME_TAKEN' + }); + } + if (error.code === 'ERR_EMAIL_TAKEN') { + return res.status(409).json({ + // ERR_USERNAME_TAKEN: Diese E-Mail-Addresse ist bereits vergeben. + error: 'ERR_EMAIL_TAKEN' + }); + } + + // Bei unerwarteten Fehlern (z.B. Datenbank oder Brovo nicht erreichbar.) + console.error('[AuthController] Fehler bei der Registierung:', error); + return res.status(500).json({ + // ERR_INTERNAL_SERVER: Ein interner Serverfehler ist aufgetreten. + error: 'ERR_INTERNAL_SERVER' + }); + } +}; + +/** + * Controller-Methode für die E-Mail-Verifizierungs-Route + */ +export const verifyEmail = async (req, res) => { + try { + // Daten aus dem Request-Body extrahieren + const { email , otpCode } = req.body; + + // Vasis-Validierung + if (!email || !otpCode) { + return res.status(400).json({ + error: 'ERR_MISSING_FIELDS' + }); + } + + // Den Service aufrufen, um die eigenliche Arbeit zu machen + await authService.verifyEmail(email, otpCode); + + // Erfolgreiche Antwort ans Frontend senden + return res.status(200).json({ + success: true, + message: 'MSG_EMAIL_VERIFIED' + }); + + } catch (error) { + if (['ERR_USER_NOT_FOUND', 'ERR_ALREADY_VERIFIED', 'ERR_INVALID_OTP', 'ERR_OTP_EXPIRED'].includes(error.code)) { + + return res.status(400).json({ + error: error.code + }); + } + + console.error('[AuthController] Fehler bei E-Mail-Verifizierung:', error); + return res.status(500).json({ + error: 'ERR_INTERNAL_SERVER' + }); + } +}; + +/** + * Controller-Methode für die Login-Route + */ +export const login = async (req, res) => { + try { + const { identifier, password } = req.body; + + // Basis-Validierung + if (!identifier || !password) { + return res.status(400).json({ + error: 'ERR_MISSING_FIELDS' + }); + } + + // Service aufrufen + const result = await authService.loginUser(identifier, password); + + // Erfolgreiche Antwort mit den Tokens + return res.status(200).json({ + success: true, + // MSG_LOGIN_SUCCESS: Erfolgreich eingeloggt + message: 'MSG_LOGIN_SUCCESS', + accessToken: result.accessToken, + refreshToken: result.refreshToken, + user: result.user + }); + + } catch (error) { + if (error.code === 'ERR_INVALID_CREDENTIALS') { + return res.status(401).json({ + error: 'ERR_INVALID_CREDENTIALS' + }); + } + if (error.code === 'ERR_EMAIL_NOT_VERIFIED') { + return res.status(403).json({ + error: 'ERR_EMAIL_NOT_VERIFIED' + }); + } + + console.error('[AuthController] Fehler beim Login:', error); + return res.status(500).json({ + error: 'ERR_INTERNAL_SERVER' + }); + } +} + +/** + * Controller-Methode für die Token-Refresh-Route + */ +export const refresh = async (req, res) => { + try { + // Das Refresh-Token aus dem Request-Body holen + const { refreshToken } = req.body; + + // Validierung: Is überhaupt ein Token mitgesendet worden ? + if (!refreshToken) { + // ERR_MISSING_TOKEN: Es wurde kein Refresh-Token übergeben + return res.status(400).json({ + error: 'ERR_MISSING_TOKEN' + }); + } + + // Den Service aufrufen, um das Token zu prüfen und ein neues zu generieren + const result = await authService.refreshAccessToken(refreshToken); + + // Das neue Access-Token an das Frontend zurück geben + return res.status(200).json({ + success: true, + accessToken: result.accessToken + }); + + } catch (error) { + // Ungültige, abgelaufene oder in der DB gelöschte Tokens abfangen + if (error.code === 'ERR_UNAUTHORIZED') { + return res.status(401).json({ + // ERR_UNAUTHORIZED: Zugriff verweigert. Die Sitzung ist ungültig. + error: 'ERR_UNAUTHORIZED' + }); + } + + // Bei unerwarteten Fehlern (z.B. Datenbank oder Brovo nicht erreichbar.) + console.error('[AuthController] Fehler beim Token-Refresh:', error); + return res.status(500).json({ + // ERR_INTERNAL_SERVER: Ein interner Serverfehler ist aufgetreten. + error: 'ERR_INTERNAL_SERVER' + }); + } +} \ No newline at end of file diff --git a/dev/lobby-backend/src/db.js b/dev/lobby-backend/src/db.js new file mode 100644 index 0000000..5a15ac9 --- /dev/null +++ b/dev/lobby-backend/src/db.js @@ -0,0 +1,36 @@ +// ============================================================================== +// Void-Genesis - Datenbank-Konfiguration +// ============================================================================== + +import pg from 'pg'; + +// Wir nutzen einen Connection Pool. Das ist deutlich effizienter als für jede +// Anfrage eine neue Verbindung zur Datenbank aufzubauen. Der Pool hält im +// Hintergrund mehrere Verbindungen offen und verteilt sie dynamisch. +const { Pool } = pg; + +const pool = new Pool({ + // Diese Variablen kommen direkt aus unserer docker-compose.yml + // (Bereich 'environment' des Backend-Containers) + host: process.env.DB_HOST, + port: process.env.DB_PORT, + user: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, +}); + +// Wir testen beim Start einmalig, ob die Verbindung klappt. +// So sehen wir im Docker-Log sofort, falls z.B. das Passwort falsch ist. +pool.connect((err, client, release) => { + if (err) { + console.error('❌ Fehler beim Verbinden mit der PostgreSQL-Datenbank:', err.stack); + } else { + console.log('✅ Erfolgreich mit der PostgreSQL-Datenbank (void_lobby) verbunden!'); + // WICHTIG: Den Client immer direkt wieder freigeben, wenn wir fertig sind + release(); + } +}); + +// Wir exportieren den Pool, damit wir ihn in anderen Dateien (wie unseren Routes) +// für Datenbankabfragen nutzen können. +export default pool; \ No newline at end of file diff --git a/dev/lobby-backend/src/routes/auth.routes.js b/dev/lobby-backend/src/routes/auth.routes.js new file mode 100644 index 0000000..abf5be7 --- /dev/null +++ b/dev/lobby-backend/src/routes/auth.routes.js @@ -0,0 +1,11 @@ +import express from 'express'; +import * as authController from '../controllers/auth.controller.js'; + +const router = express.Router(); + +router.post('/register', authController.register); +router.post('/verify-email', authController.verifyEmail); +router.post('/login', authController.login); +router.post('/refresh', authController.refresh); + +export default router; \ No newline at end of file diff --git a/dev/lobby-backend/src/scripts/initDb.js b/dev/lobby-backend/src/scripts/initDb.js new file mode 100644 index 0000000..f48c59a --- /dev/null +++ b/dev/lobby-backend/src/scripts/initDb.js @@ -0,0 +1,109 @@ +import pool from '../db.js'; + +// ============================================================================== +// Void-Genesis - Datenbank Initialisierungs-Skript +// Führt alle nötigen CREATE TABLE Befehle aus, falls die Tabellen nicht existieren. +// ============================================================================== + +const createTablesQuery = ` + +DROP TABLE IF EXISTS refresh_tokens; +DROP TABLE IF EXISTS auth_otps; +DROP TABLE IF EXISTS lobby_accounts; + +-- ------------------------------------------------------------------------------ +-- TABLE: lobby_accounts +-- Base user data for the entire Void-Genesis universe. +-- ------------------------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS lobby_accounts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + username VARCHAR(50) UNIQUE NOT NULL, + email VARCHAR(255) UNIQUE NOT NULL, + password_hash VARCHAR(255) NOT NULL, + + -- Status flags and balances (English naming) + is_email_verified BOOLEAN DEFAULT false, + res_premium INT DEFAULT 0, + is_admin BOOLEAN DEFAULT false, + + -- Timestamps for record keeping + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() +); + +-- ------------------------------------------------------------------------------ +-- TABLE: auth_otps +-- Stores temporary 6-digit codes for registration or password resets. +-- ------------------------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS auth_otps ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + account_id UUID REFERENCES lobby_accounts(id) ON DELETE CASCADE, + otp_code VARCHAR(6) NOT NULL, + + -- Defines the purpose of the OTP (e.g., 'register', 'password_reset') + type VARCHAR(20) NOT NULL, + expires_at TIMESTAMPTZ NOT NULL, + created_at TIMESTAMPTZ DEFAULT NOW() +); + +-- ------------------------------------------------------------------------------ +-- TABLE: refresh_tokens +-- Stores long-lived tokens for the "Keep me logged in" and single-session feature. +-- ------------------------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS refresh_tokens ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + account_id UUID REFERENCES lobby_accounts(id) ON DELETE CASCADE, + token VARCHAR(500) UNIQUE NOT NULL, + expires_at TIMESTAMPTZ NOT NULL, + created_at TIMESTAMPTZ DEFAULT NOW() +); + +-- ------------------------------------------------------------------------------ +-- TRIGGER: Auto-update 'updated_at' timestamp +-- ------------------------------------------------------------------------------ +-- This function sets the 'updated_at' field to the current time whenever a row is modified. +CREATE OR REPLACE FUNCTION update_modified_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$ language 'plpgsql'; + +-- We only create the trigger if it doesn't exist to avoid errors on multiple runs. +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_lobby_accounts_modtime') THEN + CREATE TRIGGER update_lobby_accounts_modtime + BEFORE UPDATE ON lobby_accounts + FOR EACH ROW + EXECUTE FUNCTION update_modified_column(); + END IF; +END $$; +`; + +// ============================================================================== +// Ausführungs-Logik +// ============================================================================== +async function initDatabase() { + console.log('⏳ Starte Datenbank-Initialisierung...'); + + // Wir holen uns eine Verbindung aus dem Pool + const client = await pool.connect(); + + try { + // Wir senden den kompletten SQL-String an die Datenbank + await client.query(createTablesQuery); + console.log('✅ Tabellen wurden erfolgreich erstellt (oder existierten bereits).'); + } catch (error) { + console.error('❌ Fehler beim Erstellen der Tabellen:', error); + } finally { + // Ganz wichtig: Verbindung wieder freigeben + client.release(); + // Den Node-Prozess beenden, damit das Skript nicht in der Konsole hängen bleibt + process.exit(); + } +} + +// Skript direkt aufrufen +initDatabase(); \ No newline at end of file diff --git a/dev/lobby-backend/src/server.js b/dev/lobby-backend/src/server.js new file mode 100644 index 0000000..716a67e --- /dev/null +++ b/dev/lobby-backend/src/server.js @@ -0,0 +1,53 @@ +// ============================================================================== +// Void-Genesis - Lobby Backend Server (Einstiegspunkt) +// ============================================================================== + +import express from 'express'; +import cors from 'cors'; +import pool from './db.js'; + +import authRoutes from './routes/auth.routes.js'; + +const app = express(); +const PORT = process.env.PORT || 3000; + +// Middleware +app.use(cors()); +app.use(express.json()); + +app.use('/api/auth', authRoutes); + +// Unsere bestehende Health-Route +app.get('/api/health', (req, res) => { + res.json({ + status: 'ok', + message: 'Void-Genesis Lobby Backend läuft!', + timestamp: new Date().toISOString() + }); +}); + +// Eine Test-Route für die Datenbank +app.get('/api/db-test', async (req, res) => { + try { + const result = await pool.query('SELECT NOW()'); + + res.json({ + status: 'success', + message: 'Datenbankverbindung steht!', + db_time: result.rows[0].now + }); + } catch (err) { + console.error('Fehler bei der DB-Abfrage:', err); + res.status(500).json({ + status: 'error', + message: 'Datenbankfehler', + error: err.message + }); + } +}); + +// Server starten +app.listen(PORT, () => { + console.log(`🚀 Void-Genesis Lobby Backend gestartet auf Port ${PORT}`); + console.log(`Environment: ${process.env.NODE_ENV}`); +}); \ No newline at end of file diff --git a/dev/lobby-backend/src/services/auth.service.js b/dev/lobby-backend/src/services/auth.service.js new file mode 100644 index 0000000..db035b4 --- /dev/null +++ b/dev/lobby-backend/src/services/auth.service.js @@ -0,0 +1,350 @@ +import argon2 from 'argon2'; +import jwt from 'jsonwebtoken'; +import pool from '../db.js'; +import { sendEmail } from './email.service.js'; + +/** + * Generiert einen zufälligen 6-stelligen Code als String + */ +const generateOTP = () => { + return Math.floor(100000 + Math.random() * 900000).toString(); +}; + +/** + * Registriert einen neuen Nutzer, speichert das OTP und löst den E-Mail-Versand aus. + * + * @param {string} username - Der gewünschte Benutzername + * @param {string} email - Die E-Mail-Addresse + * @param {string} password - Das Klartext-Passwort + * @returns {object} - Die accountId des neuen Nutzers + */ +export const registerUser = async (username, email, password) => { + // Wir holen uns einen dedizierten Client aus dem pool für unsere Transaktion + const client = await pool.connect(); + + const lowerCaseEmail = email.toLowerCase(); + + try { + // Transaktion starten: Entweder alles klappt, oder nichts wird gespeichert + await client.query('BEGIN'); + + // Prüfen, ob der Nutzer oder die E-Mail schon existiert + const checkResult = await client.query(` + SELECT username, email FROM lobby_accounts + WHERE LOWER(username) = LOWER($1) OR email = $2`, + [username, lowerCaseEmail] + ); + + if (checkResult.rows.length > 0) { + const existingRecord = checkResult.rows[0]; + + if (existingRecord.email === lowerCaseEmail) { + const error = new Error('Email already exists'); + error.code = 'ERR_EMAIL_TAKEN'; + throw error; + } else { + const error = new Error('Username already exists'); + error.code = 'ERR_USERNAME_TAKEN'; + throw error; + } + } + + // Passwort sicher hashen + const passwordHash = await argon2.hash(password); + + // Nutzer in de Datenbank eintragen + const insertAccount = await client.query(` + INSERT INTO lobby_accounts (username, email, password_hash) + VALUES ($1, $2, $3) RETURNING id`, + [username, lowerCaseEmail, passwordHash] + ); + const accountId = insertAccount.rows[0].id; + + // OTP generieren uns in der auth_otps Tabelle speichern (15min Gültigkeit) + const otpCode = generateOTP(); + await client.query(` + INSERT INTO auth_otps (account_id, otp_code, type, expires_at) + VALUES ($1, $2, 'register', NOW() + INTERVAL '15 minutes')`, + [accountId, otpCode] + ); + + // Registierungs E-Mail über unseren E-Mail Service versenden + const mailHtml = ` +

Willkommen bei Void-Genesis, ${username}!

+

Dein Verifizierungscode lautet:

+

${otpCode}

+

Dieser Code ist 15 Minuten lang gültig.

+ `; + await sendEmail(lowerCaseEmail, username, 'Void-Genesis - Aktiviere deinen Account', mailHtml); + + // Wenn wir hier ankommen, lief alles fehlerfrei, wir committen die Daten. + await client.query('COMMIT'); + + return { accountId }; + } catch (error) { + // bei JEDEM Fehler machen wir einen Rollback. Die Datenbank bleibt sauber. + await client.query('ROLLBACK'); + + if (error.code === '23505') { + if (error.constraint === 'lobby_accounts_username_key') { + const customErr = new Error('Username already exists'); + customErr.code = 'ERR_USERNAME_TAKEN'; + throw customErr; + } + if (error.constraint === 'lobby_accounts_email_key') { + const customErr = new Error('Email already exists'); + customErr.code = 'ERR_EMAIL_TAKEN'; + throw customErr; + } + } + + throw error; + } finally { + client.release(); + } +} + +/** + * Verifiziert die E-Mail-Adresse eines Nutzers + * + * @param {string} email - Die E-Mail-Addresse des Nutzers + * @param {string} otpCode - Der 6-stellige Code aus der E-Mail + * @returns {boolean} - true wenn erfolgreich + */ +export const verifyEmail = async (email, otpCode) => { + const client = await pool.connect(); + const lowerCaseEmail = email.toLowerCase(); + + try { + await client.query('BEGIN'); + + // Nutzer anhand der E-Mail suchen un die ID sowie Verifizierungsstatus abfragen + const accountResult = await client.query( + 'SELECT id, is_email_verified FROM lobby_accounts WHERE email = $1', + [lowerCaseEmail] + ); + + if (accountResult.rows.length === 0) { + // ERR_USER_NOT_FOUND: Es existiert kein Account mit dieser E-Mail + const error = new Error('User not found'); + error.code = 'ERR_USER_NOT_FOUND'; + throw error; + } + + const account = accountResult.rows[0]; + + // Prüfen, ob der Nutzer nicht ohnehin schon verifiziert ist + if (account.is_email_verified) { + // ERR_ALREADY_VERIFIED: Diese E-Mail-Addresse wurde bereits bestätigt + const error = new Error('Already verified'); + error.code = 'ERR_ALREADY_VERIFIED'; + throw error; + } + + // Den passenden OTP-Code in der Datenbank suchen + const otpResult = await client.query(` + SELECT id, expires_at FROM auth_otps + WHERE account_id = $1 AND otp_code = $2 AND type = 'register'`, + [account.id, otpCode] + ); + + if (otpResult.rows.length === 0) { + // ERR_INVALID_OTP: Der eingegebene Code ist falsch. + const error = new Error('Invalid OTP'); + error.code = 'ERR_INVALID_OTP'; + throw error; + } + + const otpRecord = otpResult.rows[0]; + + // Prüfen, ob der Cide bereits abgelaufen ist (älter als 15 Minuten) + if (new Date() > new Date(otpRecord.expires_at)) { + // ERR_OTP_EXPIRED: Der Code ist abgelaufen. Bitte fordere einen neuen an. + const error = new Error('OTP expired'); + error.code = 'ERR_OTP_EXPIRED'; + throw error; + } + + // Account als verifiziert markieren + await client.query( + 'UPDATE lobby_accounts SET is_email_verified = true WHERE id = $1', + [account.id] + ); + + // Den genutzten OTP-Code aus der Datenbank löschen (Sicherheit und Sauberkeit) + await client.query( + 'DELETE FROM auth_otps WHERE id = $1', + [otpRecord.id] + ); + + // Alles lief perfekt, Änderungen speichern + await client.query('COMMIT'); + return true; + + } catch (error) { + await client.query('ROLLBACK'); + throw error; + } finally { + client.release(); + } +}; + +/** + * Loggt einen Nutzer ein, verifiziert das Passwort uns erstellt JWT-Tokens + * + * @param {string} identifier - Kann der Benutzername oder die E-Mail-Address sein + * @param {string} password - Das eingegebene Klartext-Passwort + * @returns {object} - Enthält accessToken, refreshToken und grundlegende Nutzerdaten + */ +export const loginUser = async (identifier, password) => { + const client = await pool.connect(); + + try { + // Nutzer suchen + const accountResult = await client.query(` + SELECT id, username, email, password_hash, is_email_verified, is_admin + FROM lobby_accounts + WHERE LOWER(username) = LOWER($1) OR email = LOWER($1)`, + [identifier] + ); + + // Wenn kein Nutzer gefunden wurde + if (accountResult.rows.length === 0) { + // ERR_INVALID_CREDENTIALS: Benutzername/E-Mail oder Passwort falsch + const error = new Error('Invalid credentials'); + error.code = 'ERR_INVALID_CREDENTIALS'; + throw error; + } + + const account = accountResult.rows[0]; + + // Prüfen, ob die Email bereits bestätigt wurde + if (!account.is_email_verified) { + // ERR_EMAIL_NOT_VERIFIED: Bitte bestätige zuerst deine E-Mail-Addresse + const error = new Error('Email not verified'); + error.code = 'ERR_EMAIL_NOT_VERIFIED'; + throw error; + } + + // Passwort vergleichen (argon2) + const isPasswordValid = await argon2.verify(account.password_hash, password); + if (!isPasswordValid) { + const error = new Error('Invalied credentials'); + error.code = 'ERR_INVALID_CREDENTIALS'; + throw error; + } + + // JWT-Tokens geneieren + // Das Access-Token speichert die wichtigsten Infos für schnelle Abfragen + const tokenPayload = { + accountId: account.id, + username: account.username, + isAdmin: account.is_admin + }; + + const accessToken = jwt.sign( + tokenPayload, + process.env.JWT_ACCESS_SECRET, + { expiresIn: '15m' } + ); + + const refreshToken = jwt.sign( + { accountId: account.id }, + process.env.JWT_REFRESH_SECRET, + { expiresIn: '30d' } + ); + + // Refresh-Token in der Datenbank speichern (Vorbereitung für das Single-Session System) + await client.query(` + INSERT INTO refresh_tokens (account_id, token, expires_at) + VALUES ($1, $2, NOW() + INTERVAL '30 days')`, + [account.id, refreshToken] + ); + + // Erfolgreiche Rückgabe der Daten + return { + accessToken, + refreshToken, + user: { + id: account.id, + username: account.username + } + }; + } finally { + client.release(); + } +}; + +/** + * Prüft ein Refresh-Token und generiert ein neues, aktuelles Access-Token + * + * @param {string} refreshToken - Das langlebige Refresh-Token des Nutzers. + * @returns {object} - Enthält das neue accessToken + */ +export const refreshAccessToken = async (refreshToken) => { + const client = await pool.connect(); + + try { + // JWT-Signatur und Ablaufdatum des Refresh-Tokens kryptografisch verifizieren + // Wenn das Token manipuliert oder abgelaufen ist, wirft jwt.verify sofort einen Fehler. + const payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET); + const { accountId } = payload; + + // Sicherheits-Check: Existiert diese Token noch in unserer Datenbank? + // Wenn sich ein zweites Gerät eingeloggt hat, wurden die altenm Tokens gelöscht. + // Dieser Check schlägt dasnn fehl und blockiert den unerlaubten Zugriff! + const tokenResult = await client.query( + 'SELECT id FROM refresh_tokens WHERE account_id = $1 AND token = $2', + [accountId, refreshToken] + ); + + if (tokenResult.rows.length === 0) { + const error = new Error('Refresh token was revoked or does not exist'); + error.code = 'ERR_UNAUTHORIZED'; + throw error; + } + + // Aktuelle Benutzerdaten frosch aus der Datenbank abfragen + // Das behebt das "Stale Data" Problem. Falls der Admin Status geändert wurde, + // steht er im neuen Token sofort korrekt drin, ohne die DB bei jedem API.Call zu belasten + const accountResult = await client.query( + 'SELECT id, username, is_admin FROM lobby_accounts WHERE id = $1', + [accountId] + ); + + if (accountResult.rows.length === 0) { + const error = new Error('Account no longer exists'); + error.code = 'ERR_UNAUTHORIZED'; + throw error; + } + + const account = accountResult.rows[0]; + + // Neues kurzlebiges Access-Token erstellen (wieder für 15 min) + const tokenPayload = { + accountId: account.id, + username: account.username, + isAdmin: account.is_admin + }; + + const accessToken = jwt.sign( + tokenPayload, + process.env.JWT_ACCESS_SECRET, + { expiresIn: '15m' } + ); + + return { accessToken }; + } catch (error) { + // Abfangen von JWT-spezifischen Fehlern (z.B. Token abgelaufen oder Signatur falsch) + if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError' || error.name === 'SyntaxError') { + const customErr = new Error('Invalid, expired or malformed token'); + customErr.code = 'ERR_UNAUTHORIZED'; + throw customErr; + } + + // Alle anderen Fehhler (zB Datenbank Fehler) weiterwerfen + throw error; + } finally { + client.release(); + } +} \ No newline at end of file diff --git a/dev/lobby-backend/src/services/email.service.js b/dev/lobby-backend/src/services/email.service.js new file mode 100644 index 0000000..fc56827 --- /dev/null +++ b/dev/lobby-backend/src/services/email.service.js @@ -0,0 +1,55 @@ +/** + * Sendet eine E-Mail über die Brevo REST API. + * + * @param {string} toEmail - Die Ziel E-Mail Adresse + * @param {string} toName - Der Benutzername des Empfängers + * @param {string} subject - Der Betreff der E-Mail + * @param {string} htmlContent - Der HTML-Inhalt der E-Mail + * @returns {boolean} - true wenn erfolgreich, wirft einen Fehler wenn nicht. + */ +export const sendEmail = async (toEmail, toName, subject, htmlContent) => { + const mailMode = process.env.MAIL_MODE || 'development'; + + // Im Development-Modus loggen wir nur, um das API-Limit nicht zu belasten + if (mailMode === 'development') { + console.log(`\n[DEV-MAIL-SIMULATION] An: ${toEmail}`); + console.log(`[DEV-MAIL-SIMULATION] Betreff: ${subject}`); + console.log(`[DEV-MAIL-SIMULATION] Inhalt:\n${htmlContent}\n`); + return true; + } + + // Echter Versand über Brevo + try { + const response = await fetch('https://api.brevo.com/v3/smtp/email', { + method: 'POST', + headers: { + 'accept': 'application/json', + 'api-key': process.env.BREVO_API_KEY, + 'content-type': 'application/json' + }, + body: JSON.stringify({ + sender: { + name: process.env.BREVO_SENDER_NAME, + email: process.env.BREVO_SENDER_EMAIL + }, + to: [{ + email: toEmail, + name: toName + }], + subject: subject, + htmlContent: htmlContent + }) + }); + + if (!response.ok) { + const errorData = await response.json(); + console.error('Brevo API Fehler-Details:', errorData); + throw new Error('Fehler bei der Kommunikation mit dem E-Mail-Provider.'); + } + + return true; + } catch (error) { + console.error('Fehler im email.service:', error); + throw error; // Den Fehler werfen wir weiter, damit der Aufrufer ihn behandeln kann. + } +} \ No newline at end of file diff --git a/dev/lobby-frontend/Dockerfile.dev b/dev/lobby-frontend/Dockerfile.dev new file mode 100644 index 0000000..7601541 --- /dev/null +++ b/dev/lobby-frontend/Dockerfile.dev @@ -0,0 +1,29 @@ +# ============================================================================== +# Void-Genesis - Lobby Frontend (Dev Environment) +# ============================================================================== + +# Auch für das Frontend nutzen wir die kleine Alpine-Version von Node.js +FROM node:22-alpine + +# Arbeitsverzeichnis für das Frontend +WORKDIR /app + +# Auch hier: Erst die package-Dateien kopieren für optimales Docker-Caching +COPY package*.json ./ + +# Installiere die Frontend-Abhängigkeiten (Vite, PixiJS, etc.) +RUN npm install + +# Vite nutzt im Hintergrund 'Chokidar', um Dateiänderungen zu überwachen (Hot Reload). +# In Docker-Umgebungen klappt das manchmal nicht sofort. Durch USEPOLLING zwingen +# wir Chokidar, aktiv nach Änderungen zu suchen. Das macht das Hot-Reloading robuster. +ENV CHOKIDAR_USEPOLLING=true + +# Wir dokumentieren den Standard-Port von Vite +EXPOSE 5173 + +# Starte den Vite Dev-Server. +# WICHTIG: Das '--host' Flag ist entscheidend! Standardmäßig lauscht Vite nur auf +# localhost (127.0.0.1) im Container. Mit '--host' lauscht Vite auf 0.0.0.0 und +# erlaubt so, dass Anfragen von außerhalb des Containers (z.B. vom Browser) durchkommen. +CMD ["npm", "run", "dev", "--", "--host"] \ No newline at end of file diff --git a/dev/lobby-frontend/index.html b/dev/lobby-frontend/index.html new file mode 100644 index 0000000..fc598ba --- /dev/null +++ b/dev/lobby-frontend/index.html @@ -0,0 +1,64 @@ + + + + + + Void-Genesis | Login + + + + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + +
+ + + + + +
+
+
+ + + +
+
+ + + + + \ No newline at end of file diff --git a/dev/lobby-frontend/package-lock.json b/dev/lobby-frontend/package-lock.json new file mode 100644 index 0000000..5679e0d --- /dev/null +++ b/dev/lobby-frontend/package-lock.json @@ -0,0 +1,2152 @@ +{ + "name": "void-genesis-lobby-frontend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "void-genesis-lobby-frontend", + "version": "1.0.0", + "dependencies": { + "pixi.js": "^8.0.4" + }, + "devDependencies": { + "free-tex-packer-core": "^0.3.5", + "vite": "^5.2.8" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jvitela/mustache-wax": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jvitela/mustache-wax/-/mustache-wax-1.0.4.tgz", + "integrity": "sha512-cGugVYp0WO7w89KQlyyt2rQyCO5MbrKeqtIAkG8oBfOaiHoeGeKG7WYdBFHftvoZUuK4WFbWyn3hgFipdetC+Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "mustache": "^4.2.0" + } + }, + "node_modules/@pixi/colord": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/@pixi/colord/-/colord-2.9.6.tgz", + "integrity": "sha512-nezytU2pw587fQstUu1AsJZDVEynjskwOL+kibwcdxsMBFqPsFFNA7xl0ii/gXuDi6M0xj3mfRJj8pBSc2jCfA==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", + "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", + "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", + "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", + "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", + "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", + "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", + "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", + "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", + "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", + "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", + "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", + "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", + "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", + "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", + "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", + "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", + "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", + "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", + "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", + "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", + "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", + "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", + "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", + "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", + "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/earcut": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-3.0.0.tgz", + "integrity": "sha512-k/9fOUGO39yd2sCjrbAJvGDEQvRwRnQIZlBz43roGwUZo5SHAmyVvSFyaVVZkicRVCaDXPKlbxrUcBuJoSWunQ==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webgpu/types": { + "version": "0.1.70", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.70.tgz", + "integrity": "sha512-LFiNHHKMvmAEvwVew3JLJmTdShhbdwRFSImUshGhE2mGE8ybQzIo63l5uRp+YKnNx+8Qno8Kf6gN+DKMreIJCA==", + "license": "BSD-3-Clause" + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", + "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bignumber.js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.4.0.tgz", + "integrity": "sha512-uw4ra6Cv483Op/ebM0GBKKfxZlSmn6NgFRby5L3yGTlunLj53KQgndDlqy2WVFOwgvurocApYkSud0aO+mvrpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bmp-js": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.3.tgz", + "integrity": "sha512-epsm3Z92j5xwek9p97pVw3KbsNc0F4QnbYh+N93SpbJYuHFQQ/UAh6K+bKFGyLePH3Hudtl/Sa95Quqp0gX8IQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/centra": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/centra/-/centra-2.7.0.tgz", + "integrity": "sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", + "dev": true + }, + "node_modules/earcut": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz", + "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==", + "license": "ISC" + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/exif-parser": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", + "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==", + "dev": true + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/free-tex-packer-core": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/free-tex-packer-core/-/free-tex-packer-core-0.3.5.tgz", + "integrity": "sha512-bRVByTevxdNsMJNBcqn7AcI28Tcy7Ji1KaLgPfYKJVS1WUGefEb6DrO9GzxAN+BTbJAsVD/A0ofdOGSMK7x7hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jvitela/mustache-wax": "^1.0.1", + "jimp": "^0.2.28", + "maxrects-packer": "2.7.3", + "mustache": "^2.3.0", + "tinify": "^1.5.0" + } + }, + "node_modules/free-tex-packer-core/node_modules/mustache": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.2.tgz", + "integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ==", + "dev": true, + "license": "MIT", + "bin": { + "mustache": "bin/mustache" + }, + "engines": { + "npm": ">=1.4.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/gifuct-js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/gifuct-js/-/gifuct-js-2.1.2.tgz", + "integrity": "sha512-rI2asw77u0mGgwhV3qA+OEgYqaDn5UNqgs+Bx0FGwSpuqfYn+Ir6RQY5ENNQ8SbIiG/m5gVa7CD5RriO4f4Lsg==", + "license": "MIT", + "dependencies": { + "js-binary-schema-parser": "^2.0.3" + } + }, + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ip-regex": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-1.0.3.tgz", + "integrity": "sha512-HjpCHTuxbR/6jWJroc/VN+npo5j0T4Vv2TAI5qdEHQx7hsL767MeccGFSsLtF694EiZKTSEqgoeU6DtGFCcuqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ismobilejs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-1.1.1.tgz", + "integrity": "sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw==", + "license": "MIT" + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/jimp": { + "version": "0.2.28", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.2.28.tgz", + "integrity": "sha512-9HT7DA279xkTlry2oG30s6AtOUglNiY2UdyYpj0yNI4/NBv8PmdNC0gcldgMU4HqvbUlrM3+v+6GaHnTkH23JQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bignumber.js": "^2.1.0", + "bmp-js": "0.0.3", + "es6-promise": "^3.0.2", + "exif-parser": "^0.1.9", + "file-type": "^3.1.0", + "jpeg-js": "^0.2.0", + "load-bmfont": "^1.2.3", + "mime": "^1.3.4", + "mkdirp": "0.5.1", + "pixelmatch": "^4.0.0", + "pngjs": "^3.0.0", + "read-chunk": "^1.0.1", + "request": "^2.65.0", + "stream-to-buffer": "^0.1.0", + "tinycolor2": "^1.1.2", + "url-regex": "^3.0.0" + } + }, + "node_modules/jpeg-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.2.0.tgz", + "integrity": "sha512-Ni9PffhJtYtdD7VwxH6V2MnievekGfUefosGCHadog0/jAevRu6HPjYeMHbUemn0IPE8d4wGa8UsOGsX+iKy2g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/js-binary-schema-parser": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/js-binary-schema-parser/-/js-binary-schema-parser-2.0.3.tgz", + "integrity": "sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg==", + "license": "MIT" + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/load-bmfont": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.2.tgz", + "integrity": "sha512-qElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/dmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal": "0.0.1", + "mime": "^1.3.4", + "parse-bmfont-ascii": "^1.0.3", + "parse-bmfont-binary": "^1.0.5", + "parse-bmfont-xml": "^1.1.4", + "phin": "^3.7.1", + "xhr": "^2.0.1", + "xtend": "^4.0.0" + } + }, + "node_modules/maxrects-packer": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/maxrects-packer/-/maxrects-packer-2.7.3.tgz", + "integrity": "sha512-bG6qXujJ1QgttZVIH4WDanhoJtvbud/xP/XPyf6A69C9RdA61BM4TomFALCq2nrTa+tARRIBB4LuIFsnUQU2wA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-document": { + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz", + "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "dom-walk": "^0.1.0" + } + }, + "node_modules/minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-bmfont-xml": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz", + "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.5.0" + } + }, + "node_modules/parse-headers": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.6.tgz", + "integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-svg-path": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz", + "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==", + "license": "MIT" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true, + "license": "MIT" + }, + "node_modules/phin": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/phin/-/phin-3.7.1.tgz", + "integrity": "sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "dependencies": { + "centra": "^2.7.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/pixelmatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", + "integrity": "sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==", + "dev": true, + "license": "ISC", + "dependencies": { + "pngjs": "^3.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, + "node_modules/pixi.js": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-8.18.1.tgz", + "integrity": "sha512-6LUPWYgulZhp/w4kam2XHXB0QedISZIqrJbRdHLLQ3csn5a38uzKxAp6B5j6s89QFYaIJbg95kvgTRcbgpO1ow==", + "license": "MIT", + "workspaces": [ + "examples", + "playground" + ], + "dependencies": { + "@pixi/colord": "^2.9.6", + "@types/earcut": "^3.0.0", + "@webgpu/types": "^0.1.69", + "@xmldom/xmldom": "^0.8.12", + "earcut": "^3.0.2", + "eventemitter3": "^5.0.1", + "gifuct-js": "^2.1.2", + "ismobilejs": "^1.1.1", + "parse-svg-path": "^0.1.2", + "tiny-lru": "^11.4.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/pixijs" + } + }, + "node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/promise-nodeify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/promise-nodeify/-/promise-nodeify-3.0.1.tgz", + "integrity": "sha512-ghsSuzZXJX8iO7WVec2z7GI+Xk/EyiD+JZK7AZKhUqYfpLa/Zs4ylUD+CwwnKlG6G3HnkUPMAi6PO7zeqGKssg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6", + "npm": ">=1.3.7" + } + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.5.tgz", + "integrity": "sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/read-chunk": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-1.0.1.tgz", + "integrity": "sha512-5NLTTdX45dKFtG8CX5pKmvS9V5u9wBE+gkklN7xhDuhq3pA2I4O7ALfKxosCMcLHOhkxj6GNacZhfXtp5nlCdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/rollup": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", + "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", + "fsevents": "~2.3.2" + } + }, + "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==", + "dev": true, + "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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream-to": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stream-to/-/stream-to-0.2.2.tgz", + "integrity": "sha512-Kg1BSDTwgGiVMtTCJNlo7kk/xzL33ZuZveEBRt6rXw+f1WLK/8kmz2NVCT/Qnv0JkV85JOHcLhD82mnXsR3kPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/stream-to-buffer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stream-to-buffer/-/stream-to-buffer-0.1.0.tgz", + "integrity": "sha512-Da4WoKaZyu3nf+bIdIifh7IPkFjARBnBK+pYqn0EUJqksjV9afojjaCCHUemH30Jmu7T2qcKvlZm2ykN38uzaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "stream-to": "~0.2.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/tinify": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/tinify/-/tinify-1.8.2.tgz", + "integrity": "sha512-wkXLOj3/D2MdgWLI7TOXSbeD9xRu3GVxqt89ZWfrOyr8c4Ajd6gdowHY5ak7ASqMUu54+icHO/0BIiBg8pFIvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "https-proxy-agent": "^7.0.6", + "promise-nodeify": ">= 0.1" + }, + "engines": { + "node": ">= 14.0" + } + }, + "node_modules/tiny-lru": { + "version": "11.4.7", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.4.7.tgz", + "integrity": "sha512-w/Te7uMUVeH0CR8vZIjr+XiN41V+30lkDdK+NRIDCUYKKuL9VcmaUEmaPISuwGhLlrTGh5yu18lENtR9axSxYw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "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==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-regex": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/url-regex/-/url-regex-3.2.0.tgz", + "integrity": "sha512-dQ9cJzMou5OKr6ZzfvwJkCq3rC72PNXhqz0v3EIhF4a3Np+ujr100AhUx2cKx5ei3iymoJpJrPB3sVSEMdqAeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-regex": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/dev/lobby-frontend/package.json b/dev/lobby-frontend/package.json new file mode 100644 index 0000000..df64adc --- /dev/null +++ b/dev/lobby-frontend/package.json @@ -0,0 +1,18 @@ +{ + "name": "void-genesis-lobby-frontend", + "version": "1.0.0", + "description": "Frontend für die Master-Account-Lobby", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "pixi.js": "^8.0.4" + }, + "devDependencies": { + "free-tex-packer-core": "^0.3.5", + "vite": "^5.2.8" + } +} diff --git a/dev/lobby-frontend/scripts/generate-spritesheet.js b/dev/lobby-frontend/scripts/generate-spritesheet.js new file mode 100644 index 0000000..b101222 --- /dev/null +++ b/dev/lobby-frontend/scripts/generate-spritesheet.js @@ -0,0 +1,98 @@ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import texturePacker from 'free-tex-packer-core'; + +// ---------------------------------------------------------------------- +// ES-Modul Workaround für __dirname +// Da Vite standardmäßig ES-Module verwendet (type: "module" in package.json), +// müssen wir __dirname manuell nachbauen, um absolute Pfade zu generieren. +// ---------------------------------------------------------------------- +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// ---------------------------------------------------------------------- +// Pfad-Konfiguration +// ---------------------------------------------------------------------- +// Hier definieren wir, woher die rohen Bilder kommen und wohin der +// fertige Textur-Atlas gespeichert werden soll. +const RAW_ASSETS_DIR = path.resolve(__dirname, '../src/raw-assets/ui'); +const OUTPUT_DIR = path.resolve(__dirname, '../src/assets'); +const ATLAS_NAME = 'ui-atlas'; // Der Basisname für unsere Ausgabe (ui-atlas.png / ui-atlas.json) + +// ---------------------------------------------------------------------- +// Hauptfunktion zum Generieren des Sprite-Sheets +// ---------------------------------------------------------------------- +async function buildSpriteSheet() { + console.log(`[Packer] Lese Bilder aus: ${RAW_ASSETS_DIR}`); + + // Wir prüfen zunächst, ob der Quellordner existiert. Falls nicht, + // legen wir ihn an und brechen ab, da noch keine Bilder da sind. + if (!fs.existsSync(RAW_ASSETS_DIR)) { + fs.mkdirSync(RAW_ASSETS_DIR, { recursive: true }); + console.log('[Packer] Quellordner existierte nicht und wurde erstellt. Bitte füge Bilder hinzu und starte neu.'); + return; + } + + // Wir lesen alle Dateien im Quellordner aus. + const files = fs.readdirSync(RAW_ASSETS_DIR); + + // Wir filtern nach Bilddateien (z. B. PNG) und bereiten sie für den Packer vor. + const images = []; + for (const file of files) { + if (file.endsWith('.png')) { + const filePath = path.join(RAW_ASSETS_DIR, file); + // Der Packer benötigt den Dateipfad als "path" und den Dateiinhalt als "contents" + images.push({ + path: file, + contents: fs.readFileSync(filePath) + }); + } + } + + if (images.length === 0) { + console.log('[Packer] Keine PNG-Bilder im Quellordner gefunden. Abbruch.'); + return; + } + + // ---------------------------------------------------------------------- + // Packer-Konfiguration + // ---------------------------------------------------------------------- + const options = { + textureName: ATLAS_NAME, + width: 4096, // Maximale Breite des Atlases + height: 4096, // Maximale Höhe des Atlases + padding: 2, // Pixel-Abstand zwischen den Bildern (verhindert Rand-Bluten) + allowRotation: false, // Für UI-Elemente oft besser deaktiviert, damit sie in PixiJS nicht gedreht geladen werden + exporter: 'Pixi', // Generiert direkt ein Format, das PixiJS nativ lesen kann + removeFileExtension: true // Entfernt das ".png" in den JSON-Keys (leichter in PixiJS aufzurufen) + }; + + console.log(`[Packer] Verpacke ${images.length} Bilder...`); + + // Wir rufen den Packer asynchron auf + texturePacker(images, options, (files, error) => { + if (error) { + console.error('[Packer] Fehler beim Erstellen des Sprite-Sheets:', error); + return; + } + + // Zielordner erstellen, falls er nicht existiert + if (!fs.existsSync(OUTPUT_DIR)) { + fs.mkdirSync(OUTPUT_DIR, { recursive: true }); + } + + // Der Packer gibt ein Array von Dateien zurück (Bild + JSON). + // Wir speichern diese direkt im Zielordner. + for (const item of files) { + const outPath = path.join(OUTPUT_DIR, item.name); + fs.writeFileSync(outPath, item.buffer); + console.log(`[Packer] Erfolgreich gespeichert: ${outPath}`); + } + + console.log('[Packer] Sprite-Sheet erfolgreich erstellt!'); + }); +} + +// Skript ausführen +buildSpriteSheet(); \ No newline at end of file diff --git a/dev/lobby-frontend/src/assets/fonts/Bungee-Regular.ttf b/dev/lobby-frontend/src/assets/fonts/Bungee-Regular.ttf new file mode 100644 index 0000000..e1721ae Binary files /dev/null and b/dev/lobby-frontend/src/assets/fonts/Bungee-Regular.ttf differ diff --git a/dev/lobby-frontend/src/assets/fonts/Exo2.0-Regular.otf b/dev/lobby-frontend/src/assets/fonts/Exo2.0-Regular.otf new file mode 100644 index 0000000..4bd82f9 Binary files /dev/null and b/dev/lobby-frontend/src/assets/fonts/Exo2.0-Regular.otf differ diff --git a/dev/lobby-frontend/src/assets/fonts/Exo2.0-SemiBold.otf b/dev/lobby-frontend/src/assets/fonts/Exo2.0-SemiBold.otf new file mode 100644 index 0000000..9df61a5 Binary files /dev/null and b/dev/lobby-frontend/src/assets/fonts/Exo2.0-SemiBold.otf differ diff --git a/dev/lobby-frontend/src/assets/fonts/Gugi-Regular.ttf b/dev/lobby-frontend/src/assets/fonts/Gugi-Regular.ttf new file mode 100644 index 0000000..b500dbf Binary files /dev/null and b/dev/lobby-frontend/src/assets/fonts/Gugi-Regular.ttf differ diff --git a/dev/lobby-frontend/src/assets/fonts/Quantico-Bold.ttf b/dev/lobby-frontend/src/assets/fonts/Quantico-Bold.ttf new file mode 100644 index 0000000..4157958 Binary files /dev/null and b/dev/lobby-frontend/src/assets/fonts/Quantico-Bold.ttf differ diff --git a/dev/lobby-frontend/src/assets/fonts/WDXLLubrifontSC-Regular.ttf b/dev/lobby-frontend/src/assets/fonts/WDXLLubrifontSC-Regular.ttf new file mode 100644 index 0000000..ab7340b Binary files /dev/null and b/dev/lobby-frontend/src/assets/fonts/WDXLLubrifontSC-Regular.ttf differ diff --git a/dev/lobby-frontend/src/assets/fonts/ZenDots-Regular.ttf b/dev/lobby-frontend/src/assets/fonts/ZenDots-Regular.ttf new file mode 100644 index 0000000..aa4654d Binary files /dev/null and b/dev/lobby-frontend/src/assets/fonts/ZenDots-Regular.ttf differ diff --git a/dev/lobby-frontend/src/assets/ui-atlas.json b/dev/lobby-frontend/src/assets/ui-atlas.json new file mode 100644 index 0000000..f847c72 --- /dev/null +++ b/dev/lobby-frontend/src/assets/ui-atlas.json @@ -0,0 +1,423 @@ +{ + "frames": { + "SPR_SciFiMenus_Frame_Box_Large_02": { + "frame": { + "x": 2, + "y": 2, + "w": 997, + "h": 984 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 14, + "y": 21, + "w": 997, + "h": 984 + }, + "sourceSize": { + "w": 1024, + "h": 1024 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "SPR_SciFiMenus_Frame_Box_Large_12": { + "frame": { + "x": 1003, + "y": 2, + "w": 984, + "h": 481 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 20, + "y": 17, + "w": 984, + "h": 481 + }, + "sourceSize": { + "w": 1024, + "h": 512 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "SPR_SciFiMenus_Frame_Box_Large_12_Background": { + "frame": { + "x": 1991, + "y": 2, + "w": 898, + "h": 393 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 64, + "y": 73, + "w": 898, + "h": 393 + }, + "sourceSize": { + "w": 1024, + "h": 512 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "SPR_SciFiMenus_Ring_Medium_13": { + "frame": { + "x": 2893, + "y": 2, + "w": 826, + "h": 853 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 97, + "y": 85, + "w": 826, + "h": 853 + }, + "sourceSize": { + "w": 1024, + "h": 1024 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "SPR_SciFiMenus_Frame_Box_Large_02_Background": { + "frame": { + "x": 1991, + "y": 399, + "w": 825, + "h": 895 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 98, + "y": 63, + "w": 825, + "h": 895 + }, + "sourceSize": { + "w": 1024, + "h": 1024 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "SPR_SciFiMenus_Background_01": { + "frame": { + "x": 3723, + "y": 2, + "w": 256, + "h": 256 + }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 256, + "h": 256 + }, + "sourceSize": { + "w": 256, + "h": 256 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "SPR_SciFiMenus_Menu_Item_Selected_20": { + "frame": { + "x": 3723, + "y": 262, + "w": 256, + "h": 256 + }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 256, + "h": 256 + }, + "sourceSize": { + "w": 256, + "h": 256 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "SPR_SciFiMenus_Menu_Item_Selected_10": { + "frame": { + "x": 3723, + "y": 522, + "w": 228, + "h": 234 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 14, + "y": 10, + "w": 228, + "h": 234 + }, + "sourceSize": { + "w": 256, + "h": 256 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "ICON_SciFiMenus_Menu_Lock_Closed_01_Clean": { + "frame": { + "x": 3955, + "y": 522, + "w": 132, + "h": 164 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 62, + "y": 45, + "w": 132, + "h": 164 + }, + "sourceSize": { + "w": 256, + "h": 256 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "ICON_SciFi_Map_Message": { + "frame": { + "x": 3955, + "y": 690, + "w": 130, + "h": 88 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 64, + "y": 84, + "w": 130, + "h": 88 + }, + "sourceSize": { + "w": 256, + "h": 256 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "SPR_SciFiMenus_Menu_Item_Selected_19": { + "frame": { + "x": 3723, + "y": 760, + "w": 228, + "h": 234 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 14, + "y": 10, + "w": 228, + "h": 234 + }, + "sourceSize": { + "w": 256, + "h": 256 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "ICON_SciFiMenus_Menu_Solo_01_Clean": { + "frame": { + "x": 3955, + "y": 782, + "w": 125, + "h": 194 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 65, + "y": 31, + "w": 125, + "h": 194 + }, + "sourceSize": { + "w": 256, + "h": 256 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "SPR_SciFiMenus_Background_Hexagons_01": { + "frame": { + "x": 2820, + "y": 859, + "w": 512, + "h": 512 + }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 512, + "h": 512 + }, + "sourceSize": { + "w": 512, + "h": 512 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "ICON_Social_Discord": { + "frame": { + "x": 3336, + "y": 859, + "w": 226, + "h": 175 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 14, + "y": 41, + "w": 226, + "h": 175 + }, + "sourceSize": { + "w": 256, + "h": 256 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "SPR_SciFiMenus_Menu_Item_Background_07": { + "frame": { + "x": 3566, + "y": 998, + "w": 224, + "h": 230 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 16, + "y": 12, + "w": 224, + "h": 230 + }, + "sourceSize": { + "w": 256, + "h": 256 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "ICON_SciFiMenus_Menu_Warning_02_Clean": { + "frame": { + "x": 3336, + "y": 1038, + "w": 222, + "h": 193 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 17, + "y": 31, + "w": 222, + "h": 193 + }, + "sourceSize": { + "w": 256, + "h": 256 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + }, + "ICON_SciFiMenus_Menu_Tick_01_Clean": { + "frame": { + "x": 3794, + "y": 998, + "w": 191, + "h": 144 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 32, + "y": 56, + "w": 191, + "h": 144 + }, + "sourceSize": { + "w": 256, + "h": 256 + }, + "pivot": { + "x": 0.5, + "y": 0.5 + } + } + }, + "meta": { + "app": "http://github.com/odrick/free-tex-packer-core", + "version": "0.3.5", + "image": "ui-atlas.png", + "format": "RGBA8888", + "size": { + "w": 4089, + "h": 1373 + }, + "scale": 1 + } +} \ No newline at end of file diff --git a/dev/lobby-frontend/src/assets/ui-atlas.png b/dev/lobby-frontend/src/assets/ui-atlas.png new file mode 100644 index 0000000..43685ab Binary files /dev/null and b/dev/lobby-frontend/src/assets/ui-atlas.png differ diff --git a/dev/lobby-frontend/src/main.js b/dev/lobby-frontend/src/main.js new file mode 100644 index 0000000..fb8a04d --- /dev/null +++ b/dev/lobby-frontend/src/main.js @@ -0,0 +1,400 @@ +import { Application, Assets, Sprite, NineSliceSprite, Container, TilingSprite, Text, wordWrap } from 'pixi.js'; + +async function initGame() { + const app = new Application(); + + await app.init({ + resizeTo: window, + backgroundAlpha: 0, + antialias: true, + resolution: window.devicePixelRatio || 1, + autoDensity: true, + }); + + document.getElementById('pixi-container').appendChild(app.canvas); + + try { + const assetsToLoad = { + //background: 'src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpookyComputer_01.png', + background: 'src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpaceStation_01.png', + + uiAtlas: 'src/assets/ui-atlas.json' + }; + + const backgroundTexture = await Assets.load(assetsToLoad.background); + const uiSpritesheet = await Assets.load(assetsToLoad.uiAtlas); + + await document.fonts.load('16px "UI Font"'); + await document.fonts.load('16px "Logo Font"'); + + buildScene(app, backgroundTexture, uiSpritesheet); + + } catch (error) { + console.error("Fehler beim Laden der Assets:", error); + } +} + +function buildScene(app, bgTexture, sheet) { + // --- Farb-Variablen (pixiJS) --- + const COLOR_PANEL_BG = 0x0b192c; // Dunkelblau für Panel & Inputs + const COLOR_PATTERN = 0x00f0ff; // Cyan für das Hexagon-Muster + const COLOR_TAB_ACTIVE = 0x00f0ff; // Cyan für aktiven Tab + const COLOR_TAB_INACTIVE = 0xaaaaaa; // Grau für inaktiven Tab + const COLOR_TAB_HOVER = 0xffffff; // Weiß beim Hovern der Tabs + const COLOR_BTN_DEFAULT = 0x00aa00; // Dunkelgrün für Registrieren Button + const COLOR_BTN_HOVER = 0x00ff00; // Hellgrün beim Hovern des Buttons + const COLOR_LOGO = 0x00f0ff; // Farbe des Logos + const COLOR_DISCORD_TEXT = 0xdaffde; // Hellgrün für Discord Text + const COLOR_DISCORD_TEXT_HOVER = 0xffffff; // Weiß beim Hovern des Discord Links + + // --- Asset-Namen --- + const frameInput = 'SPR_SciFiMenus_Menu_Item_Selected_19'; // Rahmen für Eingabefelder + const frameBtn = 'SPR_SciFiMenus_Menu_Item_Selected_10'; // Rahmen für Tabs/Buttons + const menuBgName = 'SPR_SciFiMenus_Menu_Item_Background_07'; // Füll Asset für die Rahmen + const panelBgName = 'SPR_SciFiMenus_Frame_Box_Large_02_Background'; // Füll Asset für das große Panel + const patternBgName = 'SPR_SciFiMenus_Background_Hexagons_01'; // Kachelbares Pattern + const panelFmName = 'SPR_SciFiMenus_Frame_Box_Large_02'; // Rahmen für das große Panel + + // --- Icons --- + const iconUserName = 'ICON_SciFiMenus_Menu_Solo_01_Clean'; + const iconEmailName = 'ICON_SciFi_Map_Message'; + const iconLockName = 'ICON_SciFiMenus_Menu_Lock_Closed_01_Clean'; + + // --- Logo Ring --- + const logoRingName = 'SPR_SciFiMenus_Ring_Medium_13'; + + // --- Discord Panel --- + const discordFrameName = 'SPR_SciFiMenus_Frame_Box_Large_12'; + const discordBgName = 'SPR_SciFiMenus_Frame_Box_Large_12_Background'; + const discordIconName = 'ICON_Social_Discord'; + + // --- Hintergrund --- + const background = new Sprite(bgTexture); + app.stage.addChild(background); + + // --- Haupt-UI-Container --- + const uiContainer = new Container(); + app.stage.addChild(uiContainer); + + const panelBackground = new Sprite(sheet.textures[panelBgName]); + panelBackground.tint = COLOR_PANEL_BG; + panelBackground.width = 500; + panelBackground.height = 520; + panelBackground.position.set(0, -10); + uiContainer.addChild(panelBackground); + + // --- Footer Hintergund in PixiJS kacheln + const footerBackgroundContainer = new Container(); + + const footerBackground = new TilingSprite({ + texture: sheet.textures['SPR_SciFiMenus_Background_01'], + height: 40, + }); + footerBackground.tileScale.set(1.0); + footerBackground.tint = 0x000000; + footerBackground.alpha = 1.0; + footerBackgroundContainer.addChild(footerBackground); + app.stage.addChild(footerBackgroundContainer); + + const footerPattern = new TilingSprite({ + texture: sheet.textures[patternBgName], + height: 40, + }); + footerPattern.tileScale.set(0.3); + footerPattern.tint = COLOR_PATTERN; + footerPattern.alpha = 0.01; + footerBackgroundContainer.addChild(footerPattern); + app.stage.addChild(footerBackgroundContainer); + + // --- Hexagon pattern + const pattern = new TilingSprite({ + texture: sheet.textures[patternBgName], + width: 500, + height: 520, + }); + pattern.tileScale.set(0.3); + pattern.position.set(0, -10); + pattern.alpha = 0.05; + pattern.tint = COLOR_PATTERN; + uiContainer.addChild(pattern); + + // --- Main UI + const panelFrame = new NineSliceSprite({ + texture: sheet.textures[panelFmName], + leftWidth: 510, + topHeight: 510, + rightWidth: 510, + bottomHeight: 510 + }); + panelFrame.width = 500; + panelFrame.height = 500; + uiContainer.addChild(panelFrame); + + // --- Logo --- + const logoContainer = new Container(); + logoContainer.position.set(250, -120); + + const logoRing = new Sprite(sheet.textures[logoRingName]); + logoRing.anchor.set(0.5); + logoRing.tint = COLOR_LOGO; + logoRing.alpha = 1.0; + logoRing.scale.set(0.2); + logoContainer.addChild(logoRing); + + const logoText = new Text({ + text: 'Void-Genesis', + style: { + fontFamily: 'Logo Font', + fontSize: 50, + fill: COLOR_LOGO, + letterSpacing: 8, + } + }); + logoText.anchor.set(0.5); + logoContainer.addChild(logoText); + + uiContainer.addChild(logoContainer); + + // --- UI Elemente --- + const tabLogin = createUIItem(65, 60, 180, 40, frameBtn, null, COLOR_TAB_INACTIVE, 0.6); + const tabRegister = createUIItem(255, 60, 180, 40, frameBtn, null, COLOR_TAB_ACTIVE, 0.4); + + const inputUser = createUIItem(115, 135, 300, 50, frameInput, iconUserName, COLOR_PANEL_BG, 0.8); + const inputEmail = createUIItem(115, 200, 300, 50, frameInput, iconEmailName, COLOR_PANEL_BG, 0.8); + const inputLock = createUIItem(115, 265, 300, 50, frameInput, iconLockName, COLOR_PANEL_BG, 0.8); + + const btnRegister = createUIItem(65, 370, 370, 55, frameBtn, null, COLOR_BTN_DEFAULT, 0.6); + + // --- Discord Panel --- + const discordContainer = new Container(); + discordContainer.position.set(640, 380); + + const discordBackground = new NineSliceSprite({ + texture: sheet.textures[discordBgName], + leftWidth: 0, + topHeight: 0, + rightWidth: 0, + bottomHeight: 0 + }); + discordBackground.tint = COLOR_PANEL_BG; + discordBackground.width = 255; + discordBackground.height = 75; + discordContainer.addChild(discordBackground); + + const discordPattern = new TilingSprite({ + texture: sheet.textures[patternBgName], + width: 255, + height: 75, + }); + discordPattern.tileScale.set(0.3); + discordPattern.tint = COLOR_PATTERN; + discordPattern.alpha = 0.05; + discordContainer.addChild(discordPattern); + + const discordFrame = new NineSliceSprite({ + texture: sheet.textures[discordFrameName], + leftWidth: 0, + topHeight: 0, + rightWidth: 0, + bottomHeight: 0 + }); + discordFrame.width = 280; + discordFrame.height = 85; + discordFrame.position.set(-15, -10); + discordContainer.addChild(discordFrame); + + const discordIcon = new Sprite(sheet.textures[discordIconName]); + discordIcon.position.set(10, 15); + discordIcon.scale.set(0.15); + discordIcon.tint = COLOR_TAB_INACTIVE; + discordContainer.addChild(discordIcon); + + const discordText = new Text({ + text: 'Offizieller Discord Server', + style: { + fontFamily: 'UI Font', + fontSize: 14, + fill: COLOR_TAB_INACTIVE, + wordWrap: false, + wordWrapWidth: 120 + } + }); + discordText.position.set(60, 25); + discordContainer.addChild(discordText); + + uiContainer.addChild(discordContainer); + + const htmlTabLogin = document.getElementById('tab-login'); + const htmlTabRegistert = document.getElementById('tab-register'); + const htmlButton = document.getElementById('btn-register'); + let isLoginMode = false; + + htmlTabLogin.addEventListener('mouseenter', () => { + if (!isLoginMode) { + tabLogin.fill.tint = COLOR_TAB_HOVER; + tabLogin.fill.alpha = 0.8; + } + }); + + htmlTabLogin.addEventListener('mouseleave', () => { + if (!isLoginMode) { + tabLogin.fill.tint = COLOR_TAB_INACTIVE; + tabLogin.fill.alpha = 0.4; + } + }); + + htmlTabRegistert.addEventListener('mouseenter', () => { + if (isLoginMode) { + tabRegister.fill.tint = COLOR_TAB_HOVER; + tabRegister.fill.alpha = 0.8; + } + }); + + htmlTabRegistert.addEventListener('mouseleave', () => { + if (isLoginMode) { + tabRegister.fill.tint = COLOR_TAB_INACTIVE; + tabRegister.fill.alpha = 0.4; + } + }); + + htmlButton.addEventListener('mouseenter', () => { + btnRegister.fill.tint = COLOR_BTN_HOVER; + btnRegister.fill.alpha = 1.0; + }); + + htmlButton.addEventListener('mouseleave', () => { + btnRegister.fill.tint = COLOR_BTN_DEFAULT; + btnRegister.fill.alpha = 0.6; + }); + + const htmlDiscord = document.getElementById('discord-link'); + if (htmlDiscord) { + htmlDiscord.addEventListener('mouseenter', () => { + discordIcon.tint = COLOR_TAB_HOVER; + discordText.style.fill = COLOR_TAB_HOVER; + }); + htmlDiscord.addEventListener('mouseleave', () => { + discordIcon.tint = COLOR_TAB_INACTIVE; + discordText.style.fill = COLOR_TAB_INACTIVE; + }); + } + + function setLoginMode() { + if (isLoginMode) return; + isLoginMode = true; + + tabLogin.fill.tint = COLOR_TAB_ACTIVE; + tabLogin.fill.alpha = 0.6; + tabRegister.fill.tint = COLOR_TAB_INACTIVE; + tabRegister.fill.alpha = 0.4; + htmlTabLogin.classList.add('active'); + htmlTabRegistert.classList.remove('active'); + + inputEmail.container.visible = false; + document.getElementById('group-email').style.display = 'none'; + + inputLock.container.y = 200; + document.getElementById('password').style.top = '200px'; + + document.getElementById('group-remember').style.display = 'flex'; + + htmlButton.textContent = 'LOGIN'; + document.getElementById('username').placeholder = 'Username / E-Mail'; + + document.getElementById('msg-box').textContent = ''; + } + + function setRegisterMode() { + if (!isLoginMode) return; + isLoginMode = false; + + tabRegister.fill.tint = COLOR_TAB_ACTIVE; + tabRegister.fill.alpha = 0.6; + tabLogin.fill.tint = COLOR_TAB_INACTIVE; + tabLogin.fill.alpha = 0.4; + htmlTabRegistert.classList.add('active'); + htmlTabLogin.classList.remove('active'); + + inputEmail.container.visible = true; + document.getElementById('group-email').style.display = 'block'; + + inputLock.container.y = 265; + document.getElementById('password').style.top = '265px'; + + document.getElementById('group-remember').style.display = 'none'; + + htmlButton.textContent = 'REGISTRIEREN'; + document.getElementById('username').placeholder = 'Enter Username'; + + document.getElementById('msg-box').textContent = ''; + } + + function createUIItem(x, y, width, height, frameTextureName, iconTextureName, fillTint, fillAlpha) { + const itemContainer = new Container(); + itemContainer.position.set(x, y); + + const fill = new NineSliceSprite({ + texture: sheet.textures[menuBgName], + leftWidth: 120, + topHeight: 120, + rightWidth: 120, + bottomHeight: 120 + }); + + fill.width = width; + fill.height = height; + fill.tint = fillTint; + fill.alpha = fillAlpha; + itemContainer.addChild(fill); + + const frame = new NineSliceSprite({ + texture: sheet.textures[frameTextureName], + leftWidth: 100, + topHeight: 120, + rightWidth: 140, + bottomHeight: 120 + }); + + frame.width = width; + frame.height = height; + frame.alpha = 0.8; + itemContainer.addChild(frame); + + if (iconTextureName) { + const icon = new Sprite(sheet.textures[iconTextureName]); + icon.position.set(-45, (height - (icon.height * 0.15)) / 2 - 5); + icon.scale.set(0.15); + itemContainer.addChild(icon); + } + + uiContainer.addChild(itemContainer); + + return { container: itemContainer, fill, frame }; + } + + function resize() { + const screenWidth = app.screen.width; + const screenHeight = app.screen.height; + + const scaleX = screenWidth / background.texture.width; + const scaleY = screenHeight / background.texture.height; + const scale = Math.max(scaleX, scaleY); + background.scale.set(scale); + background.x = (screenWidth - background.width) / 2; + background.y = (screenHeight - background.height) / 2; + + uiContainer.x = (screenWidth - panelFrame.width) / 2; + uiContainer.y = (screenHeight - panelFrame.height) / 2; + + footerBackground.width = screenWidth; + footerPattern.width = screenWidth; + footerBackgroundContainer.y = screenHeight - 40; + } + + htmlTabLogin.addEventListener('click', setLoginMode); + htmlTabRegistert.addEventListener('click', setRegisterMode); + app.renderer.on('resize', resize); + resize(); +} + +initGame(); \ No newline at end of file diff --git a/dev/lobby-frontend/src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpaceStation_01.png b/dev/lobby-frontend/src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpaceStation_01.png new file mode 100644 index 0000000..be33e46 Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpaceStation_01.png differ diff --git a/dev/lobby-frontend/src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpookyComputer_01.png b/dev/lobby-frontend/src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpookyComputer_01.png new file mode 100644 index 0000000..514caa6 Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpookyComputer_01.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Lock_Closed_01_Clean.png b/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Lock_Closed_01_Clean.png new file mode 100644 index 0000000..8d2fabe Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Lock_Closed_01_Clean.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Solo_01_Clean.png b/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Solo_01_Clean.png new file mode 100644 index 0000000..05899a9 Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Solo_01_Clean.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Tick_01_Clean.png b/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Tick_01_Clean.png new file mode 100644 index 0000000..d37a03f Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Tick_01_Clean.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Warning_02_Clean.png b/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Warning_02_Clean.png new file mode 100644 index 0000000..0087b6d Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Warning_02_Clean.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFi_Map_Message.png b/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFi_Map_Message.png new file mode 100644 index 0000000..bfbab02 Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/ICON_SciFi_Map_Message.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/ICON_Social_Discord.png b/dev/lobby-frontend/src/raw-assets/ui/ICON_Social_Discord.png new file mode 100644 index 0000000..546bfec Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/ICON_Social_Discord.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Background_01.png b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Background_01.png new file mode 100644 index 0000000..89d0139 Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Background_01.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Background_Hexagons_01.png b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Background_Hexagons_01.png new file mode 100644 index 0000000..ab03f8c Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Background_Hexagons_01.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_02.png b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_02.png new file mode 100644 index 0000000..ef9c298 Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_02.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_02_Background.png b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_02_Background.png new file mode 100644 index 0000000..a5e4ed1 Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_02_Background.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_12.png b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_12.png new file mode 100644 index 0000000..ce8c5b1 Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_12.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_12_Background.png b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_12_Background.png new file mode 100644 index 0000000..1cf76b0 Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_12_Background.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Background_07.png b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Background_07.png new file mode 100644 index 0000000..065ba2f Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Background_07.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_10.png b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_10.png new file mode 100644 index 0000000..37d4e66 Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_10.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_19.png b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_19.png new file mode 100644 index 0000000..1b84ba9 Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_19.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_20.png b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_20.png new file mode 100644 index 0000000..016000a Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_20.png differ diff --git a/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Ring_Medium_13.png b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Ring_Medium_13.png new file mode 100644 index 0000000..87f929a Binary files /dev/null and b/dev/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Ring_Medium_13.png differ diff --git a/dev/lobby-frontend/style.css b/dev/lobby-frontend/style.css new file mode 100644 index 0000000..a451b36 --- /dev/null +++ b/dev/lobby-frontend/style.css @@ -0,0 +1,299 @@ +/* --- LOKALE SCHRIFTART LADEN --- */ +@font-face { + font-family: 'UI Font'; + /* .otf Dateien bekommen das Format 'opentype', .ttf Dateien 'truetype' */ + src: url('src/assets/fonts/Gugi-Regular.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +/* 2. Die exklusive Schrift für das Void-Genesis Logo */ +@font-face { + font-family: 'Logo Font'; + src: url('src/assets/fonts/Quantico-Bold.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +/* --- FARB- & FONT-VARIABLEN (CSS) --- */ +:root { + /* ... deine bisherigen Farb-Variablen ... */ + --color-bg-page: #000000; + --color-text-main: #ffffff; + --color-text-placeholder: rgba(255, 255, 255, 0.4); + --color-text-inactive: rgba(255, 255, 255, 0.5); + + --color-cyan-bright: #00f0ff; + --color-cyan-dark: #00a0aa; + --color-green-bright: #00ff00; + --color-error: #ff4444; + + --color-checkbox-bg: rgba(11, 25, 44, 0.8); + + /* NEU: Wir definieren die Schriftart zentral */ + --font-main: 'UI Font', sans-serif; +} + +/* Reset von Standard-Abständen */ +body, html { + margin: 0; + padding: 0; + width: 100%; + height: 100%; + overflow: hidden; + background-color: var(--color-bg-page); + + /* Wir nutzen nun unsere lokale Variable */ + font-family: var(--font-main); +} + +#game-wrapper { + position: relative; + width: 100vw; + height: 100vh; +} + +#pixi-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 0; +} + +#ui-layer { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 10; + pointer-events: none; + display: flex; + justify-content: center; + align-items: center; +} + +#register-form { + pointer-events: auto; + width: 500px; + height: 500px; + position: relative; +} + +.input-group label { + display: none; +} + +.input-group input { + position: absolute; + width: 300px; + height: 50px; + left: 115px; + + box-sizing: border-box; + padding: 10px 15px; + background: transparent; + border: none; + outline: none; + color: var(--color-text-main); + font-size: 16px; + font-family: inherit; + transition: text-shadow 0.2s ease; +} + +#username { + top: 135px; +} + +#email { + top: 200px; +} + +#password { + top: 265px; +} + +.input-group input::placeholder { + color: var(--color-text-placeholder); +} + +.input-group input:focus { + text-shadow: 0 0 8px var(--color-cyan-dark); + background: transparent; + box-shadow: none; +} + +#btn-register { + position: absolute; + width: 370px; + height: 55px; + left: 65px; + top: 370px; + + background: transparent; + border: none; + color: var(--color-text-main); + font-size: 18px; + font-weight: bold; + font-family: inherit; + text-transform: uppercase; + letter-spacing: 2px; + cursor: pointer; + transition: all 0.2s ease; +} + +#btn-register:hover { + background: transparent; + color: var(--color-text-main); + text-shadow: 0 0 15px var(--color-text-main), 0 0 30px var(--color-green-bright); +} + +.tab-btn { + position: absolute; + height: 40px; + width: 180px; + top: 60px; + + background: transparent; + border: none; + color: var(--color-text-inactive); + font-size: 14px; + font-family: inherit; + text-transform: uppercase; + cursor: pointer; + outline: none; + transition: color 0.2s ease; +} + +#tab-login { + left: 65px; +} + +#tab-register { + left: 255px; +} + +.tab-btn.active { + color: var(--color-text-main); + text-shadow: 0 0 8px var(--color-cyan-bright); +} + +.tab-btn:hover:not(.active) { + color: var(--color-text-main); +} + +.checkbox-group { + position: absolute; + left: 115px; + top: 275px; + display: flex; + align-items: center; + gap: 10px; +} + +.checkbox-group label { + color: var(--color-text-main); + font-size: 14px; + cursor: pointer; +} + +.checkbox-group input[type="checkbox"] { + appearance: none; + width: 18px; + height: 18px; + border: 1px solid var(--color-cyan-dark); + background: var(--color-checkbox-bg); + cursor: pointer; + position: relative; +} + +.checkbox-group input[type="checkbox"]:checked::after { + content: '✔'; + position: absolute; + color: var(--color-cyan-bright); + font-size: 14px; + top: -2px; + left: 2px; +} + +#msg-box { + position: absolute; + top: 330px; + left: 65px; + width: 370px; + height: 30px; + color: var(--color-error); + font-size: 14px; + text-align: center; + font-weight: bold; + display: flex; + align-items: center; + justify-content: center; +} + +/* --- NEU: DISCORD LINK --- */ +#discord-link { + position: absolute; + + /* WICHTIG: Trage hier exakt die gleichen Werte ein, + die du in der main.js beim 'discordContainer.position.set(X, Y)' hast! */ + left: 640px; /* Positiver Wert, da es jetzt rechts vom 500px Panel liegt */ + top: 380px; /* Y-Koordinate deines Panels */ + + /* Die Größe deines PixiJS Discord-Panels */ + width: 280px; + height: 85px; + + /* Komplett unsichtbar machen, wir wollen nur das Pixi-Bild darunter sehen */ + background: transparent; + text-decoration: none; + + /* Sorgt dafür, dass die Maus zur Klick-Hand wird */ + cursor: pointer; + /* Aktiviert die Klickbarkeit für diese Schicht */ + pointer-events: auto; +} + +/* --- NEU: FOOTER STYLING --- */ +#game-footer { + position: absolute; bottom: 0; left: 0; width: 100%; + /* Höhe des Footers (wird auch im Pixi Kachel-Sprite genutzt) */ + height: 40px; + + /* Z-Index hochsetzen, damit der Text über dem Pixi-Hintergrund liegt */ + z-index: 20; + /* pointer-events: none deaktivieren, damit wir den Impressum-Link anklicken können! */ + pointer-events: none; + + display: flex; justify-content: space-between; align-items: center; + padding: 0 40px; box-sizing: border-box; + + color: var(--color-text-inactive); + font-size: 12px; + /* Wir nutzen nicht mehr 'Courier New', sondern unsere UI-Schrift */ + font-family: inherit; + + /* GRAFISCHE ABSETZUNG: Wir fügen eine leuchtende Cyan-Neon-Linie oben hinzu. */ + border-top: 1px solid var(--color-text-inactive); + /* Und nutzen den Pixi Kachel-Hintergrund, um den Sci-Fi Look zu übernehmen. */ + background: transparent; +} + +/* Flexbox-Verteilung für die drei Footer-Bereiche */ +.footer-left { flex: 1; text-align: left; } +.footer-center { flex: 1; text-align: center; } +.footer-right { flex: 1; text-align: right; } + +#game-footer a { + color: var(--color-text-inactive); text-decoration: none; + transition: color 0.2s ease, text-shadow 0.2s ease; + /* Wir reaktivieren Klicks für den Link */ + pointer-events: auto; +} + +#game-footer a:hover { + color: var(--color-cyan-bright); + text-shadow: 0 0 8px var(--color-cyan-bright); +} \ No newline at end of file diff --git a/dev/lobby-frontend/vite.config.js b/dev/lobby-frontend/vite.config.js new file mode 100644 index 0000000..c057712 --- /dev/null +++ b/dev/lobby-frontend/vite.config.js @@ -0,0 +1,44 @@ +// ============================================================================== +// Void-Genesis - Vite Konfiguration +// ============================================================================== + +import { defineConfig } from 'vite'; + +export default defineConfig({ + server: { + // Erlaubt Vite, Verbindungen über diese spezifische Domain anzunehmen. + // Verhindert den "Blocked request" Fehler hinter dem Nginx Proxy Manager. + allowedHosts: [ + 'dev.void-genesis.de' + ], + // 'host: true' stellt sicher, dass Vite auf 0.0.0.0 lauscht + // (haben wir zwar im Dockerfile schon als Flag, ist hier aber sicherer) + host: true, + }, + build: { + // ---------------------------------------------------------------------- + // EULA-Konformität: Base64-Zwang für alle Assets + // ---------------------------------------------------------------------- + // Wir setzen das Limit für das Inlining von Assets auf einen extrem hohen + // Wert (hier ca. 100 MB). Standardmäßig liegt dieser Wert bei 4096 (4 KB). + // Alles, was größer ist, wird von Vite normalerweise als separate Datei + // (z. B. .png oder .jpg) im Ausgabeordner gespeichert. + // + // Durch diesen hohen Wert zwingen wir Vite dazu, sämtliche importierten + // Assets zwingend als Base64-Strings direkt in die generierten JavaScript- + // oder CSS-Dateien einzubetten. Dadurch liegen nach dem Build absolut + // keine rohen, extrahierbaren Bilddateien mehr auf unserem Server. + // ---------------------------------------------------------------------- + assetsInlineLimit: 100000000, + + // Wir deaktivieren das Generieren von Source Maps für den produktiven Build. + // Das ist wichtig, da Source Maps extrem groß werden können, wenn sie + // riesige Base64-Strings enthalten. Außerdem erschwert es das Auslesen + // unserer Struktur von außen. + sourcemap: false, + + // Optional: Falls wir den Output noch weiter minimieren wollen, + // können wir hier auch die CSS-Code-Splitting-Optionen anpassen, + // aber für den Base64-Zwang reicht das assetsInlineLimit völlig aus. + } +}); \ No newline at end of file